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:
@@ -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<Record<string, string>>];
|
||||
|
||||
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<string, readonly LucideIconNode[]>;
|
||||
|
||||
export type AppIconName = keyof typeof APP_ICONS;
|
||||
|
||||
@@ -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: `
|
||||
<svg
|
||||
[lucideIcon]="iconData()"
|
||||
[size]="size()"
|
||||
[color]="color()"
|
||||
[strokeWidth]="strokeWidth()"
|
||||
[title]="ariaLabel()"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide"
|
||||
[attr.width]="size()"
|
||||
[attr.height]="size()"
|
||||
[attr.stroke]="color()"
|
||||
[attr.stroke-width]="strokeWidth()"
|
||||
[attr.role]="ariaLabel() ? 'img' : null"
|
||||
[attr.aria-label]="ariaLabel()"
|
||||
[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,
|
||||
host: {
|
||||
@@ -40,5 +85,10 @@ export class IconComponent {
|
||||
readonly strokeWidth = input<number>(2);
|
||||
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