diff --git a/scripts/preview-theme.js b/scripts/preview-theme.js index 0f6de372..b9e53ffe 100644 --- a/scripts/preview-theme.js +++ b/scripts/preview-theme.js @@ -12,7 +12,7 @@ import Hjson from "hjson"; import snakeCase from "lodash.snakecase"; import parse from "parse-diff"; import { inspect } from "util"; -import { isValidHexColor, isValidGradient } from "../src/common/utils.js"; +import { isValidHexColor, isValidGradient } from "../src/common/color.js"; import { themes } from "../themes/index.js"; import { getGithubToken, getRepoInfo } from "./helpers.js"; diff --git a/src/cards/gist.js b/src/cards/gist.js index 9e889e74..9b6c128d 100644 --- a/src/cards/gist.js +++ b/src/cards/gist.js @@ -1,7 +1,6 @@ // @ts-check import { - getCardColors, parseEmojis, wrapTextMultiline, encodeHTML, @@ -12,6 +11,7 @@ import { createLanguageNode, } from "../common/utils.js"; import Card from "../common/Card.js"; +import { getCardColors } from "../common/color.js"; import { icons } from "../common/icons.js"; /** Import language colors. diff --git a/src/cards/repo.js b/src/cards/repo.js index bbfda52d..81d32066 100644 --- a/src/cards/repo.js +++ b/src/cards/repo.js @@ -1,11 +1,11 @@ // @ts-check import { Card } from "../common/Card.js"; +import { getCardColors } from "../common/color.js"; import { I18n } from "../common/I18n.js"; import { icons } from "../common/icons.js"; import { encodeHTML, flexLayout, - getCardColors, kFormatter, measureText, parseEmojis, diff --git a/src/cards/stats.js b/src/cards/stats.js index 6e5bdd51..301a6d78 100644 --- a/src/cards/stats.js +++ b/src/cards/stats.js @@ -1,13 +1,13 @@ // @ts-check import { Card } from "../common/Card.js"; +import { getCardColors } from "../common/color.js"; import { CustomError } from "../common/error.js"; import { I18n } from "../common/I18n.js"; import { icons, rankIcon } from "../common/icons.js"; import { clampValue, flexLayout, - getCardColors, kFormatter, measureText, } from "../common/utils.js"; diff --git a/src/cards/top-languages.js b/src/cards/top-languages.js index fec72c7c..569ccaae 100644 --- a/src/cards/top-languages.js +++ b/src/cards/top-languages.js @@ -1,12 +1,12 @@ // @ts-check import { Card } from "../common/Card.js"; +import { getCardColors } from "../common/color.js"; import { createProgressNode } from "../common/createProgressNode.js"; import { I18n } from "../common/I18n.js"; import { chunkArray, clampValue, flexLayout, - getCardColors, lowercaseTrim, measureText, formatBytes, diff --git a/src/cards/wakatime.js b/src/cards/wakatime.js index 64ded83d..b2fec340 100644 --- a/src/cards/wakatime.js +++ b/src/cards/wakatime.js @@ -1,14 +1,10 @@ // @ts-check import { Card } from "../common/Card.js"; +import { getCardColors } from "../common/color.js"; import { createProgressNode } from "../common/createProgressNode.js"; import { I18n } from "../common/I18n.js"; -import { - clampValue, - flexLayout, - getCardColors, - lowercaseTrim, -} from "../common/utils.js"; +import { clampValue, flexLayout, lowercaseTrim } from "../common/utils.js"; import { wakatimeCardLocales } from "../translations.js"; /** Import language colors. diff --git a/src/common/color.js b/src/common/color.js new file mode 100644 index 00000000..8047ad06 --- /dev/null +++ b/src/common/color.js @@ -0,0 +1,141 @@ +import { themes } from "../../themes/index.js"; + +/** + * Checks if a string is a valid hex color. + * + * @param {string} hexColor String to check. + * @returns {boolean} True if the given string is a valid hex color. + */ +const isValidHexColor = (hexColor) => { + return new RegExp( + /^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/, + ).test(hexColor); +}; + +/** + * Check if the given string is a valid gradient. + * + * @param {string[]} colors Array of colors. + * @returns {boolean} True if the given string is a valid gradient. + */ +const isValidGradient = (colors) => { + return ( + colors.length > 2 && + colors.slice(1).every((color) => isValidHexColor(color)) + ); +}; + +/** + * Retrieves a gradient if color has more than one valid hex codes else a single color. + * + * @param {string} color The color to parse. + * @param {string | string[]} fallbackColor The fallback color. + * @returns {string | string[]} The gradient or color. + */ +const fallbackColor = (color, fallbackColor) => { + let gradient = null; + + let colors = color ? color.split(",") : []; + if (colors.length > 1 && isValidGradient(colors)) { + gradient = colors; + } + + return ( + (gradient ? gradient : isValidHexColor(color) && `#${color}`) || + fallbackColor + ); +}; + +/** + * Object containing card colors. + * @typedef {{ + * titleColor: string; + * iconColor: string; + * textColor: string; + * bgColor: string | string[]; + * borderColor: string; + * ringColor: string; + * }} CardColors + */ + +/** + * Returns theme based colors with proper overrides and defaults. + * + * @param {Object} args Function arguments. + * @param {string=} args.title_color Card title color. + * @param {string=} args.text_color Card text color. + * @param {string=} args.icon_color Card icon color. + * @param {string=} args.bg_color Card background color. + * @param {string=} args.border_color Card border color. + * @param {string=} args.ring_color Card ring color. + * @param {keyof themes=} args.theme Card theme. + * @param {keyof themes=} args.fallbackTheme Fallback theme. + * @returns {CardColors} Card colors. + */ +const getCardColors = ({ + title_color, + text_color, + icon_color, + bg_color, + border_color, + ring_color, + theme, + fallbackTheme = "default", +}) => { + const defaultTheme = themes[fallbackTheme]; + const isThemeProvided = theme !== null && theme !== undefined; + const selectedTheme = isThemeProvided ? themes[theme] : defaultTheme; + const defaultBorderColor = + "border_color" in selectedTheme + ? selectedTheme.border_color + : // @ts-ignore + defaultTheme.border_color; + + // get the color provided by the user else the theme color + // finally if both colors are invalid fallback to default theme + const titleColor = fallbackColor( + title_color || selectedTheme.title_color, + "#" + defaultTheme.title_color, + ); + + // get the color provided by the user else the theme color + // finally if both colors are invalid we use the titleColor + const ringColor = fallbackColor( + // @ts-ignore + ring_color || selectedTheme.ring_color, + titleColor, + ); + const iconColor = fallbackColor( + icon_color || selectedTheme.icon_color, + "#" + defaultTheme.icon_color, + ); + const textColor = fallbackColor( + text_color || selectedTheme.text_color, + "#" + defaultTheme.text_color, + ); + const bgColor = fallbackColor( + bg_color || selectedTheme.bg_color, + "#" + defaultTheme.bg_color, + ); + + const borderColor = fallbackColor( + border_color || defaultBorderColor, + "#" + defaultBorderColor, + ); + + if ( + typeof titleColor !== "string" || + typeof textColor !== "string" || + typeof ringColor !== "string" || + typeof iconColor !== "string" || + typeof borderColor !== "string" + ) { + throw new Error( + "Unexpected behavior, all colors except background should be string.", + ); + } + + return { titleColor, iconColor, textColor, bgColor, borderColor, ringColor }; +}; + +export { isValidHexColor, isValidGradient, getCardColors }; diff --git a/src/common/index.js b/src/common/index.js index aa5ec082..5dfb0e12 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -11,15 +11,11 @@ export { renderError, encodeHTML, kFormatter, - isValidHexColor, parseBoolean, parseArray, clampValue, - isValidGradient, - fallbackColor, request, flexLayout, - getCardColors, wrapTextMultiline, logger, measureText, diff --git a/src/common/utils.js b/src/common/utils.js index f451d8b9..05478803 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -3,8 +3,8 @@ import axios from "axios"; import toEmoji from "emoji-name-map"; import wrap from "word-wrap"; -import { themes } from "../../themes/index.js"; import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js"; +import { getCardColors } from "./color.js"; /** * Auto layout utility, allows us to layout things vertically or horizontally with @@ -88,18 +88,6 @@ const kFormatter = (num) => { : Math.sign(num) * Math.abs(num); }; -/** - * Checks if a string is a valid hex color. - * - * @param {string} hexColor String to check. - * @returns {boolean} True if the given string is a valid hex color. - */ -const isValidHexColor = (hexColor) => { - return new RegExp( - /^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/, - ).test(hexColor); -}; - /** * Returns boolean if value is either "true" or "false" else the value as it is. * @@ -150,40 +138,6 @@ const clampValue = (number, min, max) => { return Math.max(min, Math.min(number, max)); }; -/** - * Check if the given string is a valid gradient. - * - * @param {string[]} colors Array of colors. - * @returns {boolean} True if the given string is a valid gradient. - */ -const isValidGradient = (colors) => { - return ( - colors.length > 2 && - colors.slice(1).every((color) => isValidHexColor(color)) - ); -}; - -/** - * Retrieves a gradient if color has more than one valid hex codes else a single color. - * - * @param {string} color The color to parse. - * @param {string | string[]} fallbackColor The fallback color. - * @returns {string | string[]} The gradient or color. - */ -const fallbackColor = (color, fallbackColor) => { - let gradient = null; - - let colors = color ? color.split(",") : []; - if (colors.length > 1 && isValidGradient(colors)) { - gradient = colors; - } - - return ( - (gradient ? gradient : isValidHexColor(color) && `#${color}`) || - fallbackColor - ); -}; - /** * @typedef {import('axios').AxiosRequestConfig['data']} AxiosRequestConfigData Axios request data. * @typedef {import('axios').AxiosRequestConfig['headers']} AxiosRequestConfigHeaders Axios request headers. @@ -205,98 +159,6 @@ const request = (data, headers) => { }); }; -/** - * Object containing card colors. - * @typedef {{ - * titleColor: string; - * iconColor: string; - * textColor: string; - * bgColor: string | string[]; - * borderColor: string; - * ringColor: string; - * }} CardColors - */ - -/** - * Returns theme based colors with proper overrides and defaults. - * - * @param {Object} args Function arguments. - * @param {string=} args.title_color Card title color. - * @param {string=} args.text_color Card text color. - * @param {string=} args.icon_color Card icon color. - * @param {string=} args.bg_color Card background color. - * @param {string=} args.border_color Card border color. - * @param {string=} args.ring_color Card ring color. - * @param {keyof themes=} args.theme Card theme. - * @param {keyof themes=} args.fallbackTheme Fallback theme. - * @returns {CardColors} Card colors. - */ -const getCardColors = ({ - title_color, - text_color, - icon_color, - bg_color, - border_color, - ring_color, - theme, - fallbackTheme = "default", -}) => { - const defaultTheme = themes[fallbackTheme]; - const isThemeProvided = theme !== null && theme !== undefined; - const selectedTheme = isThemeProvided ? themes[theme] : defaultTheme; - const defaultBorderColor = - "border_color" in selectedTheme - ? selectedTheme.border_color - : // @ts-ignore - defaultTheme.border_color; - - // get the color provided by the user else the theme color - // finally if both colors are invalid fallback to default theme - const titleColor = fallbackColor( - title_color || selectedTheme.title_color, - "#" + defaultTheme.title_color, - ); - - // get the color provided by the user else the theme color - // finally if both colors are invalid we use the titleColor - const ringColor = fallbackColor( - // @ts-ignore - ring_color || selectedTheme.ring_color, - titleColor, - ); - const iconColor = fallbackColor( - icon_color || selectedTheme.icon_color, - "#" + defaultTheme.icon_color, - ); - const textColor = fallbackColor( - text_color || selectedTheme.text_color, - "#" + defaultTheme.text_color, - ); - const bgColor = fallbackColor( - bg_color || selectedTheme.bg_color, - "#" + defaultTheme.bg_color, - ); - - const borderColor = fallbackColor( - border_color || defaultBorderColor, - "#" + defaultBorderColor, - ); - - if ( - typeof titleColor !== "string" || - typeof textColor !== "string" || - typeof ringColor !== "string" || - typeof iconColor !== "string" || - typeof borderColor !== "string" - ) { - throw new Error( - "Unexpected behavior, all colors except background should be string.", - ); - } - - return { titleColor, iconColor, textColor, bgColor, borderColor, ringColor }; -}; - // Script parameters. const ERROR_CARD_LENGTH = 576.5; @@ -332,7 +194,7 @@ const UPSTREAM_API_ERRORS = [ * @param {string=} args.renderOptions.text_color Card text color. * @param {string=} args.renderOptions.bg_color Card background color. * @param {string=} args.renderOptions.border_color Card border color. - * @param {keyof themes=} args.renderOptions.theme Card theme. + * @param {Parameters[0]["theme"]=} args.renderOptions.theme Card theme. * @param {boolean=} args.renderOptions.show_repo_link Whether to show repo link or not. * @returns {string} The SVG markup. */ @@ -561,15 +423,11 @@ export { iconWithLabel, encodeHTML, kFormatter, - isValidHexColor, parseBoolean, parseArray, clampValue, - isValidGradient, - fallbackColor, request, flexLayout, - getCardColors, wrapTextMultiline, logger, measureText, diff --git a/tests/card.test.js b/tests/card.test.js index 02e476c3..21303603 100644 --- a/tests/card.test.js +++ b/tests/card.test.js @@ -4,7 +4,7 @@ import "@testing-library/jest-dom"; import { cssToObject } from "@uppercod/css-to-object"; import { Card } from "../src/common/Card.js"; import { icons } from "../src/common/icons.js"; -import { getCardColors } from "../src/common/utils.js"; +import { getCardColors } from "../src/common/color.js"; describe("Card", () => { it("should hide border", () => { diff --git a/tests/utils.test.js b/tests/utils.test.js index 21a78e47..f7ce665c 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -4,12 +4,12 @@ import "@testing-library/jest-dom"; import { encodeHTML, formatBytes, - getCardColors, kFormatter, parseBoolean, renderError, wrapTextMultiline, } from "../src/common/utils.js"; +import { getCardColors } from "../src/common/color.js"; describe("Test utils.js", () => { it("should test kFormatter", () => {