diff --git a/api/top-langs.js b/api/top-langs.js index 23fddbc..840f88b 100644 --- a/api/top-langs.js +++ b/api/top-langs.js @@ -20,6 +20,7 @@ module.exports = async (req, res) => { bg_color, theme, cache_seconds, + layout } = req.query; let topLangs; @@ -49,6 +50,7 @@ module.exports = async (req, res) => { text_color, bg_color, theme, + layout }) ); }; diff --git a/readme.md b/readme.md index 8c31f4e..0f89061 100644 --- a/readme.md +++ b/readme.md @@ -125,6 +125,7 @@ Customization Options: | theme | string | sets inbuilt theme | 'default' | 'default_repocard' | 'default | | cache_seconds | number | manually set custom cache control | 1800 | 1800 | '1800' | | count_private | boolean | counts private contributions too if enabled | false | N/A | N/A | +| layout | string | choose a layout option | N/A | N/A | "default" | > Note on cache: Repo cards have default cache of 30mins (1800 seconds) if the fork count & star count is less than 1k otherwise it's 2hours (7200). Also note that cache is clamped to minimum of 30min and maximum of 24hours @@ -176,10 +177,22 @@ You can use `?hide=language1,language2` parameter to hide individual languages. [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&hide=javascript,html)](https://github.com/anuraghazra/github-readme-stats) ``` +### Compact Language Card Layout + +You can use the `&layout=compact` option to change the card design. + +```md +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats) +``` + ### Demo [![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/anuraghazra/github-readme-stats) +- Compact layout + +[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=anuraghazra&layout=compact)](https://github.com/anuraghazra/github-readme-stats) + --- ### All Demos diff --git a/src/renderTopLanguages.js b/src/renderTopLanguages.js index ae07cf1..e3c290c 100644 --- a/src/renderTopLanguages.js +++ b/src/renderTopLanguages.js @@ -11,11 +11,11 @@ const createProgressNode = ({ width, color, name, progress }) => { ${progress}% - @@ -23,6 +23,41 @@ const createProgressNode = ({ width, color, 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 = {}) => { @@ -34,6 +69,7 @@ const renderTopLanguages = (topLangs, options = {}) => { bg_color, hide, theme, + layout, } = options; let langs = Object.values(topLangs); @@ -54,7 +90,7 @@ const renderTopLanguages = (topLangs, options = {}) => { return !langsToHide[lowercaseTrim(lang.name)]; }); - const totalSize = langs.reduce((acc, curr) => { + const totalLanguageSize = langs.reduce((acc, curr) => { return acc + curr.size; }, 0); @@ -66,12 +102,78 @@ const renderTopLanguages = (topLangs, options = {}) => { theme, }); - const width = isNaN(card_width) ? 300 : card_width; + 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 ? "" @@ -88,18 +189,7 @@ const renderTopLanguages = (topLangs, options = {}) => { } - ${FlexLayout({ - items: langs.map((lang) => { - return createProgressNode({ - width: width, - name: lang.name, - color: lang.color || "#858585", - progress: ((lang.size / totalSize) * 100).toFixed(2), - }); - }), - gap: 40, - direction: "column", - }).join("")} + ${finalLayout} `; diff --git a/tests/renderTopLanguages.test.js b/tests/renderTopLanguages.test.js index 5ae9f07..3c19263 100644 --- a/tests/renderTopLanguages.test.js +++ b/tests/renderTopLanguages.test.js @@ -207,4 +207,19 @@ describe("Test renderTopLanguages", () => { ); }); }); + + it('should render with layout compact', () => { + document.body.innerHTML = renderTopLanguages(langs, {layout: 'compact'}); + + expect(queryByTestId(document.body, "header")).toHaveTextContent("Top Languages"); + + expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent("HTML 40.00%"); + expect(queryAllByTestId(document.body, "lang-progress")[0]).toHaveAttribute("width","120.00"); + + expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent("javascript 40.00%"); + expect(queryAllByTestId(document.body, "lang-progress")[1]).toHaveAttribute("width","120.00"); + + expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent("css 20.00%"); + expect(queryAllByTestId(document.body, "lang-progress")[2]).toHaveAttribute("width","60.00"); + }) });