const { getCardColors, FlexLayout, clampValue } = require("../src/utils"); const createProgressNode = ({ width, color, name, progress }) => { const paddingRight = 95; const progressTextX = width - paddingRight + 10; const progressWidth = width - paddingRight; const progressPercentage = clampValue(progress, 2, 100); return ` ${name} ${progress}% `; }; const createCompactLangNode = ({ lang, totalSize, x, y }) => { const percentage = ((lang.size / totalSize) * 100).toFixed(2); const color = lang.color || "#858585"; return ` ${lang.name} ${percentage}% `; }; const createLanguageTextNode = ({ langs, totalSize, x, y }) => { return langs.map((lang, index) => { if (index % 2 === 0) { return createCompactLangNode({ lang, x, y: 12.5 * index + y, totalSize, index, }); } return createCompactLangNode({ lang, x: 150, y: 12.5 + 12.5 * index, totalSize, index, }); }); }; const lowercaseTrim = (name) => name.toLowerCase().trim(); const renderTopLanguages = (topLangs, options = {}) => { const { hide_title, card_width, title_color, text_color, bg_color, hide, theme, layout, } = options; let langs = Object.values(topLangs); let langsToHide = {}; // 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)]; }); 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 = isNaN(card_width) ? 300 : card_width; let height = 45 + (langs.length + 1) * 40; let finalLayout = ""; // 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) => { const percentage = ( (lang.size / totalLanguageSize) * (width - 50) ).toFixed(2); const progress = percentage < 10 ? parseFloat(percentage) + 10 : percentage; const output = ` `; progressOffset += parseFloat(percentage); return output; }) .join(""); finalLayout = ` ${compactProgressBar} ${createLanguageTextNode({ x: 0, y: 25, langs, totalSize: totalLanguageSize, }).join("")} `; } else { finalLayout = FlexLayout({ items: langs.map((lang) => { return createProgressNode({ width: width, name: lang.name, color: lang.color || "#858585", progress: ((lang.size / totalLanguageSize) * 100).toFixed(2), }); }), gap: 40, direction: "column", }).join(""); } if (hide_title) { height -= 30; } return ` ${ hide_title ? "" : `Top Languages` } ${finalLayout} `; }; module.exports = renderTopLanguages;