242 lines
5.7 KiB
TypeScript
242 lines
5.7 KiB
TypeScript
import { getCardColors, FlexLayout, clampValue } from "../common/utils"
|
|
import Card from '../common/Card'
|
|
import { data } from "../fetcher";
|
|
import React from 'react'
|
|
import themes from "../../themes";
|
|
|
|
export interface parsedQuery {
|
|
hide?: Array<string>
|
|
hide_title?: boolean
|
|
hide_border?: boolean
|
|
card_width?: number
|
|
title_color?: string
|
|
text_color?: string
|
|
bg_color?: string
|
|
language_count?: number
|
|
show_level?: string
|
|
theme?: keyof typeof themes
|
|
cache_seconds?: string
|
|
layout?: string
|
|
}
|
|
|
|
const createProgressNode = ({ width, color, name, progress, progress2 }: {width: number ,color: string, name: string, progress: number, progress2:number}) => {
|
|
const paddingRight = 60;
|
|
const progressWidth = width - paddingRight;
|
|
const progressPercentage = clampValue(progress, 2, 100);
|
|
const progress2Percentage = clampValue(progress2, 2, 100);
|
|
|
|
return (
|
|
<>
|
|
<text data-testid="lang-name" x="2" y="15" className="lang-name">{name} {progress}%{progress2 > progress ? ` + ${progress2 - progress}%` : ''}</text>
|
|
<svg width={progressWidth}>
|
|
<rect rx="5" ry="5" x="0" y="25" width={progressWidth} height="8" fill="#ddd" />
|
|
<rect
|
|
height="8"
|
|
fill="#f2b866"
|
|
rx="5" ry="5" x="1" y="25"
|
|
width={`calc(${progress2Percentage}% - 1px)`}
|
|
/>
|
|
<rect
|
|
height="8"
|
|
fill={color}
|
|
rx="5" ry="5" x="0" y="25"
|
|
data-testid="lang-progress"
|
|
width={`${progressPercentage}%`}
|
|
/>
|
|
</svg>
|
|
</>
|
|
)
|
|
};
|
|
|
|
const createCompactLangNode = ({ lang, totalSize, x, y }: {lang: data, totalSize: number, x: number, y: number}) => {
|
|
const percentage = ((lang.size / totalSize) * 100).toFixed(2);
|
|
const color = lang.color || "#858585";
|
|
|
|
return (
|
|
<g transform={`translate(${x}, ${y})`}>
|
|
<circle cx="5" cy="6" r="5" fill={color} />
|
|
<text data-testid="lang-name" x="15" y="10" className='lang-name'>
|
|
{lang.name} {percentage}%
|
|
</text>
|
|
</g>
|
|
)
|
|
};
|
|
|
|
const createLanguageTextNode = ({ langs, totalSize, x, y }: { langs: Array<data>, totalSize: number, x: number, y: number}) => {
|
|
return langs.map((lang, index) => {
|
|
if (index % 2 === 0) {
|
|
return createCompactLangNode({
|
|
lang,
|
|
x,
|
|
y: 12.5 * index + y,
|
|
totalSize
|
|
});
|
|
}
|
|
return createCompactLangNode({
|
|
lang,
|
|
x: 150,
|
|
y: 12.5 + 12.5 * index,
|
|
totalSize
|
|
});
|
|
});
|
|
};
|
|
|
|
const lowercaseTrim = (name: string) => name.toLowerCase().trim();
|
|
|
|
const renderTopLanguages = (topLangs: Record<string, data>, options: parsedQuery = {}) => {
|
|
const {
|
|
hide_title,
|
|
hide_border,
|
|
card_width,
|
|
title_color,
|
|
text_color,
|
|
bg_color,
|
|
hide,
|
|
language_count,
|
|
theme,
|
|
layout,
|
|
} = options;
|
|
|
|
let langs = Object.values(topLangs);
|
|
let langsToHide: Record<string, boolean> = {};
|
|
|
|
// populate langsToHide map for quick lookup
|
|
// while filtering out
|
|
if (hide) {
|
|
hide.forEach((langName) => {
|
|
langsToHide[lowercaseTrim(langName)] = true;
|
|
});
|
|
}
|
|
|
|
// filter out langauges to be hidden
|
|
langs = langs
|
|
.sort((a, b) => b.size - a.size)
|
|
.filter((lang) => {
|
|
return !langsToHide[lowercaseTrim(lang.name)];
|
|
})
|
|
.slice(0, language_count || 5);
|
|
|
|
const totalLanguageSize = langs.reduce((acc, curr) => {
|
|
return acc + curr.size;
|
|
}, 0);
|
|
|
|
// returns theme based colors with proper overrides and defaults
|
|
const { titleColor, textColor, bgColor } = getCardColors({
|
|
title_color,
|
|
text_color,
|
|
bg_color,
|
|
theme,
|
|
});
|
|
|
|
let width = typeof card_width !== 'number' ? 300 : isNaN(card_width) ? 300 : card_width;
|
|
let height = 45 + (langs.length + 1) * 40;
|
|
|
|
let finalLayout: JSX.Element | Array<JSX.Element>;
|
|
|
|
// RENDER COMPACT LAYOUT
|
|
if (layout === "compact") {
|
|
width = width + 50;
|
|
height = 30 + (langs.length / 2 + 1) * 40;
|
|
|
|
// progressOffset holds the previous language's width and used to offset the next language
|
|
// so that we can stack them one after another, like this: [--][----][---]
|
|
let progressOffset = 0;
|
|
const compactProgressBar = langs
|
|
.map((lang, index) => {
|
|
const percentage = parseFloat((
|
|
(lang.size / totalLanguageSize) *
|
|
(width - 50)
|
|
).toFixed(2));
|
|
|
|
const progress =
|
|
percentage < 10 ? percentage + 10 : percentage;
|
|
|
|
const output = (
|
|
<rect
|
|
key={index}
|
|
mask="url(#rect-mask)"
|
|
data-testid="lang-progress"
|
|
x={progressOffset}
|
|
y="0"
|
|
width={progress}
|
|
height="8"
|
|
fill={lang.color || "#858585"}
|
|
/>
|
|
)
|
|
progressOffset += percentage;
|
|
return output;
|
|
})
|
|
|
|
finalLayout = (
|
|
<>
|
|
<mask id="rect-mask">
|
|
<rect x="0" y="0" width={
|
|
width - 50
|
|
} height="8" fill="white" rx="5" />
|
|
</mask>
|
|
{compactProgressBar}
|
|
{createLanguageTextNode({
|
|
x: 0,
|
|
y: 25,
|
|
langs,
|
|
totalSize: totalLanguageSize,
|
|
})}
|
|
</>
|
|
)
|
|
|
|
finalLayout = (
|
|
<>
|
|
<mask id="rect-mask">
|
|
<rect x="0" y="0" width={width - 50} height="8" fill="white" rx="5" />
|
|
</mask>
|
|
{compactProgressBar}
|
|
{createLanguageTextNode({
|
|
x: 0,
|
|
y: 25,
|
|
langs,
|
|
totalSize: totalLanguageSize,
|
|
})}
|
|
</>
|
|
)
|
|
} else {
|
|
finalLayout = FlexLayout({
|
|
items: langs.map((lang) => {
|
|
return createProgressNode({
|
|
width: width,
|
|
name: lang.name,
|
|
color: lang.color || "#858585",
|
|
progress: parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)),
|
|
progress2: parseFloat(((lang.recentSize / totalLanguageSize) * 100).toFixed(2)),
|
|
});
|
|
}),
|
|
gap: 40,
|
|
direction: "column",
|
|
})
|
|
}
|
|
|
|
const card = new Card(
|
|
width,
|
|
height,
|
|
{
|
|
titleColor,
|
|
textColor,
|
|
bgColor,
|
|
},
|
|
"Most Used Languages",
|
|
)
|
|
|
|
card.disableAnimations();
|
|
card.setHideBorder(hide_border || false);
|
|
card.setHideTitle(hide_title || false);
|
|
card.setCSS(`
|
|
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
|
|
`);
|
|
|
|
return card.render(
|
|
<svg data-testid="lang-items" x="25">
|
|
{finalLayout}
|
|
</svg>
|
|
)
|
|
};
|
|
|
|
export default renderTopLanguages
|