kopia lustrzana https://github.com/badgen/badgen
187 wiersze
7.0 KiB
TypeScript
187 wiersze
7.0 KiB
TypeScript
export { Verdana110 as calcWidth } from './calc-text-width'
|
|
import { Verdana110 as calcWidth } from './calc-text-width'
|
|
import colorPresets from './color-presets'
|
|
|
|
export type StyleOption = 'flat' | 'classic'
|
|
export type ColorPreset = keyof typeof colorPresets
|
|
|
|
export interface BadgenOptions {
|
|
status: string;
|
|
subject?: string;
|
|
color?: ColorPreset;
|
|
label?: string;
|
|
labelColor?: string
|
|
style?: StyleOption;
|
|
icon?: string;
|
|
iconWidth?: number;
|
|
scale?: number
|
|
}
|
|
|
|
export function badgen ({
|
|
label,
|
|
subject,
|
|
status,
|
|
color = 'blue',
|
|
style,
|
|
icon,
|
|
iconWidth = 13,
|
|
labelColor = '555',
|
|
scale = 1
|
|
}: BadgenOptions) {
|
|
typeAssert(typeof status === 'string', '<status> must be string')
|
|
|
|
label = label === undefined ? subject : label // subject is deprecated
|
|
if (!label && !icon) {
|
|
return bare({ status, color, style, scale })
|
|
}
|
|
|
|
color = colorPresets[color] || color
|
|
labelColor = colorPresets[labelColor] || labelColor
|
|
iconWidth = iconWidth * 10
|
|
|
|
const iconSpanWidth = icon ? (label?.length ? iconWidth + 30 : iconWidth - 18) : 0
|
|
const sbTextStart = icon ? (iconSpanWidth + 50) : 50
|
|
const sbTextWidth = label ? calcWidth(label) : 0
|
|
const stTextWidth = calcWidth(status)
|
|
const sbRectWidth = sbTextWidth + 100 + iconSpanWidth
|
|
const stRectWidth = stTextWidth + 100
|
|
const width = sbRectWidth + stRectWidth
|
|
const xlink = icon ? ' xmlns:xlink="http://www.w3.org/1999/xlink"' : ''
|
|
|
|
const gradientId = generateRandomID(5)
|
|
const maskId = generateRandomID(5)
|
|
|
|
label = label ? sanitize(label) : ''
|
|
status = sanitize(status)
|
|
color = sanitize(color)
|
|
labelColor = sanitize(labelColor)
|
|
icon = icon ? sanitize(icon) : icon
|
|
const accessibleText = createAccessibleText({label, status})
|
|
|
|
if (style === 'flat') {
|
|
return `<svg width="${scale * width / 10}" height="${scale * 20}" viewBox="0 0 ${width} 200" xmlns="http://www.w3.org/2000/svg"${xlink} role="img" aria-label="${accessibleText}">
|
|
<title>${accessibleText}</title>
|
|
<g>
|
|
<rect fill="#${labelColor}" width="${sbRectWidth}" height="200"/>
|
|
<rect fill="#${color}" x="${sbRectWidth}" width="${stRectWidth}" height="200"/>
|
|
</g>
|
|
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
|
<text x="${sbTextStart + 10}" y="148" textLength="${sbTextWidth}" fill="#000" opacity="0.1">${label}</text>
|
|
<text x="${sbTextStart}" y="138" textLength="${sbTextWidth}">${label}</text>
|
|
<text x="${sbRectWidth + 55}" y="148" textLength="${stTextWidth}" fill="#000" opacity="0.1">${status}</text>
|
|
<text x="${sbRectWidth + 45}" y="138" textLength="${stTextWidth}">${status}</text>
|
|
</g>
|
|
${icon ? `<image x="40" y="35" width="${iconWidth}" height="132" xlink:href="${icon}"/>` : ''}
|
|
</svg>`
|
|
}
|
|
|
|
return `<svg width="${scale * width / 10}" height="${scale * 20}" viewBox="0 0 ${width} 200" xmlns="http://www.w3.org/2000/svg"${xlink} role="img" aria-label="${accessibleText}">
|
|
<title>${accessibleText}</title>
|
|
<linearGradient id="${gradientId}" x2="0" y2="100%">
|
|
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
|
|
<stop offset="1" stop-opacity=".1"/>
|
|
</linearGradient>
|
|
<mask id="${maskId}"><rect width="${width}" height="200" rx="30" fill="#FFF"/></mask>
|
|
<g mask="url(#${maskId})">
|
|
<rect width="${sbRectWidth}" height="200" fill="#${labelColor}"/>
|
|
<rect width="${stRectWidth}" height="200" fill="#${color}" x="${sbRectWidth}"/>
|
|
<rect width="${width}" height="200" fill="url(#${gradientId})"/>
|
|
</g>
|
|
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
|
<text x="${sbTextStart + 10}" y="148" textLength="${sbTextWidth}" fill="#000" opacity="0.25">${label}</text>
|
|
<text x="${sbTextStart}" y="138" textLength="${sbTextWidth}">${label}</text>
|
|
<text x="${sbRectWidth + 55}" y="148" textLength="${stTextWidth}" fill="#000" opacity="0.25">${status}</text>
|
|
<text x="${sbRectWidth + 45}" y="138" textLength="${stTextWidth}">${status}</text>
|
|
</g>
|
|
${icon ? `<image x="40" y="35" width="${iconWidth}" height="130" xlink:href="${icon}"/>` : ''}
|
|
</svg>`
|
|
}
|
|
|
|
function bare ({ status, color = 'blue', style, scale = 1 }: BadgenOptions) {
|
|
typeAssert(typeof status === 'string', '<status> must be string')
|
|
color = colorPresets[color] || color || colorPresets.blue
|
|
|
|
const stTextWidth = calcWidth(status)
|
|
const stRectWidth = stTextWidth + 115
|
|
|
|
const gradientId = generateRandomID(5)
|
|
const maskId = generateRandomID(5)
|
|
|
|
status = sanitize(status)
|
|
color = sanitize(color)
|
|
|
|
if (style === 'flat') {
|
|
return `<svg width="${scale * stRectWidth / 10}" height="${scale * 20}" viewBox="0 0 ${stRectWidth} 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="${status}">
|
|
<title>${status}</title>
|
|
<g>
|
|
<rect fill="#${color}" x="0" width="${stRectWidth}" height="200"/>
|
|
</g>
|
|
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
|
<text x="65" y="148" textLength="${stTextWidth}" fill="#000" opacity="0.1">${status}</text>
|
|
<text x="55" y="138" textLength="${stTextWidth}">${status}</text>
|
|
</g>
|
|
</svg>`
|
|
}
|
|
|
|
return `<svg width="${scale * stRectWidth / 10}" height="${scale * 20}" viewBox="0 0 ${stRectWidth} 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="${status}">
|
|
<title>${status}</title>
|
|
<linearGradient id="${gradientId}" x2="0" y2="100%">
|
|
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
|
|
<stop offset="1" stop-opacity=".1"/>
|
|
</linearGradient>
|
|
<mask id="${maskId}"><rect width="${stRectWidth}" height="200" rx="30" fill="#FFF"/></mask>
|
|
<g mask="url(#${maskId})">
|
|
<rect width="${stRectWidth}" height="200" fill="#${color}" x="0"/>
|
|
<rect width="${stRectWidth}" height="200" fill="url(#${gradientId})"/>
|
|
</g>
|
|
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
|
<text x="65" y="148" textLength="${stTextWidth}" fill="#000" opacity="0.25">${status}</text>
|
|
<text x="55" y="138" textLength="${stTextWidth}">${status}</text>
|
|
</g>
|
|
</svg>`
|
|
}
|
|
|
|
function sanitize (str: string): string {
|
|
return str
|
|
.replace(/\u0026/g, '&')
|
|
.replace(/\u003C/g, '<')
|
|
.replace(/\u003E/g, '>')
|
|
.replace(/\u0022/g, '"')
|
|
.replace(/\u0027/g, ''')
|
|
}
|
|
|
|
interface AccessibleTextProps {
|
|
status: string;
|
|
label?: string;
|
|
}
|
|
|
|
function generateRandomID(length: number): string {
|
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
let result = '';
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function createAccessibleText({label, status}: AccessibleTextProps): string {
|
|
const labelPrefix = label ? `${label}: ` : '';
|
|
return labelPrefix + status;
|
|
}
|
|
|
|
function typeAssert (assertion: boolean, message: string): void {
|
|
if (!assertion) throw new TypeError(message)
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
badgen: typeof badgen;
|
|
}
|
|
}
|
|
|
|
if (typeof window === 'object') {
|
|
window.badgen = badgen
|
|
}
|