diff --git a/api/pin.js b/api/pin.js index 21ecf966b3ff4..4002e6c6ca5ce 100644 --- a/api/pin.js +++ b/api/pin.js @@ -24,6 +24,7 @@ export default async (req, res) => { locale, border_radius, border_color, + card_width, } = req.query; res.setHeader("Content-Type", "image/svg+xml"); @@ -80,6 +81,7 @@ export default async (req, res) => { border_color, show_owner: parseBoolean(show_owner), locale: locale ? locale.toLowerCase() : null, + card_width: parseInt(card_width, 10), }), ); } catch (err) { diff --git a/api/top-langs.js b/api/top-langs.js index d9bf6b09da01a..a319666824c5f 100644 --- a/api/top-langs.js +++ b/api/top-langs.js @@ -33,9 +33,9 @@ export default async (req, res) => { border_color, disable_animations, hide_progress, + progress_bar_bg_color, } = req.query; res.setHeader("Content-Type", "image/svg+xml"); - if (blacklist.includes(username)) { return res.send(renderError("Something went wrong")); } @@ -96,6 +96,7 @@ export default async (req, res) => { locale: locale ? locale.toLowerCase() : null, disable_animations: parseBoolean(disable_animations), hide_progress: parseBoolean(hide_progress), + progress_bar_bg_color, }), ); } catch (err) { diff --git a/readme.md b/readme.md index ff826b3f6e863..7f9d93a763570 100644 --- a/readme.md +++ b/readme.md @@ -366,7 +366,8 @@ If we don't support your language, please consider contributing! * `hide` - Hides the [specified items](#hiding-individual-stats) from stats *(Comma-separated values)*. Default: `[] (blank array)`. * `hide_title` - *(boolean)*. Default: `false`. -* `card_width` - Sets the card's width manually *(number)*. Default: `500px (approx.)`. +* `card_width` - Sets the card's width manually *(number)*. Default: `287px`. +* `card_min_width` - Sets the minimum card's width *(number)*. Default: `287px`. * `hide_rank` - *(boolean)* hides the rank and automatically resizes the card width. Default: `false`. * `rank_icon` - Shows alternative rank icon (i.e. `github`, `percentile` or `default`). Default: `default`. * `show_icons` - *(boolean)*. Default: `false`. @@ -386,6 +387,24 @@ If we don't support your language, please consider contributing! #### Repo Card Exclusive Options * `show_owner` - Shows the repo's owner name *(boolean)*. Default: `false`. +* `icon_size` - Sets the icon size manually *(number)*. Default: `16px`. +* `getBadgeSVG` - Retrieves the repository description and wraps it to fit the card width. +* `renderRepoCard` * Renders repository card details (i.e. `name`,`nameWithOwner`,`description`,`primaryLanguage`,`isArchived`,`isTemplate`,`starCount`). Default: `default`. +* `lineheight` - Sets line height manually *(number)*.Default:16. +* `header` - Shows header with `showOwner` and `namewithOwner` . +* `langName` - Stores the name of the primary programming language used in the repository. +* `langColor` - Stores the color associated with the primary programming language used in the repository. +* `desc` - Stores the description of the GitHub repository. Emojis are parsed within this description. +* `multiLineDescription` - Stores the description text broken into multiple lines to fit within the card's layout. +* `descriptionLines` - Represents the number of lines in the description. +* `descriptionSvg.Number` - Contains SVG text for rendering the repository description on the card. +* `height` - Represents the height of the GitHub repository card. It is calculated based on the number of lines in the description. +* `i18n` - Provides internationalization support and translations for the card. +* `colors` - Contains theme-based colors used for rendering the card, including title color, icon color, text color, background color, and border color. +* `svgLanguage` - Contains SVG representation of the primary programming language label. +* `totalStars` - Stores the total number of stars (GitHub repository stargazers) in a human-readable format. +* `totalForks` - Stores the total number of forks (GitHub repository forks) in a human-readable format. +* `starAndForkCount` - Contains SVG representation of stars and forks count along with the primary language label. #### Gist Card Exclusive Options @@ -396,7 +415,8 @@ If we don't support your language, please consider contributing! * `hide` - Hides the languages specified from the card *(Comma-separated values)*. Default: `[] (blank array)`. * `hide_title` - *(boolean)*. Default: `false`. * `layout` - Switches between five available layouts `normal` & `compact` & `donut` & `donut-vertical` & `pie`. Default: `normal`. -* `card_width` - Sets the card's width manually *(number)*. Default `300`. +* `card_width` - Sets the card's width manually *(number)*. Default `300px`. +* `card_min_width` - Sets the card's minimum width *(number)*.Default `280px`. * `langs_count` - Shows more languages on the card, between 1-20 *(number)*. Default: `5` for `normal` and `donut`, `6` for other layouts. * `exclude_repo` - Excludes specified repositories *(Comma-separated values)*. Default: `[] (blank array)`. * `custom_title` - Sets a custom title for the card *(string)*. Default `Most Used Languages`. diff --git a/src/cards/repo-card.js b/src/cards/repo-card.js index 09b5841880a97..a24cc0a36155e 100644 --- a/src/cards/repo-card.js +++ b/src/cards/repo-card.js @@ -2,6 +2,7 @@ import { Card } from "../common/Card.js"; import { I18n } from "../common/I18n.js"; import { icons } from "../common/icons.js"; + import { encodeHTML, flexLayout, @@ -15,7 +16,10 @@ import { } from "../common/utils.js"; import { repoCardLocales } from "../translations.js"; +const DEFAULT_CARD_WIDTH = 300; +const MIN_CARD_WIDTH = 400; const ICON_SIZE = 16; +const card_height = undefined ; /** * Retrieves the repository description and wraps it to fit the card width. @@ -73,6 +77,8 @@ const renderRepoCard = (repo, options = {}) => { border_radius, border_color, locale, + card_width, + card_height, } = options; const lineHeight = 10; @@ -87,14 +93,24 @@ const renderRepoCard = (repo, options = {}) => { .map((line) => `${encodeHTML(line)}`) .join(""); - const height = - (descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight; - const i18n = new I18n({ locale, translations: repoCardLocales, }); + let width = card_width + ? isNaN(card_width) + ? DEFAULT_CARD_WIDTH + : card_width < MIN_CARD_WIDTH + ? MIN_CARD_WIDTH + : card_width + : DEFAULT_CARD_WIDTH; + const height = + (descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight; + const height = card_height ?? ( + (descriptionLines > 1 ? 120 : 110) + descriptionLines * lineHeight +); + // returns theme based colors with proper overrides and defaults const colors = getCardColors({ title_color, @@ -137,7 +153,7 @@ const renderRepoCard = (repo, options = {}) => { const card = new Card({ defaultTitle: header.length > 35 ? `${header.slice(0, 35)}...` : header, titlePrefixIcon: icons.contribs, - width: 400, + width, height, border_radius, colors, @@ -175,5 +191,5 @@ const renderRepoCard = (repo, options = {}) => { `); }; -export { renderRepoCard }; +export { renderRepoCard, DEFAULT_CARD_WIDTH, MIN_CARD_WIDTH }; export default renderRepoCard; diff --git a/src/cards/stats-card.js b/src/cards/stats-card.js index 5f57205602016..a84baaee15ef7 100644 --- a/src/cards/stats-card.js +++ b/src/cards/stats-card.js @@ -541,5 +541,5 @@ const renderStatsCard = (stats, options = {}) => { `); }; -export { renderStatsCard }; +export { renderStatsCard, RANK_CARD_DEFAULT_WIDTH, RANK_CARD_MIN_WIDTH }; export default renderStatsCard; diff --git a/src/cards/top-languages-card.js b/src/cards/top-languages-card.js index 758bd34baff5d..65d632de8e99e 100644 --- a/src/cards/top-languages-card.js +++ b/src/cards/top-languages-card.js @@ -203,11 +203,19 @@ const trimTopLanguages = (topLangs, langs_count, hide) => { * @param {number} props.width The card width * @param {string} props.color Color of the programming language. * @param {string} props.name Name of the programming language. + * @param {string} props.progBgColor Color of the background of progress bar * @param {number} props.progress Usage of the programming language in percentage. * @param {number} props.index Index of the programming language. * @returns {string} Programming language SVG node. */ -const createProgressTextNode = ({ width, color, name, progress, index }) => { +const createProgressTextNode = ({ + width, + color, + name, + progress, + index, + progBgColor, +}) => { const staggerDelay = (index + 3) * 150; const paddingRight = 95; const progressTextX = width - paddingRight + 10; @@ -223,7 +231,9 @@ const createProgressTextNode = ({ width, color, name, progress, index }) => { color, width: progressWidth, progress, - progressBarBackgroundColor: "#ddd", + progressBarBackgroundColor: progBgColor + ? `#${progBgColor}` + : "#ddd", delay: staggerDelay + 300, })} @@ -322,9 +332,10 @@ const createDonutLanguagesNode = ({ langs, totalSize }) => { * @param {Lang[]} langs Array of programming languages. * @param {number} width Card width. * @param {number} totalLanguageSize Total size of all languages. + * @param {string} progBgColor Sets progress bar background color. * @returns {string} Normal layout card SVG object. */ -const renderNormalLayout = (langs, width, totalLanguageSize) => { +const renderNormalLayout = (langs, width, totalLanguageSize, progBgColor) => { return flexLayout({ items: langs.map((lang, index) => { return createProgressTextNode({ @@ -335,6 +346,7 @@ const renderNormalLayout = (langs, width, totalLanguageSize) => { ((lang.size / totalLanguageSize) * 100).toFixed(2), ), index, + progBgColor, }); }), gap: 40, @@ -383,14 +395,14 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress) => { return ` ${ - hideProgress - ? "" - : ` - - - - ${compactProgressBar} - ` + !hideProgress + ? ` + + + + ${compactProgressBar} + ` + : "" } ${createLanguageTextNode({ @@ -738,8 +750,8 @@ const renderTopLanguages = (topLangs, options = {}) => { border_radius, border_color, disable_animations, + progBgColor, } = options; - const i18n = new I18n({ locale, translations: langCardLocales, @@ -798,7 +810,12 @@ const renderTopLanguages = (topLangs, options = {}) => { width = width + 50; // padding finalLayout = renderDonutLayout(langs, width, totalLanguageSize); } else { - finalLayout = renderNormalLayout(langs, width, totalLanguageSize); + finalLayout = renderNormalLayout( + langs, + width, + totalLanguageSize, + progBgColor, + ); } const card = new Card({ diff --git a/src/cards/types.d.ts b/src/cards/types.d.ts index dce964d21af7e..d2ff21177d02e 100644 --- a/src/cards/types.d.ts +++ b/src/cards/types.d.ts @@ -6,6 +6,7 @@ export type CommonOptions = { icon_color: string; text_color: string; bg_color: string; + progress_bar_bg_color:string; theme: ThemeNames; border_radius: number; border_color: string; @@ -32,6 +33,7 @@ export type StatCardOptions = CommonOptions & { export type RepoCardOptions = CommonOptions & { show_owner: boolean; + card_width: number; }; export type TopLangOptions = CommonOptions & { diff --git a/tests/pin.test.js b/tests/pin.test.js index 0dcb5aa9768fd..8f09b20dd9593 100644 --- a/tests/pin.test.js +++ b/tests/pin.test.js @@ -73,6 +73,7 @@ describe("Test /api/pin", () => { text_color: "fff", bg_color: "fff", full_name: "1", + card_width: 100, }, }; const res = { diff --git a/tests/renderRepoCard.test.js b/tests/renderRepoCard.test.js index 61c9bc6da9ac5..e04c3d4d47c20 100644 --- a/tests/renderRepoCard.test.js +++ b/tests/renderRepoCard.test.js @@ -1,7 +1,11 @@ import { queryByTestId } from "@testing-library/dom"; import "@testing-library/jest-dom"; import { cssToObject } from "@uppercod/css-to-object"; -import { renderRepoCard } from "../src/cards/repo-card.js"; +import { + renderRepoCard, + DEFAULT_CARD_WIDTH, + MIN_CARD_WIDTH, +} from "../src/cards/repo-card.js"; import { expect, it, describe } from "@jest/globals"; import { themes } from "../themes/index.js"; @@ -338,4 +342,56 @@ describe("Test renderRepoCard", () => { "No description provided", ); }); + + it("should render with custom width set", () => { + document.body.innerHTML = renderRepoCard({ + ...data_repo.repository, + description: undefined, + isArchived: true, + }); + + expect(document.querySelector("svg")).toHaveAttribute( + "width", + DEFAULT_CARD_WIDTH.toString(), + ); + + document.body.innerHTML = renderRepoCard( + { + ...data_repo.repository, + description: undefined, + isArchived: true, + }, + { card_width: 400 }, + ); + expect(document.querySelector("svg")).toHaveAttribute("width", "400"); + }); + + it("should render with min width", () => { + document.body.innerHTML = renderRepoCard( + { + ...data_repo.repository, + description: undefined, + isArchived: true, + }, + { card_width: 190 }, + ); + + expect(document.querySelector("svg")).toHaveAttribute( + "width", + MIN_CARD_WIDTH.toString(), + ); + + document.body.innerHTML = renderRepoCard( + { + ...data_repo.repository, + description: undefined, + isArchived: true, + }, + { card_width: 100 }, + ); + expect(document.querySelector("svg")).toHaveAttribute( + "width", + MIN_CARD_WIDTH.toString(), + ); + }); }); diff --git a/tests/renderStatsCard.test.js b/tests/renderStatsCard.test.js index abbf2100306d7..2c89a1542fccf 100644 --- a/tests/renderStatsCard.test.js +++ b/tests/renderStatsCard.test.js @@ -4,7 +4,11 @@ import { queryByTestId, } from "@testing-library/dom"; import { cssToObject } from "@uppercod/css-to-object"; -import { renderStatsCard } from "../src/cards/stats-card.js"; +import { + renderStatsCard, + RANK_CARD_DEFAULT_WIDTH, + RANK_CARD_MIN_WIDTH, +} from "../src/cards/stats-card.js"; import { expect, it, describe } from "@jest/globals"; import { CustomError } from "../src/common/utils.js"; @@ -131,7 +135,10 @@ describe("Test renderStatsCard", () => { it("should render with custom width set", () => { document.body.innerHTML = renderStatsCard(stats); - expect(document.querySelector("svg")).toHaveAttribute("width", "450"); + expect(document.querySelector("svg")).toHaveAttribute( + "width", + RANK_CARD_DEFAULT_WIDTH.toString(), + ); document.body.innerHTML = renderStatsCard(stats, { card_width: 500 }); expect(document.querySelector("svg")).toHaveAttribute("width", "500"); @@ -139,7 +146,10 @@ describe("Test renderStatsCard", () => { it("should render with custom width set and limit minimum width", () => { document.body.innerHTML = renderStatsCard(stats, { card_width: 1 }); - expect(document.querySelector("svg")).toHaveAttribute("width", "420"); + expect(document.querySelector("svg")).toHaveAttribute( + "width", + RANK_CARD_MIN_WIDTH.toString(), + ); // Test default minimum card width without rank circle. document.body.innerHTML = renderStatsCard(stats, { diff --git a/tests/renderTopLanguagesCard.test.js b/tests/renderTopLanguagesCard.test.js index 6b7ef62aa30dd..da152c22d1dce 100644 --- a/tests/renderTopLanguagesCard.test.js +++ b/tests/renderTopLanguagesCard.test.js @@ -15,6 +15,7 @@ import { donutCenterTranslation, trimTopLanguages, renderTopLanguages, + DEFAULT_CARD_WIDTH, MIN_CARD_WIDTH, getDefaultLanguagesCountByLayout, } from "../src/cards/top-languages-card.js"; @@ -428,7 +429,10 @@ describe("Test renderTopLanguages", () => { it("should render with custom width set", () => { document.body.innerHTML = renderTopLanguages(langs, {}); - expect(document.querySelector("svg")).toHaveAttribute("width", "300"); + expect(document.querySelector("svg")).toHaveAttribute( + "width", + DEFAULT_CARD_WIDTH.toString(), + ); document.body.innerHTML = renderTopLanguages(langs, { card_width: 400 }); expect(document.querySelector("svg")).toHaveAttribute("width", "400");