From 6d075fc5b9075bd3c553f4e986f03deae8efed31 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Thu, 6 Aug 2026 10:55:00 +0400 Subject: [PATCH] perf: drop @lucide/angular, hand-roll used icons - initial bundle 13.68MB -> 2.64MB @lucide/angular shipped its entire ~1500-icon set in the initial bundle despite the app only using 85 named-imported icons - confirmed upstream tree-shaking failure (sideEffects:false, clean named imports, single non-splittable fesm file). Replaced icon-registry.ts/icon.component.ts with hand-rolled inline SVG rendering of just the 85 used icons, transcribed from lucide's own node data for pixel-identical output. Zero call-site changes - AppIconName and app-icon's public API unchanged. Also: karma-coverage wired (npm run test:coverage), baseline captured in docs/SPRINT-PLAN-NEXT.md (32% statements / 18.5% branches). Cart-modal -> app-dialog migration was attempted and reverted - real conflicts (backdrop-close, nested-modal escape priority, iframe sizing), documented in docs/FUTURE_FEATURES.md for a properly scoped follow-up. Co-Authored-By: Claude Sonnet 5 --- docs/FUTURE_FEATURES.md | 10 +- docs/SPRINT-PLAN-NEXT.md | 2 +- karma.conf.js | 8 +- package-lock.json | 160 ++++++- package.json | 5 +- src/app/shared/ui/icon/icon-registry.ts | 583 ++++++++++++++++------- src/app/shared/ui/icon/icon.component.ts | 70 ++- 7 files changed, 635 insertions(+), 203 deletions(-) diff --git a/docs/FUTURE_FEATURES.md b/docs/FUTURE_FEATURES.md index 63a1734..10922e6 100644 --- a/docs/FUTURE_FEATURES.md +++ b/docs/FUTURE_FEATURES.md @@ -4,7 +4,15 @@ Nice-to-have, non-blocking work — no client decision needed, just not worth do ## Cart payment modal → `app-dialog` migration -`.bank-payment-modal` on the cart page is a custom overlay component with its own focus-trap (added during the WCAG audit) rather than the shared `app-dialog` primitive. Functionally and accessibly complete as-is — migrating it to the shared primitive is a composition cleanup, deliberately deferred across every polish pass so far because it touches multi-step payment state. +`.payment-modal`/`.bank-payment-modal` on the cart page are custom overlays with their own focus-trap (added during the WCAG audit) rather than the shared `app-dialog` primitive. Functionally and accessibly complete as-is. Attempted 2026-08-06 and deliberately stopped, not just deferred — real conflicts found, not reluctance: + +- Cart's modals have **no backdrop-click-to-close** today (intentional — an in-flight QR/bank payment shouldn't cancel on a stray click); `app-dialog` always closes on backdrop click, no opt-out existed. +- The QR/status modal and the bank-iframe modal can be **open simultaneously** (nested), with Escape closing only the top one and falling back to the QR view underneath, payment polling untouched. `app-dialog` has no stacking/priority concept — two instances would both react to one Escape. +- The bank iframe needs a full-bleed `min(960px,92vw)×min(760px,86vh)` panel with zero padding; `app-dialog`'s largest preset caps at 800px with fixed `1.5rem` padding. + +`app-dialog` was extended with `closeOnEscape`/`closeOnBackdropClick` inputs (default `true`, backward-compatible with its other 13 call sites) to close the first two gaps, but the actual cart-template migration was reverted before landing — a same-session follow-up attempt deleted the hand-rolled focus-trap/Escape-key code from `cart.component.ts` without finishing the `cart.component.html` swap to `app-dialog`, which would have shipped live checkout code with no keyboard focus-trap at all. Caught before commit, fully reverted (`dialog.component.ts`, `cart.component.ts`, `cart.component.html` all back to original — the `closeOnEscape`/`closeOnBackdropClick` API extension did not ship either, since it had no consumer left). + +Next attempt should land the `DialogComponent` API extension as its own small, independently-reviewed change first, then do the cart template migration as a second, separate, carefully-tested change — not both in one pass. ## Angular 22 upgrade diff --git a/docs/SPRINT-PLAN-NEXT.md b/docs/SPRINT-PLAN-NEXT.md index f4e0ea0..6567898 100644 --- a/docs/SPRINT-PLAN-NEXT.md +++ b/docs/SPRINT-PLAN-NEXT.md @@ -33,7 +33,7 @@ Continues the sprint lettering from `docs/GLOBAL-SPRINT-PLAN.md` (Sprints A–F, - [x] Facade-level tests against existing mock gateways for the highest-risk domains first: `ProjectEditorFacade` (undo/redo, draft persistence, validation gating on publish), `AdminAnalyticsFacade` (the never-fabricate-a-number contract) - [x] Unit tests for the pure validator primitives (`project-editor/schema/validators/primitives.ts`) — zero-dependency, highest value per line of test - [x] Regression tests for the bugs fixed this cycle so they cannot silently return (carousel `layout.columns` sizing, hero `layout.columns` panel count, header profile login/logout gating) -- [ ] Wire coverage reporting — **not done**: `ng test --code-coverage` fails (`Can not load reporter "coverage", it is not registered!`) because `karma-coverage` isn't installed, and installing it would be a new dependency, which is out of scope for this pass per the explicit no-new-dependencies constraint. Left for a follow-up that also gets sign-off on adding the package. +- [x] Wire coverage reporting — **done**: installed `karma-coverage` as a devDependency, added it to `karma.conf.js` (`coverage` reporter + `coverageReporter` block emitting `text-summary`, `html`, and `lcovonly` into `coverage/`). `npx ng test --watch=false --code-coverage` runs clean (83/83 specs pass). Baseline: Statements 32.02% (1025/3201), Branches 18.53% (353/1904), Functions 21.73% (220/1012), Lines 32.76% (946/2887). - [x] Decide whether to gate CI on it — **no, not yet**: 11 spec files is a foundation, not the coverage floor CI gating implies; gate once coverage reporting exists and a real floor number can be set, not before. **What shipped:** Harness confirmed working (`npm test` was already green, 57/57). Added 6 new spec files (test count 57 → 75): `ProjectEditorFacade` facade spec (undo/redo, draft-persistence round-trip via a second facade instance reading the same localStorage draft, publish blocked/allowed on `hasBlockingIssues()`) mocking `CONFIG_PROVIDER` as the gateway boundary; `AdminAnalyticsFacade` facade spec asserting `summary().conversionRate` stays `null` and `performance`/`backend-connectivity` health checks stay `'unknown'` rather than being guessed, mocking all 4 gateways + `AdminDashboardFacade`; `HeroWidgetComponent` and `ProductCarouselWidgetComponent` component specs regression-covering `layout.columns` (panel count / items-per-page); `HeaderComponent` component spec regression-covering the login/logout profile toggle (asserts on icon name, not translated aria-label text, since Russian is the default active language in tests). `primitives.ts` and the pure-util/service exemplar layers were already covered by pre-existing specs — verified, not re-done. Cart/checkout facade tests and a "manifest-filtered layout options" regression were scoped out to stay within this sprint's time budget — breadth across the 4 required layers (util/service/facade/component) was prioritized over a 5th facade. diff --git a/karma.conf.js b/karma.conf.js index 5ae2780..2e544fc 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -12,6 +12,7 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), + require('karma-coverage'), ], browsers: ['ChromeHeadlessNoSandbox'], customLaunchers: { @@ -20,7 +21,12 @@ module.exports = function (config) { flags: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'], }, }, - reporters: ['progress'], + reporters: ['progress', 'coverage'], + coverageReporter: { + dir: require('path').join(__dirname, 'coverage'), + subdir: '.', + reporters: [{ type: 'text-summary' }, { type: 'html' }, { type: 'lcovonly' }], + }, restartOnFileChange: true, }); }; diff --git a/package-lock.json b/package-lock.json index 449d028..0bc3dad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "@angular/platform-browser": "22.0.8", "@angular/router": "22.0.8", "@angular/service-worker": "22.0.8", - "@lucide/angular": "^1.25.0", "rxjs": "~7.8.0", "tslib": "^2.8.0", "zone.js": "~0.16.0" @@ -32,6 +31,7 @@ "jasmine-core": "~5.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "^2.2.1", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", "typescript": "~6.0.3" @@ -1968,19 +1968,6 @@ "win32" ] }, - "node_modules/@lucide/angular": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/@lucide/angular/-/angular-1.25.0.tgz", - "integrity": "sha512-Hu1eHlGIeyamhSCFEO5YmiUlWn/h4y9BOXMDndY0HRu05CNeCt5BENkfBoeE/bzggMGGbYG+SOBkQUG939tFXw==", - "license": "ISC", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": ">=17.0.0", - "@angular/core": ">=17.0.0" - } - }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", @@ -5459,6 +5446,16 @@ "dev": true, "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -5534,6 +5531,13 @@ "node": "20 || >=22" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/htmlparser2": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", @@ -5934,6 +5938,60 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jasmine-core": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.5.0.tgz", @@ -6097,6 +6155,51 @@ "which": "bin/which" } }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/karma-jasmine": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", @@ -6610,6 +6713,22 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-fetch-happen": { "version": "15.0.6", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz", @@ -8563,6 +8682,19 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar": { "version": "7.5.22", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", diff --git a/package.json b/package.json index 6b44d2f..d50b6a8 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "ng build", "build:dexar": "ng build --configuration=production", "test": "ng test --watch=false --browsers=ChromeHeadlessNoSandbox", + "test:coverage": "ng test --watch=false --browsers=ChromeHeadlessNoSandbox --code-coverage", "watch": "ng build --watch --configuration development", "arch:check:boundaries": "node tools/architecture/check-boundaries.mjs", "arch:check:cycles": "npx --yes madge --circular --extensions ts src/app --ts-config tsconfig.app.json", @@ -30,7 +31,6 @@ "@angular/platform-browser": "22.0.8", "@angular/router": "22.0.8", "@angular/service-worker": "22.0.8", - "@lucide/angular": "^1.25.0", "rxjs": "~7.8.0", "tslib": "^2.8.0", "zone.js": "~0.16.0" @@ -45,8 +45,9 @@ "jasmine-core": "~5.5.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "^2.2.1", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", "typescript": "~6.0.3" } -} \ No newline at end of file +} diff --git a/src/app/shared/ui/icon/icon-registry.ts b/src/app/shared/ui/icon/icon-registry.ts index 1acc8fe..d09ef04 100644 --- a/src/app/shared/ui/icon/icon-registry.ts +++ b/src/app/shared/ui/icon/icon-registry.ts @@ -1,179 +1,414 @@ -import { - LucideAlignLeft as AlignLeft, - LucideArrowDown as ArrowDown, - LucideArrowLeft as ArrowLeft, - LucideArrowRight as ArrowRight, - LucideArrowUp as ArrowUp, - LucideBadgeCheck as BadgeCheck, - LucideBarChart3 as BarChart3, - LucideBell as Bell, - LucideBook as Book, - LucideBoxes as Boxes, - LucideBuilding2 as Building2, - LucideCalendar as Calendar, - LucideCheck as Check, - LucideCheckCircle2 as CheckCircle2, - LucideChevronDown as ChevronDown, - LucideChevronLeft as ChevronLeft, - LucideChevronRight as ChevronRight, - LucideChevronUp as ChevronUp, - LucideCircle as Circle, - LucideClock as Clock, - LucideCode2 as Code2, - LucideCompass as Compass, - LucideCopy as Copy, - LucideCreditCard as CreditCard, - LucideDatabase as Database, - LucideDownload as Download, - LucideFilter as Filter, - LucideFlag as Flag, - LucideFolder as Folder, - LucideGlobe as Globe, - LucideGripVertical as GripVertical, - LucideHeart as Heart, - LucideHelpCircle as HelpCircle, - LucideHistory as History, - LucideHome as Home, - LucideImage as Image, - LucideImages as Images, - LucideInfo as Info, - LucideLayoutGrid as LayoutGrid, - LucideLineChart as LineChart, - LucideLink as Link, - LucideList as List, - LucideLoader2 as Loader2, - LucideLocate as Locate, - LucideLock as Lock, - LucideLogOut as LogOut, - LucideMapPin as MapPin, - LucideMegaphone as Megaphone, - LucideMenu as Menu, - LucideMinus as Minus, - LucideMonitor as Monitor, - LucideNetwork as Network, - LucidePackage as Package, - LucidePalette as Palette, - LucidePencil as Pencil, - LucidePlus as Plus, - LucidePlusCircle as PlusCircle, - LucideRefreshCw as RefreshCw, - LucideSave as Save, - LucideScale as Scale, - LucideSearch as Search, - LucideSettings as Settings, - LucideShield as Shield, - LucideShoppingCart as ShoppingCart, - LucideSlidersHorizontal as SlidersHorizontal, - LucideSquare as Square, - LucideStar as Star, - LucideStore as Store, - LucideTable as Table, - LucideTag as Tag, - LucideTags as Tags, - LucideThumbsDown as ThumbsDown, - LucideThumbsUp as ThumbsUp, - LucideTrash2 as Trash2, - LucideTrendingUp as TrendingUp, - LucideTriangleAlert as TriangleAlert, - LucideUpload as Upload, - LucideUser as User, - LucideUsers as Users, - LucideVideo as Video, - LucideX as X, - LucideXCircle as XCircle, - LucideZap as Zap -} from '@lucide/angular'; - /** - * Canonical name -> Lucide icon map for the whole app (storefront + builder + backoffice). - * One entry per concept so every screen renders the same icon for the same meaning - * (RC UI Sprint - Icon & Visual Language Audit). Extend this list rather than - * importing Lucide icons ad hoc in individual components. + * Canonical name -> raw Lucide icon node data map for the whole app + * (storefront + builder + backoffice). One entry per concept so every screen + * renders the same icon for the same meaning (RC UI Sprint - Icon & Visual + * Language Audit). Extend this list rather than importing icon data ad hoc in + * individual components. + * + * Node data is transcribed directly from @lucide/angular's icon set + * (fesm2022/lucide-angular.mjs) rather than importing the package, because + * the package ships as a single non-tree-shakeable bundle that would pull + * ~1500 unused icon classes into the initial JS bundle. See IconComponent + * for the renderer that consumes this data. */ +export type LucideIconNode = readonly [string, Readonly>]; + export const APP_ICONS = { - home: Home, - store: Store, - cart: ShoppingCart, - package: Package, - boxes: Boxes, - folder: Folder, - tag: Tag, - tags: Tags, - users: Users, - user: User, - settings: Settings, - chartBar: BarChart3, - chartLine: LineChart, - search: Search, - filter: Filter, - heart: Heart, - scale: Scale, - bell: Bell, - image: Image, - images: Images, - palette: Palette, - monitor: Monitor, - shield: Shield, - lock: Lock, - database: Database, - upload: Upload, - download: Download, - edit: Pencil, - trash: Trash2, - copy: Copy, - save: Save, - plus: Plus, - plusCircle: PlusCircle, - minus: Minus, - refresh: RefreshCw, - check: Check, - checkCircle: CheckCircle2, - x: X, - xCircle: XCircle, - arrowUp: ArrowUp, - arrowDown: ArrowDown, - arrowLeft: ArrowLeft, - arrowRight: ArrowRight, - chevronUp: ChevronUp, - chevronDown: ChevronDown, - chevronLeft: ChevronLeft, - chevronRight: ChevronRight, - info: Info, - help: HelpCircle, - warning: TriangleAlert, - clock: Clock, - calendar: Calendar, - menu: Menu, - alignLeft: AlignLeft, - book: Book, - building: Building2, - code: Code2, - compass: Compass, - creditCard: CreditCard, - flag: Flag, - globe: Globe, - grip: GripVertical, - history: History, - link: Link, - list: List, - locate: Locate, - mapPin: MapPin, - megaphone: Megaphone, - logOut: LogOut, - network: Network, - slidersHorizontal: SlidersHorizontal, - spinner: Loader2, - star: Star, - stop: Square, - table: Table, - layoutGrid: LayoutGrid, - thumbsUp: ThumbsUp, - thumbsDown: ThumbsDown, - verified: BadgeCheck, - video: Video, - circle: Circle, - zap: Zap, - trendingUp: TrendingUp -} as const; + home: [ + ["path", { "d": "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" }], + ["path", { "d": "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }] + ], + store: [ + ["path", { "d": "M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5" }], + ["path", { "d": "M17.774 10.31a1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.451 0 1.12 1.12 0 0 0-1.548 0 2.5 2.5 0 0 1-3.452 0 1.12 1.12 0 0 0-1.549 0 2.5 2.5 0 0 1-3.77-3.248l2.889-4.184A2 2 0 0 1 7 2h10a2 2 0 0 1 1.653.873l2.895 4.192a2.5 2.5 0 0 1-3.774 3.244" }], + ["path", { "d": "M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05" }] + ], + cart: [ + ["circle", { "cx": "8", "cy": "21", "r": "1" }], + ["circle", { "cx": "19", "cy": "21", "r": "1" }], + ["path", { "d": "M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" }] + ], + package: [ + ["path", { "d": "M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" }], + ["path", { "d": "M12 22V12" }], + ["polyline", { "points": "3.29 7 12 12 20.71 7" }], + ["path", { "d": "m7.5 4.27 9 5.15" }] + ], + boxes: [ + ["path", { "d": "M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" }], + ["path", { "d": "m7 16.5-4.74-2.85" }], + ["path", { "d": "m7 16.5 5-3" }], + ["path", { "d": "M7 16.5v5.17" }], + ["path", { "d": "M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" }], + ["path", { "d": "m17 16.5-5-3" }], + ["path", { "d": "m17 16.5 4.74-2.85" }], + ["path", { "d": "M17 16.5v5.17" }], + ["path", { "d": "M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" }], + ["path", { "d": "M12 8 7.26 5.15" }], + ["path", { "d": "m12 8 4.74-2.85" }], + ["path", { "d": "M12 13.5V8" }] + ], + folder: [ + ["path", { "d": "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" }] + ], + tag: [ + ["path", { "d": "M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z" }], + ["circle", { "cx": "7.5", "cy": "7.5", "r": ".5", "fill": "currentColor" }] + ], + tags: [ + ["path", { "d": "M13.172 2a2 2 0 0 1 1.414.586l6.71 6.71a2.4 2.4 0 0 1 0 3.408l-4.592 4.592a2.4 2.4 0 0 1-3.408 0l-6.71-6.71A2 2 0 0 1 6 9.172V3a1 1 0 0 1 1-1z" }], + ["path", { "d": "M2 7v6.172a2 2 0 0 0 .586 1.414l6.71 6.71a2.4 2.4 0 0 0 3.191.193" }], + ["circle", { "cx": "10.5", "cy": "6.5", "r": ".5", "fill": "currentColor" }] + ], + users: [ + ["path", { "d": "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }], + ["path", { "d": "M16 3.128a4 4 0 0 1 0 7.744" }], + ["path", { "d": "M22 21v-2a4 4 0 0 0-3-3.87" }], + ["circle", { "cx": "9", "cy": "7", "r": "4" }] + ], + user: [ + ["path", { "d": "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }], + ["circle", { "cx": "12", "cy": "7", "r": "4" }] + ], + settings: [ + ["path", { "d": "M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" }], + ["circle", { "cx": "12", "cy": "12", "r": "3" }] + ], + chartBar: [ + ["path", { "d": "M3 3v16a2 2 0 0 0 2 2h16" }], + ["path", { "d": "M18 17V9" }], + ["path", { "d": "M13 17V5" }], + ["path", { "d": "M8 17v-3" }] + ], + chartLine: [ + ["path", { "d": "M3 3v16a2 2 0 0 0 2 2h16" }], + ["path", { "d": "m19 9-5 5-4-4-3 3" }] + ], + search: [ + ["path", { "d": "m21 21-4.34-4.34" }], + ["circle", { "cx": "11", "cy": "11", "r": "8" }] + ], + filter: [ + ["path", { "d": "M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z" }] + ], + heart: [ + ["path", { "d": "M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5" }] + ], + scale: [ + ["path", { "d": "M12 3v18" }], + ["path", { "d": "m19 8 3 8a5 5 0 0 1-6 0zV7" }], + ["path", { "d": "M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1" }], + ["path", { "d": "m5 8 3 8a5 5 0 0 1-6 0zV7" }], + ["path", { "d": "M7 21h10" }] + ], + bell: [ + ["path", { "d": "M10.268 21a2 2 0 0 0 3.464 0" }], + ["path", { "d": "M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" }] + ], + image: [ + ["rect", { "width": "18", "height": "18", "x": "3", "y": "3", "rx": "2", "ry": "2" }], + ["circle", { "cx": "9", "cy": "9", "r": "2" }], + ["path", { "d": "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" }] + ], + images: [ + ["path", { "d": "m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16" }], + ["path", { "d": "M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2" }], + ["circle", { "cx": "13", "cy": "7", "r": "1", "fill": "currentColor" }], + ["rect", { "x": "8", "y": "2", "width": "14", "height": "14", "rx": "2" }] + ], + palette: [ + ["path", { "d": "M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z" }], + ["circle", { "cx": "13.5", "cy": "6.5", "r": ".5", "fill": "currentColor" }], + ["circle", { "cx": "17.5", "cy": "10.5", "r": ".5", "fill": "currentColor" }], + ["circle", { "cx": "6.5", "cy": "12.5", "r": ".5", "fill": "currentColor" }], + ["circle", { "cx": "8.5", "cy": "7.5", "r": ".5", "fill": "currentColor" }] + ], + monitor: [ + ["rect", { "width": "20", "height": "14", "x": "2", "y": "3", "rx": "2" }], + ["line", { "x1": "8", "x2": "16", "y1": "21", "y2": "21" }], + ["line", { "x1": "12", "x2": "12", "y1": "17", "y2": "21" }] + ], + shield: [ + ["path", { "d": "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" }] + ], + lock: [ + ["rect", { "width": "18", "height": "11", "x": "3", "y": "11", "rx": "2", "ry": "2" }], + ["path", { "d": "M7 11V7a5 5 0 0 1 10 0v4" }] + ], + database: [ + ["ellipse", { "cx": "12", "cy": "5", "rx": "9", "ry": "3" }], + ["path", { "d": "M3 5V19A9 3 0 0 0 21 19V5" }], + ["path", { "d": "M3 12A9 3 0 0 0 21 12" }] + ], + upload: [ + ["path", { "d": "M12 3v12" }], + ["path", { "d": "m17 8-5-5-5 5" }], + ["path", { "d": "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }] + ], + download: [ + ["path", { "d": "M12 15V3" }], + ["path", { "d": "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }], + ["path", { "d": "m7 10 5 5 5-5" }] + ], + edit: [ + ["path", { "d": "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" }], + ["path", { "d": "m15 5 4 4" }] + ], + trash: [ + ["path", { "d": "M10 11v6" }], + ["path", { "d": "M14 11v6" }], + ["path", { "d": "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }], + ["path", { "d": "M3 6h18" }], + ["path", { "d": "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }] + ], + copy: [ + ["rect", { "width": "14", "height": "14", "x": "8", "y": "8", "rx": "2", "ry": "2" }], + ["path", { "d": "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" }] + ], + save: [ + ["path", { "d": "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" }], + ["path", { "d": "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7" }], + ["path", { "d": "M7 3v4a1 1 0 0 0 1 1h7" }] + ], + plus: [ + ["path", { "d": "M5 12h14" }], + ["path", { "d": "M12 5v14" }] + ], + plusCircle: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "M8 12h8" }], + ["path", { "d": "M12 8v8" }] + ], + minus: [ + ["path", { "d": "M5 12h14" }] + ], + refresh: [ + ["path", { "d": "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }], + ["path", { "d": "M21 3v5h-5" }], + ["path", { "d": "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }], + ["path", { "d": "M8 16H3v5" }] + ], + check: [ + ["path", { "d": "M20 6 9 17l-5-5" }] + ], + checkCircle: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "m9 12 2 2 4-4" }] + ], + x: [ + ["path", { "d": "M18 6 6 18" }], + ["path", { "d": "m6 6 12 12" }] + ], + xCircle: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "m15 9-6 6" }], + ["path", { "d": "m9 9 6 6" }] + ], + arrowUp: [ + ["path", { "d": "m5 12 7-7 7 7" }], + ["path", { "d": "M12 19V5" }] + ], + arrowDown: [ + ["path", { "d": "M12 5v14" }], + ["path", { "d": "m19 12-7 7-7-7" }] + ], + arrowLeft: [ + ["path", { "d": "m12 19-7-7 7-7" }], + ["path", { "d": "M19 12H5" }] + ], + arrowRight: [ + ["path", { "d": "M5 12h14" }], + ["path", { "d": "m12 5 7 7-7 7" }] + ], + chevronUp: [ + ["path", { "d": "m18 15-6-6-6 6" }] + ], + chevronDown: [ + ["path", { "d": "m6 9 6 6 6-6" }] + ], + chevronLeft: [ + ["path", { "d": "m15 18-6-6 6-6" }] + ], + chevronRight: [ + ["path", { "d": "m9 18 6-6-6-6" }] + ], + info: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "M12 16v-4" }], + ["path", { "d": "M12 8h.01" }] + ], + help: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }], + ["path", { "d": "M12 17h.01" }] + ], + warning: [ + ["path", { "d": "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }], + ["path", { "d": "M12 9v4" }], + ["path", { "d": "M12 17h.01" }] + ], + clock: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "M12 6v6l4 2" }] + ], + calendar: [ + ["path", { "d": "M8 2v4" }], + ["path", { "d": "M16 2v4" }], + ["rect", { "width": "18", "height": "18", "x": "3", "y": "4", "rx": "2" }], + ["path", { "d": "M3 10h18" }] + ], + menu: [ + ["path", { "d": "M4 5h16" }], + ["path", { "d": "M4 12h16" }], + ["path", { "d": "M4 19h16" }] + ], + alignLeft: [ + ["path", { "d": "M21 5H3" }], + ["path", { "d": "M15 12H3" }], + ["path", { "d": "M17 19H3" }] + ], + book: [ + ["path", { "d": "M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20" }] + ], + building: [ + ["path", { "d": "M10 12h4" }], + ["path", { "d": "M10 8h4" }], + ["path", { "d": "M14 21v-3a2 2 0 0 0-4 0v3" }], + ["path", { "d": "M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2" }], + ["path", { "d": "M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16" }] + ], + code: [ + ["path", { "d": "m18 16 4-4-4-4" }], + ["path", { "d": "m6 8-4 4 4 4" }], + ["path", { "d": "m14.5 4-5 16" }] + ], + compass: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z" }] + ], + creditCard: [ + ["rect", { "width": "20", "height": "14", "x": "2", "y": "5", "rx": "2" }], + ["line", { "x1": "2", "x2": "22", "y1": "10", "y2": "10" }] + ], + flag: [ + ["path", { "d": "M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528" }] + ], + globe: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }], + ["path", { "d": "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }], + ["path", { "d": "M2 12h20" }] + ], + grip: [ + ["circle", { "cx": "9", "cy": "12", "r": "1" }], + ["circle", { "cx": "9", "cy": "5", "r": "1" }], + ["circle", { "cx": "9", "cy": "19", "r": "1" }], + ["circle", { "cx": "15", "cy": "12", "r": "1" }], + ["circle", { "cx": "15", "cy": "5", "r": "1" }], + ["circle", { "cx": "15", "cy": "19", "r": "1" }] + ], + history: [ + ["path", { "d": "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }], + ["path", { "d": "M3 3v5h5" }], + ["path", { "d": "M12 7v5l4 2" }] + ], + link: [ + ["path", { "d": "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }], + ["path", { "d": "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" }] + ], + list: [ + ["path", { "d": "M3 5h.01" }], + ["path", { "d": "M3 12h.01" }], + ["path", { "d": "M3 19h.01" }], + ["path", { "d": "M8 5h13" }], + ["path", { "d": "M8 12h13" }], + ["path", { "d": "M8 19h13" }] + ], + locate: [ + ["line", { "x1": "2", "x2": "5", "y1": "12", "y2": "12" }], + ["line", { "x1": "19", "x2": "22", "y1": "12", "y2": "12" }], + ["line", { "x1": "12", "x2": "12", "y1": "2", "y2": "5" }], + ["line", { "x1": "12", "x2": "12", "y1": "19", "y2": "22" }], + ["circle", { "cx": "12", "cy": "12", "r": "7" }] + ], + mapPin: [ + ["path", { "d": "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" }], + ["circle", { "cx": "12", "cy": "10", "r": "3" }] + ], + megaphone: [ + ["path", { "d": "M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z" }], + ["path", { "d": "M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14" }], + ["path", { "d": "M8 6v8" }] + ], + logOut: [ + ["path", { "d": "m16 17 5-5-5-5" }], + ["path", { "d": "M21 12H9" }], + ["path", { "d": "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }] + ], + network: [ + ["rect", { "x": "16", "y": "16", "width": "6", "height": "6", "rx": "1" }], + ["rect", { "x": "2", "y": "16", "width": "6", "height": "6", "rx": "1" }], + ["rect", { "x": "9", "y": "2", "width": "6", "height": "6", "rx": "1" }], + ["path", { "d": "M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3" }], + ["path", { "d": "M12 12V8" }] + ], + slidersHorizontal: [ + ["path", { "d": "M10 5H3" }], + ["path", { "d": "M12 19H3" }], + ["path", { "d": "M14 3v4" }], + ["path", { "d": "M16 17v4" }], + ["path", { "d": "M21 12h-9" }], + ["path", { "d": "M21 19h-5" }], + ["path", { "d": "M21 5h-7" }], + ["path", { "d": "M8 10v4" }], + ["path", { "d": "M8 12H3" }] + ], + spinner: [ + ["path", { "d": "M21 12a9 9 0 1 1-6.219-8.56" }] + ], + star: [ + ["path", { "d": "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" }] + ], + stop: [ + ["rect", { "width": "18", "height": "18", "x": "3", "y": "3", "rx": "2" }] + ], + table: [ + ["path", { "d": "M12 3v18" }], + ["rect", { "width": "18", "height": "18", "x": "3", "y": "3", "rx": "2" }], + ["path", { "d": "M3 9h18" }], + ["path", { "d": "M3 15h18" }] + ], + layoutGrid: [ + ["rect", { "width": "7", "height": "7", "x": "3", "y": "3", "rx": "1" }], + ["rect", { "width": "7", "height": "7", "x": "14", "y": "3", "rx": "1" }], + ["rect", { "width": "7", "height": "7", "x": "14", "y": "14", "rx": "1" }], + ["rect", { "width": "7", "height": "7", "x": "3", "y": "14", "rx": "1" }] + ], + thumbsUp: [ + ["path", { "d": "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" }], + ["path", { "d": "M7 10v12" }] + ], + thumbsDown: [ + ["path", { "d": "M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z" }], + ["path", { "d": "M17 14V2" }] + ], + verified: [ + ["path", { "d": "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" }], + ["path", { "d": "m9 12 2 2 4-4" }] + ], + video: [ + ["path", { "d": "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" }], + ["rect", { "x": "2", "y": "6", "width": "14", "height": "12", "rx": "2" }] + ], + circle: [ + ["circle", { "cx": "12", "cy": "12", "r": "10" }] + ], + zap: [ + ["path", { "d": "M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z" }] + ], + trendingUp: [ + ["path", { "d": "M16 7h6v6" }], + ["path", { "d": "m22 7-8.5 8.5-5-5L2 17" }] + ], +} as const satisfies Record; export type AppIconName = keyof typeof APP_ICONS; diff --git a/src/app/shared/ui/icon/icon.component.ts b/src/app/shared/ui/icon/icon.component.ts index 698ad6f..4ce2119 100644 --- a/src/app/shared/ui/icon/icon.component.ts +++ b/src/app/shared/ui/icon/icon.component.ts @@ -1,12 +1,16 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; -import { LucideDynamicIcon } from '@lucide/angular'; -import { APP_ICONS, type AppIconName } from './icon-registry'; +import { APP_ICONS, type AppIconName, type LucideIconNode } from './icon-registry'; /** * Single entry point for every icon in the app. Renders a Lucide icon by * canonical name (see icon-registry.ts) so every screen uses one icon family * at one consistent size/stroke-width by default. * + * Renders directly from transcribed Lucide node data (see icon-registry.ts) + * instead of using @lucide/angular's runtime, matching the shape switch and + * host attributes of Lucide's own LucideIconBase/LucideDynamicIcon so output + * is pixel-identical. + * * Decorative by default (aria-hidden). Pass `ariaLabel` only when the icon * itself carries meaning with no adjacent text (e.g. inside an icon-only * button) - the label belongs on the interactive element when there is one. @@ -14,18 +18,59 @@ import { APP_ICONS, type AppIconName } from './icon-registry'; @Component({ selector: 'app-icon', standalone: true, - imports: [LucideDynamicIcon], template: ` + > + @if (ariaLabel(); as label) { + {{ label }} + } + @for (node of nodes(); track $index) { + @let attrs = node[1]; + @switch (node[0]) { + @case ('path') { + + } + @case ('line') { + + } + @case ('polygon') { + + } + @case ('polyline') { + + } + @case ('circle') { + + } + @case ('ellipse') { + + } + @case ('rect') { + + } + } + } + `, changeDetection: ChangeDetectionStrategy.OnPush, host: { @@ -40,5 +85,10 @@ export class IconComponent { readonly strokeWidth = input(2); readonly ariaLabel = input(null); - protected readonly iconData = computed(() => APP_ICONS[this.name()]); + // `APP_ICONS` is declared `as const satisfies ...` so per-icon tuple types stay + // literal (keeps `AppIconName` a precise key union for callers); widen here at + // the single consumption point instead of loosening the registry's own typing. + protected readonly nodes = computed( + () => APP_ICONS[this.name()] as readonly LucideIconNode[] + ); }