perf: drop @lucide/angular, hand-roll used icons - initial bundle 13.68MB -> 2.64MB
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
@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 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,15 @@ Nice-to-have, non-blocking work — no client decision needed, just not worth do
|
|||||||
|
|
||||||
## Cart payment modal → `app-dialog` migration
|
## 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
|
## Angular 22 upgrade
|
||||||
|
|
||||||
|
|||||||
@@ -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] 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] 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)
|
- [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.
|
- [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.
|
**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.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ module.exports = function (config) {
|
|||||||
require('karma-jasmine'),
|
require('karma-jasmine'),
|
||||||
require('karma-chrome-launcher'),
|
require('karma-chrome-launcher'),
|
||||||
require('karma-jasmine-html-reporter'),
|
require('karma-jasmine-html-reporter'),
|
||||||
|
require('karma-coverage'),
|
||||||
],
|
],
|
||||||
browsers: ['ChromeHeadlessNoSandbox'],
|
browsers: ['ChromeHeadlessNoSandbox'],
|
||||||
customLaunchers: {
|
customLaunchers: {
|
||||||
@@ -20,7 +21,12 @@ module.exports = function (config) {
|
|||||||
flags: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
|
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,
|
restartOnFileChange: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
160
package-lock.json
generated
160
package-lock.json
generated
@@ -17,7 +17,6 @@
|
|||||||
"@angular/platform-browser": "22.0.8",
|
"@angular/platform-browser": "22.0.8",
|
||||||
"@angular/router": "22.0.8",
|
"@angular/router": "22.0.8",
|
||||||
"@angular/service-worker": "22.0.8",
|
"@angular/service-worker": "22.0.8",
|
||||||
"@lucide/angular": "^1.25.0",
|
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.8.0",
|
"tslib": "^2.8.0",
|
||||||
"zone.js": "~0.16.0"
|
"zone.js": "~0.16.0"
|
||||||
@@ -32,6 +31,7 @@
|
|||||||
"jasmine-core": "~5.5.0",
|
"jasmine-core": "~5.5.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
"karma-coverage": "^2.2.1",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"typescript": "~6.0.3"
|
"typescript": "~6.0.3"
|
||||||
@@ -1968,19 +1968,6 @@
|
|||||||
"win32"
|
"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": {
|
"node_modules/@modelcontextprotocol/sdk": {
|
||||||
"version": "1.29.0",
|
"version": "1.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
|
||||||
@@ -5459,6 +5446,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/has-symbols": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
@@ -5534,6 +5531,13 @@
|
|||||||
"node": "20 || >=22"
|
"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": {
|
"node_modules/htmlparser2": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
|
||||||
@@ -5934,6 +5938,60 @@
|
|||||||
"node": ">=10"
|
"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": {
|
"node_modules/jasmine-core": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.5.0.tgz",
|
||||||
@@ -6097,6 +6155,51 @@
|
|||||||
"which": "bin/which"
|
"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": {
|
"node_modules/karma-jasmine": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz",
|
||||||
@@ -6610,6 +6713,22 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@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": {
|
"node_modules/make-fetch-happen": {
|
||||||
"version": "15.0.6",
|
"version": "15.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz",
|
"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"
|
"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": {
|
"node_modules/tar": {
|
||||||
"version": "7.5.22",
|
"version": "7.5.22",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"build:dexar": "ng build --configuration=production",
|
"build:dexar": "ng build --configuration=production",
|
||||||
"test": "ng test --watch=false --browsers=ChromeHeadlessNoSandbox",
|
"test": "ng test --watch=false --browsers=ChromeHeadlessNoSandbox",
|
||||||
|
"test:coverage": "ng test --watch=false --browsers=ChromeHeadlessNoSandbox --code-coverage",
|
||||||
"watch": "ng build --watch --configuration development",
|
"watch": "ng build --watch --configuration development",
|
||||||
"arch:check:boundaries": "node tools/architecture/check-boundaries.mjs",
|
"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",
|
"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/platform-browser": "22.0.8",
|
||||||
"@angular/router": "22.0.8",
|
"@angular/router": "22.0.8",
|
||||||
"@angular/service-worker": "22.0.8",
|
"@angular/service-worker": "22.0.8",
|
||||||
"@lucide/angular": "^1.25.0",
|
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.8.0",
|
"tslib": "^2.8.0",
|
||||||
"zone.js": "~0.16.0"
|
"zone.js": "~0.16.0"
|
||||||
@@ -45,8 +45,9 @@
|
|||||||
"jasmine-core": "~5.5.0",
|
"jasmine-core": "~5.5.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.2.0",
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
"karma-coverage": "^2.2.1",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"typescript": "~6.0.3"
|
"typescript": "~6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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).
|
* Canonical name -> raw Lucide icon node data map for the whole app
|
||||||
* One entry per concept so every screen renders the same icon for the same meaning
|
* (storefront + builder + backoffice). One entry per concept so every screen
|
||||||
* (RC UI Sprint - Icon & Visual Language Audit). Extend this list rather than
|
* renders the same icon for the same meaning (RC UI Sprint - Icon & Visual
|
||||||
* importing Lucide icons ad hoc in individual components.
|
* 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<Record<string, string>>];
|
||||||
|
|
||||||
export const APP_ICONS = {
|
export const APP_ICONS = {
|
||||||
home: Home,
|
home: [
|
||||||
store: Store,
|
["path", { "d": "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" }],
|
||||||
cart: ShoppingCart,
|
["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" }]
|
||||||
package: Package,
|
],
|
||||||
boxes: Boxes,
|
store: [
|
||||||
folder: Folder,
|
["path", { "d": "M15 21v-5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v5" }],
|
||||||
tag: Tag,
|
["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" }],
|
||||||
tags: Tags,
|
["path", { "d": "M4 10.95V19a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8.05" }]
|
||||||
users: Users,
|
],
|
||||||
user: User,
|
cart: [
|
||||||
settings: Settings,
|
["circle", { "cx": "8", "cy": "21", "r": "1" }],
|
||||||
chartBar: BarChart3,
|
["circle", { "cx": "19", "cy": "21", "r": "1" }],
|
||||||
chartLine: LineChart,
|
["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" }]
|
||||||
search: Search,
|
],
|
||||||
filter: Filter,
|
package: [
|
||||||
heart: Heart,
|
["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" }],
|
||||||
scale: Scale,
|
["path", { "d": "M12 22V12" }],
|
||||||
bell: Bell,
|
["polyline", { "points": "3.29 7 12 12 20.71 7" }],
|
||||||
image: Image,
|
["path", { "d": "m7.5 4.27 9 5.15" }]
|
||||||
images: Images,
|
],
|
||||||
palette: Palette,
|
boxes: [
|
||||||
monitor: Monitor,
|
["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" }],
|
||||||
shield: Shield,
|
["path", { "d": "m7 16.5-4.74-2.85" }],
|
||||||
lock: Lock,
|
["path", { "d": "m7 16.5 5-3" }],
|
||||||
database: Database,
|
["path", { "d": "M7 16.5v5.17" }],
|
||||||
upload: Upload,
|
["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" }],
|
||||||
download: Download,
|
["path", { "d": "m17 16.5-5-3" }],
|
||||||
edit: Pencil,
|
["path", { "d": "m17 16.5 4.74-2.85" }],
|
||||||
trash: Trash2,
|
["path", { "d": "M17 16.5v5.17" }],
|
||||||
copy: Copy,
|
["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" }],
|
||||||
save: Save,
|
["path", { "d": "M12 8 7.26 5.15" }],
|
||||||
plus: Plus,
|
["path", { "d": "m12 8 4.74-2.85" }],
|
||||||
plusCircle: PlusCircle,
|
["path", { "d": "M12 13.5V8" }]
|
||||||
minus: Minus,
|
],
|
||||||
refresh: RefreshCw,
|
folder: [
|
||||||
check: Check,
|
["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" }]
|
||||||
checkCircle: CheckCircle2,
|
],
|
||||||
x: X,
|
tag: [
|
||||||
xCircle: XCircle,
|
["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" }],
|
||||||
arrowUp: ArrowUp,
|
["circle", { "cx": "7.5", "cy": "7.5", "r": ".5", "fill": "currentColor" }]
|
||||||
arrowDown: ArrowDown,
|
],
|
||||||
arrowLeft: ArrowLeft,
|
tags: [
|
||||||
arrowRight: ArrowRight,
|
["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" }],
|
||||||
chevronUp: ChevronUp,
|
["path", { "d": "M2 7v6.172a2 2 0 0 0 .586 1.414l6.71 6.71a2.4 2.4 0 0 0 3.191.193" }],
|
||||||
chevronDown: ChevronDown,
|
["circle", { "cx": "10.5", "cy": "6.5", "r": ".5", "fill": "currentColor" }]
|
||||||
chevronLeft: ChevronLeft,
|
],
|
||||||
chevronRight: ChevronRight,
|
users: [
|
||||||
info: Info,
|
["path", { "d": "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }],
|
||||||
help: HelpCircle,
|
["path", { "d": "M16 3.128a4 4 0 0 1 0 7.744" }],
|
||||||
warning: TriangleAlert,
|
["path", { "d": "M22 21v-2a4 4 0 0 0-3-3.87" }],
|
||||||
clock: Clock,
|
["circle", { "cx": "9", "cy": "7", "r": "4" }]
|
||||||
calendar: Calendar,
|
],
|
||||||
menu: Menu,
|
user: [
|
||||||
alignLeft: AlignLeft,
|
["path", { "d": "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }],
|
||||||
book: Book,
|
["circle", { "cx": "12", "cy": "7", "r": "4" }]
|
||||||
building: Building2,
|
],
|
||||||
code: Code2,
|
settings: [
|
||||||
compass: Compass,
|
["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" }],
|
||||||
creditCard: CreditCard,
|
["circle", { "cx": "12", "cy": "12", "r": "3" }]
|
||||||
flag: Flag,
|
],
|
||||||
globe: Globe,
|
chartBar: [
|
||||||
grip: GripVertical,
|
["path", { "d": "M3 3v16a2 2 0 0 0 2 2h16" }],
|
||||||
history: History,
|
["path", { "d": "M18 17V9" }],
|
||||||
link: Link,
|
["path", { "d": "M13 17V5" }],
|
||||||
list: List,
|
["path", { "d": "M8 17v-3" }]
|
||||||
locate: Locate,
|
],
|
||||||
mapPin: MapPin,
|
chartLine: [
|
||||||
megaphone: Megaphone,
|
["path", { "d": "M3 3v16a2 2 0 0 0 2 2h16" }],
|
||||||
logOut: LogOut,
|
["path", { "d": "m19 9-5 5-4-4-3 3" }]
|
||||||
network: Network,
|
],
|
||||||
slidersHorizontal: SlidersHorizontal,
|
search: [
|
||||||
spinner: Loader2,
|
["path", { "d": "m21 21-4.34-4.34" }],
|
||||||
star: Star,
|
["circle", { "cx": "11", "cy": "11", "r": "8" }]
|
||||||
stop: Square,
|
],
|
||||||
table: Table,
|
filter: [
|
||||||
layoutGrid: LayoutGrid,
|
["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" }]
|
||||||
thumbsUp: ThumbsUp,
|
],
|
||||||
thumbsDown: ThumbsDown,
|
heart: [
|
||||||
verified: BadgeCheck,
|
["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" }]
|
||||||
video: Video,
|
],
|
||||||
circle: Circle,
|
scale: [
|
||||||
zap: Zap,
|
["path", { "d": "M12 3v18" }],
|
||||||
trendingUp: TrendingUp
|
["path", { "d": "m19 8 3 8a5 5 0 0 1-6 0zV7" }],
|
||||||
} as const;
|
["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<string, readonly LucideIconNode[]>;
|
||||||
|
|
||||||
export type AppIconName = keyof typeof APP_ICONS;
|
export type AppIconName = keyof typeof APP_ICONS;
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
||||||
import { LucideDynamicIcon } from '@lucide/angular';
|
import { APP_ICONS, type AppIconName, type LucideIconNode } from './icon-registry';
|
||||||
import { APP_ICONS, type AppIconName } from './icon-registry';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single entry point for every icon in the app. Renders a Lucide icon by
|
* 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
|
* canonical name (see icon-registry.ts) so every screen uses one icon family
|
||||||
* at one consistent size/stroke-width by default.
|
* 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
|
* Decorative by default (aria-hidden). Pass `ariaLabel` only when the icon
|
||||||
* itself carries meaning with no adjacent text (e.g. inside an icon-only
|
* 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.
|
* 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({
|
@Component({
|
||||||
selector: 'app-icon',
|
selector: 'app-icon',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [LucideDynamicIcon],
|
|
||||||
template: `
|
template: `
|
||||||
<svg
|
<svg
|
||||||
[lucideIcon]="iconData()"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
[size]="size()"
|
viewBox="0 0 24 24"
|
||||||
[color]="color()"
|
fill="none"
|
||||||
[strokeWidth]="strokeWidth()"
|
stroke-linecap="round"
|
||||||
[title]="ariaLabel()"
|
stroke-linejoin="round"
|
||||||
|
class="lucide"
|
||||||
|
[attr.width]="size()"
|
||||||
|
[attr.height]="size()"
|
||||||
|
[attr.stroke]="color()"
|
||||||
|
[attr.stroke-width]="strokeWidth()"
|
||||||
[attr.role]="ariaLabel() ? 'img' : null"
|
[attr.role]="ariaLabel() ? 'img' : null"
|
||||||
[attr.aria-label]="ariaLabel()"
|
[attr.aria-label]="ariaLabel()"
|
||||||
[attr.aria-hidden]="ariaLabel() ? null : 'true'"
|
[attr.aria-hidden]="ariaLabel() ? null : 'true'"
|
||||||
></svg>
|
>
|
||||||
|
@if (ariaLabel(); as label) {
|
||||||
|
<title>{{ label }}</title>
|
||||||
|
}
|
||||||
|
@for (node of nodes(); track $index) {
|
||||||
|
@let attrs = node[1];
|
||||||
|
@switch (node[0]) {
|
||||||
|
@case ('path') {
|
||||||
|
<path [attr.d]="attrs['d']" [attr.fill]="attrs['fill']" />
|
||||||
|
}
|
||||||
|
@case ('line') {
|
||||||
|
<line [attr.x1]="attrs['x1']" [attr.x2]="attrs['x2']" [attr.y1]="attrs['y1']" [attr.y2]="attrs['y2']" />
|
||||||
|
}
|
||||||
|
@case ('polygon') {
|
||||||
|
<polygon [attr.points]="attrs['points']" />
|
||||||
|
}
|
||||||
|
@case ('polyline') {
|
||||||
|
<polyline [attr.points]="attrs['points']" />
|
||||||
|
}
|
||||||
|
@case ('circle') {
|
||||||
|
<circle [attr.cx]="attrs['cx']" [attr.cy]="attrs['cy']" [attr.r]="attrs['r']" [attr.fill]="attrs['fill']" />
|
||||||
|
}
|
||||||
|
@case ('ellipse') {
|
||||||
|
<ellipse [attr.cx]="attrs['cx']" [attr.cy]="attrs['cy']" [attr.rx]="attrs['rx']" [attr.ry]="attrs['ry']" />
|
||||||
|
}
|
||||||
|
@case ('rect') {
|
||||||
|
<rect
|
||||||
|
[attr.x]="attrs['x']"
|
||||||
|
[attr.y]="attrs['y']"
|
||||||
|
[attr.width]="attrs['width']"
|
||||||
|
[attr.height]="attrs['height']"
|
||||||
|
[attr.rx]="attrs['rx']"
|
||||||
|
[attr.ry]="attrs['ry']"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</svg>
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
host: {
|
host: {
|
||||||
@@ -40,5 +85,10 @@ export class IconComponent {
|
|||||||
readonly strokeWidth = input<number>(2);
|
readonly strokeWidth = input<number>(2);
|
||||||
readonly ariaLabel = input<string | null>(null);
|
readonly ariaLabel = input<string | null>(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[]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user