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:
@@ -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?"
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
@@ -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",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user