/** * Inline SVG icon library for the plank training app. * * Each icon is a 24x24 Material-style path. We bake the color directly into * the SVG data URI, so consumers can switch between "active" and "inactive" * variants by simply selecting a different key — no font loading, no CSS * pseudo-element tricks, and exact pixel rendering at any size. * * Usage: * const icons = require('../../utils/icons') * const map = icons.build({ primary: '#FF6B35', primaryLight: '#FF8C5A', success: '#4CAF50' }) * // map.playFill => "data:image/svg+xml;..." (orange play) * // map.play => "data:image/svg+xml;..." (gray play) * * Naming convention: * = outlined / inactive (neutral gray) * Fill = filled / active (theme color) */ const PATHS = { // Navigation & UI home: '', homeFill: '', calendar: '', calendarFill: '', settings: '', settingsFill: '', back: '', right: '', close: '', check: '', // Status & feedback hot: '', hotFill: '', like: '', likeFill: '', // Training specific play: '', playFill: '', stop: '', stopFill: '', pause: '', pauseFill: '', time: '', timeFill: '', // Loop / repeat (circuit training entry) repeat: '', repeatFill: '', // Markers & targets target: '', targetFill: '', mark: '', markFill: '', // Stats & charts rank: '', rankFill: '', crown: '', crownFill: '', // Trophy (record celebrations) — Material emoji-events trophy: '', trophyFill: '', // Badge icons (training milestone medals) — replace the old emoji set. // Single-color fills; a gray (locked) and white White // (unlocked, sits on the theme-color disc) variant are both built. bolt: '', moon: '', diamond: '', star: '', // Phosphor medal — scaled from 256 to 24 viewBox (a 命令的 large-arc/sweep // 标志位保持 0/1,仅坐标缩放)。试点勋章图标,挂在 30 天「神登」上。 medal: '', // Phosphor 勋章系全套(同为 256→24 缩放)。7 级递进: // sealCheck(3天)→medal(7天)→medalMilitary(14天)→trophyPh(30天) // →certificate(60天)→shieldStar(80天)→crownPh(100天)。 // trophyPh/crownPh 带 Ph 后缀避免与下方 Material 版 trophy/crown 冲突 // (后者仍被完成弹窗/排行榜使用)。 sealCheck: '', medalMilitary: '', trophyPh: '', certificate: '', shieldStar: '', crownPh: '', // Party popper (first-time celebrations) — Material celebration party: '', partyFill: '', // Sparkle (generic celebrations + confetti particles) sparkle: '', sparkleFill: '', form: '', formFill: '', // Notification notification: '', notificationFill: '', notificationForbid: '', notificationForbidFill: '', // People & users people: '', peopleFill: '', // Actions delete: '', deleteFill: '', info: '', infoFill: '', // Theme & preferences skin: '', skinFill: '', // Lightbulb & tip light: '', lightFill: '', // Voice voice: '', voiceFill: '', // Round check roundCheck: '', roundCheckFill: '', // Emoji / face emoji: '', emojiFill: '', // Edit (pencil) edit: '', editFill: '' } // NOTE: must encodeURIComponent the SVG so that the `#` in colors like // `#FF6B35` doesn't get parsed as a fragment-identifier delimiter — that // would silently truncate the SVG and the image would fail to render. const _svg = (path, color) => { const svg = '' + path + '' return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg) } const INACTIVE = '#999999' const DANGER = '#E53935' // 勋章主题色变体(个人资料昵称旁浅底展示用)。复用同一 path 对象引用, // build 循环对 `*Fill` 命名自动生成主题色版 out.xxxFill —— 浅色背景上 // 白版会隐形,灰版像未解锁,主题色版才像"已获得的荣誉勋章"。 PATHS.sealCheckFill = PATHS.sealCheck PATHS.medalFill = PATHS.medal PATHS.medalMilitaryFill = PATHS.medalMilitary PATHS.trophyPhFill = PATHS.trophyPh PATHS.certificateFill = PATHS.certificate PATHS.shieldStarFill = PATHS.shieldStar PATHS.crownPhFill = PATHS.crownPh /** * Build an icon map keyed by name. Two variants per name: * = inactive (gray) * Fill = active (theme primary) * Plus semantic aliases (delete, etc.) with appropriate colors. * * @param {object} theme - { primary, primaryLight, success, danger } * @returns {object} { : dataUri, ... } */ const build = (theme) => { const t = theme || {} const p = t.primary || '#FF6B35' const s = t.success || '#4CAF50' const d = t.danger || DANGER const out = {} Object.keys(PATHS).forEach((name) => { if (name.endsWith('Fill')) { // Fill variant: filled path + primary color. // Do NOT write out[base] here — that would overwrite the outlined // variant set by the base-name entry below with the wrong path. out[name] = _svg(PATHS[name], p) } else { // Outlined variant: outlined path + inactive gray. out[name] = _svg(PATHS[name], INACTIVE) } }) // Semantic overrides out.deleteActive = _svg(PATHS.deleteFill, d) out.deleteInactive = _svg(PATHS.delete, INACTIVE) out.successFill = _svg(PATHS.roundCheckFill, s) out.successLine = _svg(PATHS.roundCheck, INACTIVE) // White variants for icons sitting on the theme-gradient card / disc // (completion modal, podium accents, the index hero card, etc.) AND for // icons rendered on the primary (filled) buttons, whose background is the // same theme color as a *Fill variant — orange-on-orange would vanish. // Baked-in white is REQUIRED: these are data-URI SVG tags, so CSS // color / currentColor cannot recolor them; only a white SVG works. out.trophyWhite = _svg(PATHS.trophyFill, '#FFFFFF') out.partyWhite = _svg(PATHS.partyFill, '#FFFFFF') out.hotWhite = _svg(PATHS.hotFill, '#FFFFFF') out.formWhite = _svg(PATHS.formFill, '#FFFFFF') out.playWhite = _svg(PATHS.playFill, '#FFFFFF') out.sparkleWhite = _svg(PATHS.sparkleFill, '#FFFFFF') out.checkWhite = _svg(PATHS.check, '#FFFFFF') // Badge white variants — sit on the theme-color (filled) node disc. out.boltWhite = _svg(PATHS.bolt, '#FFFFFF') out.moonWhite = _svg(PATHS.moon, '#FFFFFF') out.diamondWhite = _svg(PATHS.diamond, '#FFFFFF') out.starWhite = _svg(PATHS.star, '#FFFFFF') out.crownWhite = _svg(PATHS.crown, '#FFFFFF') // Phosphor medal white (trial badge icon) out.medalWhite = _svg(PATHS.medal, '#FFFFFF') // Phosphor 勋章系全套 white(解锁后坐主题色圆盘) out.sealCheckWhite = _svg(PATHS.sealCheck, '#FFFFFF') out.medalMilitaryWhite = _svg(PATHS.medalMilitary, '#FFFFFF') out.trophyPhWhite = _svg(PATHS.trophyPh, '#FFFFFF') out.certificateWhite = _svg(PATHS.certificate, '#FFFFFF') out.shieldStarWhite = _svg(PATHS.shieldStar, '#FFFFFF') out.crownPhWhite = _svg(PATHS.crownPh, '#FFFFFF') return out } module.exports = { build, PATHS, INACTIVE }