From f3c8a8cc96d29968f84b3e6ce35d20204bf6307a Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Mon, 20 Jul 2026 01:49:38 +0400 Subject: [PATCH] feat(icons): add Lucide icon foundation Add @lucide/angular dependency and a single shared entry point for every icon in the app: app-icon (src/app/shared/ui/icon), backed by a canonical name -> Lucide-icon registry (icon-registry.ts) covering every concept the RC icon audit needs across storefront, builder, and backoffice. One name per meaning, one default size/stroke-width, so every screen renders the same icon the same way. Package note: lucide-angular (unscoped) is deprecated upstream in favor of @lucide/angular - installed the maintained package directly. Co-Authored-By: Claude Sonnet 5 --- package-lock.json | 14 ++ package.json | 1 + src/app/shared/ui/icon/icon-registry.ts | 162 +++++++++++++++++++++++ src/app/shared/ui/icon/icon.component.ts | 39 ++++++ 4 files changed, 216 insertions(+) create mode 100644 src/app/shared/ui/icon/icon-registry.ts create mode 100644 src/app/shared/ui/icon/icon.component.ts diff --git a/package-lock.json b/package-lock.json index 5f3adef..1c91b97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser": "21.1.5", "@angular/router": "21.1.5", "@angular/service-worker": "21.1.5", + "@lucide/angular": "^1.25.0", "primeicons": "^7.0.0", "primeng": "^21.0.3", "rxjs": "~7.8.0", @@ -2009,6 +2010,19 @@ "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.26.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", diff --git a/package.json b/package.json index 98cee47..d25036d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@angular/platform-browser": "21.1.5", "@angular/router": "21.1.5", "@angular/service-worker": "21.1.5", + "@lucide/angular": "^1.25.0", "primeicons": "^7.0.0", "primeng": "^21.0.3", "rxjs": "~7.8.0", diff --git a/src/app/shared/ui/icon/icon-registry.ts b/src/app/shared/ui/icon/icon-registry.ts new file mode 100644 index 0000000..1a7c2b2 --- /dev/null +++ b/src/app/shared/ui/icon/icon-registry.ts @@ -0,0 +1,162 @@ +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, + 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, + LucideLock as Lock, + LucideLogOut as LogOut, + 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, + 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, + LucideTrash2 as Trash2, + LucideTriangleAlert as TriangleAlert, + LucideUpload as Upload, + LucideUser as User, + LucideUsers as Users, + LucideVideo as Video, + LucideX as X, + LucideXCircle as XCircle +} 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. + */ +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, + plus: Plus, + plusCircle: PlusCircle, + minus: Minus, + 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, + history: History, + link: Link, + list: List, + megaphone: Megaphone, + logOut: LogOut, + network: Network, + slidersHorizontal: SlidersHorizontal, + spinner: Loader2, + star: Star, + stop: Square, + table: Table, + layoutGrid: LayoutGrid, + verified: BadgeCheck, + video: Video, + circle: Circle +} as const; + +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 new file mode 100644 index 0000000..3057de8 --- /dev/null +++ b/src/app/shared/ui/icon/icon.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; +import { LucideDynamicIcon } from '@lucide/angular'; +import { APP_ICONS, type AppIconName } 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. + * + * 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. + */ +@Component({ + selector: 'app-icon', + standalone: true, + imports: [LucideDynamicIcon], + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'app-icon', + '[style.display]': "'inline-flex'" + } +}) +export class IconComponent { + readonly name = input.required(); + readonly size = input(20); + readonly strokeWidth = input(2); + readonly ariaLabel = input(null); + + protected readonly iconData = computed(() => APP_ICONS[this.name()]); +}