// @ts-check import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js"; import { getCardColors } from "./color.js"; import { encodeHTML } from "./html.js"; import { clampValue } from "./ops.js"; /** * Auto layout utility, allows us to layout things vertically or horizontally with * proper gaping. * * @param {object} props Function properties. * @param {string[]} props.items Array of items to layout. * @param {number} props.gap Gap between items. * @param {"column" | "row"=} props.direction Direction to layout items. * @param {number[]=} props.sizes Array of sizes for each item. * @returns {string[]} Array of items with proper layout. */ const flexLayout = ({ items, gap, direction, sizes = [] }) => { let lastSize = 0; // filter() for filtering out empty strings return items.filter(Boolean).map((item, i) => { const size = sizes[i] || 0; let transform = `translate(${lastSize}, 0)`; if (direction === "column") { transform = `translate(0, ${lastSize})`; } lastSize += size + gap; return `${item}`; }); }; /** * Creates a node to display the primary programming language of the repository/gist. * * @param {string} langName Language name. * @param {string} langColor Language color. * @returns {string} Language display SVG object. */ const createLanguageNode = (langName, langColor) => { return ` ${langName} `; }; /** * Create a node to indicate progress in percentage along a horizontal line. * * @param {Object} params Object that contains the createProgressNode parameters. * @param {number} params.x X-axis position. * @param {number} params.y Y-axis position. * @param {number} params.width Width of progress bar. * @param {string} params.color Progress color. * @param {number} params.progress Progress value. * @param {string} params.progressBarBackgroundColor Progress bar bg color. * @param {number} params.delay Delay before animation starts. * @returns {string} Progress node. */ const createProgressNode = ({ x, y, width, color, progress, progressBarBackgroundColor, delay, }) => { const progressPercentage = clampValue(progress, 2, 100); return ` `; }; /** * Creates an icon with label to display repository/gist stats like forks, stars, etc. * * @param {string} icon The icon to display. * @param {number|string} label The label to display. * @param {string} testid The testid to assign to the label. * @param {number} iconSize The size of the icon. * @returns {string} Icon with label SVG object. */ const iconWithLabel = (icon, label, testid, iconSize) => { if (typeof label === "number" && label <= 0) { return ""; } const iconSvg = ` ${icon} `; const text = `${label}`; return flexLayout({ items: [iconSvg, text], gap: 20 }).join(""); }; // Script parameters. const ERROR_CARD_LENGTH = 576.5; const UPSTREAM_API_ERRORS = [ TRY_AGAIN_LATER, SECONDARY_ERROR_MESSAGES.MAX_RETRY, ]; /** * Renders error message on the card. * * @param {object} args Function arguments. * @param {string} args.message Main error message. * @param {string} [args.secondaryMessage=""] The secondary error message. * @param {object} [args.renderOptions={}] Render options. * @param {string=} args.renderOptions.title_color Card title color. * @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 {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. */ const renderError = ({ message, secondaryMessage = "", renderOptions = {}, }) => { const { title_color, text_color, bg_color, border_color, theme = "default", show_repo_link = true, } = renderOptions; // returns theme based colors with proper overrides and defaults const { titleColor, textColor, bgColor, borderColor } = getCardColors({ title_color, text_color, icon_color: "", bg_color, border_color, ring_color: "", theme, }); return ` Something went wrong!${ UPSTREAM_API_ERRORS.includes(secondaryMessage) || !show_repo_link ? "" : " file an issue at https://tiny.one/readme-stats" } ${encodeHTML(message)} ${secondaryMessage} `; }; /** * Retrieve text length. * * @see https://stackoverflow.com/a/48172630/10629172 * @param {string} str String to measure. * @param {number} fontSize Font size. * @returns {number} Text length. */ const measureText = (str, fontSize = 10) => { // prettier-ignore const widths = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2796875, 0.2765625, 0.3546875, 0.5546875, 0.5546875, 0.8890625, 0.665625, 0.190625, 0.3328125, 0.3328125, 0.3890625, 0.5828125, 0.2765625, 0.3328125, 0.2765625, 0.3015625, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.2765625, 0.2765625, 0.584375, 0.5828125, 0.584375, 0.5546875, 1.0140625, 0.665625, 0.665625, 0.721875, 0.721875, 0.665625, 0.609375, 0.7765625, 0.721875, 0.2765625, 0.5, 0.665625, 0.5546875, 0.8328125, 0.721875, 0.7765625, 0.665625, 0.7765625, 0.721875, 0.665625, 0.609375, 0.721875, 0.665625, 0.94375, 0.665625, 0.665625, 0.609375, 0.2765625, 0.3546875, 0.2765625, 0.4765625, 0.5546875, 0.3328125, 0.5546875, 0.5546875, 0.5, 0.5546875, 0.5546875, 0.2765625, 0.5546875, 0.5546875, 0.221875, 0.240625, 0.5, 0.221875, 0.8328125, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.3328125, 0.5, 0.2765625, 0.5546875, 0.5, 0.721875, 0.5, 0.5, 0.5, 0.3546875, 0.259375, 0.353125, 0.5890625, ]; const avg = 0.5279276315789471; return ( str .split("") .map((c) => c.charCodeAt(0) < widths.length ? widths[c.charCodeAt(0)] : avg, ) .reduce((cur, acc) => acc + cur) * fontSize ); }; export { ERROR_CARD_LENGTH, renderError, createLanguageNode, createProgressNode, iconWithLabel, flexLayout, measureText, };