top-langs: add hide_values option (#181)

based on https://github.com/anuraghazra/github-readme-stats/pull/4850 by
@claw-explorer

This PR adds a new hide_values query parameter for the Top Languages
card that hides the percentage/byte display values while keeping
progress bars and chart visualizations intact.

<img width="300" height="245" alt="top-langs5"
src="https://github.com/user-attachments/assets/6c7486c3-5611-41c0-95ff-0d9183cea320"
/>
<img width="300" height="350" alt="top-langs"
src="https://github.com/user-attachments/assets/a0cc5bed-3f8e-4934-9c30-3e7d5a240865"
/>
<img width="350" height="215" alt="top-langs2"
src="https://github.com/user-attachments/assets/9d9c06cc-aa0d-4e47-9cdd-bce298583742"
/>
<img width="300" height="350" alt="top-langs3"
src="https://github.com/user-attachments/assets/f4a51889-0b8f-4289-a082-e2107208a123"
/>
<img width="300" height="140" alt="top-langs4"
src="https://github.com/user-attachments/assets/d88c2ea2-0422-482b-aade-8cf5799f12cd"
/>
This commit is contained in:
Martin
2026-04-25 20:29:08 +02:00
committed by GitHub
parent 457c457cab
commit eab14c8932
9 changed files with 168 additions and 9 deletions
+4
View File
@@ -76,6 +76,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
>();
const [customTitle, setCustomTitle] = useState("");
const [langsCount, setLangsCount] = useState<number | undefined>();
const [hideValues, setHideValues] = useState(false);
const [showAllStats, setShowAllStats] = useState(false);
const [showIcons, setShowIcons] = useState(false);
const [includeAllCommits, setIncludeAllCommits] = useState(true);
@@ -121,6 +122,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
descriptionLines,
customTitle,
langsCount,
hideValues,
showAllStats,
showIcons,
includeAllCommits,
@@ -306,6 +308,8 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {
setCustomTitle={setCustomTitle}
langsCount={langsCount}
setLangsCount={setLangsCount}
hideValues={hideValues}
setHideValues={setHideValues}
showAllStats={showAllStats}
setShowAllStats={setShowAllStats}
showIcons={showIcons}
@@ -22,6 +22,7 @@ const baseOptions = {
descriptionLines: undefined,
customTitle: "",
langsCount: undefined,
hideValues: false,
showAllStats: false,
showIcons: false,
includeAllCommits: false,
@@ -19,6 +19,7 @@ interface Options {
descriptionLines: number | undefined;
customTitle: string;
langsCount: number | undefined;
hideValues: boolean;
showAllStats: boolean;
showIcons: boolean;
includeAllCommits: boolean;
@@ -41,6 +42,7 @@ export function getFullSuffix({
descriptionLines,
customTitle,
langsCount,
hideValues,
showAllStats,
showIcons,
includeAllCommits,
@@ -138,6 +140,10 @@ export function getFullSuffix({
fullSuffix += `&langs_count=${langsCount}`;
}
if (hideValues && selectedCard === CardType.TOP_LANGS) {
fullSuffix += `&hide_values=true`;
}
if (showAllStats && selectedCard === CardType.STATS) {
fullSuffix += `&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented`;
}
@@ -47,6 +47,8 @@ interface CustomizeStageProps {
setCustomTitle: Updater<string>;
langsCount: number | undefined;
setLangsCount: Updater<number | undefined>;
hideValues: boolean;
setHideValues: Updater<boolean>;
showIcons: boolean;
setShowIcons: Updater<boolean>;
showAllStats: boolean;
@@ -87,6 +89,8 @@ export function CustomizeStage({
setCustomTitle,
langsCount,
setLangsCount,
hideValues,
setHideValues,
showIcons,
setShowIcons,
showAllStats,
@@ -321,6 +325,15 @@ export function CustomizeStage({
max={20}
/>
)}
{cardType === CardType.TOP_LANGS && (
<CheckboxSection
title="Hide Values?"
text="Hide language percentages or bytes while keeping the selected layout visible."
question="Hide values?"
checked={hideValues}
onCheckedChange={setHideValues}
/>
)}
{cardType === CardType.WAKATIME && (
<CheckboxSection
title="Show Percentages?"
+1
View File
@@ -417,6 +417,7 @@ You can customize the appearance and behavior of the top languages card using th
| `disable_animations` | Disables all animations in the card. | boolean | `false` |
| `prog_bar_bg_color` | Background color of the bars. (Applies only to `normal` layout.) | string (hex color) | `#ddd` |
| `hide_progress` | Uses the compact layout option, hides percentages, and removes the bars. | boolean | `false` |
| `hide_values` | Hides language percentages or bytes while keeping the progress bars or chart. | boolean | `false` |
| `size_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `1` |
| `count_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `0` |
| `stats_format` | Switches between two available formats for language's stats `percentages` and `bytes`. | enum | `percentages` |
+2
View File
@@ -35,6 +35,7 @@ export default async (
role,
disable_animations,
hide_progress,
hide_values,
stats_format,
},
pat = null,
@@ -128,6 +129,7 @@ export default async (
locale: locale ? locale.toLowerCase() : null,
disable_animations: parseBoolean(disable_animations),
hide_progress: parseBoolean(hide_progress),
hide_values: parseBoolean(hide_values),
stats_format,
}),
};
+55 -9
View File
@@ -221,6 +221,7 @@ const getDisplayValue = (size, percentages, format) => {
* @param {number} props.size Size of the programming language.
* @param {number} props.totalSize Total size of all languages.
* @param {string} props.statsFormat Stats format.
* @param {boolean=} props.hideValues Whether to hide stats values.
* @param {number} props.index Index of the programming language.
* @returns {string} Programming language SVG node.
*/
@@ -232,10 +233,11 @@ const createProgressTextNode = ({
size,
totalSize,
statsFormat,
hideValues,
index,
}) => {
const staggerDelay = (index + 3) * 150;
const paddingRight = 95;
const paddingRight = hideValues ? CARD_PADDING * 2 : 95;
const progressTextX = width - paddingRight + 10;
const progressWidth = width - paddingRight;
@@ -245,7 +247,7 @@ const createProgressTextNode = ({
return `
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
<text data-testid="lang-name" x="2" y="15" class="lang-name">${name}</text>
<text x="${progressTextX}" y="34" class="lang-name">${displayValue}</text>
${hideValues ? "" : `<text x="${progressTextX}" y="34" class="lang-name">${displayValue}</text>`}
${createProgressNode({
x: 0,
y: 25,
@@ -266,6 +268,7 @@ const createProgressTextNode = ({
* @param {Lang} props.lang Programming language object.
* @param {number} props.totalSize Total size of all languages.
* @param {boolean=} props.hideProgress Whether to hide percentage.
* @param {boolean=} props.hideValues Whether to hide stats values (percentages/bytes).
* @param {string=} props.statsFormat Stats format
* @param {number} props.index Index of the programming language.
* @returns {string} Compact layout programming language SVG node.
@@ -274,6 +277,7 @@ const createCompactLangNode = ({
lang,
totalSize,
hideProgress,
hideValues,
statsFormat = "percentages",
index,
}) => {
@@ -287,7 +291,7 @@ const createCompactLangNode = ({
<g class="stagger" style="animation-delay: ${staggerDelay}ms">
<circle cx="5" cy="6" r="5" fill="${color}" />
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
${lang.name} ${hideProgress ? "" : displayValue}
${lang.name} ${hideProgress || hideValues ? "" : displayValue}
</text>
</g>
`;
@@ -300,6 +304,7 @@ const createCompactLangNode = ({
* @param {Lang[]} props.langs Array of programming languages.
* @param {number} props.totalSize Total size of all languages.
* @param {boolean=} props.hideProgress Whether to hide percentage.
* @param {boolean=} props.hideValues Whether to hide stats values.
* @param {string=} props.statsFormat Stats format
* @returns {string} Programming languages SVG node.
*/
@@ -307,6 +312,7 @@ const createLanguageTextNode = ({
langs,
totalSize,
hideProgress,
hideValues,
statsFormat,
}) => {
const longestLang = getLongestLang(langs);
@@ -318,6 +324,7 @@ const createLanguageTextNode = ({
lang,
totalSize,
hideProgress,
hideValues,
statsFormat,
index,
}),
@@ -344,16 +351,23 @@ const createLanguageTextNode = ({
* @param {object} props Function properties.
* @param {Lang[]} props.langs Array of programming languages.
* @param {number} props.totalSize Total size of all languages.
* @param {boolean=} props.hideValues Whether to hide stats values.
* @param {string} props.statsFormat Stats format
* @returns {string} Donut layout programming language SVG node.
*/
const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => {
const createDonutLanguagesNode = ({
langs,
totalSize,
hideValues,
statsFormat,
}) => {
return flexLayout({
items: langs.map((lang, index) => {
return createCompactLangNode({
lang,
totalSize,
hideProgress: false,
hideValues,
statsFormat,
index,
});
@@ -371,6 +385,7 @@ const createDonutLanguagesNode = ({ langs, totalSize, statsFormat }) => {
* @param {number} totalLanguageSize Total size of all languages.
* @param progBarBgColor Color of the background of progress bar.
* @param {string} statsFormat Stats format.
* @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Normal layout card SVG object.
*/
const renderNormalLayout = (
@@ -379,6 +394,7 @@ const renderNormalLayout = (
totalLanguageSize,
progBarBgColor,
statsFormat,
hideValues,
) => {
return flexLayout({
items: langs.map((lang, index) => {
@@ -389,6 +405,7 @@ const renderNormalLayout = (
size: lang.size,
totalSize: totalLanguageSize,
statsFormat,
hideValues,
index,
progBarBgColor,
});
@@ -406,6 +423,7 @@ const renderNormalLayout = (
* @param {number} totalLanguageSize Total size of all languages.
* @param {boolean=} hideProgress Whether to hide progress bar.
* @param {string} statsFormat Stats format.
* @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Compact layout card SVG object.
*/
const renderCompactLayout = (
@@ -414,6 +432,7 @@ const renderCompactLayout = (
totalLanguageSize,
hideProgress,
statsFormat = "percentages",
hideValues,
) => {
const paddingRight = 50;
const offsetWidth = width - paddingRight;
@@ -461,6 +480,7 @@ const renderCompactLayout = (
totalSize: totalLanguageSize,
hideProgress,
statsFormat,
hideValues,
})}
</g>
`;
@@ -472,9 +492,15 @@ const renderCompactLayout = (
* @param {Lang[]} langs Array of programming languages.
* @param {number} totalLanguageSize Total size of all languages.
* @param {string} statsFormat Stats format.
* @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Compact layout card SVG object.
*/
const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => {
const renderDonutVerticalLayout = (
langs,
totalLanguageSize,
statsFormat,
hideValues,
) => {
// Donut vertical chart radius and total length
const radius = 80;
const totalCircleLength = getCircleLength(radius);
@@ -531,6 +557,7 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => {
totalSize: totalLanguageSize,
hideProgress: false,
statsFormat,
hideValues,
})}
</svg>
</g>
@@ -544,9 +571,10 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat) => {
* @param {Lang[]} langs Array of programming languages.
* @param {number} totalLanguageSize Total size of all languages.
* @param {string} statsFormat Stats format.
* @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Compact layout card SVG object.
*/
const renderPieLayout = (langs, totalLanguageSize, statsFormat) => {
const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
// Pie chart radius and center coordinates
const radius = 90;
const centerX = 150;
@@ -628,6 +656,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat) => {
totalSize: totalLanguageSize,
hideProgress: false,
statsFormat,
hideValues,
})}
</svg>
</g>
@@ -679,9 +708,16 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
* @param {number} width Card width.
* @param {number} totalLanguageSize Total size of all languages.
* @param {string} statsFormat Stats format.
* @param {boolean=} hideValues Whether to hide stats values.
* @returns {string} Donut layout card SVG object.
*/
const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat) => {
const renderDonutLayout = (
langs,
width,
totalLanguageSize,
statsFormat,
hideValues,
) => {
const centerX = width / 3;
const centerY = width / 3;
const radius = centerX - 60;
@@ -724,7 +760,7 @@ const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat) => {
return `
<g transform="translate(0, 0)">
<g transform="translate(0, 0)">
${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, statsFormat })}
${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, hideValues, statsFormat })}
</g>
<g transform="translate(125, ${donutCenterTranslation(langs.length)})">
@@ -800,6 +836,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
prog_bar_bg_color,
hide,
hide_progress,
hide_values,
theme,
layout,
custom_title,
@@ -850,13 +887,19 @@ const renderTopLanguages = (topLangs, options = {}) => {
});
} else if (layout === "pie") {
height = calculatePieLayoutHeight(langs.length);
finalLayout = renderPieLayout(langs, totalLanguageSize, stats_format);
finalLayout = renderPieLayout(
langs,
totalLanguageSize,
stats_format,
hide_values,
);
} else if (layout === "donut-vertical") {
height = calculateDonutVerticalLayoutHeight(langs.length);
finalLayout = renderDonutVerticalLayout(
langs,
totalLanguageSize,
stats_format,
hide_values,
);
} else if (layout === "compact" || hide_progress == true) {
height =
@@ -868,6 +911,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
totalLanguageSize,
hide_progress,
stats_format,
hide_values,
);
} else if (layout === "donut") {
height = calculateDonutLayoutHeight(langs.length);
@@ -877,6 +921,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
width,
totalLanguageSize,
stats_format,
hide_values,
);
} else {
finalLayout = renderNormalLayout(
@@ -885,6 +930,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
totalLanguageSize,
fallbackColor(prog_bar_bg_color, "#ddd"),
stats_format,
hide_values,
);
}
+1
View File
@@ -53,6 +53,7 @@ export type TopLangOptions = CommonOptions & {
langs_count: number;
disable_animations: boolean;
hide_progress: boolean;
hide_values: boolean;
prog_bar_bg_color: string;
stats_format: "percentages" | "bytes";
};
@@ -909,4 +909,89 @@ describe("Test renderTopLanguages", () => {
"css 100.0 B",
);
});
it("should hide stats values when hide_values is true (compact layout)", () => {
document.body.innerHTML = renderTopLanguages(langs, {
layout: "compact",
hide_values: true,
});
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
"HTML",
);
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
"javascript",
);
});
it("should hide stats values when hide_values is true (normal layout)", () => {
document.body.innerHTML = renderTopLanguages(langs, {
hide_values: true,
});
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
"HTML",
);
expect(queryAllByTestId(document.body, "lang-name").length).toBe(3);
});
it("should hide stats values when hide_values is true (donut layout)", () => {
document.body.innerHTML = renderTopLanguages(langs, {
layout: "donut",
hide_values: true,
});
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
"HTML",
);
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
"javascript",
);
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
"css",
);
});
it("should hide stats values when hide_values is true (pie layout)", () => {
document.body.innerHTML = renderTopLanguages(langs, {
layout: "pie",
hide_values: true,
});
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
"HTML",
);
expect(queryAllByTestId(document.body, "lang-name")[1]).toHaveTextContent(
"javascript",
);
expect(queryAllByTestId(document.body, "lang-name")[2]).toHaveTextContent(
"css",
);
expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute(
"size",
"40",
);
document.body.innerHTML = renderTopLanguages(
{ HTML: langs.HTML },
{
layout: "pie",
hide_values: true,
},
);
expect(queryAllByTestId(document.body, "lang-name")[0]).toHaveTextContent(
"HTML",
);
expect(queryAllByTestId(document.body, "lang-pie")).toHaveLength(1);
expect(queryAllByTestId(document.body, "lang-pie")[0]).toHaveAttribute(
"size",
"100",
);
expect(queryAllByTestId(document.body, "lang-pie")[0].tagName).toBe(
"circle",
);
});
});