@lifecorner/angular-ui-kit 3.21.0 → 3.21.1
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.
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +11 -11
- package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +6 -6
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/theme-contrast.spec.ts +239 -0
|
@@ -355,7 +355,7 @@ var NotificationCenterComponent = class NotificationCenterComponent {
|
|
|
355
355
|
},
|
|
356
356
|
ngImport: i0,
|
|
357
357
|
template: "<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\" [decorative]=\"true\"></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')\" [decorative]=\"true\"></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",
|
|
358
|
-
styles: [":host{display:block}.lc-nc{border:1px solid var(--color-divider);border-radius:var(--border-radius-lg);background-color:var(--color-surface);background-image:var(--gradient-surface);overflow:hidden}.lc-nc__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__header-left{display:flex;align-items:center;gap:8px}.lc-nc__title{margin:0;font-size:var(--typography-font-size-base, 1rem);font-weight:600;color:var(--color-text-primary)}.lc-nc__badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:var(--border-radius-full);background:var(--color-error-fill);color:var(--color-on-error);font-size:.6875rem;font-weight:600}.lc-nc__header-actions{display:flex;gap:8px}.lc-nc__action-btn{border:none;background:none;padding:4px 8px;border-radius:var(--border-radius-sm);font-size:.75rem;font-weight:500;cursor:pointer;color:var(--color-primary-600);transition:background .15s}.lc-nc__action-btn:hover{background:var(--color-primary-50)}.lc-nc__action-btn--danger{color:var(--color-error-default)}.lc-nc__action-btn--danger:hover{background:#9d0e0e29}.lc-nc__search{padding:8px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__search-field{width:100%;display:block}.lc-nc__filters{display:flex;gap:4px;padding:8px 16px;border-bottom:1px solid var(--color-divider);overflow-x:auto}.lc-nc__filter{border:1px solid var(--color-divider);background:transparent;padding:4px 10px;border-radius:var(--border-radius-full);font-size:.6875rem;font-weight:500;cursor:pointer;white-space:nowrap;color:var(--color-text-secondary);transition:all .15s}.lc-nc__filter:hover:not(.lc-nc__filter--active){background:var(--color-surface-2)}.lc-nc__filter--active{background:var(--color-primary-fill);color:var(--color-on-primary);border-color:var(--color-primary-500)}@media(forced-colors:active){.lc-nc__filter--active:not(:focus-visible){outline:2px solid Highlight;outline-offset:1px}}.lc-nc__list{overflow-y:auto}.lc-nc__group-header{padding:6px 16px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-tertiary);background:var(--color-surface-2);border-bottom:1px solid var(--color-divider)}.lc-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border-bottom:1px solid var(--color-divider);cursor:pointer;transition:background .15s}.lc-nc__item:hover{background:var(--color-surface-2)}.lc-nc__item:last-child{border-bottom:none}.lc-nc__item--unread,.lc-nc__item--unread:hover{background:var(--color-surface-selected)}.lc-nc__item--urgent{border-left:3px solid var(--color-error)}.lc-nc__item-icon{flex-shrink:0;margin-top:2px}.lc-nc__item-icon--info{color:var(--color-info-
|
|
358
|
+
styles: [":host{display:block}.lc-nc{border:1px solid var(--color-divider);border-radius:var(--border-radius-lg);background-color:var(--color-surface);background-image:var(--gradient-surface);overflow:hidden}.lc-nc__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__header-left{display:flex;align-items:center;gap:8px}.lc-nc__title{margin:0;font-size:var(--typography-font-size-base, 1rem);font-weight:600;color:var(--color-text-primary)}.lc-nc__badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:var(--border-radius-full);background:var(--color-error-fill);color:var(--color-on-error);font-size:.6875rem;font-weight:600}.lc-nc__header-actions{display:flex;gap:8px}.lc-nc__action-btn{border:none;background:none;padding:4px 8px;border-radius:var(--border-radius-sm);font-size:.75rem;font-weight:500;cursor:pointer;color:var(--color-primary-600);transition:background .15s}.lc-nc__action-btn:hover{background:var(--color-primary-50)}.lc-nc__action-btn--danger{color:var(--color-error-default)}.lc-nc__action-btn--danger:hover{background:#9d0e0e29}.lc-nc__search{padding:8px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__search-field{width:100%;display:block}.lc-nc__filters{display:flex;gap:4px;padding:8px 16px;border-bottom:1px solid var(--color-divider);overflow-x:auto}.lc-nc__filter{border:1px solid var(--color-divider);background:transparent;padding:4px 10px;border-radius:var(--border-radius-full);font-size:.6875rem;font-weight:500;cursor:pointer;white-space:nowrap;color:var(--color-text-secondary);transition:all .15s}.lc-nc__filter:hover:not(.lc-nc__filter--active){background:var(--color-surface-2)}.lc-nc__filter--active{background:var(--color-primary-fill);color:var(--color-on-primary);border-color:var(--color-primary-500)}@media(forced-colors:active){.lc-nc__filter--active:not(:focus-visible){outline:2px solid Highlight;outline-offset:1px}}.lc-nc__list{overflow-y:auto}.lc-nc__group-header{padding:6px 16px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-tertiary);background:var(--color-surface-2);border-bottom:1px solid var(--color-divider)}.lc-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border-bottom:1px solid var(--color-divider);cursor:pointer;transition:background .15s}.lc-nc__item:hover{background:var(--color-surface-2)}.lc-nc__item:last-child{border-bottom:none}.lc-nc__item--unread,.lc-nc__item--unread:hover{background:var(--color-surface-selected)}.lc-nc__item--urgent{border-left:3px solid var(--color-error)}.lc-nc__item-icon{flex-shrink:0;margin-top:2px}.lc-nc__item-icon--info{color:var(--color-on-info-subtle)}.lc-nc__item-icon--success{color:var(--color-on-success-subtle)}.lc-nc__item-icon--warning{color:var(--color-on-warning-subtle)}.lc-nc__item-icon--error{color:var(--color-on-error-subtle)}.lc-nc__item-body{flex:1;min-width:0}.lc-nc__item-header{display:flex;align-items:center;gap:6px}.lc-nc__item-title{display:block;min-width:0;max-width:100%;padding:0;border:none;background:none;font:inherit;font-size:var(--typography-font-size-sm, .875rem);font-weight:500;text-align:left;color:var(--color-text-primary);cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-nc__item-title:focus-visible{outline:none;border-radius:var(--border-radius-sm);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-nc__item--unread .lc-nc__item-title{font-weight:600}.lc-nc__item-priority{font-size:.625rem;font-weight:600;padding:1px 5px;border-radius:var(--border-radius-sm);flex-shrink:0;text-transform:uppercase}.lc-nc__item-priority--high{background:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.lc-nc__item-priority--urgent{background:var(--color-error-subtle);color:var(--color-on-error-subtle)}.lc-nc__item-priority--low{background:var(--color-surface-hover);color:var(--color-text-tertiary)}.lc-nc__item-message{margin:2px 0 0;font-size:.75rem;color:var(--color-text-tertiary);line-height:1.4;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.lc-nc__item-footer{display:flex;align-items:center;gap:8px;margin-top:4px}.lc-nc__item-time{font-size:.6875rem;color:var(--color-text-tertiary)}.lc-nc__item-category{font-size:.625rem;padding:1px 5px;border-radius:var(--border-radius-sm);background:var(--color-surface-hover);color:var(--color-text-tertiary)}.lc-nc__item-action{border:none;background:none;padding:0;font-size:.6875rem;font-weight:600;color:var(--color-primary-600);cursor:pointer}.lc-nc__item-action:hover{text-decoration:underline}.lc-nc__item-dismiss{flex-shrink:0;width:24px;height:24px;border:none;border-radius:var(--border-radius-sm);background:transparent;cursor:pointer;font-size:16px;color:var(--color-text-tertiary);display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;opacity:0}.lc-nc__item:hover .lc-nc__item-dismiss,.lc-nc__item:focus-within .lc-nc__item-dismiss{opacity:1}.lc-nc__item-dismiss:hover{background:var(--color-surface-hover);color:var(--color-text-secondary)}.lc-nc__item-dismiss:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-nc__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:40px 16px;color:var(--color-text-tertiary)}.lc-nc__empty-icon{font-size:32px;opacity:.5}.lc-nc__empty-text{font-size:var(--typography-font-size-sm, .875rem)}\n"],
|
|
359
359
|
dependencies: [{
|
|
360
360
|
kind: "component",
|
|
361
361
|
type: IconComponent,
|
|
@@ -418,7 +418,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
418
418
|
])],
|
|
419
419
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
420
420
|
template: "<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\" [decorative]=\"true\"></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')\" [decorative]=\"true\"></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",
|
|
421
|
-
styles: [":host{display:block}.lc-nc{border:1px solid var(--color-divider);border-radius:var(--border-radius-lg);background-color:var(--color-surface);background-image:var(--gradient-surface);overflow:hidden}.lc-nc__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__header-left{display:flex;align-items:center;gap:8px}.lc-nc__title{margin:0;font-size:var(--typography-font-size-base, 1rem);font-weight:600;color:var(--color-text-primary)}.lc-nc__badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:var(--border-radius-full);background:var(--color-error-fill);color:var(--color-on-error);font-size:.6875rem;font-weight:600}.lc-nc__header-actions{display:flex;gap:8px}.lc-nc__action-btn{border:none;background:none;padding:4px 8px;border-radius:var(--border-radius-sm);font-size:.75rem;font-weight:500;cursor:pointer;color:var(--color-primary-600);transition:background .15s}.lc-nc__action-btn:hover{background:var(--color-primary-50)}.lc-nc__action-btn--danger{color:var(--color-error-default)}.lc-nc__action-btn--danger:hover{background:#9d0e0e29}.lc-nc__search{padding:8px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__search-field{width:100%;display:block}.lc-nc__filters{display:flex;gap:4px;padding:8px 16px;border-bottom:1px solid var(--color-divider);overflow-x:auto}.lc-nc__filter{border:1px solid var(--color-divider);background:transparent;padding:4px 10px;border-radius:var(--border-radius-full);font-size:.6875rem;font-weight:500;cursor:pointer;white-space:nowrap;color:var(--color-text-secondary);transition:all .15s}.lc-nc__filter:hover:not(.lc-nc__filter--active){background:var(--color-surface-2)}.lc-nc__filter--active{background:var(--color-primary-fill);color:var(--color-on-primary);border-color:var(--color-primary-500)}@media(forced-colors:active){.lc-nc__filter--active:not(:focus-visible){outline:2px solid Highlight;outline-offset:1px}}.lc-nc__list{overflow-y:auto}.lc-nc__group-header{padding:6px 16px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-tertiary);background:var(--color-surface-2);border-bottom:1px solid var(--color-divider)}.lc-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border-bottom:1px solid var(--color-divider);cursor:pointer;transition:background .15s}.lc-nc__item:hover{background:var(--color-surface-2)}.lc-nc__item:last-child{border-bottom:none}.lc-nc__item--unread,.lc-nc__item--unread:hover{background:var(--color-surface-selected)}.lc-nc__item--urgent{border-left:3px solid var(--color-error)}.lc-nc__item-icon{flex-shrink:0;margin-top:2px}.lc-nc__item-icon--info{color:var(--color-info-
|
|
421
|
+
styles: [":host{display:block}.lc-nc{border:1px solid var(--color-divider);border-radius:var(--border-radius-lg);background-color:var(--color-surface);background-image:var(--gradient-surface);overflow:hidden}.lc-nc__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__header-left{display:flex;align-items:center;gap:8px}.lc-nc__title{margin:0;font-size:var(--typography-font-size-base, 1rem);font-weight:600;color:var(--color-text-primary)}.lc-nc__badge{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:var(--border-radius-full);background:var(--color-error-fill);color:var(--color-on-error);font-size:.6875rem;font-weight:600}.lc-nc__header-actions{display:flex;gap:8px}.lc-nc__action-btn{border:none;background:none;padding:4px 8px;border-radius:var(--border-radius-sm);font-size:.75rem;font-weight:500;cursor:pointer;color:var(--color-primary-600);transition:background .15s}.lc-nc__action-btn:hover{background:var(--color-primary-50)}.lc-nc__action-btn--danger{color:var(--color-error-default)}.lc-nc__action-btn--danger:hover{background:#9d0e0e29}.lc-nc__search{padding:8px 16px;border-bottom:1px solid var(--color-divider)}.lc-nc__search-field{width:100%;display:block}.lc-nc__filters{display:flex;gap:4px;padding:8px 16px;border-bottom:1px solid var(--color-divider);overflow-x:auto}.lc-nc__filter{border:1px solid var(--color-divider);background:transparent;padding:4px 10px;border-radius:var(--border-radius-full);font-size:.6875rem;font-weight:500;cursor:pointer;white-space:nowrap;color:var(--color-text-secondary);transition:all .15s}.lc-nc__filter:hover:not(.lc-nc__filter--active){background:var(--color-surface-2)}.lc-nc__filter--active{background:var(--color-primary-fill);color:var(--color-on-primary);border-color:var(--color-primary-500)}@media(forced-colors:active){.lc-nc__filter--active:not(:focus-visible){outline:2px solid Highlight;outline-offset:1px}}.lc-nc__list{overflow-y:auto}.lc-nc__group-header{padding:6px 16px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-tertiary);background:var(--color-surface-2);border-bottom:1px solid var(--color-divider)}.lc-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;border-bottom:1px solid var(--color-divider);cursor:pointer;transition:background .15s}.lc-nc__item:hover{background:var(--color-surface-2)}.lc-nc__item:last-child{border-bottom:none}.lc-nc__item--unread,.lc-nc__item--unread:hover{background:var(--color-surface-selected)}.lc-nc__item--urgent{border-left:3px solid var(--color-error)}.lc-nc__item-icon{flex-shrink:0;margin-top:2px}.lc-nc__item-icon--info{color:var(--color-on-info-subtle)}.lc-nc__item-icon--success{color:var(--color-on-success-subtle)}.lc-nc__item-icon--warning{color:var(--color-on-warning-subtle)}.lc-nc__item-icon--error{color:var(--color-on-error-subtle)}.lc-nc__item-body{flex:1;min-width:0}.lc-nc__item-header{display:flex;align-items:center;gap:6px}.lc-nc__item-title{display:block;min-width:0;max-width:100%;padding:0;border:none;background:none;font:inherit;font-size:var(--typography-font-size-sm, .875rem);font-weight:500;text-align:left;color:var(--color-text-primary);cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-nc__item-title:focus-visible{outline:none;border-radius:var(--border-radius-sm);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-nc__item--unread .lc-nc__item-title{font-weight:600}.lc-nc__item-priority{font-size:.625rem;font-weight:600;padding:1px 5px;border-radius:var(--border-radius-sm);flex-shrink:0;text-transform:uppercase}.lc-nc__item-priority--high{background:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.lc-nc__item-priority--urgent{background:var(--color-error-subtle);color:var(--color-on-error-subtle)}.lc-nc__item-priority--low{background:var(--color-surface-hover);color:var(--color-text-tertiary)}.lc-nc__item-message{margin:2px 0 0;font-size:.75rem;color:var(--color-text-tertiary);line-height:1.4;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.lc-nc__item-footer{display:flex;align-items:center;gap:8px;margin-top:4px}.lc-nc__item-time{font-size:.6875rem;color:var(--color-text-tertiary)}.lc-nc__item-category{font-size:.625rem;padding:1px 5px;border-radius:var(--border-radius-sm);background:var(--color-surface-hover);color:var(--color-text-tertiary)}.lc-nc__item-action{border:none;background:none;padding:0;font-size:.6875rem;font-weight:600;color:var(--color-primary-600);cursor:pointer}.lc-nc__item-action:hover{text-decoration:underline}.lc-nc__item-dismiss{flex-shrink:0;width:24px;height:24px;border:none;border-radius:var(--border-radius-sm);background:transparent;cursor:pointer;font-size:16px;color:var(--color-text-tertiary);display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;opacity:0}.lc-nc__item:hover .lc-nc__item-dismiss,.lc-nc__item:focus-within .lc-nc__item-dismiss{opacity:1}.lc-nc__item-dismiss:hover{background:var(--color-surface-hover);color:var(--color-text-secondary)}.lc-nc__item-dismiss:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-nc__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:40px 16px;color:var(--color-text-tertiary)}.lc-nc__empty-icon{font-size:32px;opacity:.5}.lc-nc__empty-text{font-size:var(--typography-font-size-sm, .875rem)}\n"]
|
|
422
422
|
}]
|
|
423
423
|
}],
|
|
424
424
|
propDecorators: {
|
|
@@ -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, ɵ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\" [decorative]=\"true\"></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')\" [decorative]=\"true\"></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\" [decorative]=\"true\"></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')\" [decorative]=\"true\"></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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"}
|
|
@@ -73,7 +73,7 @@ var PipelineComponent = class PipelineComponent {
|
|
|
73
73
|
outputs: { stepClick: "stepClick" },
|
|
74
74
|
ngImport: i0,
|
|
75
75
|
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",
|
|
76
|
-
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-
|
|
76
|
+
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-surface)}.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-on-warning-subtle);color:var(--color-on-warning-subtle)}.lc-pipeline__step--error .lc-pipeline__node{border-color:var(--color-on-error-subtle);color:var(--color-on-error-subtle)}.lc-pipeline__step--pending .lc-pipeline__node{border-style:dashed}.lc-pipeline__step--pending .lc-pipeline__label{color:var(--color-text-secondary)}\n"],
|
|
77
77
|
dependencies: [{
|
|
78
78
|
kind: "component",
|
|
79
79
|
type: IconComponent,
|
|
@@ -123,7 +123,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
123
123
|
])],
|
|
124
124
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
125
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",
|
|
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-
|
|
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-surface)}.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-on-warning-subtle);color:var(--color-on-warning-subtle)}.lc-pipeline__step--error .lc-pipeline__node{border-color:var(--color-on-error-subtle);color:var(--color-on-error-subtle)}.lc-pipeline__step--pending .lc-pipeline__node{border-style:dashed}.lc-pipeline__step--pending .lc-pipeline__label{color:var(--color-text-secondary)}\n"]
|
|
127
127
|
}]
|
|
128
128
|
}],
|
|
129
129
|
propDecorators: {
|
|
@@ -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, ɵ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,
|
|
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,spIAAA;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,spIAAA;EAAA,CAAA"}
|
|
@@ -98,7 +98,7 @@ var ProgressBarComponent = class ProgressBarComponent {
|
|
|
98
98
|
host: { properties: { "class.lc-progress-bar--inline": "inline()" } },
|
|
99
99
|
ngImport: i0,
|
|
100
100
|
template: "@if (variant() === 'linear') {\n <div\n [class]=\"barClasses()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div class=\"progress-bar__track\">\n <div\n class=\"progress-bar__fill\"\n [style.width.%]=\"indeterminate() ? 0 : clampedValue()\"\n ></div>\n </div>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar__label\">{{ clampedValue() }}%</span>\n }\n </div>\n} @else {\n <div\n class=\"progress-bar-circular\"\n [class]=\"'progress-bar-circular progress-bar-circular--' + color() + ' progress-bar-circular--' + size()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <svg viewBox=\"0 0 100 100\" class=\"progress-bar-circular__svg\">\n <circle\n class=\"progress-bar-circular__track\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n />\n <circle\n class=\"progress-bar-circular__fill\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n [attr.stroke-dasharray]=\"circleCircumference\"\n [attr.stroke-dashoffset]=\"indeterminate() ? circleCircumference * 0.75 : strokeDashoffset()\"\n [class.progress-bar-circular__fill--indeterminate]=\"indeterminate()\"\n />\n </svg>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar-circular__label\">{{ clampedValue() }}%</span>\n }\n </div>\n}\n",
|
|
101
|
-
styles: [":host{display:block;width:100%}:host(.lc-progress-bar--inline){display:inline-flex;width:var(--lc-progress-bar-inline-width, 5rem);vertical-align:middle}.progress-bar{display:flex;align-items:center;gap:.75rem;width:100%}.progress-bar__track{flex:1;background-color:var(--color-surface-hover);border-radius:var(--border-radius-full);overflow:hidden}.progress-bar__fill{height:100%;border-radius:var(--border-radius-full);transition:width .3s ease-in-out}.progress-bar__label{font-size:.75rem;font-weight:600;color:var(--color-text-secondary);min-width:2.5rem;text-align:right}.progress-bar--xs .progress-bar__track{height:var(--lc-progress-bar-height, .125rem)}.progress-bar--sm .progress-bar__track{height:var(--lc-progress-bar-height, .25rem)}.progress-bar--md .progress-bar__track{height:var(--lc-progress-bar-height, .5rem)}.progress-bar--lg .progress-bar__track{height:var(--lc-progress-bar-height, .75rem)}.progress-bar--primary .progress-bar__fill{background-color:#208497}.progress-bar--secondary .progress-bar__fill{background-color:#7097af}.progress-bar--success .progress-bar__fill{background-color
|
|
101
|
+
styles: [":host{display:block;width:100%}:host(.lc-progress-bar--inline){display:inline-flex;width:var(--lc-progress-bar-inline-width, 5rem);vertical-align:middle}.progress-bar{display:flex;align-items:center;gap:.75rem;width:100%}.progress-bar__track{flex:1;background-color:var(--color-surface-hover);border-radius:var(--border-radius-full);overflow:hidden}.progress-bar__fill{height:100%;border-radius:var(--border-radius-full);transition:width .3s ease-in-out}.progress-bar__label{font-size:.75rem;font-weight:600;color:var(--color-text-secondary);min-width:2.5rem;text-align:right}.progress-bar--xs .progress-bar__track{height:var(--lc-progress-bar-height, .125rem)}.progress-bar--sm .progress-bar__track{height:var(--lc-progress-bar-height, .25rem)}.progress-bar--md .progress-bar__track{height:var(--lc-progress-bar-height, .5rem)}.progress-bar--lg .progress-bar__track{height:var(--lc-progress-bar-height, .75rem)}.progress-bar--primary .progress-bar__fill{background-color:#208497}.progress-bar--secondary .progress-bar__fill{background-color:#7097af}.progress-bar--success .progress-bar__fill{background-color:var(--color-on-success-subtle)}.progress-bar--warning .progress-bar__fill{background-color:var(--color-on-warning-subtle)}.progress-bar--error .progress-bar__fill{background-color:var(--color-on-error-subtle)}.progress-bar--info .progress-bar__fill{background-color:var(--color-on-info-subtle)}.progress-bar--indeterminate .progress-bar__fill{width:40%!important;animation:progress-indeterminate 1.5s ease-in-out infinite}@keyframes progress-indeterminate{0%{transform:translate(-100%)}to{transform:translate(350%)}}.progress-bar-circular{position:relative;display:inline-flex;align-items:center;justify-content:center}.progress-bar-circular__svg{transform:rotate(-90deg)}.progress-bar-circular__track{stroke:var(--color-surface-hover)}.progress-bar-circular__fill{transition:stroke-dashoffset .3s ease-in-out;stroke-linecap:round}.progress-bar-circular__fill--indeterminate{animation:circular-indeterminate 1.5s linear infinite;transform-origin:center}.progress-bar-circular__label{position:absolute;font-weight:700;color:var(--color-text-primary)}.progress-bar-circular--xs{width:2rem;height:2rem}.progress-bar-circular--xs .progress-bar-circular__track,.progress-bar-circular--xs .progress-bar-circular__fill{stroke-width:8}.progress-bar-circular--xs .progress-bar-circular__label{font-size:.5rem}.progress-bar-circular--sm{width:3rem;height:3rem}.progress-bar-circular--sm .progress-bar-circular__track,.progress-bar-circular--sm .progress-bar-circular__fill{stroke-width:7}.progress-bar-circular--sm .progress-bar-circular__label{font-size:.625rem}.progress-bar-circular--md{width:4.5rem;height:4.5rem}.progress-bar-circular--md .progress-bar-circular__track,.progress-bar-circular--md .progress-bar-circular__fill{stroke-width:6}.progress-bar-circular--md .progress-bar-circular__label{font-size:.8125rem}.progress-bar-circular--lg{width:6rem;height:6rem}.progress-bar-circular--lg .progress-bar-circular__track,.progress-bar-circular--lg .progress-bar-circular__fill{stroke-width:5}.progress-bar-circular--lg .progress-bar-circular__label{font-size:1rem}.progress-bar-circular--primary .progress-bar-circular__fill{stroke:#208497}.progress-bar-circular--secondary .progress-bar-circular__fill{stroke:#7097af}.progress-bar-circular--success .progress-bar-circular__fill{stroke:var(--color-on-success-subtle)}.progress-bar-circular--warning .progress-bar-circular__fill{stroke:var(--color-on-warning-subtle)}.progress-bar-circular--error .progress-bar-circular__fill{stroke:var(--color-on-error-subtle)}.progress-bar-circular--info .progress-bar-circular__fill{stroke:var(--color-on-info-subtle)}@keyframes circular-indeterminate{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.progress-bar--indeterminate .progress-bar__fill{width:100%!important;animation:none;transform:none;-webkit-mask-image:repeating-linear-gradient(45deg,#000 0 .375rem,transparent .375rem .75rem);mask-image:repeating-linear-gradient(45deg,#000 0 .375rem,transparent .375rem .75rem)}.progress-bar-circular__fill--indeterminate{animation:none;stroke-dasharray:10.472 10.472;stroke-dashoffset:0;stroke-linecap:butt}}@media(forced-colors:active){.progress-bar .progress-bar__fill{forced-color-adjust:none;background-color:Highlight}.progress-bar__track{outline:1px solid CanvasText}.progress-bar-circular .progress-bar-circular__fill{forced-color-adjust:none;stroke:Highlight}.progress-bar-circular__track{stroke:GrayText}.progress-bar__label,.progress-bar-circular__label{color:CanvasText}}\n"],
|
|
102
102
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
103
103
|
});
|
|
104
104
|
};
|
|
@@ -115,7 +115,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
115
115
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
116
116
|
host: { "[class.lc-progress-bar--inline]": "inline()" },
|
|
117
117
|
template: "@if (variant() === 'linear') {\n <div\n [class]=\"barClasses()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div class=\"progress-bar__track\">\n <div\n class=\"progress-bar__fill\"\n [style.width.%]=\"indeterminate() ? 0 : clampedValue()\"\n ></div>\n </div>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar__label\">{{ clampedValue() }}%</span>\n }\n </div>\n} @else {\n <div\n class=\"progress-bar-circular\"\n [class]=\"'progress-bar-circular progress-bar-circular--' + color() + ' progress-bar-circular--' + size()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <svg viewBox=\"0 0 100 100\" class=\"progress-bar-circular__svg\">\n <circle\n class=\"progress-bar-circular__track\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n />\n <circle\n class=\"progress-bar-circular__fill\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n [attr.stroke-dasharray]=\"circleCircumference\"\n [attr.stroke-dashoffset]=\"indeterminate() ? circleCircumference * 0.75 : strokeDashoffset()\"\n [class.progress-bar-circular__fill--indeterminate]=\"indeterminate()\"\n />\n </svg>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar-circular__label\">{{ clampedValue() }}%</span>\n }\n </div>\n}\n",
|
|
118
|
-
styles: [":host{display:block;width:100%}:host(.lc-progress-bar--inline){display:inline-flex;width:var(--lc-progress-bar-inline-width, 5rem);vertical-align:middle}.progress-bar{display:flex;align-items:center;gap:.75rem;width:100%}.progress-bar__track{flex:1;background-color:var(--color-surface-hover);border-radius:var(--border-radius-full);overflow:hidden}.progress-bar__fill{height:100%;border-radius:var(--border-radius-full);transition:width .3s ease-in-out}.progress-bar__label{font-size:.75rem;font-weight:600;color:var(--color-text-secondary);min-width:2.5rem;text-align:right}.progress-bar--xs .progress-bar__track{height:var(--lc-progress-bar-height, .125rem)}.progress-bar--sm .progress-bar__track{height:var(--lc-progress-bar-height, .25rem)}.progress-bar--md .progress-bar__track{height:var(--lc-progress-bar-height, .5rem)}.progress-bar--lg .progress-bar__track{height:var(--lc-progress-bar-height, .75rem)}.progress-bar--primary .progress-bar__fill{background-color:#208497}.progress-bar--secondary .progress-bar__fill{background-color:#7097af}.progress-bar--success .progress-bar__fill{background-color
|
|
118
|
+
styles: [":host{display:block;width:100%}:host(.lc-progress-bar--inline){display:inline-flex;width:var(--lc-progress-bar-inline-width, 5rem);vertical-align:middle}.progress-bar{display:flex;align-items:center;gap:.75rem;width:100%}.progress-bar__track{flex:1;background-color:var(--color-surface-hover);border-radius:var(--border-radius-full);overflow:hidden}.progress-bar__fill{height:100%;border-radius:var(--border-radius-full);transition:width .3s ease-in-out}.progress-bar__label{font-size:.75rem;font-weight:600;color:var(--color-text-secondary);min-width:2.5rem;text-align:right}.progress-bar--xs .progress-bar__track{height:var(--lc-progress-bar-height, .125rem)}.progress-bar--sm .progress-bar__track{height:var(--lc-progress-bar-height, .25rem)}.progress-bar--md .progress-bar__track{height:var(--lc-progress-bar-height, .5rem)}.progress-bar--lg .progress-bar__track{height:var(--lc-progress-bar-height, .75rem)}.progress-bar--primary .progress-bar__fill{background-color:#208497}.progress-bar--secondary .progress-bar__fill{background-color:#7097af}.progress-bar--success .progress-bar__fill{background-color:var(--color-on-success-subtle)}.progress-bar--warning .progress-bar__fill{background-color:var(--color-on-warning-subtle)}.progress-bar--error .progress-bar__fill{background-color:var(--color-on-error-subtle)}.progress-bar--info .progress-bar__fill{background-color:var(--color-on-info-subtle)}.progress-bar--indeterminate .progress-bar__fill{width:40%!important;animation:progress-indeterminate 1.5s ease-in-out infinite}@keyframes progress-indeterminate{0%{transform:translate(-100%)}to{transform:translate(350%)}}.progress-bar-circular{position:relative;display:inline-flex;align-items:center;justify-content:center}.progress-bar-circular__svg{transform:rotate(-90deg)}.progress-bar-circular__track{stroke:var(--color-surface-hover)}.progress-bar-circular__fill{transition:stroke-dashoffset .3s ease-in-out;stroke-linecap:round}.progress-bar-circular__fill--indeterminate{animation:circular-indeterminate 1.5s linear infinite;transform-origin:center}.progress-bar-circular__label{position:absolute;font-weight:700;color:var(--color-text-primary)}.progress-bar-circular--xs{width:2rem;height:2rem}.progress-bar-circular--xs .progress-bar-circular__track,.progress-bar-circular--xs .progress-bar-circular__fill{stroke-width:8}.progress-bar-circular--xs .progress-bar-circular__label{font-size:.5rem}.progress-bar-circular--sm{width:3rem;height:3rem}.progress-bar-circular--sm .progress-bar-circular__track,.progress-bar-circular--sm .progress-bar-circular__fill{stroke-width:7}.progress-bar-circular--sm .progress-bar-circular__label{font-size:.625rem}.progress-bar-circular--md{width:4.5rem;height:4.5rem}.progress-bar-circular--md .progress-bar-circular__track,.progress-bar-circular--md .progress-bar-circular__fill{stroke-width:6}.progress-bar-circular--md .progress-bar-circular__label{font-size:.8125rem}.progress-bar-circular--lg{width:6rem;height:6rem}.progress-bar-circular--lg .progress-bar-circular__track,.progress-bar-circular--lg .progress-bar-circular__fill{stroke-width:5}.progress-bar-circular--lg .progress-bar-circular__label{font-size:1rem}.progress-bar-circular--primary .progress-bar-circular__fill{stroke:#208497}.progress-bar-circular--secondary .progress-bar-circular__fill{stroke:#7097af}.progress-bar-circular--success .progress-bar-circular__fill{stroke:var(--color-on-success-subtle)}.progress-bar-circular--warning .progress-bar-circular__fill{stroke:var(--color-on-warning-subtle)}.progress-bar-circular--error .progress-bar-circular__fill{stroke:var(--color-on-error-subtle)}.progress-bar-circular--info .progress-bar-circular__fill{stroke:var(--color-on-info-subtle)}@keyframes circular-indeterminate{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.progress-bar--indeterminate .progress-bar__fill{width:100%!important;animation:none;transform:none;-webkit-mask-image:repeating-linear-gradient(45deg,#000 0 .375rem,transparent .375rem .75rem);mask-image:repeating-linear-gradient(45deg,#000 0 .375rem,transparent .375rem .75rem)}.progress-bar-circular__fill--indeterminate{animation:none;stroke-dasharray:10.472 10.472;stroke-dashoffset:0;stroke-linecap:butt}}@media(forced-colors:active){.progress-bar .progress-bar__fill{forced-color-adjust:none;background-color:Highlight}.progress-bar__track{outline:1px solid CanvasText}.progress-bar-circular .progress-bar-circular__fill{forced-color-adjust:none;stroke:Highlight}.progress-bar-circular__track{stroke:GrayText}.progress-bar__label,.progress-bar-circular__label{color:CanvasText}}\n"]
|
|
119
119
|
}]
|
|
120
120
|
}],
|
|
121
121
|
propDecorators: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-progress-bar.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/progress-bar/progress-bar.component.ts","../../../../libs/angular-ui-kit/src/lib/progress-bar/progress-bar.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n} from '@angular/core';\n\nexport type ProgressBarColor = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info';\nexport type ProgressBarSize = 'xs' | 'sm' | 'md' | 'lg';\nexport type ProgressBarVariant = 'linear' | 'circular';\n\n/**\n * Progress bar component for displaying completion status.\n *\n * Features:\n * - Linear and circular progress variants\n * - Color theme options (primary, secondary, success, warning, error)\n * - Multiple size presets (xs, sm, md, lg)\n * - Animated value transitions\n * - Accessible with ARIA progressbar role\n *\n * The bar is a visual aid; the text next to it carries the meaning. Write the\n * status as text ('USM wird geladen …') and announce changes from a live\n * region (`role=\"status\"`). Screen readers don't announce a progress bar's\n * changes by themselves. Give `ariaLabel` the same words as the status. Under\n * `forced-colors: active` the fill is `Highlight`, the linear track gets a\n * 1 px `CanvasText` outline and the circle's track is `GrayText` (3.17).\n *\n * @example\n * ```html\n * <lc-progress-bar [value]=\"75\" color=\"primary\" size=\"md\"></lc-progress-bar>\n * <lc-progress-bar [value]=\"50\" variant=\"circular\" color=\"success\"></lc-progress-bar>\n *\n * <!-- thin bar inside a list row: fixed width, flows with the line -->\n * <lc-progress-bar [value]=\"60\" size=\"sm\" [inline]=\"true\"\n * style=\"--lc-progress-bar-height: 6px\"></lc-progress-bar>\n * ```\n */\n@Component({\n selector: 'lc-progress-bar',\n standalone: true,\n templateUrl: './progress-bar.component.html',\n styleUrls: ['./progress-bar.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.lc-progress-bar--inline]': 'inline()',\n },\n})\nexport class ProgressBarComponent {\n /**\n * Progress value between 0 and 100.\n * @default 0\n */\n value = input<number>(0);\n\n /**\n * Color theme.\n * @default 'primary'\n */\n color = input<ProgressBarColor>('primary');\n\n /**\n * Height/thickness of the bar.\n * @default 'md'\n */\n size = input<ProgressBarSize>('md');\n\n /**\n * Visual variant.\n * @default 'linear'\n */\n variant = input<ProgressBarVariant>('linear');\n\n /**\n * Whether to show the percentage label.\n * @default false\n */\n showLabel = input<boolean>(false);\n\n /**\n * Inline mode for list rows: the bar flows with the surrounding line at a\n * fixed width instead of filling its container. Width is settable via\n * `--lc-progress-bar-inline-width` (default 5rem), the track height via\n * `--lc-progress-bar-height` (e.g. 6px), which also works in block mode.\n * @default false\n */\n inline = input<boolean>(false);\n\n /**\n * Progress of unknown length: no value (`aria-valuenow` is absent) and no\n * label. The linear bar moves a 40 % segment across the track, and the\n * circle spins. Under `prefers-reduced-motion: reduce` nothing moves: the\n * linear bar shows a full-width striped fill and the circle a dashed ring,\n * so it still reads as busy, not as a value (3.17).\n * @default false\n */\n indeterminate = input<boolean>(false);\n\n /**\n * Accessible label for screen readers.\n */\n ariaLabel = input<string>('Progress');\n\n protected clampedValue = computed(() => Math.max(0, Math.min(100, this.value())));\n\n protected barClasses = computed(() => {\n return [\n 'progress-bar',\n `progress-bar--${this.color()}`,\n `progress-bar--${this.size()}`,\n this.indeterminate() ? 'progress-bar--indeterminate' : '',\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n // Circular variant helpers\n protected readonly circleRadius = 40;\n protected readonly circleCircumference = 2 * Math.PI * 40;\n\n protected strokeDashoffset = computed(() => {\n return this.circleCircumference * (1 - this.clampedValue() / 100);\n });\n}\n","@if (variant() === 'linear') {\n <div\n [class]=\"barClasses()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div class=\"progress-bar__track\">\n <div\n class=\"progress-bar__fill\"\n [style.width.%]=\"indeterminate() ? 0 : clampedValue()\"\n ></div>\n </div>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar__label\">{{ clampedValue() }}%</span>\n }\n </div>\n} @else {\n <div\n class=\"progress-bar-circular\"\n [class]=\"'progress-bar-circular progress-bar-circular--' + color() + ' progress-bar-circular--' + size()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <svg viewBox=\"0 0 100 100\" class=\"progress-bar-circular__svg\">\n <circle\n class=\"progress-bar-circular__track\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n />\n <circle\n class=\"progress-bar-circular__fill\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n [attr.stroke-dasharray]=\"circleCircumference\"\n [attr.stroke-dashoffset]=\"indeterminate() ? circleCircumference * 0.75 : strokeDashoffset()\"\n [class.progress-bar-circular__fill--indeterminate]=\"indeterminate()\"\n />\n </svg>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar-circular__label\">{{ clampedValue() }}%</span>\n }\n </div>\n}\n"],"mappings":";;AAgDA,IAAa,uBAAb,MAAa,qBAAoB;CAK/B,QAAQ,MAAc,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAMxB,QAAQ,MAAwB,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAM1C,OAAO,MAAuB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAMnC,UAAU,MAA0B,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAM7C,YAAY,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CASjC,SAAS,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAU9B,gBAAgB,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAKrC,YAAY,MAAc,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAE3B,eAAe,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAK,CAAE,CAAC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEvE,aAAa,eAAc;EACnC,OAAO;GACL;GACA,iBAAiB,KAAK,MAAK;GAC3B,iBAAiB,KAAK,KAAI;GAC1B,KAAK,cAAa,IAAK,gCAAgC;EACxD,CAAA,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CACb,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGiB,eAAe;CACf,sBAAsB,IAAI,KAAK,KAAK;CAE7C,mBAAmB,eAAc;EACzC,OAAO,KAAK,uBAAuB,IAAI,KAAK,aAAY,IAAK;CAC/D,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;CA1ES,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,OAAA;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,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;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,QAAA;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;EAAA;EAAA,MAAA,EAAA,YAAA,EAAA,iCAAA,WAAA,EAAA;EAAA,UAAA;EAAA,UChDjC;EAiDA,QAAA,CAAA,
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-progress-bar.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/progress-bar/progress-bar.component.ts","../../../../libs/angular-ui-kit/src/lib/progress-bar/progress-bar.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n} from '@angular/core';\n\nexport type ProgressBarColor = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info';\nexport type ProgressBarSize = 'xs' | 'sm' | 'md' | 'lg';\nexport type ProgressBarVariant = 'linear' | 'circular';\n\n/**\n * Progress bar component for displaying completion status.\n *\n * Features:\n * - Linear and circular progress variants\n * - Color theme options (primary, secondary, success, warning, error)\n * - Multiple size presets (xs, sm, md, lg)\n * - Animated value transitions\n * - Accessible with ARIA progressbar role\n *\n * The bar is a visual aid; the text next to it carries the meaning. Write the\n * status as text ('USM wird geladen …') and announce changes from a live\n * region (`role=\"status\"`). Screen readers don't announce a progress bar's\n * changes by themselves. Give `ariaLabel` the same words as the status. Under\n * `forced-colors: active` the fill is `Highlight`, the linear track gets a\n * 1 px `CanvasText` outline and the circle's track is `GrayText` (3.17).\n *\n * @example\n * ```html\n * <lc-progress-bar [value]=\"75\" color=\"primary\" size=\"md\"></lc-progress-bar>\n * <lc-progress-bar [value]=\"50\" variant=\"circular\" color=\"success\"></lc-progress-bar>\n *\n * <!-- thin bar inside a list row: fixed width, flows with the line -->\n * <lc-progress-bar [value]=\"60\" size=\"sm\" [inline]=\"true\"\n * style=\"--lc-progress-bar-height: 6px\"></lc-progress-bar>\n * ```\n */\n@Component({\n selector: 'lc-progress-bar',\n standalone: true,\n templateUrl: './progress-bar.component.html',\n styleUrls: ['./progress-bar.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.lc-progress-bar--inline]': 'inline()',\n },\n})\nexport class ProgressBarComponent {\n /**\n * Progress value between 0 and 100.\n * @default 0\n */\n value = input<number>(0);\n\n /**\n * Color theme.\n * @default 'primary'\n */\n color = input<ProgressBarColor>('primary');\n\n /**\n * Height/thickness of the bar.\n * @default 'md'\n */\n size = input<ProgressBarSize>('md');\n\n /**\n * Visual variant.\n * @default 'linear'\n */\n variant = input<ProgressBarVariant>('linear');\n\n /**\n * Whether to show the percentage label.\n * @default false\n */\n showLabel = input<boolean>(false);\n\n /**\n * Inline mode for list rows: the bar flows with the surrounding line at a\n * fixed width instead of filling its container. Width is settable via\n * `--lc-progress-bar-inline-width` (default 5rem), the track height via\n * `--lc-progress-bar-height` (e.g. 6px), which also works in block mode.\n * @default false\n */\n inline = input<boolean>(false);\n\n /**\n * Progress of unknown length: no value (`aria-valuenow` is absent) and no\n * label. The linear bar moves a 40 % segment across the track, and the\n * circle spins. Under `prefers-reduced-motion: reduce` nothing moves: the\n * linear bar shows a full-width striped fill and the circle a dashed ring,\n * so it still reads as busy, not as a value (3.17).\n * @default false\n */\n indeterminate = input<boolean>(false);\n\n /**\n * Accessible label for screen readers.\n */\n ariaLabel = input<string>('Progress');\n\n protected clampedValue = computed(() => Math.max(0, Math.min(100, this.value())));\n\n protected barClasses = computed(() => {\n return [\n 'progress-bar',\n `progress-bar--${this.color()}`,\n `progress-bar--${this.size()}`,\n this.indeterminate() ? 'progress-bar--indeterminate' : '',\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n // Circular variant helpers\n protected readonly circleRadius = 40;\n protected readonly circleCircumference = 2 * Math.PI * 40;\n\n protected strokeDashoffset = computed(() => {\n return this.circleCircumference * (1 - this.clampedValue() / 100);\n });\n}\n","@if (variant() === 'linear') {\n <div\n [class]=\"barClasses()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <div class=\"progress-bar__track\">\n <div\n class=\"progress-bar__fill\"\n [style.width.%]=\"indeterminate() ? 0 : clampedValue()\"\n ></div>\n </div>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar__label\">{{ clampedValue() }}%</span>\n }\n </div>\n} @else {\n <div\n class=\"progress-bar-circular\"\n [class]=\"'progress-bar-circular progress-bar-circular--' + color() + ' progress-bar-circular--' + size()\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"indeterminate() ? null : clampedValue()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"100\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n <svg viewBox=\"0 0 100 100\" class=\"progress-bar-circular__svg\">\n <circle\n class=\"progress-bar-circular__track\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n />\n <circle\n class=\"progress-bar-circular__fill\"\n cx=\"50\" cy=\"50\" [attr.r]=\"circleRadius\"\n fill=\"none\"\n [attr.stroke-dasharray]=\"circleCircumference\"\n [attr.stroke-dashoffset]=\"indeterminate() ? circleCircumference * 0.75 : strokeDashoffset()\"\n [class.progress-bar-circular__fill--indeterminate]=\"indeterminate()\"\n />\n </svg>\n @if (showLabel() && !indeterminate()) {\n <span class=\"progress-bar-circular__label\">{{ clampedValue() }}%</span>\n }\n </div>\n}\n"],"mappings":"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"}
|
|
@@ -209,7 +209,7 @@ var RatingComponent = class RatingComponent {
|
|
|
209
209
|
}],
|
|
210
210
|
ngImport: i0,
|
|
211
211
|
template: "<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",
|
|
212
|
-
styles: [".rating{display:inline-flex;align-items:center;gap:.5rem}.rating__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.rating__stars{display:inline-flex;align-items:center;gap:.125rem}.rating__star{display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:.125rem;cursor:pointer;border-radius:.25rem;transition:transform .15s ease}.rating__star:hover:not(:disabled){transform:scale(1.15)}.rating__star:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.rating__star:disabled{cursor:default}.rating__icon{transition:color .15s ease}.rating__icon--filled{color:var(--color-warning)}.rating__icon--half{position:relative;display:inline-flex;color:var(--color-border-strong)}.rating__icon--empty{color:var(--color-border-strong)}.rating__icon-fill{position:absolute;inset:0;color:var(--color-warning);clip-path:inset(0 50% 0 0)}:host-context([dir=rtl]) .rating__icon-fill{clip-path:inset(0 0 0 50%)}.rating__value{font-size:var(--typography-font-size-sm, .875rem);font-variant-numeric:tabular-nums;color:var(--color-text-tertiary)}.rating--sm .rating__star{padding:.0625rem}.rating--lg .rating__star{padding:.1875rem}.rating--lg .rating__value{font-size:var(--typography-font-size-base, 1rem)}.rating--readonly .rating__star{cursor:default}.rating--readonly .rating__star:hover{transform:none}.rating--disabled{opacity:.5;pointer-events:none}\n"],
|
|
212
|
+
styles: [".rating{display:inline-flex;align-items:center;gap:.5rem}.rating__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.rating__stars{display:inline-flex;align-items:center;gap:.125rem}.rating__star{display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:.125rem;cursor:pointer;border-radius:.25rem;transition:transform .15s ease}.rating__star:hover:not(:disabled){transform:scale(1.15)}.rating__star:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.rating__star:disabled{cursor:default}.rating__icon{transition:color .15s ease}.rating__icon--filled{color:var(--color-on-warning-subtle)}.rating__icon--half{position:relative;display:inline-flex;color:var(--color-border-strong)}.rating__icon--empty{color:var(--color-border-strong)}.rating__icon-fill{position:absolute;inset:0;color:var(--color-on-warning-subtle);clip-path:inset(0 50% 0 0)}:host-context([dir=rtl]) .rating__icon-fill{clip-path:inset(0 0 0 50%)}.rating__value{font-size:var(--typography-font-size-sm, .875rem);font-variant-numeric:tabular-nums;color:var(--color-text-tertiary)}.rating--sm .rating__star{padding:.0625rem}.rating--lg .rating__star{padding:.1875rem}.rating--lg .rating__value{font-size:var(--typography-font-size-base, 1rem)}.rating--readonly .rating__star{cursor:default}.rating--readonly .rating__star:hover{transform:none}.rating--disabled{opacity:.5;pointer-events:none}\n"],
|
|
213
213
|
dependencies: [{
|
|
214
214
|
kind: "component",
|
|
215
215
|
type: IconComponent,
|
|
@@ -247,7 +247,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
247
247
|
multi: true
|
|
248
248
|
}],
|
|
249
249
|
template: "<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",
|
|
250
|
-
styles: [".rating{display:inline-flex;align-items:center;gap:.5rem}.rating__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.rating__stars{display:inline-flex;align-items:center;gap:.125rem}.rating__star{display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:.125rem;cursor:pointer;border-radius:.25rem;transition:transform .15s ease}.rating__star:hover:not(:disabled){transform:scale(1.15)}.rating__star:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.rating__star:disabled{cursor:default}.rating__icon{transition:color .15s ease}.rating__icon--filled{color:var(--color-warning)}.rating__icon--half{position:relative;display:inline-flex;color:var(--color-border-strong)}.rating__icon--empty{color:var(--color-border-strong)}.rating__icon-fill{position:absolute;inset:0;color:var(--color-warning);clip-path:inset(0 50% 0 0)}:host-context([dir=rtl]) .rating__icon-fill{clip-path:inset(0 0 0 50%)}.rating__value{font-size:var(--typography-font-size-sm, .875rem);font-variant-numeric:tabular-nums;color:var(--color-text-tertiary)}.rating--sm .rating__star{padding:.0625rem}.rating--lg .rating__star{padding:.1875rem}.rating--lg .rating__value{font-size:var(--typography-font-size-base, 1rem)}.rating--readonly .rating__star{cursor:default}.rating--readonly .rating__star:hover{transform:none}.rating--disabled{opacity:.5;pointer-events:none}\n"]
|
|
250
|
+
styles: [".rating{display:inline-flex;align-items:center;gap:.5rem}.rating__label{font-size:var(--typography-font-size-sm, .875rem);font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary)}.rating__stars{display:inline-flex;align-items:center;gap:.125rem}.rating__star{display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:.125rem;cursor:pointer;border-radius:.25rem;transition:transform .15s ease}.rating__star:hover:not(:disabled){transform:scale(1.15)}.rating__star:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.rating__star:disabled{cursor:default}.rating__icon{transition:color .15s ease}.rating__icon--filled{color:var(--color-on-warning-subtle)}.rating__icon--half{position:relative;display:inline-flex;color:var(--color-border-strong)}.rating__icon--empty{color:var(--color-border-strong)}.rating__icon-fill{position:absolute;inset:0;color:var(--color-on-warning-subtle);clip-path:inset(0 50% 0 0)}:host-context([dir=rtl]) .rating__icon-fill{clip-path:inset(0 0 0 50%)}.rating__value{font-size:var(--typography-font-size-sm, .875rem);font-variant-numeric:tabular-nums;color:var(--color-text-tertiary)}.rating--sm .rating__star{padding:.0625rem}.rating--lg .rating__star{padding:.1875rem}.rating--lg .rating__value{font-size:var(--typography-font-size-base, 1rem)}.rating--readonly .rating__star{cursor:default}.rating--readonly .rating__star:hover{transform:none}.rating--disabled{opacity:.5;pointer-events:none}\n"]
|
|
251
251
|
}]
|
|
252
252
|
}],
|
|
253
253
|
propDecorators: {
|
|
@@ -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, ɵ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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"}
|
|
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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"}
|