@lifecorner/angular-ui-kit 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +2 -1
  2. package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs +4 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs.map +1 -1
  4. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs +16 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-alert.mjs.map +1 -1
  6. package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs +4 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
  8. package/fesm2022/lifecorner-angular-ui-kit-button.mjs +4 -4
  9. package/fesm2022/lifecorner-angular-ui-kit-button.mjs.map +1 -1
  10. package/fesm2022/lifecorner-angular-ui-kit-callout.mjs +16 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-callout.mjs.map +1 -1
  12. package/fesm2022/lifecorner-angular-ui-kit-chat.mjs +14 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-chat.mjs.map +1 -1
  14. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs +4 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
  16. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +4 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
  18. package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs +4 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs.map +1 -1
  20. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs +4 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs.map +1 -1
  22. package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs +4 -1
  23. package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs.map +1 -1
  24. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs +14 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs.map +1 -1
  26. package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs +4 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs.map +1 -1
  28. package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs +4 -0
  29. package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs.map +1 -1
  30. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +4 -1
  31. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
  32. package/fesm2022/lifecorner-angular-ui-kit-footer.mjs +4 -1
  33. package/fesm2022/lifecorner-angular-ui-kit-footer.mjs.map +1 -1
  34. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs +14 -1
  35. package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
  36. package/fesm2022/lifecorner-angular-ui-kit-header.mjs +18 -1
  37. package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
  38. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs +186 -275
  39. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs.map +1 -1
  40. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs +803 -0
  41. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs.map +1 -0
  42. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs +297 -0
  43. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs.map +1 -0
  44. package/fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs +38 -0
  45. package/fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs.map +1 -0
  46. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +4 -1
  47. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
  48. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +4 -1
  49. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
  50. package/fesm2022/lifecorner-angular-ui-kit-modal.mjs +4 -1
  51. package/fesm2022/lifecorner-angular-ui-kit-modal.mjs.map +1 -1
  52. package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs +12 -1
  53. package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs.map +1 -1
  54. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +14 -1
  55. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
  56. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +4 -1
  57. package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
  58. package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs +4 -1
  59. package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs.map +1 -1
  60. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +12 -1
  61. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
  62. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +4 -1
  63. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
  64. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs +4 -1
  65. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs.map +1 -1
  66. package/fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs +262 -0
  67. package/fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs.map +1 -0
  68. package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs +18 -40
  69. package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs.map +1 -1
  70. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +4 -1
  71. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  72. package/fesm2022/lifecorner-angular-ui-kit-toast.mjs +16 -1
  73. package/fesm2022/lifecorner-angular-ui-kit-toast.mjs.map +1 -1
  74. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs +18 -1
  75. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs.map +1 -1
  76. package/fesm2022/lifecorner-angular-ui-kit.mjs +2 -0
  77. package/package.json +17 -1
  78. package/types/lifecorner-angular-ui-kit-icon.d.ts +125 -5
  79. package/types/lifecorner-angular-ui-kit-icons.d.ts +140 -0
  80. package/types/lifecorner-angular-ui-kit-internal-icon-set.d.ts +31 -0
  81. package/types/lifecorner-angular-ui-kit-internal-navigation.d.ts +28 -0
  82. package/types/lifecorner-angular-ui-kit-sidenav-placeholder.d.ts +163 -0
  83. package/types/lifecorner-angular-ui-kit-sidenav.d.ts +4 -2
  84. package/types/lifecorner-angular-ui-kit.d.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-notification-center.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/notification-center/notification-center.component.ts","../../../../libs/angular-ui-kit/src/lib/notification-center/notification-center.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n signal,\n computed,\n output,\n} from '@angular/core';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\nimport { SearchInputComponent } from '@lifecorner/angular-ui-kit/search-input';\n\nexport type NotificationType = 'info' | 'success' | 'warning' | 'error';\nexport type NotificationPriority = 'low' | 'normal' | 'high' | 'urgent';\n\nexport interface Notification {\n id: string;\n title: string;\n message?: string;\n type?: NotificationType;\n priority?: NotificationPriority;\n timestamp: Date;\n read?: boolean;\n icon?: string;\n category?: string;\n actionLabel?: string;\n actionUrl?: string;\n}\n\nconst TYPE_ICONS: Record<NotificationType, string> = {\n info: 'information-circle',\n success: 'check-circle',\n warning: 'exclamation-triangle',\n error: 'x-circle',\n};\n\nconst PRIORITY_LABELS: Record<NotificationPriority, string> = {\n low: 'Low',\n normal: 'Normal',\n high: 'High',\n urgent: 'Urgent',\n};\n\n@Component({\n selector: 'lc-notification-center',\n standalone: true,\n imports: [IconComponent, SearchInputComponent],\n templateUrl: './notification-center.component.html',\n styleUrls: ['./notification-center.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NotificationCenterComponent {\n readonly notifications = input.required<Notification[]>();\n readonly title = input('Notifications');\n readonly showFilter = input(true);\n readonly showTimestamp = input(true);\n readonly showPriority = input(false);\n readonly emptyMessage = input('No notifications');\n readonly maxHeight = input('480px');\n readonly groupByCategory = input(false);\n /** Locale used for absolute dates (older than a week). */\n readonly dateLocale = input('de-DE');\n\n /** Label of the mark-all-read action. */\n readonly markAllReadLabel = input('Mark all read');\n\n /** Label of the clear-all action. */\n readonly clearAllLabel = input('Clear all');\n\n /** Placeholder of the search field. */\n readonly searchPlaceholder = input('Search notifications…');\n\n /** Label of the \"all\" filter (the count follows in parentheses). */\n readonly filterAllLabel = input('All');\n\n /** Label of the error filter. */\n readonly filterErrorLabel = input('Error');\n\n /** Label of the warning filter. */\n readonly filterWarningLabel = input('Warning');\n\n /** Label of the success filter. */\n readonly filterSuccessLabel = input('Success');\n\n /** Label of the info filter. */\n readonly filterInfoLabel = input('Info');\n\n /** Accessible name of a dismiss button, followed by the notification's title. */\n readonly dismissAriaLabel = input('Dismiss notification:');\n\n /** Group header for notifications without `category` (`groupByCategory`). */\n readonly otherCategoryLabel = input('Other');\n\n /** Priority badge for `low` (`showPriority`). */\n readonly lowPriorityLabel = input('Low');\n\n /** Priority badge for `high` (`showPriority`). */\n readonly highPriorityLabel = input('High');\n\n /** Priority badge for `urgent` (`showPriority`). */\n readonly urgentPriorityLabel = input('Urgent');\n\n /** Timestamp of notifications younger than a minute. */\n readonly justNowLabel = input('Just now');\n\n /** Timestamp under an hour; `{n}` is replaced by the minutes. */\n readonly minutesAgoLabel = input('{n}m ago');\n\n /** Timestamp under a day; `{n}` is replaced by the hours. */\n readonly hoursAgoLabel = input('{n}h ago');\n\n /** Timestamp under a week; `{n}` is replaced by the days (older ones show the date in `dateLocale`). */\n readonly daysAgoLabel = input('{n}d ago');\n\n readonly notificationClick = output<Notification>();\n readonly notificationDismiss = output<string>();\n readonly notificationAction = output<Notification>();\n readonly markAllRead = output<void>();\n readonly clearAll = output<void>();\n\n protected activeFilter = signal<NotificationType | 'all'>('all');\n protected searchQuery = signal('');\n\n protected readonly unreadCount = computed(() =>\n this.notifications().filter(n => !n.read).length\n );\n\n protected readonly filteredNotifications = computed(() => {\n let items = this.notifications();\n const filter = this.activeFilter();\n const query = this.searchQuery().toLowerCase().trim();\n\n if (filter !== 'all') {\n items = items.filter(n => n.type === filter);\n }\n\n if (query) {\n items = items.filter(\n n =>\n n.title.toLowerCase().includes(query) ||\n n.message?.toLowerCase().includes(query) ||\n n.category?.toLowerCase().includes(query)\n );\n }\n\n // Copy before sorting: without a filter `items` is the caller's array and\n // `sort` would reorder it in place\n return [...items].sort((a, b) => {\n const prio = this.priorityWeight(b.priority) - this.priorityWeight(a.priority);\n if (prio !== 0) return prio;\n return b.timestamp.getTime() - a.timestamp.getTime();\n });\n });\n\n /**\n * The list as rendered: one group per category, or a single group without a\n * category (`null`, no header) when grouping is off.\n */\n protected readonly groupedNotifications = computed((): { category: string | null; items: Notification[] }[] => {\n const items = this.filteredNotifications();\n if (!this.groupByCategory()) return [{ category: null, items }];\n const map = new Map<string, Notification[]>();\n for (const n of items) {\n const cat = n.category || this.otherCategoryLabel();\n if (!map.has(cat)) map.set(cat, []);\n map.get(cat)!.push(n);\n }\n return Array.from(map.entries()).map(([category, items]) => ({ category, items }));\n });\n\n protected readonly filterCounts = computed(() => {\n const items = this.notifications();\n return {\n all: items.length,\n info: items.filter(n => n.type === 'info').length,\n success: items.filter(n => n.type === 'success').length,\n warning: items.filter(n => n.type === 'warning').length,\n error: items.filter(n => n.type === 'error').length,\n };\n });\n\n protected readonly hasNotifications = computed(() => this.filteredNotifications().length > 0);\n\n protected getTypeIcon(type?: NotificationType): string {\n return type ? TYPE_ICONS[type] : 'information-circle';\n }\n\n protected getPriorityLabel(priority?: NotificationPriority): string {\n switch (priority) {\n case 'low':\n return this.lowPriorityLabel();\n case 'high':\n return this.highPriorityLabel();\n case 'urgent':\n return this.urgentPriorityLabel();\n default:\n return priority ? PRIORITY_LABELS[priority] : '';\n }\n }\n\n protected formatTime(date: Date): string {\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return this.justNowLabel();\n if (mins < 60) return this.minutesAgoLabel().replace('{n}', String(mins));\n const hours = Math.floor(mins / 60);\n if (hours < 24) return this.hoursAgoLabel().replace('{n}', String(hours));\n const days = Math.floor(hours / 24);\n if (days < 7) return this.daysAgoLabel().replace('{n}', String(days));\n return date.toLocaleDateString(this.dateLocale(), { day: '2-digit', month: '2-digit', year: 'numeric' });\n }\n\n protected setFilter(filter: NotificationType | 'all'): void {\n this.activeFilter.set(filter);\n }\n\n protected onSearch(query: string): void {\n this.searchQuery.set(query);\n }\n\n protected onClick(notification: Notification): void {\n this.notificationClick.emit(notification);\n }\n\n protected onDismiss(id: string, event: Event): void {\n event.stopPropagation();\n this.notificationDismiss.emit(id);\n }\n\n protected onAction(notification: Notification, event: Event): void {\n event.stopPropagation();\n this.notificationAction.emit(notification);\n }\n\n protected onMarkAllRead(): void {\n this.markAllRead.emit();\n }\n\n protected onClearAll(): void {\n this.clearAll.emit();\n }\n\n private priorityWeight(p?: NotificationPriority): number {\n switch (p) {\n case 'urgent': return 4;\n case 'high': return 3;\n case 'normal': return 2;\n case 'low': return 1;\n default: return 2;\n }\n }\n}\n","<div class=\"lc-nc\">\n <!-- Header -->\n <div class=\"lc-nc__header\">\n <div class=\"lc-nc__header-left\">\n <h3 class=\"lc-nc__title\">{{ title() }}</h3>\n @if (unreadCount() > 0) {\n <span class=\"lc-nc__badge\">{{ unreadCount() }}</span>\n }\n </div>\n <div class=\"lc-nc__header-actions\">\n @if (unreadCount() > 0) {\n <button type=\"button\" class=\"lc-nc__action-btn\" (click)=\"onMarkAllRead()\">{{ markAllReadLabel() }}</button>\n }\n @if (notifications().length > 0) {\n <button type=\"button\" class=\"lc-nc__action-btn lc-nc__action-btn--danger\" (click)=\"onClearAll()\">{{ clearAllLabel() }}</button>\n }\n </div>\n </div>\n\n <!-- Search -->\n <div class=\"lc-nc__search\">\n <lc-search-input\n class=\"lc-nc__search-field\"\n [placeholder]=\"searchPlaceholder()\"\n [debounceMs]=\"0\"\n (searchChange)=\"onSearch($event)\"\n />\n </div>\n\n <!-- Filters -->\n @if (showFilter()) {\n <div class=\"lc-nc__filters\">\n <button\n type=\"button\"\n class=\"lc-nc__filter\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'all'\"\n [attr.aria-pressed]=\"activeFilter() === 'all'\"\n (click)=\"setFilter('all')\"\n >{{ filterAllLabel() }} ({{ filterCounts().all }})</button>\n @if (filterCounts().error > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--error\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'error'\"\n [attr.aria-pressed]=\"activeFilter() === 'error'\"\n (click)=\"setFilter('error')\"\n >{{ filterErrorLabel() }} ({{ filterCounts().error }})</button>\n }\n @if (filterCounts().warning > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--warning\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'warning'\"\n [attr.aria-pressed]=\"activeFilter() === 'warning'\"\n (click)=\"setFilter('warning')\"\n >{{ filterWarningLabel() }} ({{ filterCounts().warning }})</button>\n }\n @if (filterCounts().success > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--success\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'success'\"\n [attr.aria-pressed]=\"activeFilter() === 'success'\"\n (click)=\"setFilter('success')\"\n >{{ filterSuccessLabel() }} ({{ filterCounts().success }})</button>\n }\n @if (filterCounts().info > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--info\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'info'\"\n [attr.aria-pressed]=\"activeFilter() === 'info'\"\n (click)=\"setFilter('info')\"\n >{{ filterInfoLabel() }} ({{ filterCounts().info }})</button>\n }\n </div>\n }\n\n <!-- Notification list -->\n <div class=\"lc-nc__list\" [style.max-height]=\"maxHeight()\">\n @if (!hasNotifications()) {\n <div class=\"lc-nc__empty\">\n <lc-icon name=\"bell\" size=\"lg\" class=\"lc-nc__empty-icon\"></lc-icon>\n <span class=\"lc-nc__empty-text\">{{ emptyMessage() }}</span>\n </div>\n } @else {\n @for (group of groupedNotifications(); track group.category ?? '') {\n <div class=\"lc-nc__group\">\n @if (group.category !== null) {\n <div class=\"lc-nc__group-header\">{{ group.category }}</div>\n }\n <!-- The whole row is a mouse target; the title is a real button so\n keyboard and screen-reader users can activate it (its click bubbles\n to the row), and the dismiss/action buttons stop propagation. -->\n @for (n of group.items; track n.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- row is a mouse target; its title is a real button for keyboard users -->\n <div\n class=\"lc-nc__item\"\n [class.lc-nc__item--unread]=\"!n.read\"\n [class.lc-nc__item--urgent]=\"n.priority === 'urgent'\"\n (click)=\"onClick(n)\"\n >\n <lc-icon [name]=\"getTypeIcon(n.type)\" size=\"sm\" class=\"lc-nc__item-icon\" [class]=\"'lc-nc__item-icon lc-nc__item-icon--' + (n.type || 'info')\"></lc-icon>\n <div class=\"lc-nc__item-body\">\n <div class=\"lc-nc__item-header\">\n <button type=\"button\" class=\"lc-nc__item-title\">{{ n.title }}</button>\n @if (showPriority() && n.priority && n.priority !== 'normal') {\n <span [class]=\"'lc-nc__item-priority lc-nc__item-priority--' + n.priority\">\n {{ getPriorityLabel(n.priority) }}\n </span>\n }\n </div>\n @if (n.message) {\n <p class=\"lc-nc__item-message\">{{ n.message }}</p>\n }\n <div class=\"lc-nc__item-footer\">\n @if (showTimestamp()) {\n <span class=\"lc-nc__item-time\">{{ formatTime(n.timestamp) }}</span>\n }\n @if (n.category) {\n <span class=\"lc-nc__item-category\">{{ n.category }}</span>\n }\n @if (n.actionLabel) {\n <button type=\"button\" class=\"lc-nc__item-action\" (click)=\"onAction(n, $event)\">{{ n.actionLabel }}</button>\n }\n </div>\n </div>\n <button\n type=\"button\"\n class=\"lc-nc__item-dismiss\"\n (click)=\"onDismiss(n.id, $event)\"\n [attr.aria-label]=\"dismissAriaLabel() + ' ' + n.title\"\n >×</button>\n </div>\n }\n </div>\n }\n }\n </div>\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-notification-center.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/notification-center/notification-center.component.ts","../../../../libs/angular-ui-kit/src/lib/notification-center/notification-center.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n signal,\n computed,\n output,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport {\n ICON_CHECK_CIRCLE,\n ICON_CIRCLE_X,\n ICON_EXCLAMATION_TRIANGLE,\n ICON_INFORMATION_CIRCLE,\n} from '@lifecorner/angular-ui-kit/icons';\nimport { SearchInputComponent } from '@lifecorner/angular-ui-kit/search-input';\n\nexport type NotificationType = 'info' | 'success' | 'warning' | 'error';\nexport type NotificationPriority = 'low' | 'normal' | 'high' | 'urgent';\n\nexport interface Notification {\n id: string;\n title: string;\n message?: string;\n type?: NotificationType;\n priority?: NotificationPriority;\n timestamp: Date;\n read?: boolean;\n icon?: string;\n category?: string;\n actionLabel?: string;\n actionUrl?: string;\n}\n\nconst TYPE_ICONS: Record<NotificationType, string> = {\n info: 'information-circle',\n success: 'check-circle',\n warning: 'exclamation-triangle',\n error: 'x-circle',\n};\n\nconst PRIORITY_LABELS: Record<NotificationPriority, string> = {\n low: 'Low',\n normal: 'Normal',\n high: 'High',\n urgent: 'Urgent',\n};\n\n@Component({\n selector: 'lc-notification-center',\n standalone: true,\n imports: [IconComponent, SearchInputComponent],\n viewProviders: [ɵprovideKitIcons([ICON_INFORMATION_CIRCLE, ICON_CHECK_CIRCLE, ICON_EXCLAMATION_TRIANGLE, ICON_CIRCLE_X])],\n templateUrl: './notification-center.component.html',\n styleUrls: ['./notification-center.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NotificationCenterComponent {\n readonly notifications = input.required<Notification[]>();\n readonly title = input('Notifications');\n readonly showFilter = input(true);\n readonly showTimestamp = input(true);\n readonly showPriority = input(false);\n readonly emptyMessage = input('No notifications');\n readonly maxHeight = input('480px');\n readonly groupByCategory = input(false);\n /** Locale used for absolute dates (older than a week). */\n readonly dateLocale = input('de-DE');\n\n /** Label of the mark-all-read action. */\n readonly markAllReadLabel = input('Mark all read');\n\n /** Label of the clear-all action. */\n readonly clearAllLabel = input('Clear all');\n\n /** Placeholder of the search field. */\n readonly searchPlaceholder = input('Search notifications…');\n\n /** Label of the \"all\" filter (the count follows in parentheses). */\n readonly filterAllLabel = input('All');\n\n /** Label of the error filter. */\n readonly filterErrorLabel = input('Error');\n\n /** Label of the warning filter. */\n readonly filterWarningLabel = input('Warning');\n\n /** Label of the success filter. */\n readonly filterSuccessLabel = input('Success');\n\n /** Label of the info filter. */\n readonly filterInfoLabel = input('Info');\n\n /** Accessible name of a dismiss button, followed by the notification's title. */\n readonly dismissAriaLabel = input('Dismiss notification:');\n\n /** Group header for notifications without `category` (`groupByCategory`). */\n readonly otherCategoryLabel = input('Other');\n\n /** Priority badge for `low` (`showPriority`). */\n readonly lowPriorityLabel = input('Low');\n\n /** Priority badge for `high` (`showPriority`). */\n readonly highPriorityLabel = input('High');\n\n /** Priority badge for `urgent` (`showPriority`). */\n readonly urgentPriorityLabel = input('Urgent');\n\n /** Timestamp of notifications younger than a minute. */\n readonly justNowLabel = input('Just now');\n\n /** Timestamp under an hour; `{n}` is replaced by the minutes. */\n readonly minutesAgoLabel = input('{n}m ago');\n\n /** Timestamp under a day; `{n}` is replaced by the hours. */\n readonly hoursAgoLabel = input('{n}h ago');\n\n /** Timestamp under a week; `{n}` is replaced by the days (older ones show the date in `dateLocale`). */\n readonly daysAgoLabel = input('{n}d ago');\n\n readonly notificationClick = output<Notification>();\n readonly notificationDismiss = output<string>();\n readonly notificationAction = output<Notification>();\n readonly markAllRead = output<void>();\n readonly clearAll = output<void>();\n\n protected activeFilter = signal<NotificationType | 'all'>('all');\n protected searchQuery = signal('');\n\n protected readonly unreadCount = computed(() =>\n this.notifications().filter(n => !n.read).length\n );\n\n protected readonly filteredNotifications = computed(() => {\n let items = this.notifications();\n const filter = this.activeFilter();\n const query = this.searchQuery().toLowerCase().trim();\n\n if (filter !== 'all') {\n items = items.filter(n => n.type === filter);\n }\n\n if (query) {\n items = items.filter(\n n =>\n n.title.toLowerCase().includes(query) ||\n n.message?.toLowerCase().includes(query) ||\n n.category?.toLowerCase().includes(query)\n );\n }\n\n // Copy before sorting: without a filter `items` is the caller's array and\n // `sort` would reorder it in place\n return [...items].sort((a, b) => {\n const prio = this.priorityWeight(b.priority) - this.priorityWeight(a.priority);\n if (prio !== 0) return prio;\n return b.timestamp.getTime() - a.timestamp.getTime();\n });\n });\n\n /**\n * The list as rendered: one group per category, or a single group without a\n * category (`null`, no header) when grouping is off.\n */\n protected readonly groupedNotifications = computed((): { category: string | null; items: Notification[] }[] => {\n const items = this.filteredNotifications();\n if (!this.groupByCategory()) return [{ category: null, items }];\n const map = new Map<string, Notification[]>();\n for (const n of items) {\n const cat = n.category || this.otherCategoryLabel();\n if (!map.has(cat)) map.set(cat, []);\n map.get(cat)!.push(n);\n }\n return Array.from(map.entries()).map(([category, items]) => ({ category, items }));\n });\n\n protected readonly filterCounts = computed(() => {\n const items = this.notifications();\n return {\n all: items.length,\n info: items.filter(n => n.type === 'info').length,\n success: items.filter(n => n.type === 'success').length,\n warning: items.filter(n => n.type === 'warning').length,\n error: items.filter(n => n.type === 'error').length,\n };\n });\n\n protected readonly hasNotifications = computed(() => this.filteredNotifications().length > 0);\n\n protected getTypeIcon(type?: NotificationType): string {\n return type ? TYPE_ICONS[type] : 'information-circle';\n }\n\n protected getPriorityLabel(priority?: NotificationPriority): string {\n switch (priority) {\n case 'low':\n return this.lowPriorityLabel();\n case 'high':\n return this.highPriorityLabel();\n case 'urgent':\n return this.urgentPriorityLabel();\n default:\n return priority ? PRIORITY_LABELS[priority] : '';\n }\n }\n\n protected formatTime(date: Date): string {\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return this.justNowLabel();\n if (mins < 60) return this.minutesAgoLabel().replace('{n}', String(mins));\n const hours = Math.floor(mins / 60);\n if (hours < 24) return this.hoursAgoLabel().replace('{n}', String(hours));\n const days = Math.floor(hours / 24);\n if (days < 7) return this.daysAgoLabel().replace('{n}', String(days));\n return date.toLocaleDateString(this.dateLocale(), { day: '2-digit', month: '2-digit', year: 'numeric' });\n }\n\n protected setFilter(filter: NotificationType | 'all'): void {\n this.activeFilter.set(filter);\n }\n\n protected onSearch(query: string): void {\n this.searchQuery.set(query);\n }\n\n protected onClick(notification: Notification): void {\n this.notificationClick.emit(notification);\n }\n\n protected onDismiss(id: string, event: Event): void {\n event.stopPropagation();\n this.notificationDismiss.emit(id);\n }\n\n protected onAction(notification: Notification, event: Event): void {\n event.stopPropagation();\n this.notificationAction.emit(notification);\n }\n\n protected onMarkAllRead(): void {\n this.markAllRead.emit();\n }\n\n protected onClearAll(): void {\n this.clearAll.emit();\n }\n\n private priorityWeight(p?: NotificationPriority): number {\n switch (p) {\n case 'urgent': return 4;\n case 'high': return 3;\n case 'normal': return 2;\n case 'low': return 1;\n default: return 2;\n }\n }\n}\n","<div class=\"lc-nc\">\n <!-- Header -->\n <div class=\"lc-nc__header\">\n <div class=\"lc-nc__header-left\">\n <h3 class=\"lc-nc__title\">{{ title() }}</h3>\n @if (unreadCount() > 0) {\n <span class=\"lc-nc__badge\">{{ unreadCount() }}</span>\n }\n </div>\n <div class=\"lc-nc__header-actions\">\n @if (unreadCount() > 0) {\n <button type=\"button\" class=\"lc-nc__action-btn\" (click)=\"onMarkAllRead()\">{{ markAllReadLabel() }}</button>\n }\n @if (notifications().length > 0) {\n <button type=\"button\" class=\"lc-nc__action-btn lc-nc__action-btn--danger\" (click)=\"onClearAll()\">{{ clearAllLabel() }}</button>\n }\n </div>\n </div>\n\n <!-- Search -->\n <div class=\"lc-nc__search\">\n <lc-search-input\n class=\"lc-nc__search-field\"\n [placeholder]=\"searchPlaceholder()\"\n [debounceMs]=\"0\"\n (searchChange)=\"onSearch($event)\"\n />\n </div>\n\n <!-- Filters -->\n @if (showFilter()) {\n <div class=\"lc-nc__filters\">\n <button\n type=\"button\"\n class=\"lc-nc__filter\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'all'\"\n [attr.aria-pressed]=\"activeFilter() === 'all'\"\n (click)=\"setFilter('all')\"\n >{{ filterAllLabel() }} ({{ filterCounts().all }})</button>\n @if (filterCounts().error > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--error\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'error'\"\n [attr.aria-pressed]=\"activeFilter() === 'error'\"\n (click)=\"setFilter('error')\"\n >{{ filterErrorLabel() }} ({{ filterCounts().error }})</button>\n }\n @if (filterCounts().warning > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--warning\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'warning'\"\n [attr.aria-pressed]=\"activeFilter() === 'warning'\"\n (click)=\"setFilter('warning')\"\n >{{ filterWarningLabel() }} ({{ filterCounts().warning }})</button>\n }\n @if (filterCounts().success > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--success\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'success'\"\n [attr.aria-pressed]=\"activeFilter() === 'success'\"\n (click)=\"setFilter('success')\"\n >{{ filterSuccessLabel() }} ({{ filterCounts().success }})</button>\n }\n @if (filterCounts().info > 0) {\n <button\n type=\"button\"\n class=\"lc-nc__filter lc-nc__filter--info\"\n [class.lc-nc__filter--active]=\"activeFilter() === 'info'\"\n [attr.aria-pressed]=\"activeFilter() === 'info'\"\n (click)=\"setFilter('info')\"\n >{{ filterInfoLabel() }} ({{ filterCounts().info }})</button>\n }\n </div>\n }\n\n <!-- Notification list -->\n <div class=\"lc-nc__list\" [style.max-height]=\"maxHeight()\">\n @if (!hasNotifications()) {\n <div class=\"lc-nc__empty\">\n <lc-icon name=\"bell\" size=\"lg\" class=\"lc-nc__empty-icon\"></lc-icon>\n <span class=\"lc-nc__empty-text\">{{ emptyMessage() }}</span>\n </div>\n } @else {\n @for (group of groupedNotifications(); track group.category ?? '') {\n <div class=\"lc-nc__group\">\n @if (group.category !== null) {\n <div class=\"lc-nc__group-header\">{{ group.category }}</div>\n }\n <!-- The whole row is a mouse target; the title is a real button so\n keyboard and screen-reader users can activate it (its click bubbles\n to the row), and the dismiss/action buttons stop propagation. -->\n @for (n of group.items; track n.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- row is a mouse target; its title is a real button for keyboard users -->\n <div\n class=\"lc-nc__item\"\n [class.lc-nc__item--unread]=\"!n.read\"\n [class.lc-nc__item--urgent]=\"n.priority === 'urgent'\"\n (click)=\"onClick(n)\"\n >\n <lc-icon [name]=\"getTypeIcon(n.type)\" size=\"sm\" class=\"lc-nc__item-icon\" [class]=\"'lc-nc__item-icon lc-nc__item-icon--' + (n.type || 'info')\"></lc-icon>\n <div class=\"lc-nc__item-body\">\n <div class=\"lc-nc__item-header\">\n <button type=\"button\" class=\"lc-nc__item-title\">{{ n.title }}</button>\n @if (showPriority() && n.priority && n.priority !== 'normal') {\n <span [class]=\"'lc-nc__item-priority lc-nc__item-priority--' + n.priority\">\n {{ getPriorityLabel(n.priority) }}\n </span>\n }\n </div>\n @if (n.message) {\n <p class=\"lc-nc__item-message\">{{ n.message }}</p>\n }\n <div class=\"lc-nc__item-footer\">\n @if (showTimestamp()) {\n <span class=\"lc-nc__item-time\">{{ formatTime(n.timestamp) }}</span>\n }\n @if (n.category) {\n <span class=\"lc-nc__item-category\">{{ n.category }}</span>\n }\n @if (n.actionLabel) {\n <button type=\"button\" class=\"lc-nc__item-action\" (click)=\"onAction(n, $event)\">{{ n.actionLabel }}</button>\n }\n </div>\n </div>\n <button\n type=\"button\"\n class=\"lc-nc__item-dismiss\"\n (click)=\"onDismiss(n.id, $event)\"\n [attr.aria-label]=\"dismissAriaLabel() + ' ' + n.title\"\n >×</button>\n </div>\n }\n </div>\n }\n }\n </div>\n</div>\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, computed, forwardRef, input, output, signal, viewChild } from "@angular/core";
3
3
  import { NG_VALUE_ACCESSOR } from "@angular/forms";
4
- import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
4
+ import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
5
+ import { ICON_PLUS } from "@lifecorner/angular-ui-kit/icons";
5
6
  function decimalsOf(n) {
6
7
  if (!Number.isFinite(n)) return 0;
7
8
  const [mantissa, exponent] = String(n).split("e");
@@ -273,6 +274,7 @@ var NumberInputComponent = class NumberInputComponent {
273
274
  "strict"
274
275
  ]
275
276
  }],
277
+ viewProviders: [ɵprovideKitIcons([ICON_PLUS])],
276
278
  changeDetection: i0.ChangeDetectionStrategy.OnPush
277
279
  });
278
280
  };
@@ -287,6 +289,7 @@ i0.ɵɵngDeclareClassMetadata({
287
289
  selector: "lc-number-input",
288
290
  standalone: true,
289
291
  imports: [IconComponent],
292
+ viewProviders: [ɵprovideKitIcons([ICON_PLUS])],
290
293
  changeDetection: ChangeDetectionStrategy.OnPush,
291
294
  providers: [{
292
295
  provide: NG_VALUE_ACCESSOR,
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-number-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.ts","../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n output,\n computed,\n signal,\n forwardRef,\n viewChild,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/** Number of decimals a number is written with (`0.001` → 3, `1e-7` → 7, `5` → 0). */\nfunction decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const [mantissa, exponent] = String(n).split('e');\n const decimals = (mantissa.split('.')[1]?.length ?? 0) - (exponent ? Number(exponent) : 0);\n return Math.max(0, decimals);\n}\n\n/**\n * Reduces raw text to something `Number()` can parse: digits, one leading\n * minus and one decimal point. A comma is taken as a decimal separator so a\n * German keyboard layout does not silently lose the fraction.\n */\nfunction sanitizeNumericText(raw: string): string {\n let out = '';\n let seenPoint = false;\n for (const ch of raw) {\n if (ch >= '0' && ch <= '9') {\n out += ch;\n } else if (ch === '-' && out.length === 0) {\n out += ch;\n } else if ((ch === '.' || ch === ',') && !seenPoint) {\n seenPoint = true;\n out += '.';\n }\n }\n return out;\n}\n\n@Component({\n selector: 'lc-number-input',\n standalone: true,\n imports: [IconComponent],\n templateUrl: './number-input.component.html',\n styleUrls: ['./number-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NumberInputComponent),\n multi: true,\n },\n ],\n})\n/**\n * Number input component with increment/decrement controls.\n *\n * Features:\n * - Increment and decrement buttons (also ArrowUp / ArrowDown)\n * - Configurable min, max, and step values\n * - Value clamping within bounds; stepping is rounded to the step's precision\n * - Non-numeric characters are rejected as they are typed\n * - Disabled state support\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-number-input label=\"Quantity\" [min]=\"0\" [max]=\"100\" [step]=\"1\" />\n * ```\n */\nexport class NumberInputComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Minimum value */\n min = input<number | undefined>(undefined);\n\n /** Maximum value */\n max = input<number | undefined>(undefined);\n\n /** Step increment */\n step = input<number>(1);\n\n /** Label text */\n label = input<string>();\n\n /** Accessible name for the field when no visible `label` is rendered. */\n ariaLabel = input<string>();\n\n /** Placeholder */\n placeholder = input<string>('0');\n\n /** Helper text shown below the control (linked via `aria-describedby`). */\n helperText = input<string>();\n\n /** Error message shown below the control; marks the field `aria-invalid`. */\n error = input<string>();\n\n /** Disabled state */\n disabled = input<boolean>(false);\n\n /** Accessible name of the minus button. */\n decrementAriaLabel = input('Decrease value');\n\n /** Accessible name of the plus button. */\n incrementAriaLabel = input('Increase value');\n\n /** Emits value on change */\n valueChange = output<number>();\n\n /** Per-instance id wiring the label, hint and error to the field. */\n readonly inputId = `lc-number-input-${++NumberInputComponent.nextId}`;\n\n protected value = signal<number | null>(null);\n\n /**\n * Text shown in the field. It is re-derived from the model on `writeValue`,\n * stepping and blur — never while typing — so an in-progress entry such as\n * \"1.\" or \"-\" is not snapped back to \"1\" under the user's cursor.\n */\n protected readonly text = signal('');\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n /** Decimals implied by `step` — stepped results are rounded to it so 0.1 + 0.2 does not drift. */\n private readonly stepPrecision = computed(() => decimalsOf(this.step()));\n\n /** Fractions need a decimal key on touch keyboards; integers do not. */\n protected readonly inputMode = computed(() => (this.stepPrecision() > 0 ? 'decimal' : 'numeric'));\n\n protected readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.error()) ids.push(`${this.inputId}-error`);\n else if (this.helperText()) ids.push(`${this.inputId}-helper`);\n return ids.length ? ids.join(' ') : null;\n });\n\n protected containerClasses = computed(() => {\n const classes = ['number-input'];\n if (this.isDisabled()) classes.push('number-input--disabled');\n if (this.error()) classes.push('number-input--error');\n return classes.join(' ');\n });\n\n protected canDecrement = computed(() => {\n const val = this.value();\n const minV = this.min();\n if (val === null || minV === undefined) return true;\n return val > minV;\n });\n\n protected canIncrement = computed(() => {\n const val = this.value();\n const maxV = this.max();\n if (val === null || maxV === undefined) return true;\n return val < maxV;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: number | null) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n decrement(): void {\n this.stepBy(-1);\n }\n\n increment(): void {\n this.stepBy(1);\n }\n\n private stepBy(direction: 1 | -1): void {\n const cur = this.value() ?? 0;\n // Keep whatever precision the user typed (1.25 + 0.1 → 1.35), but never\n // less than the step's own — that is what removes the binary drift.\n const precision = Math.max(this.stepPrecision(), decimalsOf(cur));\n const next = this.clamp(Number((cur + direction * this.step()).toFixed(precision)));\n this.setValue(next);\n this.text.set(this.format(next));\n }\n\n onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n if (this.canIncrement()) this.increment();\n } else if (event.key === 'ArrowDown') {\n event.preventDefault();\n if (this.canDecrement()) this.decrement();\n }\n }\n\n onInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n const raw = target.value;\n const text = sanitizeNumericText(raw);\n // Property binding alone would not repaint the field when the model does\n // not change (typing \"a\" after \"12\" leaves 12 as-is), so the stripped\n // text is written straight back to the element.\n if (text !== raw) {\n const caret = Math.max(0, (target.selectionStart ?? text.length) - (raw.length - text.length));\n target.value = text;\n target.setSelectionRange(caret, caret);\n }\n this.text.set(text);\n // \"\" would parse as 0; it and the other in-progress entries (\"-\", \".\")\n // mean the model has no number yet.\n const parsed = text === '' ? NaN : Number(text);\n if (Number.isNaN(parsed)) {\n if (this.value() !== null) {\n this.value.set(null);\n this.onChange(null);\n }\n return;\n }\n this.setValue(parsed);\n }\n\n onBlur(): void {\n this.onTouched();\n // Clamp on blur\n const val = this.value();\n if (val !== null) {\n const clamped = this.clamp(val);\n if (clamped !== val) this.setValue(clamped);\n }\n // Whatever half-typed text is left (\"1.\", \"-\", \"007\") is replaced by the\n // model's canonical form. The element is written directly as well, in\n // case the DOM drifted from the bound text without an input event.\n const canonical = this.format(this.value());\n this.text.set(canonical);\n const el = this.inputEl()?.nativeElement;\n if (el) el.value = canonical;\n }\n\n private format(val: number | null): string {\n return val === null ? '' : String(val);\n }\n\n private clamp(val: number): number {\n const minV = this.min();\n const maxV = this.max();\n if (minV !== undefined) val = Math.max(minV, val);\n if (maxV !== undefined) val = Math.min(maxV, val);\n return val;\n }\n\n private setValue(val: number): void {\n if (val === this.value()) return;\n this.value.set(val);\n this.valueChange.emit(val);\n this.onChange(val);\n }\n\n // ControlValueAccessor\n writeValue(val: number | null): void {\n this.value.set(val);\n this.text.set(this.format(val));\n }\n\n registerOnChange(fn: (val: number | null) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" size=\"sm\" [decorative]=\"true\" />\n </button>\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n [attr.inputmode]=\"inputMode()\"\n class=\"number-input__input\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--increment\"\n [disabled]=\"isDisabled() || !canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"incrementAriaLabel()\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n </button>\n </div>\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"number-input__error\" role=\"alert\">{{ error() }}</p>\n } @else if (helperText()) {\n <p [id]=\"inputId + '-helper'\" class=\"number-input__helper\">{{ helperText() }}</p>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-number-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.ts","../../../../libs/angular-ui-kit/src/lib/number-input/number-input.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n output,\n computed,\n signal,\n forwardRef,\n viewChild,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS } from '@lifecorner/angular-ui-kit/icons';\n\n/** Number of decimals a number is written with (`0.001` → 3, `1e-7` → 7, `5` → 0). */\nfunction decimalsOf(n: number): number {\n if (!Number.isFinite(n)) return 0;\n const [mantissa, exponent] = String(n).split('e');\n const decimals = (mantissa.split('.')[1]?.length ?? 0) - (exponent ? Number(exponent) : 0);\n return Math.max(0, decimals);\n}\n\n/**\n * Reduces raw text to something `Number()` can parse: digits, one leading\n * minus and one decimal point. A comma is taken as a decimal separator so a\n * German keyboard layout does not silently lose the fraction.\n */\nfunction sanitizeNumericText(raw: string): string {\n let out = '';\n let seenPoint = false;\n for (const ch of raw) {\n if (ch >= '0' && ch <= '9') {\n out += ch;\n } else if (ch === '-' && out.length === 0) {\n out += ch;\n } else if ((ch === '.' || ch === ',') && !seenPoint) {\n seenPoint = true;\n out += '.';\n }\n }\n return out;\n}\n\n@Component({\n selector: 'lc-number-input',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_PLUS])],\n templateUrl: './number-input.component.html',\n styleUrls: ['./number-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NumberInputComponent),\n multi: true,\n },\n ],\n})\n/**\n * Number input component with increment/decrement controls.\n *\n * Features:\n * - Increment and decrement buttons (also ArrowUp / ArrowDown)\n * - Configurable min, max, and step values\n * - Value clamping within bounds; stepping is rounded to the step's precision\n * - Non-numeric characters are rejected as they are typed\n * - Disabled state support\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-number-input label=\"Quantity\" [min]=\"0\" [max]=\"100\" [step]=\"1\" />\n * ```\n */\nexport class NumberInputComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Minimum value */\n min = input<number | undefined>(undefined);\n\n /** Maximum value */\n max = input<number | undefined>(undefined);\n\n /** Step increment */\n step = input<number>(1);\n\n /** Label text */\n label = input<string>();\n\n /** Accessible name for the field when no visible `label` is rendered. */\n ariaLabel = input<string>();\n\n /** Placeholder */\n placeholder = input<string>('0');\n\n /** Helper text shown below the control (linked via `aria-describedby`). */\n helperText = input<string>();\n\n /** Error message shown below the control; marks the field `aria-invalid`. */\n error = input<string>();\n\n /** Disabled state */\n disabled = input<boolean>(false);\n\n /** Accessible name of the minus button. */\n decrementAriaLabel = input('Decrease value');\n\n /** Accessible name of the plus button. */\n incrementAriaLabel = input('Increase value');\n\n /** Emits value on change */\n valueChange = output<number>();\n\n /** Per-instance id wiring the label, hint and error to the field. */\n readonly inputId = `lc-number-input-${++NumberInputComponent.nextId}`;\n\n protected value = signal<number | null>(null);\n\n /**\n * Text shown in the field. It is re-derived from the model on `writeValue`,\n * stepping and blur — never while typing — so an in-progress entry such as\n * \"1.\" or \"-\" is not snapped back to \"1\" under the user's cursor.\n */\n protected readonly text = signal('');\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n\n /** Decimals implied by `step` — stepped results are rounded to it so 0.1 + 0.2 does not drift. */\n private readonly stepPrecision = computed(() => decimalsOf(this.step()));\n\n /** Fractions need a decimal key on touch keyboards; integers do not. */\n protected readonly inputMode = computed(() => (this.stepPrecision() > 0 ? 'decimal' : 'numeric'));\n\n protected readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.error()) ids.push(`${this.inputId}-error`);\n else if (this.helperText()) ids.push(`${this.inputId}-helper`);\n return ids.length ? ids.join(' ') : null;\n });\n\n protected containerClasses = computed(() => {\n const classes = ['number-input'];\n if (this.isDisabled()) classes.push('number-input--disabled');\n if (this.error()) classes.push('number-input--error');\n return classes.join(' ');\n });\n\n protected canDecrement = computed(() => {\n const val = this.value();\n const minV = this.min();\n if (val === null || minV === undefined) return true;\n return val > minV;\n });\n\n protected canIncrement = computed(() => {\n const val = this.value();\n const maxV = this.max();\n if (val === null || maxV === undefined) return true;\n return val < maxV;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: number | null) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n decrement(): void {\n this.stepBy(-1);\n }\n\n increment(): void {\n this.stepBy(1);\n }\n\n private stepBy(direction: 1 | -1): void {\n const cur = this.value() ?? 0;\n // Keep whatever precision the user typed (1.25 + 0.1 → 1.35), but never\n // less than the step's own — that is what removes the binary drift.\n const precision = Math.max(this.stepPrecision(), decimalsOf(cur));\n const next = this.clamp(Number((cur + direction * this.step()).toFixed(precision)));\n this.setValue(next);\n this.text.set(this.format(next));\n }\n\n onKeydown(event: KeyboardEvent): void {\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n if (this.canIncrement()) this.increment();\n } else if (event.key === 'ArrowDown') {\n event.preventDefault();\n if (this.canDecrement()) this.decrement();\n }\n }\n\n onInput(event: Event): void {\n const target = event.target as HTMLInputElement;\n const raw = target.value;\n const text = sanitizeNumericText(raw);\n // Property binding alone would not repaint the field when the model does\n // not change (typing \"a\" after \"12\" leaves 12 as-is), so the stripped\n // text is written straight back to the element.\n if (text !== raw) {\n const caret = Math.max(0, (target.selectionStart ?? text.length) - (raw.length - text.length));\n target.value = text;\n target.setSelectionRange(caret, caret);\n }\n this.text.set(text);\n // \"\" would parse as 0; it and the other in-progress entries (\"-\", \".\")\n // mean the model has no number yet.\n const parsed = text === '' ? NaN : Number(text);\n if (Number.isNaN(parsed)) {\n if (this.value() !== null) {\n this.value.set(null);\n this.onChange(null);\n }\n return;\n }\n this.setValue(parsed);\n }\n\n onBlur(): void {\n this.onTouched();\n // Clamp on blur\n const val = this.value();\n if (val !== null) {\n const clamped = this.clamp(val);\n if (clamped !== val) this.setValue(clamped);\n }\n // Whatever half-typed text is left (\"1.\", \"-\", \"007\") is replaced by the\n // model's canonical form. The element is written directly as well, in\n // case the DOM drifted from the bound text without an input event.\n const canonical = this.format(this.value());\n this.text.set(canonical);\n const el = this.inputEl()?.nativeElement;\n if (el) el.value = canonical;\n }\n\n private format(val: number | null): string {\n return val === null ? '' : String(val);\n }\n\n private clamp(val: number): number {\n const minV = this.min();\n const maxV = this.max();\n if (minV !== undefined) val = Math.max(minV, val);\n if (maxV !== undefined) val = Math.min(maxV, val);\n return val;\n }\n\n private setValue(val: number): void {\n if (val === this.value()) return;\n this.value.set(val);\n this.valueChange.emit(val);\n this.onChange(val);\n }\n\n // ControlValueAccessor\n writeValue(val: number | null): void {\n this.value.set(val);\n this.text.set(this.format(val));\n }\n\n registerOnChange(fn: (val: number | null) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"number-input__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n <div class=\"number-input__control\">\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--decrement\"\n [disabled]=\"isDisabled() || !canDecrement()\"\n (click)=\"decrement()\"\n [attr.aria-label]=\"decrementAriaLabel()\"\n >\n <lc-icon name=\"minus\" size=\"sm\" [decorative]=\"true\" />\n </button>\n <input\n #inputEl\n type=\"text\"\n [id]=\"inputId\"\n [attr.inputmode]=\"inputMode()\"\n class=\"number-input__input\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"number-input__button number-input__button--increment\"\n [disabled]=\"isDisabled() || !canIncrement()\"\n (click)=\"increment()\"\n [attr.aria-label]=\"incrementAriaLabel()\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n </button>\n </div>\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"number-input__error\" role=\"alert\">{{ error() }}</p>\n } @else if (helperText()) {\n <p [id]=\"inputId + '-helper'\" class=\"number-input__helper\">{{ helperText() }}</p>\n }\n</div>\n"],"mappings":";;;;;AAgBA,SAAS,WAAW,GAAS;CAC3B,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;CAChC,MAAM,CAAC,UAAU,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG;CAChD,MAAM,YAAY,SAAS,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,UAAU,MAAM,WAAW,OAAO,QAAQ,IAAI;CACxF,OAAO,KAAK,IAAI,GAAG,QAAQ;AAC7B;AAOA,SAAS,oBAAoB,KAAW;CACtC,IAAI,MAAM;CACV,IAAI,YAAY;CAChB,KAAK,MAAM,MAAM,KACf,IAAI,MAAM,OAAO,MAAM,KACrB,OAAO;MACF,IAAI,OAAO,OAAO,IAAI,WAAW,GACtC,OAAO;MACF,KAAK,OAAO,OAAO,OAAO,QAAQ,CAAC,WAAW;EACnD,YAAY;EACZ,OAAO;CACT;CAEF,OAAO;AACT;AAkCA,IAAa,uBAAb,MAAa,qBAAoB;CACvB,OAAO,SAAS;CAGxB,MAAM,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,+BAAA,CAAA,CAAA;CAG1C,MAAM,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,+BAAA,CAAA,CAAA;CAG1C,OAAO,MAAc,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGvB,QAAQ,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGvB,YAAY,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAG3B,cAAc,MAAc,KAAI,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGhC,aAAa,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAG5B,QAAQ,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGvB,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAGhC,qBAAqB,MAAM,kBAAiB,GAAA,YAAA,CAAA,EAAA,WAAA,qBAAA,CAAA,+BAAA,CAAA,CAAA;CAG5C,qBAAqB,MAAM,kBAAiB,GAAA,YAAA,CAAA,EAAA,WAAA,qBAAA,CAAA,+BAAA,CAAA,CAAA;CAG5C,cAAc,OAAM;CAGX,UAAU,mBAAmB,EAAE,qBAAqB;CAEnD,QAAQ,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAO1B,OAAO,OAAO,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGnB,eAAe,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG1B,aAAa,eAAe,KAAK,SAAQ,KAAM,KAAK,aAAY,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAErE,UAAU,UAAwC,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAG5D,gBAAgB,eAAe,WAAW,KAAK,KAAI,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAGrD,YAAY,eAAgB,KAAK,cAAa,IAAK,IAAI,YAAY,WAAW,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAE9E,cAAc,eAAc;EAC7C,MAAM,MAAgB,CAAA;EACtB,IAAI,KAAK,MAAK,GAAI,IAAI,KAAK,GAAG,KAAK,QAAO,OAAQ;OAC7C,IAAI,KAAK,WAAU,GAAI,IAAI,KAAK,GAAG,KAAK,QAAO,QAAS;EAC7D,OAAO,IAAI,SAAS,IAAI,KAAK,GAAG,IAAI;CACtC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAEQ,mBAAmB,eAAc;EACzC,MAAM,UAAU,CAAC,cAAc;EAC/B,IAAI,KAAK,WAAU,GAAI,QAAQ,KAAK,wBAAwB;EAC5D,IAAI,KAAK,MAAK,GAAI,QAAQ,KAAK,qBAAqB;EACpD,OAAO,QAAQ,KAAK,GAAG;CACzB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;CAEQ,eAAe,eAAc;EACrC,MAAM,MAAM,KAAK,MAAK;EACtB,MAAM,OAAO,KAAK,IAAG;EACrB,IAAI,QAAQ,QAAQ,SAAS,QAAW,OAAO;EAC/C,OAAO,MAAM;CACf,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEQ,eAAe,eAAc;EACrC,MAAM,MAAM,KAAK,MAAK;EACtB,MAAM,OAAO,KAAK,IAAG;EACrB,IAAI,QAAQ,QAAQ,SAAS,QAAW,OAAO;EAC/C,OAAO,MAAM;CACf,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGM,iBAA8C,CAAE;CAEhD,kBAA6B,CAAE;CAEvC,YAAS;EACP,KAAK,OAAO,EAAE;CAChB;CAEA,YAAS;EACP,KAAK,OAAO,CAAC;CACf;CAEQ,OAAO,WAAiB;EAC9B,MAAM,MAAM,KAAK,MAAK,KAAM;EAG5B,MAAM,YAAY,KAAK,IAAI,KAAK,cAAa,GAAI,WAAW,GAAG,CAAC;EAChE,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,YAAY,KAAK,KAAI,EAAA,CAAI,QAAQ,SAAS,CAAC,CAAC;EAClF,KAAK,SAAS,IAAI;EAClB,KAAK,KAAK,IAAI,KAAK,OAAO,IAAI,CAAC;CACjC;CAEA,UAAU,OAAoB;EAC5B,IAAI,MAAM,QAAQ,WAAW;GAC3B,MAAM,eAAc;GACpB,IAAI,KAAK,aAAY,GAAI,KAAK,UAAS;EACzC,OAAO,IAAI,MAAM,QAAQ,aAAa;GACpC,MAAM,eAAc;GACpB,IAAI,KAAK,aAAY,GAAI,KAAK,UAAS;EACzC;CACF;CAEA,QAAQ,OAAY;EAClB,MAAM,SAAS,MAAM;EACrB,MAAM,MAAM,OAAO;EACnB,MAAM,OAAO,oBAAoB,GAAG;EAIpC,IAAI,SAAS,KAAK;GAChB,MAAM,QAAQ,KAAK,IAAI,IAAI,OAAO,kBAAkB,KAAK,WAAW,IAAI,SAAS,KAAK,OAAO;GAC7F,OAAO,QAAQ;GACf,OAAO,kBAAkB,OAAO,KAAK;EACvC;EACA,KAAK,KAAK,IAAI,IAAI;EAGlB,MAAM,SAAS,SAAS,KAAK,MAAM,OAAO,IAAI;EAC9C,IAAI,OAAO,MAAM,MAAM,GAAG;GACxB,IAAI,KAAK,MAAK,MAAO,MAAM;IACzB,KAAK,MAAM,IAAI,IAAI;IACnB,KAAK,SAAS,IAAI;GACpB;GACA;EACF;EACA,KAAK,SAAS,MAAM;CACtB;CAEA,SAAM;EACJ,KAAK,UAAS;EAEd,MAAM,MAAM,KAAK,MAAK;EACtB,IAAI,QAAQ,MAAM;GAChB,MAAM,UAAU,KAAK,MAAM,GAAG;GAC9B,IAAI,YAAY,KAAK,KAAK,SAAS,OAAO;EAC5C;EAIA,MAAM,YAAY,KAAK,OAAO,KAAK,MAAK,CAAE;EAC1C,KAAK,KAAK,IAAI,SAAS;EACvB,MAAM,KAAK,KAAK,QAAO,CAAE,EAAE;EAC3B,IAAI,IAAI,GAAG,QAAQ;CACrB;CAEQ,OAAO,KAAkB;EAC/B,OAAO,QAAQ,OAAO,KAAK,OAAO,GAAG;CACvC;CAEQ,MAAM,KAAW;EACvB,MAAM,OAAO,KAAK,IAAG;EACrB,MAAM,OAAO,KAAK,IAAG;EACrB,IAAI,SAAS,QAAW,MAAM,KAAK,IAAI,MAAM,GAAG;EAChD,IAAI,SAAS,QAAW,MAAM,KAAK,IAAI,MAAM,GAAG;EAChD,OAAO;CACT;CAEQ,SAAS,KAAW;EAC1B,IAAI,QAAQ,KAAK,MAAK,GAAI;EAC1B,KAAK,MAAM,IAAI,GAAG;EAClB,KAAK,YAAY,KAAK,GAAG;EACzB,KAAK,SAAS,GAAG;CACnB;CAGA,WAAW,KAAkB;EAC3B,KAAK,MAAM,IAAI,GAAG;EAClB,KAAK,KAAK,IAAI,KAAK,OAAO,GAAG,CAAC;CAChC;CAEA,iBAAiB,IAAgC;EAC/C,KAAK,WAAW;CAClB;CAEA,kBAAkB,IAAc;EAC9B,KAAK,YAAY;CACnB;CAEA,iBAAiB,YAAmB;EAClC,KAAK,aAAa,IAAI,UAAU;CAClC;CA5MW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAoB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,KAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,KAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,oBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,oBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,aAAA,cAAA;EAAA,WAxBpB,CACT;GACE,SAAS;GACT,aAAa,iBAAiB,oBAAoB;GAClD,OAAO;EACR,CACF;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,SAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UC1DH;EAgDA,QAAA,CAAA,ojEAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MDDY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,eACR,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AA4BnC,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EAhChC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAiB,YACf;GAAI,SACP,CAAC,aAAa;GAAC,eACT,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;GAAC,iBAG7B,wBAAwB;GAAM,WACpC,CACT;IACE,SAAS;IACT,aAAa,iBAAgB,oBAAqB;IAClD,OAAO;GACR,CACF;GAAA,UAAA;GAAA,QAAA,CAAA,ojEAAA;EAAA,CAAA;CA2EkE,CAAA;CAAA,gBAAA;EAAA,KAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,KAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,oBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,oBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,WAAS,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
@@ -1,7 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, computed, forwardRef, input, output, signal } from "@angular/core";
3
3
  import { NG_VALUE_ACCESSOR } from "@angular/forms";
4
- import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
4
+ import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
5
+ import { ICON_EYE, ICON_EYE_SLASH } from "@lifecorner/angular-ui-kit/icons";
5
6
  import { AlertComponent } from "@lifecorner/angular-ui-kit/alert";
6
7
  let nextUniqueId = 0;
7
8
  const SYMBOL_PATTERN = /[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/;
@@ -412,6 +413,7 @@ var PasswordInputComponent = class PasswordInputComponent {
412
413
  ],
413
414
  outputs: ["dismissed", "visibleChange"]
414
415
  }],
416
+ viewProviders: [ɵprovideKitIcons([ICON_EYE, ICON_EYE_SLASH])],
415
417
  changeDetection: i0.ChangeDetectionStrategy.OnPush
416
418
  });
417
419
  };
@@ -426,6 +428,7 @@ i0.ɵɵngDeclareClassMetadata({
426
428
  selector: "lc-password-input",
427
429
  standalone: true,
428
430
  imports: [IconComponent, AlertComponent],
431
+ viewProviders: [ɵprovideKitIcons([ICON_EYE, ICON_EYE_SLASH])],
429
432
  providers: [{
430
433
  provide: NG_VALUE_ACCESSOR,
431
434
  useExisting: forwardRef(() => PasswordInputComponent),
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-password-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/password-input/password-input.component.ts","../../../../libs/angular-ui-kit/src/lib/password-input/password-input.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n computed,\n forwardRef,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\nimport { AlertComponent } from '@lifecorner/angular-ui-kit/alert';\nimport type { InputAutocapitalize, InputEnterKeyHint } from '@lifecorner/angular-ui-kit/input';\n\nlet nextUniqueId = 0;\n\n/**\n * Password strength information\n */\nexport interface PasswordStrength {\n score: number; // 0-5 (number of requirements met)\n level: 'Weak' | 'Fair' | 'Good' | 'Strong';\n percentage: number; // 0-100\n}\n\n/**\n * Individual password requirement\n */\nexport interface PasswordRequirement {\n label: string;\n met: boolean;\n icon: string;\n}\n\nconst SYMBOL_PATTERN = /[!@#$%^&*()_+\\-=[\\]{}|;:,.<>?]/;\n\n/** The length rule when no `minLength` is set (the 3.2 rule). */\nconst DEFAULT_MIN_LENGTH = 8;\n\n/**\n * How the strength meter scores a password:\n * - `composition` (default, 3.2): share of five rules met — length and four character classes.\n * - `length`: the length only, as NIST SP 800-63B asks (no composition rules):\n * below `minLength` Weak, from it Fair, from 1.5 × Good, from 2 × Strong.\n */\nexport type PasswordStrengthMode = 'composition' | 'length';\n\n/**\n * Password Input Component with Show/Hide Toggle and Strength Meter\n *\n * Features:\n * - Show/Hide password toggle (eye icon)\n * - Real-time password strength indicator (Weak/Fair/Good/Strong)\n * - Visual strength meter bar with color coding\n * - Validation feedback (length, uppercase, lowercase, digit, symbol)\n * - Accessible ARIA attributes\n * - Reactive Forms ControlValueAccessor implementation\n *\n * Strength levels (share of the 5 requirements met):\n * - Weak: < 40%\n * - Fair: 40–59%\n * - Good: 60–79%\n * - Strong: >= 80%\n *\n * Requirements:\n * - Minimum 8 characters\n * - At least one uppercase letter (A-Z)\n * - At least one lowercase letter (a-z)\n * - At least one digit (0-9)\n * - At least one symbol (!@#$%^&*()_+-=[]{}|;:,.<>?)\n *\n * NIST-style use (3.3): `strengthMode=\"length\"`, `minLength`, `helperText` for\n * the rule, or no meter and no checklist at all.\n *\n * Usage:\n * ```html\n * <lc-password-input\n * [formControl]=\"passwordControl\"\n * label=\"Password\"\n * placeholder=\"Enter your password\"\n * [showStrengthMeter]=\"true\"\n * ></lc-password-input>\n * ```\n */\n@Component({\n selector: 'lc-password-input',\n standalone: true,\n imports: [IconComponent, AlertComponent],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => PasswordInputComponent),\n multi: true,\n },\n ],\n templateUrl: './password-input.component.html',\n styleUrls: ['./password-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class PasswordInputComponent implements ControlValueAccessor {\n /** Label text (displayed above the input) */\n readonly label = input<string>('Password');\n\n /** Placeholder text */\n readonly placeholder = input<string>('Enter password');\n\n /** Whether to show the strength meter bar + level text */\n readonly showStrengthMeter = input<boolean>(false);\n\n /** Whether to show the requirements checklist */\n readonly showRequirements = input<boolean>(false);\n\n /** Whether the input is disabled */\n readonly disabled = input<boolean>(false);\n\n /** Whether the input is required */\n readonly required = input<boolean>(false);\n\n /**\n * Show the `*` of a `required` field; `false` keeps `required` and\n * `aria-required` without the asterisk.\n * @default true\n */\n readonly requiredIndicator = input<boolean>(true);\n\n /** Error message to display (also sets aria-invalid) */\n readonly error = input<string | undefined>(undefined);\n\n /** Help below the field, e.g. the password rule; linked by `aria-describedby`. The error replaces it. */\n readonly helperText = input<string>();\n\n /**\n * Minimum length: the native `minlength` and the length rule of the\n * requirement list and the meter. Unset = no attribute, rule of 8 (3.2).\n */\n readonly minLength = input<number>();\n\n /** How the strength meter scores: `composition` (3.2) or `length` (NIST, no character-class rules). */\n readonly strengthMode = input<PasswordStrengthMode>('composition');\n\n /**\n * Native `autocomplete`: `current-password` (sign-in), `new-password`\n * (registration, change), so password managers fill and save the right\n * password. Unset = no attribute.\n */\n readonly autocomplete = input<string>();\n\n /** Native `name` of the inner input. Unset = no attribute. */\n readonly name = input<string>();\n\n /**\n * Native `autocapitalize`. Default `off`: a shown password must not be\n * capitalised by the on-screen keyboard.\n * @default 'off'\n */\n readonly autocapitalize = input<InputAutocapitalize>('off');\n\n /**\n * Native `spellcheck`. Default `false`: a shown password must never go to a\n * spell checker (some send text to a server).\n * @default false\n */\n readonly spellcheck = input<boolean>(false);\n\n /** Native `enterkeyhint`: the label of the on-screen keyboard's Enter key. Unset = no attribute. */\n readonly enterkeyhint = input<InputEnterKeyHint>();\n\n /** Accessible name of the visibility toggle while the password is hidden. */\n readonly showPasswordAriaLabel = input('Show password');\n\n /** Accessible name of the visibility toggle while the password is shown. */\n readonly hidePasswordAriaLabel = input('Hide password');\n\n /** Screen-reader prefix of the strength level (`showStrengthMeter`). */\n readonly strengthLabel = input('Password strength:');\n\n /** Shown for strength level `Weak`. */\n readonly weakLabel = input('Weak');\n\n /** Shown for strength level `Fair`. */\n readonly fairLabel = input('Fair');\n\n /** Shown for strength level `Good`. */\n readonly goodLabel = input('Good');\n\n /** Shown for strength level `Strong`. */\n readonly strongLabel = input('Strong');\n\n /** Accessible name of the requirement list (`showRequirements`). */\n readonly requirementsAriaLabel = input('Password requirements');\n\n /** Requirement: at least 8 characters. */\n readonly minLengthRequirementLabel = input('At least 8 characters');\n\n /** Requirement: one uppercase letter. */\n readonly uppercaseRequirementLabel = input('One uppercase letter (A-Z)');\n\n /** Requirement: one lowercase letter. */\n readonly lowercaseRequirementLabel = input('One lowercase letter (a-z)');\n\n /** Requirement: one digit. */\n readonly digitRequirementLabel = input('One digit (0-9)');\n\n /** Requirement: one symbol. */\n readonly symbolRequirementLabel = input('One symbol (!@#$%^&*)');\n\n /** Screen-reader suffix of a met requirement. */\n readonly requirementMetLabel = input('(met)');\n\n /** Screen-reader suffix of an unmet requirement. */\n readonly requirementNotMetLabel = input('(not met)');\n\n /** Emitted whenever the value (and thus the strength) is recalculated */\n readonly strengthChange = output<PasswordStrength>();\n\n /** Per-instance ids so label/error linkage survives several inputs on one page */\n readonly inputId = `lc-password-input-${nextUniqueId++}`;\n readonly errorId = `${this.inputId}-error`;\n readonly helperId = `${this.inputId}-helper`;\n readonly strengthId = `${this.inputId}-strength`;\n\n // Internal state\n readonly value = signal<string>('');\n readonly isPasswordVisible = signal<boolean>(false);\n\n // Internal disabled state (for ControlValueAccessor)\n private readonly formDisabled = signal<boolean>(false);\n\n /** Computed disabled state from both input and form control */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /** The length rule: `minLength`, else 8. */\n readonly requiredLength = computed(() => this.minLength() ?? DEFAULT_MIN_LENGTH);\n\n readonly requirements = computed<PasswordRequirement[]>(() => {\n const password = this.value();\n const checks: Array<[string, boolean]> = [[this.minLengthRequirementLabel(), password.length >= this.requiredLength()]];\n if (this.strengthMode() === 'composition') {\n checks.push(\n [this.uppercaseRequirementLabel(), /[A-Z]/.test(password)],\n [this.lowercaseRequirementLabel(), /[a-z]/.test(password)],\n [this.digitRequirementLabel(), /[0-9]/.test(password)],\n [this.symbolRequirementLabel(), SYMBOL_PATTERN.test(password)],\n );\n }\n return checks.map(([label, met]) => ({ label, met, icon: met ? '✓' : '✗' }));\n });\n\n readonly strength = computed<PasswordStrength>(() =>\n this.strengthMode() === 'length' ? this.lengthStrength() : this.compositionStrength(),\n );\n\n /** The strength level as shown to the user (the level itself stays English: it is API and class name). */\n readonly strengthText = computed(() => {\n switch (this.strength().level) {\n case 'Strong':\n return this.strongLabel();\n case 'Good':\n return this.goodLabel();\n case 'Fair':\n return this.fairLabel();\n default:\n return this.weakLabel();\n }\n });\n\n /** Lower-cased level for the modifier classes (lc-strength-weak …) */\n readonly strengthLevelClass = computed(() => `lc-strength-${this.strength().level.toLowerCase()}`);\n\n readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.error()) ids.push(this.errorId);\n else if (this.helperText()) ids.push(this.helperId);\n if (this.showStrengthMeter()) ids.push(this.strengthId);\n return ids.length ? ids.join(' ') : null;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: string) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n /**\n * ControlValueAccessor: Write value from form control\n */\n writeValue(value: string | null | undefined): void {\n this.value.set(value || '');\n this.strengthChange.emit(this.strength());\n }\n\n /**\n * ControlValueAccessor: Register onChange callback\n */\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n /**\n * ControlValueAccessor: Register onTouched callback\n */\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n /**\n * ControlValueAccessor: Set disabled state\n */\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n /**\n * Toggle password visibility\n */\n togglePasswordVisibility(): void {\n this.isPasswordVisible.update((visible) => !visible);\n }\n\n /**\n * Handle input changes\n */\n onInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.value.set(input.value);\n this.onChange(input.value);\n this.strengthChange.emit(this.strength());\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n /** 3.2 scoring: the share of the five rules met. */\n private compositionStrength(): PasswordStrength {\n const requirements = this.requirements();\n const score = requirements.filter((r) => r.met).length;\n const percentage = (score / requirements.length) * 100;\n\n let level: PasswordStrength['level'] = 'Weak';\n if (percentage >= 80) {\n level = 'Strong';\n } else if (percentage >= 60) {\n level = 'Good';\n } else if (percentage >= 40) {\n level = 'Fair';\n }\n\n return { score, level, percentage };\n }\n\n /** NIST scoring: the length against `minLength` (score 0–4 like the four levels). */\n private lengthStrength(): PasswordStrength {\n const length = this.value().length;\n const min = this.requiredLength();\n const percentage = Math.min(100, Math.round((length / (2 * min)) * 100));\n\n let level: PasswordStrength['level'] = 'Weak';\n let score = length > 0 ? 1 : 0;\n if (length >= 2 * min) {\n level = 'Strong';\n score = 4;\n } else if (length >= 1.5 * min) {\n level = 'Good';\n score = 3;\n } else if (length >= min) {\n level = 'Fair';\n score = 2;\n }\n\n return { score, level, percentage };\n }\n}\n","<div class=\"lc-password-input\">\n <label [for]=\"inputId\" class=\"lc-label\">\n {{ label() }}\n @if (required() && requiredIndicator()) {\n <span class=\"lc-required\" aria-hidden=\"true\">*</span>\n }\n </label>\n\n <div class=\"lc-input-wrapper\">\n <input\n [id]=\"inputId\"\n [type]=\"isPasswordVisible() ? 'text' : 'password'\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.minlength]=\"minLength() ?? null\"\n [attr.autocomplete]=\"autocomplete() ?? null\"\n [attr.name]=\"name() ?? null\"\n [attr.autocapitalize]=\"autocapitalize()\"\n [attr.spellcheck]=\"spellcheck() === undefined ? null : spellcheck() ? 'true' : 'false'\"\n [attr.enterkeyhint]=\"enterkeyhint() ?? null\"\n autocorrect=\"off\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n class=\"lc-input\"\n [class.lc-input-error]=\"error()\"\n />\n\n <button\n type=\"button\"\n (click)=\"togglePasswordVisibility()\"\n [attr.aria-label]=\"isPasswordVisible() ? hidePasswordAriaLabel() : showPasswordAriaLabel()\"\n [attr.aria-pressed]=\"isPasswordVisible()\"\n [attr.aria-controls]=\"inputId\"\n class=\"lc-toggle-btn\"\n [disabled]=\"isDisabled()\"\n >\n @if (isPasswordVisible()) {\n <lc-icon name=\"eye-slash\" size=\"md\" [decorative]=\"true\"></lc-icon>\n } @else {\n <lc-icon name=\"eye\" size=\"md\" [decorative]=\"true\"></lc-icon>\n }\n </button>\n </div>\n\n @if (error()) {\n <div [id]=\"errorId\" class=\"lc-error\" role=\"alert\">\n {{ error() }}\n </div>\n } @else if (helperText()) {\n <div [id]=\"helperId\" class=\"lc-helper\">\n {{ helperText() }}\n </div>\n }\n\n @if (showStrengthMeter()) {\n <div class=\"lc-strength-meter\">\n <!-- The bar is decorative; the live label below carries the meaning -->\n <div class=\"lc-strength-bar\" aria-hidden=\"true\">\n <div class=\"lc-strength-fill\" [class]=\"strengthLevelClass()\" [style.width.%]=\"strength().percentage\"></div>\n </div>\n <div [id]=\"strengthId\" class=\"lc-strength-label\" [class]=\"strengthLevelClass()\" role=\"status\" aria-live=\"polite\">\n <span class=\"lc-sr-only\">{{ strengthLabel() }} </span>{{ strengthText() }}\n </div>\n </div>\n }\n\n @if (showRequirements()) {\n <lc-alert variant=\"info\" [dismissible]=\"false\">\n <ul class=\"lc-requirements\" [attr.aria-label]=\"requirementsAriaLabel()\">\n @for (req of requirements(); track req.label) {\n <li\n class=\"lc-requirement\"\n [class.lc-requirement-met]=\"req.met\"\n [class.lc-requirement-unmet]=\"!req.met\"\n >\n <span class=\"lc-requirement-icon\" aria-hidden=\"true\">{{ req.icon }}</span>\n <span class=\"lc-requirement-label\">{{ req.label }}</span>\n <span class=\"lc-sr-only\">{{ req.met ? requirementMetLabel() : requirementNotMetLabel() }}</span>\n </li>\n }\n </ul>\n </lc-alert>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-password-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/password-input/password-input.component.ts","../../../../libs/angular-ui-kit/src/lib/password-input/password-input.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n computed,\n forwardRef,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_EYE, ICON_EYE_SLASH } from '@lifecorner/angular-ui-kit/icons';\nimport { AlertComponent } from '@lifecorner/angular-ui-kit/alert';\nimport type { InputAutocapitalize, InputEnterKeyHint } from '@lifecorner/angular-ui-kit/input';\n\nlet nextUniqueId = 0;\n\n/**\n * Password strength information\n */\nexport interface PasswordStrength {\n score: number; // 0-5 (number of requirements met)\n level: 'Weak' | 'Fair' | 'Good' | 'Strong';\n percentage: number; // 0-100\n}\n\n/**\n * Individual password requirement\n */\nexport interface PasswordRequirement {\n label: string;\n met: boolean;\n icon: string;\n}\n\nconst SYMBOL_PATTERN = /[!@#$%^&*()_+\\-=[\\]{}|;:,.<>?]/;\n\n/** The length rule when no `minLength` is set (the 3.2 rule). */\nconst DEFAULT_MIN_LENGTH = 8;\n\n/**\n * How the strength meter scores a password:\n * - `composition` (default, 3.2): share of five rules met — length and four character classes.\n * - `length`: the length only, as NIST SP 800-63B asks (no composition rules):\n * below `minLength` Weak, from it Fair, from 1.5 × Good, from 2 × Strong.\n */\nexport type PasswordStrengthMode = 'composition' | 'length';\n\n/**\n * Password Input Component with Show/Hide Toggle and Strength Meter\n *\n * Features:\n * - Show/Hide password toggle (eye icon)\n * - Real-time password strength indicator (Weak/Fair/Good/Strong)\n * - Visual strength meter bar with color coding\n * - Validation feedback (length, uppercase, lowercase, digit, symbol)\n * - Accessible ARIA attributes\n * - Reactive Forms ControlValueAccessor implementation\n *\n * Strength levels (share of the 5 requirements met):\n * - Weak: < 40%\n * - Fair: 40–59%\n * - Good: 60–79%\n * - Strong: >= 80%\n *\n * Requirements:\n * - Minimum 8 characters\n * - At least one uppercase letter (A-Z)\n * - At least one lowercase letter (a-z)\n * - At least one digit (0-9)\n * - At least one symbol (!@#$%^&*()_+-=[]{}|;:,.<>?)\n *\n * NIST-style use (3.3): `strengthMode=\"length\"`, `minLength`, `helperText` for\n * the rule, or no meter and no checklist at all.\n *\n * Usage:\n * ```html\n * <lc-password-input\n * [formControl]=\"passwordControl\"\n * label=\"Password\"\n * placeholder=\"Enter your password\"\n * [showStrengthMeter]=\"true\"\n * ></lc-password-input>\n * ```\n */\n@Component({\n selector: 'lc-password-input',\n standalone: true,\n imports: [IconComponent, AlertComponent],\n viewProviders: [ɵprovideKitIcons([ICON_EYE, ICON_EYE_SLASH])],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => PasswordInputComponent),\n multi: true,\n },\n ],\n templateUrl: './password-input.component.html',\n styleUrls: ['./password-input.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class PasswordInputComponent implements ControlValueAccessor {\n /** Label text (displayed above the input) */\n readonly label = input<string>('Password');\n\n /** Placeholder text */\n readonly placeholder = input<string>('Enter password');\n\n /** Whether to show the strength meter bar + level text */\n readonly showStrengthMeter = input<boolean>(false);\n\n /** Whether to show the requirements checklist */\n readonly showRequirements = input<boolean>(false);\n\n /** Whether the input is disabled */\n readonly disabled = input<boolean>(false);\n\n /** Whether the input is required */\n readonly required = input<boolean>(false);\n\n /**\n * Show the `*` of a `required` field; `false` keeps `required` and\n * `aria-required` without the asterisk.\n * @default true\n */\n readonly requiredIndicator = input<boolean>(true);\n\n /** Error message to display (also sets aria-invalid) */\n readonly error = input<string | undefined>(undefined);\n\n /** Help below the field, e.g. the password rule; linked by `aria-describedby`. The error replaces it. */\n readonly helperText = input<string>();\n\n /**\n * Minimum length: the native `minlength` and the length rule of the\n * requirement list and the meter. Unset = no attribute, rule of 8 (3.2).\n */\n readonly minLength = input<number>();\n\n /** How the strength meter scores: `composition` (3.2) or `length` (NIST, no character-class rules). */\n readonly strengthMode = input<PasswordStrengthMode>('composition');\n\n /**\n * Native `autocomplete`: `current-password` (sign-in), `new-password`\n * (registration, change), so password managers fill and save the right\n * password. Unset = no attribute.\n */\n readonly autocomplete = input<string>();\n\n /** Native `name` of the inner input. Unset = no attribute. */\n readonly name = input<string>();\n\n /**\n * Native `autocapitalize`. Default `off`: a shown password must not be\n * capitalised by the on-screen keyboard.\n * @default 'off'\n */\n readonly autocapitalize = input<InputAutocapitalize>('off');\n\n /**\n * Native `spellcheck`. Default `false`: a shown password must never go to a\n * spell checker (some send text to a server).\n * @default false\n */\n readonly spellcheck = input<boolean>(false);\n\n /** Native `enterkeyhint`: the label of the on-screen keyboard's Enter key. Unset = no attribute. */\n readonly enterkeyhint = input<InputEnterKeyHint>();\n\n /** Accessible name of the visibility toggle while the password is hidden. */\n readonly showPasswordAriaLabel = input('Show password');\n\n /** Accessible name of the visibility toggle while the password is shown. */\n readonly hidePasswordAriaLabel = input('Hide password');\n\n /** Screen-reader prefix of the strength level (`showStrengthMeter`). */\n readonly strengthLabel = input('Password strength:');\n\n /** Shown for strength level `Weak`. */\n readonly weakLabel = input('Weak');\n\n /** Shown for strength level `Fair`. */\n readonly fairLabel = input('Fair');\n\n /** Shown for strength level `Good`. */\n readonly goodLabel = input('Good');\n\n /** Shown for strength level `Strong`. */\n readonly strongLabel = input('Strong');\n\n /** Accessible name of the requirement list (`showRequirements`). */\n readonly requirementsAriaLabel = input('Password requirements');\n\n /** Requirement: at least 8 characters. */\n readonly minLengthRequirementLabel = input('At least 8 characters');\n\n /** Requirement: one uppercase letter. */\n readonly uppercaseRequirementLabel = input('One uppercase letter (A-Z)');\n\n /** Requirement: one lowercase letter. */\n readonly lowercaseRequirementLabel = input('One lowercase letter (a-z)');\n\n /** Requirement: one digit. */\n readonly digitRequirementLabel = input('One digit (0-9)');\n\n /** Requirement: one symbol. */\n readonly symbolRequirementLabel = input('One symbol (!@#$%^&*)');\n\n /** Screen-reader suffix of a met requirement. */\n readonly requirementMetLabel = input('(met)');\n\n /** Screen-reader suffix of an unmet requirement. */\n readonly requirementNotMetLabel = input('(not met)');\n\n /** Emitted whenever the value (and thus the strength) is recalculated */\n readonly strengthChange = output<PasswordStrength>();\n\n /** Per-instance ids so label/error linkage survives several inputs on one page */\n readonly inputId = `lc-password-input-${nextUniqueId++}`;\n readonly errorId = `${this.inputId}-error`;\n readonly helperId = `${this.inputId}-helper`;\n readonly strengthId = `${this.inputId}-strength`;\n\n // Internal state\n readonly value = signal<string>('');\n readonly isPasswordVisible = signal<boolean>(false);\n\n // Internal disabled state (for ControlValueAccessor)\n private readonly formDisabled = signal<boolean>(false);\n\n /** Computed disabled state from both input and form control */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /** The length rule: `minLength`, else 8. */\n readonly requiredLength = computed(() => this.minLength() ?? DEFAULT_MIN_LENGTH);\n\n readonly requirements = computed<PasswordRequirement[]>(() => {\n const password = this.value();\n const checks: Array<[string, boolean]> = [[this.minLengthRequirementLabel(), password.length >= this.requiredLength()]];\n if (this.strengthMode() === 'composition') {\n checks.push(\n [this.uppercaseRequirementLabel(), /[A-Z]/.test(password)],\n [this.lowercaseRequirementLabel(), /[a-z]/.test(password)],\n [this.digitRequirementLabel(), /[0-9]/.test(password)],\n [this.symbolRequirementLabel(), SYMBOL_PATTERN.test(password)],\n );\n }\n return checks.map(([label, met]) => ({ label, met, icon: met ? '✓' : '✗' }));\n });\n\n readonly strength = computed<PasswordStrength>(() =>\n this.strengthMode() === 'length' ? this.lengthStrength() : this.compositionStrength(),\n );\n\n /** The strength level as shown to the user (the level itself stays English: it is API and class name). */\n readonly strengthText = computed(() => {\n switch (this.strength().level) {\n case 'Strong':\n return this.strongLabel();\n case 'Good':\n return this.goodLabel();\n case 'Fair':\n return this.fairLabel();\n default:\n return this.weakLabel();\n }\n });\n\n /** Lower-cased level for the modifier classes (lc-strength-weak …) */\n readonly strengthLevelClass = computed(() => `lc-strength-${this.strength().level.toLowerCase()}`);\n\n readonly describedBy = computed(() => {\n const ids: string[] = [];\n if (this.error()) ids.push(this.errorId);\n else if (this.helperText()) ids.push(this.helperId);\n if (this.showStrengthMeter()) ids.push(this.strengthId);\n return ids.length ? ids.join(' ') : null;\n });\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: string) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n /**\n * ControlValueAccessor: Write value from form control\n */\n writeValue(value: string | null | undefined): void {\n this.value.set(value || '');\n this.strengthChange.emit(this.strength());\n }\n\n /**\n * ControlValueAccessor: Register onChange callback\n */\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n /**\n * ControlValueAccessor: Register onTouched callback\n */\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n /**\n * ControlValueAccessor: Set disabled state\n */\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n /**\n * Toggle password visibility\n */\n togglePasswordVisibility(): void {\n this.isPasswordVisible.update((visible) => !visible);\n }\n\n /**\n * Handle input changes\n */\n onInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.value.set(input.value);\n this.onChange(input.value);\n this.strengthChange.emit(this.strength());\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n /** 3.2 scoring: the share of the five rules met. */\n private compositionStrength(): PasswordStrength {\n const requirements = this.requirements();\n const score = requirements.filter((r) => r.met).length;\n const percentage = (score / requirements.length) * 100;\n\n let level: PasswordStrength['level'] = 'Weak';\n if (percentage >= 80) {\n level = 'Strong';\n } else if (percentage >= 60) {\n level = 'Good';\n } else if (percentage >= 40) {\n level = 'Fair';\n }\n\n return { score, level, percentage };\n }\n\n /** NIST scoring: the length against `minLength` (score 0–4 like the four levels). */\n private lengthStrength(): PasswordStrength {\n const length = this.value().length;\n const min = this.requiredLength();\n const percentage = Math.min(100, Math.round((length / (2 * min)) * 100));\n\n let level: PasswordStrength['level'] = 'Weak';\n let score = length > 0 ? 1 : 0;\n if (length >= 2 * min) {\n level = 'Strong';\n score = 4;\n } else if (length >= 1.5 * min) {\n level = 'Good';\n score = 3;\n } else if (length >= min) {\n level = 'Fair';\n score = 2;\n }\n\n return { score, level, percentage };\n }\n}\n","<div class=\"lc-password-input\">\n <label [for]=\"inputId\" class=\"lc-label\">\n {{ label() }}\n @if (required() && requiredIndicator()) {\n <span class=\"lc-required\" aria-hidden=\"true\">*</span>\n }\n </label>\n\n <div class=\"lc-input-wrapper\">\n <input\n [id]=\"inputId\"\n [type]=\"isPasswordVisible() ? 'text' : 'password'\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.minlength]=\"minLength() ?? null\"\n [attr.autocomplete]=\"autocomplete() ?? null\"\n [attr.name]=\"name() ?? null\"\n [attr.autocapitalize]=\"autocapitalize()\"\n [attr.spellcheck]=\"spellcheck() === undefined ? null : spellcheck() ? 'true' : 'false'\"\n [attr.enterkeyhint]=\"enterkeyhint() ?? null\"\n autocorrect=\"off\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n class=\"lc-input\"\n [class.lc-input-error]=\"error()\"\n />\n\n <button\n type=\"button\"\n (click)=\"togglePasswordVisibility()\"\n [attr.aria-label]=\"isPasswordVisible() ? hidePasswordAriaLabel() : showPasswordAriaLabel()\"\n [attr.aria-pressed]=\"isPasswordVisible()\"\n [attr.aria-controls]=\"inputId\"\n class=\"lc-toggle-btn\"\n [disabled]=\"isDisabled()\"\n >\n @if (isPasswordVisible()) {\n <lc-icon name=\"eye-slash\" size=\"md\" [decorative]=\"true\"></lc-icon>\n } @else {\n <lc-icon name=\"eye\" size=\"md\" [decorative]=\"true\"></lc-icon>\n }\n </button>\n </div>\n\n @if (error()) {\n <div [id]=\"errorId\" class=\"lc-error\" role=\"alert\">\n {{ error() }}\n </div>\n } @else if (helperText()) {\n <div [id]=\"helperId\" class=\"lc-helper\">\n {{ helperText() }}\n </div>\n }\n\n @if (showStrengthMeter()) {\n <div class=\"lc-strength-meter\">\n <!-- The bar is decorative; the live label below carries the meaning -->\n <div class=\"lc-strength-bar\" aria-hidden=\"true\">\n <div class=\"lc-strength-fill\" [class]=\"strengthLevelClass()\" [style.width.%]=\"strength().percentage\"></div>\n </div>\n <div [id]=\"strengthId\" class=\"lc-strength-label\" [class]=\"strengthLevelClass()\" role=\"status\" aria-live=\"polite\">\n <span class=\"lc-sr-only\">{{ strengthLabel() }} </span>{{ strengthText() }}\n </div>\n </div>\n }\n\n @if (showRequirements()) {\n <lc-alert variant=\"info\" [dismissible]=\"false\">\n <ul class=\"lc-requirements\" [attr.aria-label]=\"requirementsAriaLabel()\">\n @for (req of requirements(); track req.label) {\n <li\n class=\"lc-requirement\"\n [class.lc-requirement-met]=\"req.met\"\n [class.lc-requirement-unmet]=\"!req.met\"\n >\n <span class=\"lc-requirement-icon\" aria-hidden=\"true\">{{ req.icon }}</span>\n <span class=\"lc-requirement-label\">{{ req.label }}</span>\n <span class=\"lc-sr-only\">{{ req.met ? requirementMetLabel() : requirementNotMetLabel() }}</span>\n </li>\n }\n </ul>\n </lc-alert>\n }\n</div>\n"],"mappings":";;;;;;AAeA,IAAI,eAAe;AAoBnB,MAAM,iBAAiB;AAGvB,MAAM,qBAAqB;AA+D3B,IAAa,yBAAb,MAAa,uBAAsB;CAExB,QAAQ,MAAc,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,cAAc,MAAc,kBAAiB,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAG7C,oBAAoB,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAGzC,mBAAmB,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;CAGxC,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAGhC,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAOhC,oBAAoB,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAGxC,QAAQ,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAG5C,aAAa,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAM5B,YAAY,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAG3B,eAAe,MAA4B,eAAc,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAOzD,eAAe,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG9B,OAAO,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAOtB,iBAAiB,MAA2B,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAOlD,aAAa,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGlC,eAAe,MAA0B,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGzC,wBAAwB,MAAM,iBAAgB,GAAA,YAAA,CAAA,EAAA,WAAA,wBAAA,CAAA,+BAAA,CAAA,CAAA;CAG9C,wBAAwB,MAAM,iBAAgB,GAAA,YAAA,CAAA,EAAA,WAAA,wBAAA,CAAA,+BAAA,CAAA,CAAA;CAG9C,gBAAgB,MAAM,sBAAqB,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAG3C,YAAY,MAAM,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,YAAY,MAAM,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,YAAY,MAAM,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGzB,cAAc,MAAM,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAG7B,wBAAwB,MAAM,yBAAwB,GAAA,YAAA,CAAA,EAAA,WAAA,wBAAA,CAAA,+BAAA,CAAA,CAAA;CAGtD,4BAA4B,MAAM,yBAAwB,GAAA,YAAA,CAAA,EAAA,WAAA,4BAAA,CAAA,+BAAA,CAAA,CAAA;CAG1D,4BAA4B,MAAM,8BAA6B,GAAA,YAAA,CAAA,EAAA,WAAA,4BAAA,CAAA,+BAAA,CAAA,CAAA;CAG/D,4BAA4B,MAAM,8BAA6B,GAAA,YAAA,CAAA,EAAA,WAAA,4BAAA,CAAA,+BAAA,CAAA,CAAA;CAG/D,wBAAwB,MAAM,mBAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,wBAAA,CAAA,+BAAA,CAAA,CAAA;CAGhD,yBAAyB,MAAM,yBAAwB,GAAA,YAAA,CAAA,EAAA,WAAA,yBAAA,CAAA,+BAAA,CAAA,CAAA;CAGvD,sBAAsB,MAAM,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,sBAAA,CAAA,+BAAA,CAAA,CAAA;CAGpC,yBAAyB,MAAM,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,yBAAA,CAAA,+BAAA,CAAA,CAAA;CAG3C,iBAAiB,OAAM;CAGvB,UAAU,qBAAqB;CAC/B,UAAU,GAAG,KAAK,QAAO;CACzB,WAAW,GAAG,KAAK,QAAO;CAC1B,aAAa,GAAG,KAAK,QAAO;CAG5B,QAAQ,OAAe,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAC1B,oBAAoB,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAGlC,eAAe,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG7C,aAAa,eAAe,KAAK,SAAQ,KAAM,KAAK,aAAY,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGnE,iBAAiB,eAAe,KAAK,UAAS,KAAM,oBAAmB,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAEvE,eAAe,eAAqC;EAC3D,MAAM,WAAW,KAAK,MAAK;EAC3B,MAAM,SAAmC,CAAC,CAAC,KAAK,0BAAyB,GAAI,SAAS,UAAU,KAAK,eAAc,CAAE,CAAC;EACtH,IAAI,KAAK,aAAY,MAAO,eAC1B,OAAO,KACL,CAAC,KAAK,0BAAyB,GAAI,QAAQ,KAAK,QAAQ,CAAC,GACzD,CAAC,KAAK,0BAAyB,GAAI,QAAQ,KAAK,QAAQ,CAAC,GACzD,CAAC,KAAK,sBAAqB,GAAI,QAAQ,KAAK,QAAQ,CAAC,GACrD,CAAC,KAAK,uBAAsB,GAAI,eAAe,KAAK,QAAQ,CAAC,CAAC;EAGlE,OAAO,OAAO,KAAK,CAAC,OAAO,UAAU;GAAE;GAAO;GAAK,MAAM,MAAM,MAAM;EAAG,EAAG;CAC7E,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEO,WAAW,eAClB,KAAK,aAAY,MAAO,WAAW,KAAK,eAAc,IAAK,KAAK,oBAAmB,GACpF,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAGQ,eAAe,eAAc;EACpC,QAAQ,KAAK,SAAQ,CAAE,CAAC,OAAxB;GACE,KAAK,UACH,OAAO,KAAK,YAAW;GACzB,KAAK,QACH,OAAO,KAAK,UAAS;GACvB,KAAK,QACH,OAAO,KAAK,UAAS;GACvB,SACE,OAAO,KAAK,UAAS;EACzB;CACF,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGO,qBAAqB,eAAe,eAAe,KAAK,SAAQ,CAAE,CAAC,MAAM,YAAW,KAAK,GAAA,YAAA,CAAA,EAAA,WAAA,qBAAA,CAAA,+BAAA,CAAA,CAAA;CAEzF,cAAc,eAAc;EACnC,MAAM,MAAgB,CAAA;EACtB,IAAI,KAAK,MAAK,GAAI,IAAI,KAAK,KAAK,OAAO;OAClC,IAAI,KAAK,WAAU,GAAI,IAAI,KAAK,KAAK,QAAQ;EAClD,IAAI,KAAK,kBAAiB,GAAI,IAAI,KAAK,KAAK,UAAU;EACtD,OAAO,IAAI,SAAS,IAAI,KAAK,GAAG,IAAI;CACtC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGM,iBAAyC,CAAE;CAE3C,kBAA6B,CAAE;CAKvC,WAAW,OAAgC;EACzC,KAAK,MAAM,IAAI,SAAS,EAAE;EAC1B,KAAK,eAAe,KAAK,KAAK,SAAQ,CAAE;CAC1C;CAKA,iBAAiB,IAA2B;EAC1C,KAAK,WAAW;CAClB;CAKA,kBAAkB,IAAc;EAC9B,KAAK,YAAY;CACnB;CAKA,iBAAiB,YAAmB;EAClC,KAAK,aAAa,IAAI,UAAU;CAClC;CAKA,2BAAwB;EACtB,KAAK,kBAAkB,QAAQ,YAAY,CAAC,OAAO;CACrD;CAKA,QAAQ,OAAY;EAClB,MAAM,QAAQ,MAAM;EACpB,KAAK,MAAM,IAAI,MAAM,KAAK;EAC1B,KAAK,SAAS,MAAM,KAAK;EACzB,KAAK,eAAe,KAAK,KAAK,SAAQ,CAAE;CAC1C;CAKA,SAAM;EACJ,KAAK,UAAS;CAChB;CAGQ,sBAAmB;EACzB,MAAM,eAAe,KAAK,aAAY;EACtC,MAAM,QAAQ,aAAa,QAAQ,MAAM,EAAE,GAAG,CAAC,CAAC;EAChD,MAAM,aAAc,QAAQ,aAAa,SAAU;EAEnD,IAAI,QAAmC;EACvC,IAAI,cAAc,IAChB,QAAQ;OACH,IAAI,cAAc,IACvB,QAAQ;OACH,IAAI,cAAc,IACvB,QAAQ;EAGV,OAAO;GAAE;GAAO;GAAO;EAAU;CACnC;CAGQ,iBAAc;EACpB,MAAM,SAAS,KAAK,MAAK,CAAE,CAAC;EAC5B,MAAM,MAAM,KAAK,eAAc;EAC/B,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,MAAO,UAAU,IAAI,OAAQ,GAAG,CAAC;EAEvE,IAAI,QAAmC;EACvC,IAAI,QAAQ,SAAS,IAAI,IAAI;EAC7B,IAAI,UAAU,IAAI,KAAK;GACrB,QAAQ;GACR,QAAQ;EACV,OAAO,IAAI,UAAU,MAAM,KAAK;GAC9B,QAAQ;GACR,QAAQ;EACV,OAAO,IAAI,UAAU,KAAK;GACxB,QAAQ;GACR,QAAQ;EACV;EAEA,OAAO;GAAE;GAAO;GAAO;EAAU;CACnC;CAlRW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAsB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAtB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAsB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,mBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,kBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,mBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,uBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,uBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,eAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,uBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,2BAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,2BAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,2BAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,uBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,wBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,qBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,wBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,gBAAA,iBAAA;EAAA,WAXtB,CACT;GACE,SAAS;GACT,aAAa,iBAAiB,sBAAsB;GACpD,OAAO;EACR,CACF;EAAA,UAAA;EAAA,UChGH;EAyFA,QAAA,CAAA,i1GAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MDDY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAAc,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA,SAAA,CAAA,aAAA,eAAA;EAAA,CAAA;EAAA,eACxB,CAAC,iBAAiB,CAAC,UAAU,cAAc,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAYlD,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EAhBlC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAmB,YACjB;GAAI,SACP,CAAC,eAAe,cAAc;GAAC,eACzB,CAAC,iBAAiB,CAAC,UAAU,cAAc,CAAC,CAAC;GAAC,WAClD,CACT;IACE,SAAS;IACT,aAAa,iBAAgB,sBAAuB;IACpD,OAAO;GACR,CACF;GAAA,iBAGgB,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,i1GAAA;EAAA,CAAA"}
@@ -1,7 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, computed, input, output } from "@angular/core";
3
3
  import { NgTemplateOutlet } from "@angular/common";
4
- import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
4
+ import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
5
+ import { ICON_ALERT_TRIANGLE, ICON_CHECK, ICON_X } from "@lifecorner/angular-ui-kit/icons";
5
6
  const STATUS_ICON = {
6
7
  complete: "check",
7
8
  current: "point",
@@ -96,6 +97,11 @@ var PipelineComponent = class PipelineComponent {
96
97
  "ngTemplateOutletInjector"
97
98
  ]
98
99
  }],
100
+ viewProviders: [ɵprovideKitIcons([
101
+ ICON_CHECK,
102
+ ICON_ALERT_TRIANGLE,
103
+ ICON_X
104
+ ])],
99
105
  changeDetection: i0.ChangeDetectionStrategy.OnPush
100
106
  });
101
107
  };
@@ -110,6 +116,11 @@ i0.ɵɵngDeclareClassMetadata({
110
116
  selector: "lc-pipeline",
111
117
  standalone: true,
112
118
  imports: [IconComponent, NgTemplateOutlet],
119
+ viewProviders: [ɵprovideKitIcons([
120
+ ICON_CHECK,
121
+ ICON_ALERT_TRIANGLE,
122
+ ICON_X
123
+ ])],
113
124
  changeDetection: ChangeDetectionStrategy.OnPush,
114
125
  template: "<ol [class]=\"hostClass()\">\n @for (step of steps(); track $index; let last = $last) {\n <li [class]=\"stepClass(step)\" [class.lc-pipeline__step--last]=\"last\">\n @if (clickable()) {\n <button type=\"button\" class=\"lc-pipeline__hit\" (click)=\"onActivate(step)\">\n <ng-container [ngTemplateOutlet]=\"body\" [ngTemplateOutletContext]=\"{ $implicit: step }\" />\n </button>\n } @else {\n <ng-container [ngTemplateOutlet]=\"body\" [ngTemplateOutletContext]=\"{ $implicit: step }\" />\n }\n </li>\n }\n</ol>\n\n<ng-template #body let-step>\n <span class=\"lc-pipeline__marker\">\n <span class=\"lc-pipeline__connector\" aria-hidden=\"true\"></span>\n <span class=\"lc-pipeline__node\">\n @if (iconFor(step)) {\n <lc-icon [name]=\"iconFor(step)\" size=\"xs\" [decorative]=\"true\" />\n }\n </span>\n </span>\n <span class=\"lc-pipeline__text\">\n <span class=\"lc-pipeline__label\">{{ step.label }}</span>\n @if (step.caption) {\n <span class=\"lc-pipeline__caption\">{{ step.caption }}</span>\n }\n </span>\n</ng-template>\n",
115
126
  styles: [":host{display:block}.lc-pipeline{--lc-pipeline-node-size: 1.75rem;--lc-pipeline-line-weight: 2px;--lc-pipeline-gap: var(--spacing-2);--lc-pipeline-line: var(--color-border-strong);--lc-pipeline-line-done: var(--color-primary-500);--lc-pipeline-node-bg: var(--color-surface);--lc-pipeline-node-fg: var(--color-text-tertiary);--lc-pipeline-node-border: var(--lc-pipeline-line)}.lc-pipeline--size-sm{--lc-pipeline-node-size: 1.25rem;--lc-pipeline-line-weight: 1px;--lc-pipeline-gap: var(--spacing-1)}.lc-pipeline--size-sm .lc-pipeline__label{font-size:var(--font-size-xs)}.lc-pipeline--size-sm .lc-pipeline__caption{font-size:var(--font-size-2xs, .6875rem)}.lc-pipeline--size-sm .lc-pipeline__node lc-icon{transform:scale(.85)}.lc-pipeline{list-style:none;margin:0;padding:0;display:flex}.lc-pipeline__step{position:relative;display:flex;min-width:0}.lc-pipeline__hit{display:flex;background:none;border:0;padding:0;margin:0;font:inherit;color:inherit;text-align:inherit;cursor:pointer;width:100%}.lc-pipeline__hit:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px;border-radius:var(--border-radius-sm)}.lc-pipeline__marker{flex-shrink:0}.lc-pipeline__node{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;width:var(--lc-pipeline-node-size);height:var(--lc-pipeline-node-size);border-radius:var(--border-radius-full);background-color:var(--lc-pipeline-node-bg);border:var(--lc-pipeline-line-weight) solid var(--lc-pipeline-node-border);color:var(--lc-pipeline-node-fg);box-sizing:border-box}.lc-pipeline__label{display:block;font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);color:var(--color-text-primary)}.lc-pipeline__caption{display:block;font-size:var(--font-size-xs);color:var(--color-text-tertiary);line-height:1.35}.lc-pipeline--horizontal{align-items:flex-start;gap:var(--lc-pipeline-gap)}.lc-pipeline--horizontal .lc-pipeline__step{flex:1 1 0;flex-direction:column;align-items:flex-start}.lc-pipeline--horizontal .lc-pipeline__hit{flex-direction:column;align-items:flex-start}.lc-pipeline--horizontal .lc-pipeline__connector{position:absolute;top:calc(var(--lc-pipeline-node-size) / 2);left:calc(var(--lc-pipeline-node-size) / 2);width:calc(100% + var(--lc-pipeline-gap));height:var(--lc-pipeline-line-weight);transform:translateY(-50%);background-color:var(--lc-pipeline-line)}.lc-pipeline--horizontal .lc-pipeline__step--last .lc-pipeline__connector{display:none}.lc-pipeline--horizontal .lc-pipeline__text{margin-top:var(--spacing-2);padding-right:var(--spacing-3)}.lc-pipeline--vertical{flex-direction:column;gap:0}.lc-pipeline--vertical .lc-pipeline__step{flex-direction:row;align-items:flex-start;gap:var(--spacing-3);padding-bottom:var(--spacing-4)}.lc-pipeline--vertical .lc-pipeline__hit{flex-direction:row;align-items:flex-start;gap:var(--spacing-3)}.lc-pipeline--vertical .lc-pipeline__connector{position:absolute;left:calc(var(--lc-pipeline-node-size) / 2);top:var(--lc-pipeline-node-size);width:var(--lc-pipeline-line-weight);height:calc(100% - var(--lc-pipeline-node-size));transform:translate(-50%);background-color:var(--lc-pipeline-line)}.lc-pipeline--vertical .lc-pipeline__step--last .lc-pipeline__connector{display:none}.lc-pipeline--vertical .lc-pipeline__text{padding-top:calc((var(--lc-pipeline-node-size) - 1.25rem) / 2)}.lc-pipeline__step--complete .lc-pipeline__node{background-color:var(--color-primary-500);border-color:var(--color-primary-500);color:var(--color-on-primary, #fff)}.lc-pipeline__step--complete .lc-pipeline__connector{background-color:var(--lc-pipeline-line-done)}.lc-pipeline__step--current .lc-pipeline__node{border-color:var(--color-primary-500);color:var(--color-primary-500);box-shadow:var(--shadow-brand-glow)}.lc-pipeline__step--current .lc-pipeline__connector{background-color:var(--lc-pipeline-line-done)}.lc-pipeline__step--warning .lc-pipeline__node{border-color:var(--color-warning);color:var(--color-warning)}.lc-pipeline__step--error .lc-pipeline__node{border-color:var(--color-error);color:var(--color-error)}.lc-pipeline__step--pending .lc-pipeline__node{border-style:dashed}.lc-pipeline__step--pending .lc-pipeline__label{color:var(--color-text-secondary)}\n"]
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-pipeline.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/pipeline/pipeline.component.ts","../../../../libs/angular-ui-kit/src/lib/pipeline/pipeline.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n computed,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/**\n * Status of a single pipeline node.\n * - complete: finished (teal check)\n * - current: in progress / active (teal, subtle pulse)\n * - pending: not started yet (muted, hollow)\n * - warning: needs attention (amber)\n * - error: failed (red)\n */\nexport type PipelineStatus =\n | 'complete'\n | 'current'\n | 'pending'\n | 'warning'\n | 'error';\n\nexport interface PipelineStep {\n /** Node label. */\n label: string;\n /** Optional secondary line under the label. */\n caption?: string;\n /** Status that drives the node color and default icon. */\n status: PipelineStatus;\n /** Optional icon (Tabler name) overriding the status default. */\n icon?: string;\n /** Optional opaque payload echoed back in `stepClick`. */\n id?: string;\n}\n\nexport type PipelineOrientation = 'horizontal' | 'vertical';\nexport type PipelineSize = 'sm' | 'md';\n\nconst STATUS_ICON: Record<PipelineStatus, string> = {\n complete: 'check',\n current: 'point',\n pending: '',\n warning: 'alert-triangle',\n error: 'x',\n};\n\n/**\n * Pipeline — a status timeline of connected process nodes.\n *\n * Unlike {@link StepperComponent} (a navigation stepper whose states derive\n * from the active index), each pipeline node carries its own explicit status,\n * so it can show completed, current, pending, warning and error nodes in the\n * same chain — with an optional caption under each label.\n *\n * @example\n * ```html\n * <lc-pipeline\n * [steps]=\"[\n * { label: 'Connect', caption: 'Token valid', status: 'complete' },\n * { label: 'Checkout', caption: '2 hours ago', status: 'complete' },\n * { label: 'Analyze', caption: 'Running…', status: 'current' },\n * { label: 'Graph', caption: 'Out of date', status: 'warning' }\n * ]\" />\n * ```\n */\n@Component({\n selector: 'lc-pipeline',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet],\n templateUrl: './pipeline.component.html',\n styleUrls: ['./pipeline.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class PipelineComponent {\n /** Ordered pipeline nodes. */\n readonly steps = input.required<readonly PipelineStep[]>();\n\n /** Layout direction. @default 'horizontal' */\n readonly orientation = input<PipelineOrientation>('horizontal');\n\n /**\n * Node/connector scale.\n * - md: default nodes (1.75rem)\n * - sm: compact spine — smaller nodes, thinner connectors, tighter grid\n * @default 'md'\n */\n readonly size = input<PipelineSize>('md');\n\n /** Whether nodes are clickable (emits `stepClick`). @default false */\n readonly clickable = input<boolean>(false);\n\n /** Emitted when a node is activated (only when `clickable`). */\n readonly stepClick = output<PipelineStep>();\n\n protected readonly hostClass = computed(\n () =>\n `lc-pipeline lc-pipeline--${this.orientation()} lc-pipeline--size-${this.size()}`,\n );\n\n protected iconFor(step: PipelineStep): string {\n return step.icon ?? STATUS_ICON[step.status];\n }\n\n protected stepClass(step: PipelineStep): string {\n return `lc-pipeline__step lc-pipeline__step--${step.status}`;\n }\n\n protected onActivate(step: PipelineStep): void {\n if (this.clickable()) {\n this.stepClick.emit(step);\n }\n }\n}\n","<ol [class]=\"hostClass()\">\n @for (step of steps(); track $index; let last = $last) {\n <li [class]=\"stepClass(step)\" [class.lc-pipeline__step--last]=\"last\">\n @if (clickable()) {\n <button type=\"button\" class=\"lc-pipeline__hit\" (click)=\"onActivate(step)\">\n <ng-container [ngTemplateOutlet]=\"body\" [ngTemplateOutletContext]=\"{ $implicit: step }\" />\n </button>\n } @else {\n <ng-container [ngTemplateOutlet]=\"body\" [ngTemplateOutletContext]=\"{ $implicit: step }\" />\n }\n </li>\n }\n</ol>\n\n<ng-template #body let-step>\n <span class=\"lc-pipeline__marker\">\n <span class=\"lc-pipeline__connector\" aria-hidden=\"true\"></span>\n <span class=\"lc-pipeline__node\">\n @if (iconFor(step)) {\n <lc-icon [name]=\"iconFor(step)\" size=\"xs\" [decorative]=\"true\" />\n }\n </span>\n </span>\n <span class=\"lc-pipeline__text\">\n <span class=\"lc-pipeline__label\">{{ step.label }}</span>\n @if (step.caption) {\n <span class=\"lc-pipeline__caption\">{{ step.caption }}</span>\n }\n </span>\n</ng-template>\n"],"mappings":";;;;AAyCA,MAAM,cAA8C;CAClD,UAAU;CACV,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACR;AA6BD,IAAa,oBAAb,MAAa,kBAAiB;CAEnB,QAAQ,MAAM,SAAmC,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGjD,cAAc,MAA2B,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAQtD,OAAO,MAAoB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGhC,YAAY,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,YAAY,OAAM;CAER,YAAY,eAE3B,4BAA4B,KAAK,YAAW,EAAE,qBAAsB,KAAK,KAAI,KAChF,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAES,QAAQ,MAAkB;EAClC,OAAO,KAAK,QAAQ,YAAY,KAAK;CACvC;CAEU,UAAU,MAAkB;EACpC,OAAO,wCAAwC,KAAK;CACtD;CAEU,WAAW,MAAkB;EACrC,IAAI,KAAK,UAAS,GAChB,KAAK,UAAU,KAAK,IAAI;CAE5B;CAtCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAiB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,WAAA,YAAA;EAAA,UAAA;EAAA,UC5E9B;EA8BA,QAAA,CAAA,unIAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MDyCY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAAgB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAK9B,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EAR7B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAa,YACX;GAAI,SACP,CAAC,eAAe,gBAAgB;GAAC,iBAGzB,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,unIAAA;EAAA,CAAA"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-pipeline.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/pipeline/pipeline.component.ts","../../../../libs/angular-ui-kit/src/lib/pipeline/pipeline.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n computed,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_ALERT_TRIANGLE, ICON_CHECK, ICON_X } from '@lifecorner/angular-ui-kit/icons';\n\n/**\n * Status of a single pipeline node.\n * - complete: finished (teal check)\n * - current: in progress / active (teal, subtle pulse)\n * - pending: not started yet (muted, hollow)\n * - warning: needs attention (amber)\n * - error: failed (red)\n */\nexport type PipelineStatus =\n | 'complete'\n | 'current'\n | 'pending'\n | 'warning'\n | 'error';\n\nexport interface PipelineStep {\n /** Node label. */\n label: string;\n /** Optional secondary line under the label. */\n caption?: string;\n /** Status that drives the node color and default icon. */\n status: PipelineStatus;\n /** Optional icon (Tabler name) overriding the status default. */\n icon?: string;\n /** Optional opaque payload echoed back in `stepClick`. */\n id?: string;\n}\n\nexport type PipelineOrientation = 'horizontal' | 'vertical';\nexport type PipelineSize = 'sm' | 'md';\n\nconst STATUS_ICON: Record<PipelineStatus, string> = {\n complete: 'check',\n current: 'point',\n pending: '',\n warning: 'alert-triangle',\n error: 'x',\n};\n\n/**\n * Pipeline — a status timeline of connected process nodes.\n *\n * Unlike {@link StepperComponent} (a navigation stepper whose states derive\n * from the active index), each pipeline node carries its own explicit status,\n * so it can show completed, current, pending, warning and error nodes in the\n * same chain — with an optional caption under each label.\n *\n * @example\n * ```html\n * <lc-pipeline\n * [steps]=\"[\n * { label: 'Connect', caption: 'Token valid', status: 'complete' },\n * { label: 'Checkout', caption: '2 hours ago', status: 'complete' },\n * { label: 'Analyze', caption: 'Running…', status: 'current' },\n * { label: 'Graph', caption: 'Out of date', status: 'warning' }\n * ]\" />\n * ```\n */\n@Component({\n selector: 'lc-pipeline',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK, ICON_ALERT_TRIANGLE, ICON_X])],\n templateUrl: './pipeline.component.html',\n styleUrls: ['./pipeline.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class PipelineComponent {\n /** Ordered pipeline nodes. */\n readonly steps = input.required<readonly PipelineStep[]>();\n\n /** Layout direction. @default 'horizontal' */\n readonly orientation = input<PipelineOrientation>('horizontal');\n\n /**\n * Node/connector scale.\n * - md: default nodes (1.75rem)\n * - sm: compact spine — smaller nodes, thinner connectors, tighter grid\n * @default 'md'\n */\n readonly size = input<PipelineSize>('md');\n\n /** Whether nodes are clickable (emits `stepClick`). @default false */\n readonly clickable = input<boolean>(false);\n\n /** Emitted when a node is activated (only when `clickable`). */\n readonly stepClick = output<PipelineStep>();\n\n protected readonly hostClass = computed(\n () =>\n `lc-pipeline lc-pipeline--${this.orientation()} lc-pipeline--size-${this.size()}`,\n );\n\n protected iconFor(step: PipelineStep): string {\n return step.icon ?? STATUS_ICON[step.status];\n }\n\n protected stepClass(step: PipelineStep): string {\n return `lc-pipeline__step lc-pipeline__step--${step.status}`;\n }\n\n protected onActivate(step: PipelineStep): void {\n if (this.clickable()) {\n this.stepClick.emit(step);\n }\n }\n}\n","<ol [class]=\"hostClass()\">\n @for (step of steps(); track $index; let last = $last) {\n <li [class]=\"stepClass(step)\" [class.lc-pipeline__step--last]=\"last\">\n @if (clickable()) {\n <button type=\"button\" class=\"lc-pipeline__hit\" (click)=\"onActivate(step)\">\n <ng-container [ngTemplateOutlet]=\"body\" [ngTemplateOutletContext]=\"{ $implicit: step }\" />\n </button>\n } @else {\n <ng-container [ngTemplateOutlet]=\"body\" [ngTemplateOutletContext]=\"{ $implicit: step }\" />\n }\n </li>\n }\n</ol>\n\n<ng-template #body let-step>\n <span class=\"lc-pipeline__marker\">\n <span class=\"lc-pipeline__connector\" aria-hidden=\"true\"></span>\n <span class=\"lc-pipeline__node\">\n @if (iconFor(step)) {\n <lc-icon [name]=\"iconFor(step)\" size=\"xs\" [decorative]=\"true\" />\n }\n </span>\n </span>\n <span class=\"lc-pipeline__text\">\n <span class=\"lc-pipeline__label\">{{ step.label }}</span>\n @if (step.caption) {\n <span class=\"lc-pipeline__caption\">{{ step.caption }}</span>\n }\n </span>\n</ng-template>\n"],"mappings":";;;;;AA0CA,MAAM,cAA8C;CAClD,UAAU;CACV,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACR;AA8BD,IAAa,oBAAb,MAAa,kBAAiB;CAEnB,QAAQ,MAAM,SAAmC,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGjD,cAAc,MAA2B,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAQtD,OAAO,MAAoB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGhC,YAAY,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,YAAY,OAAM;CAER,YAAY,eAE3B,4BAA4B,KAAK,YAAW,EAAE,qBAAsB,KAAK,KAAI,KAChF,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAES,QAAQ,MAAkB;EAClC,OAAO,KAAK,QAAQ,YAAY,KAAK;CACvC;CAEU,UAAU,MAAkB;EACpC,OAAO,wCAAwC,KAAK;CACtD;CAEU,WAAW,MAAkB;EACrC,IAAI,KAAK,UAAS,GAChB,KAAK,UAAU,KAAK,IAAI;CAE5B;CAtCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAiB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,WAAA,YAAA;EAAA,UAAA;EAAA,UC9E9B;EA8BA,QAAA,CAAA,unIAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MD0CY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAAgB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,eAC1B,CAAC,iBAAiB;GAAC;GAAY;GAAqB;EAAM,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAKjE,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EAT7B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAa,YACX;GAAI,SACP,CAAC,eAAe,gBAAgB;GAAC,eAC3B,CAAC,iBAAiB;IAAC;IAAY;IAAqB;GAAM,CAAC,CAAC;GAAC,iBAG3D,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,unIAAA;EAAA,CAAA"}
@@ -1,7 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, computed, forwardRef, input, output, signal, viewChildren } from "@angular/core";
3
3
  import { NG_VALUE_ACCESSOR } from "@angular/forms";
4
- import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
4
+ import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
5
+ import { ICON_STAR, ICON_STAR_SOLID } from "@lifecorner/angular-ui-kit/icons";
5
6
  var RatingComponent = class RatingComponent {
6
7
  static nextId = 0;
7
8
  max = input(5, ...ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []);
@@ -223,6 +224,7 @@ var RatingComponent = class RatingComponent {
223
224
  "strict"
224
225
  ]
225
226
  }],
227
+ viewProviders: [ɵprovideKitIcons([ICON_STAR, ICON_STAR_SOLID])],
226
228
  changeDetection: i0.ChangeDetectionStrategy.OnPush
227
229
  });
228
230
  };
@@ -237,6 +239,7 @@ i0.ɵɵngDeclareClassMetadata({
237
239
  selector: "lc-rating",
238
240
  standalone: true,
239
241
  imports: [IconComponent],
242
+ viewProviders: [ɵprovideKitIcons([ICON_STAR, ICON_STAR_SOLID])],
240
243
  changeDetection: ChangeDetectionStrategy.OnPush,
241
244
  providers: [{
242
245
  provide: NG_VALUE_ACCESSOR,
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-rating.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/rating/rating.component.ts","../../../../libs/angular-ui-kit/src/lib/rating/rating.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n output,\n computed,\n signal,\n forwardRef,\n viewChildren,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\nexport type RatingSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'lc-rating',\n standalone: true,\n imports: [IconComponent],\n templateUrl: './rating.component.html',\n styleUrls: ['./rating.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => RatingComponent),\n multi: true,\n },\n ],\n})\n/**\n * Rating component for star-based value selection.\n *\n * Features:\n * - Configurable maximum star count\n * - Half-star rating support (click the left half of a star, or step with the arrow keys)\n * - Multiple size variants (sm, md, lg)\n * - Read-only and disabled states\n * - Hover preview with visual feedback\n * - Keyboard: Arrow keys step the value, Home/End jump to the ends\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-rating [max]=\"5\" [allowHalf]=\"true\" label=\"Rate this\" />\n * ```\n */\nexport class RatingComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Maximum number of stars */\n max = input<number>(5);\n\n /** Size variant */\n size = input<RatingSize>('md');\n\n /** Whether the rating is read-only */\n readonly = input<boolean>(false);\n\n /** Whether the rating is disabled */\n disabled = input<boolean>(false);\n\n /** Whether to allow half-star ratings */\n allowHalf = input<boolean>(false);\n\n /** Label text */\n label = input<string>();\n\n /** Whether to show the numeric value */\n showValue = input<boolean>(false);\n\n /** Accessible name of the star group when no visible `label` is set. */\n ariaLabel = input('Rating');\n\n /** Emits the selected rating */\n ratingChange = output<number>();\n\n /** Per-instance id naming the radiogroup by the visible label. */\n protected readonly labelId = `lc-rating-${++RatingComponent.nextId}-label`;\n\n protected value = signal(0);\n protected hoveredValue = signal(0);\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /** Whether clicks and keys change the value. Read-only stays focusable, it just does not react. */\n protected readonly interactive = computed(() => !this.readonly() && !this.isDisabled());\n\n private readonly starButtons = viewChildren<ElementRef<HTMLButtonElement>>('starBtn');\n\n protected stars = computed(() => {\n return Array.from({ length: this.max() }, (_, i) => i + 1);\n });\n\n /** Smallest change the value can make. */\n private readonly stepSize = computed(() => (this.allowHalf() ? 0.5 : 1));\n\n /**\n * The star that carries the group's single tab stop (roving tabindex): the\n * one holding the current value, or the first when nothing is selected.\n */\n protected readonly focusableStar = computed(() => Math.max(1, Math.ceil(this.value())));\n\n protected ratingClasses = computed(() => {\n const classes = ['rating', `rating--${this.size()}`];\n if (this.readonly()) classes.push('rating--readonly');\n if (this.isDisabled()) classes.push('rating--disabled');\n return classes.join(' ');\n });\n\n protected getStarState(star: number): 'full' | 'half' | 'empty' {\n const active = this.hoveredValue() || this.value();\n if (star <= Math.floor(active)) return 'full';\n if (this.allowHalf() && star - 0.5 <= active) return 'half';\n return 'empty';\n }\n\n protected isStarFilled(star: number): boolean {\n return this.getStarState(star) === 'full';\n }\n\n protected isStarHalf(star: number): boolean {\n return this.getStarState(star) === 'half';\n }\n\n /** `aria-checked` for the radio that represents the current value (2.5 lives on star 3). */\n protected isStarChecked(star: number): boolean {\n const val = this.value();\n return val > 0 && Math.ceil(val) === star;\n }\n\n /** Radio label — names the half value on the star that holds it. */\n protected starLabel(star: number): string {\n const val = this.value();\n const shown = this.isStarChecked(star) && val !== star ? val : star;\n return `${shown} of ${this.max()} stars`;\n }\n\n /**\n * The value a pointer at `clientX` over `star` stands for: the left half of\n * a star (right half in RTL) is the half step when halves are allowed.\n * Keyboard-triggered clicks carry no coordinates and mean the whole star.\n */\n private valueAt(star: number, event: MouseEvent): number {\n if (!this.allowHalf() || event.detail === 0) return star;\n const el = event.currentTarget as HTMLElement;\n const rect = el.getBoundingClientRect();\n if (!rect.width) return star;\n const fromStart = event.clientX - rect.left;\n const rtl = getComputedStyle(el).direction === 'rtl';\n const inFirstHalf = rtl ? fromStart > rect.width / 2 : fromStart < rect.width / 2;\n return inFirstHalf ? star - 0.5 : star;\n }\n\n protected onStarClick(star: number, event: MouseEvent): void {\n if (!this.interactive()) return;\n this.select(this.valueAt(star, event));\n this.onTouched();\n }\n\n protected onStarHover(star: number, event: MouseEvent): void {\n if (!this.interactive()) return;\n this.hoveredValue.set(this.valueAt(star, event));\n }\n\n protected onMouseLeave(): void {\n this.hoveredValue.set(0);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n const step = this.stepSize();\n const cur = this.value();\n let next: number;\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = Math.min(this.max(), cur + step);\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n next = Math.max(step, cur - step);\n break;\n case 'Home':\n next = step;\n break;\n case 'End':\n next = this.max();\n break;\n default:\n return;\n }\n event.preventDefault();\n this.select(next);\n // Roving tabindex: focus follows the checked star.\n this.starButtons()[this.focusableStar() - 1]?.nativeElement.focus();\n }\n\n /** Marks the control touched once focus leaves the group (not when it moves between stars). */\n protected onFocusOut(event: FocusEvent): void {\n const group = event.currentTarget as HTMLElement;\n if (event.relatedTarget instanceof Node && group.contains(event.relatedTarget)) return;\n this.onTouched();\n }\n\n private select(val: number): void {\n if (val === this.value()) return;\n this.value.set(val);\n this.ratingChange.emit(val);\n this.onChange(val);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: number) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n writeValue(val: number): void {\n this.value.set(val ?? 0);\n }\n\n registerOnChange(fn: (val: number) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div [class]=\"ratingClasses()\" (mouseleave)=\"onMouseLeave()\">\n @if (label()) {\n <span class=\"rating__label\" [id]=\"labelId\">{{ label() }}</span>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- keydown bubbles from the focused star radio (roving tabindex) -->\n <div\n class=\"rating__stars\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label() ? labelId : null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-readonly]=\"readonly() ? true : null\"\n [attr.aria-disabled]=\"isDisabled() ? true : null\"\n (keydown)=\"onKeydown($event)\"\n (focusout)=\"onFocusOut($event)\"\n >\n @for (star of stars(); track star) {\n <button\n #starBtn\n type=\"button\"\n class=\"rating__star\"\n role=\"radio\"\n [class.rating__star--filled]=\"isStarFilled(star)\"\n [class.rating__star--half]=\"isStarHalf(star)\"\n [disabled]=\"isDisabled()\"\n [tabindex]=\"star === focusableStar() ? 0 : -1\"\n [attr.aria-checked]=\"isStarChecked(star)\"\n [attr.aria-label]=\"starLabel(star)\"\n (click)=\"onStarClick(star, $event)\"\n (mousemove)=\"onStarHover(star, $event)\"\n >\n @if (isStarFilled(star)) {\n <lc-icon name=\"star\" variant=\"solid\" [size]=\"size()\" class=\"rating__icon rating__icon--filled\" [decorative]=\"true\" />\n } @else if (isStarHalf(star)) {\n <span class=\"rating__icon rating__icon--half\">\n <lc-icon name=\"star\" [size]=\"size()\" [decorative]=\"true\" />\n <lc-icon name=\"star\" variant=\"solid\" [size]=\"size()\" class=\"rating__icon-fill\" [decorative]=\"true\" />\n </span>\n } @else {\n <lc-icon name=\"star\" [size]=\"size()\" class=\"rating__icon rating__icon--empty\" [decorative]=\"true\" />\n }\n </button>\n }\n </div>\n @if (showValue()) {\n <span class=\"rating__value\">{{ value() }}/{{ max() }}</span>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-rating.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/rating/rating.component.ts","../../../../libs/angular-ui-kit/src/lib/rating/rating.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n input,\n output,\n computed,\n signal,\n forwardRef,\n viewChildren,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_STAR, ICON_STAR_SOLID } from '@lifecorner/angular-ui-kit/icons';\n\nexport type RatingSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'lc-rating',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_STAR, ICON_STAR_SOLID])],\n templateUrl: './rating.component.html',\n styleUrls: ['./rating.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => RatingComponent),\n multi: true,\n },\n ],\n})\n/**\n * Rating component for star-based value selection.\n *\n * Features:\n * - Configurable maximum star count\n * - Half-star rating support (click the left half of a star, or step with the arrow keys)\n * - Multiple size variants (sm, md, lg)\n * - Read-only and disabled states\n * - Hover preview with visual feedback\n * - Keyboard: Arrow keys step the value, Home/End jump to the ends\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-rating [max]=\"5\" [allowHalf]=\"true\" label=\"Rate this\" />\n * ```\n */\nexport class RatingComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Maximum number of stars */\n max = input<number>(5);\n\n /** Size variant */\n size = input<RatingSize>('md');\n\n /** Whether the rating is read-only */\n readonly = input<boolean>(false);\n\n /** Whether the rating is disabled */\n disabled = input<boolean>(false);\n\n /** Whether to allow half-star ratings */\n allowHalf = input<boolean>(false);\n\n /** Label text */\n label = input<string>();\n\n /** Whether to show the numeric value */\n showValue = input<boolean>(false);\n\n /** Accessible name of the star group when no visible `label` is set. */\n ariaLabel = input('Rating');\n\n /** Emits the selected rating */\n ratingChange = output<number>();\n\n /** Per-instance id naming the radiogroup by the visible label. */\n protected readonly labelId = `lc-rating-${++RatingComponent.nextId}-label`;\n\n protected value = signal(0);\n protected hoveredValue = signal(0);\n\n /** Disabled through the form control (`setDisabledState`), as opposed to the input. */\n private readonly formDisabled = signal(false);\n\n /** Disabled by either route — the one flag the template consults. */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /** Whether clicks and keys change the value. Read-only stays focusable, it just does not react. */\n protected readonly interactive = computed(() => !this.readonly() && !this.isDisabled());\n\n private readonly starButtons = viewChildren<ElementRef<HTMLButtonElement>>('starBtn');\n\n protected stars = computed(() => {\n return Array.from({ length: this.max() }, (_, i) => i + 1);\n });\n\n /** Smallest change the value can make. */\n private readonly stepSize = computed(() => (this.allowHalf() ? 0.5 : 1));\n\n /**\n * The star that carries the group's single tab stop (roving tabindex): the\n * one holding the current value, or the first when nothing is selected.\n */\n protected readonly focusableStar = computed(() => Math.max(1, Math.ceil(this.value())));\n\n protected ratingClasses = computed(() => {\n const classes = ['rating', `rating--${this.size()}`];\n if (this.readonly()) classes.push('rating--readonly');\n if (this.isDisabled()) classes.push('rating--disabled');\n return classes.join(' ');\n });\n\n protected getStarState(star: number): 'full' | 'half' | 'empty' {\n const active = this.hoveredValue() || this.value();\n if (star <= Math.floor(active)) return 'full';\n if (this.allowHalf() && star - 0.5 <= active) return 'half';\n return 'empty';\n }\n\n protected isStarFilled(star: number): boolean {\n return this.getStarState(star) === 'full';\n }\n\n protected isStarHalf(star: number): boolean {\n return this.getStarState(star) === 'half';\n }\n\n /** `aria-checked` for the radio that represents the current value (2.5 lives on star 3). */\n protected isStarChecked(star: number): boolean {\n const val = this.value();\n return val > 0 && Math.ceil(val) === star;\n }\n\n /** Radio label — names the half value on the star that holds it. */\n protected starLabel(star: number): string {\n const val = this.value();\n const shown = this.isStarChecked(star) && val !== star ? val : star;\n return `${shown} of ${this.max()} stars`;\n }\n\n /**\n * The value a pointer at `clientX` over `star` stands for: the left half of\n * a star (right half in RTL) is the half step when halves are allowed.\n * Keyboard-triggered clicks carry no coordinates and mean the whole star.\n */\n private valueAt(star: number, event: MouseEvent): number {\n if (!this.allowHalf() || event.detail === 0) return star;\n const el = event.currentTarget as HTMLElement;\n const rect = el.getBoundingClientRect();\n if (!rect.width) return star;\n const fromStart = event.clientX - rect.left;\n const rtl = getComputedStyle(el).direction === 'rtl';\n const inFirstHalf = rtl ? fromStart > rect.width / 2 : fromStart < rect.width / 2;\n return inFirstHalf ? star - 0.5 : star;\n }\n\n protected onStarClick(star: number, event: MouseEvent): void {\n if (!this.interactive()) return;\n this.select(this.valueAt(star, event));\n this.onTouched();\n }\n\n protected onStarHover(star: number, event: MouseEvent): void {\n if (!this.interactive()) return;\n this.hoveredValue.set(this.valueAt(star, event));\n }\n\n protected onMouseLeave(): void {\n this.hoveredValue.set(0);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n const step = this.stepSize();\n const cur = this.value();\n let next: number;\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = Math.min(this.max(), cur + step);\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n next = Math.max(step, cur - step);\n break;\n case 'Home':\n next = step;\n break;\n case 'End':\n next = this.max();\n break;\n default:\n return;\n }\n event.preventDefault();\n this.select(next);\n // Roving tabindex: focus follows the checked star.\n this.starButtons()[this.focusableStar() - 1]?.nativeElement.focus();\n }\n\n /** Marks the control touched once focus leaves the group (not when it moves between stars). */\n protected onFocusOut(event: FocusEvent): void {\n const group = event.currentTarget as HTMLElement;\n if (event.relatedTarget instanceof Node && group.contains(event.relatedTarget)) return;\n this.onTouched();\n }\n\n private select(val: number): void {\n if (val === this.value()) return;\n this.value.set(val);\n this.ratingChange.emit(val);\n this.onChange(val);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (val: number) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n writeValue(val: number): void {\n this.value.set(val ?? 0);\n }\n\n registerOnChange(fn: (val: number) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n}\n","<div [class]=\"ratingClasses()\" (mouseleave)=\"onMouseLeave()\">\n @if (label()) {\n <span class=\"rating__label\" [id]=\"labelId\">{{ label() }}</span>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- keydown bubbles from the focused star radio (roving tabindex) -->\n <div\n class=\"rating__stars\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label() ? labelId : null\"\n [attr.aria-label]=\"label() ? null : ariaLabel()\"\n [attr.aria-readonly]=\"readonly() ? true : null\"\n [attr.aria-disabled]=\"isDisabled() ? true : null\"\n (keydown)=\"onKeydown($event)\"\n (focusout)=\"onFocusOut($event)\"\n >\n @for (star of stars(); track star) {\n <button\n #starBtn\n type=\"button\"\n class=\"rating__star\"\n role=\"radio\"\n [class.rating__star--filled]=\"isStarFilled(star)\"\n [class.rating__star--half]=\"isStarHalf(star)\"\n [disabled]=\"isDisabled()\"\n [tabindex]=\"star === focusableStar() ? 0 : -1\"\n [attr.aria-checked]=\"isStarChecked(star)\"\n [attr.aria-label]=\"starLabel(star)\"\n (click)=\"onStarClick(star, $event)\"\n (mousemove)=\"onStarHover(star, $event)\"\n >\n @if (isStarFilled(star)) {\n <lc-icon name=\"star\" variant=\"solid\" [size]=\"size()\" class=\"rating__icon rating__icon--filled\" [decorative]=\"true\" />\n } @else if (isStarHalf(star)) {\n <span class=\"rating__icon rating__icon--half\">\n <lc-icon name=\"star\" [size]=\"size()\" [decorative]=\"true\" />\n <lc-icon name=\"star\" variant=\"solid\" [size]=\"size()\" class=\"rating__icon-fill\" [decorative]=\"true\" />\n </span>\n } @else {\n <lc-icon name=\"star\" [size]=\"size()\" class=\"rating__icon rating__icon--empty\" [decorative]=\"true\" />\n }\n </button>\n }\n </div>\n @if (showValue()) {\n <span class=\"rating__value\">{{ value() }}/{{ max() }}</span>\n }\n</div>\n"],"mappings":"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"}