@lifecorner/angular-ui-kit 3.36.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +4 -4
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-hero.mjs +24 -3
- package/fesm2022/lifecorner-angular-ui-kit-hero.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-internal-chart.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-internal-chart.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-list.mjs +10 -5
- package/fesm2022/lifecorner-angular-ui-kit-list.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs +15 -10
- package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs +27 -4
- package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs +2 -2
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/_theme-light.scss +8 -1
- package/src/styles/theme-contrast.spec.ts +44 -0
- package/src/styles/theme-modes.spec.ts +20 -5
- package/types/lifecorner-angular-ui-kit-hero.d.ts +18 -1
- package/types/lifecorner-angular-ui-kit-internal-chart.d.ts +6 -0
- package/types/lifecorner-angular-ui-kit-list.d.ts +10 -0
- package/types/lifecorner-angular-ui-kit-select.d.ts +13 -1
- package/types/lifecorner-angular-ui-kit-state-strip.d.ts +20 -2
- package/types/lifecorner-angular-ui-kit-table.d.ts +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-list.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/list/list-item-template.directive.ts","../../../../libs/angular-ui-kit/src/lib/list/list.component.ts","../../../../libs/angular-ui-kit/src/lib/list/list.component.html"],"sourcesContent":["import { Directive, TemplateRef, inject } from '@angular/core';\nimport { ListItem } from './list.component';\n\n/**\n * Directive for providing a custom template to render each list item.\n *\n * @example\n * ```html\n * <lc-list [items]=\"runs()\" (itemClick)=\"onRunClick($event)\">\n * <ng-template lcListItem let-item>\n * <lc-badge [variant]=\"getStatusVariant(item.status)\">{{ item.status }}</lc-badge>\n * <span>{{ item.symbol }}</span>\n * <span class=\"text-sm text-neutral-500\">{{ item.date }}</span>\n * </ng-template>\n * </lc-list>\n * ```\n */\n@Directive({\n selector: '[lcListItem]',\n standalone: true,\n})\nexport class ListItemTemplateDirective {\n public readonly template = inject(TemplateRef<{ $implicit: ListItem }>);\n}\n","import {\n AfterRenderRef,\n Component,\n ChangeDetectionStrategy,\n ElementRef,\n Injector,\n input,\n computed,\n contentChild,\n effect,\n inject,\n isDevMode,\n output,\n viewChild,\n afterNextRender,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_RIGHT } from '@lifecorner/angular-ui-kit/icons';\nimport { ButtonComponent, ButtonSize, ButtonVariant } from '@lifecorner/angular-ui-kit/button';\nimport { ListItemTemplateDirective } from './list-item-template.directive';\nimport type { AvatarColor } from '@lifecorner/angular-ui-kit/avatar';\n\n/**\n * List orientation type\n */\nexport type ListOrientation = 'vertical' | 'horizontal';\n\n/**\n * List visual variant\n * - default: flat rows (original)\n * - card: each item rendered as a mini-card with border, radius and shadow on hover\n */\nexport type ListVariant = 'default' | 'card';\n\n/**\n * List item interface\n */\nexport interface ListItem {\n /** Item label text (primary line) */\n label: string;\n /** Secondary text shown below the label */\n subtitle?: string;\n /** Description text shown below subtitle (third line) */\n description?: string;\n /** Optional icon identifier */\n icon?: string;\n /** Avatar URL or initials (renders a circular avatar instead of icon) */\n avatar?: string;\n /**\n * Background of avatar initials: `default` or a member colour `member-1` …\n * `member-6` with white initials (as `lc-avatar`'s `color`).\n */\n avatarColor?: AvatarColor;\n /** Badge text (e.g. count, status label) shown on the trailing side */\n badge?: string;\n /** Badge color variant */\n badgeVariant?: 'default' | 'primary' | 'success' | 'warning' | 'error';\n /** Metadata text shown on the trailing side (e.g. date, size) */\n metadata?: string;\n /** Optional action label */\n action?: string;\n /**\n * Accessible name of this row's action when the visible label repeats on\n * every row (e.g. „Entfernen“ → „iPhone · Safari entfernen“). The visible\n * label must be part of it (WCAG 2.5.3).\n */\n actionAriaLabel?: string;\n /**\n * URL of the row link (3.16, TD-4). The row renders as an `<a>` over its\n * full area (at least 44 px high) with real link semantics (middle click,\n * \"open in new tab\"). Not rendered with `href`: `action` (no control inside\n * a link; a dev warning). Not emitted: `itemClick` (see `linkClick`). With\n * `disabled` the row is the plain disabled row, no link. A custom\n * `lcListItem` template must not hold controls in `href` rows.\n */\n href?: string;\n /** Item identifier */\n id?: string;\n /** Whether the item is disabled */\n disabled?: boolean;\n /**\n * Whether the item is the current choice: a tinted row and, since 3.33,\n * `aria-current=\"true\"` on the row element (the button with `clickable`,\n * the link with `href`, the row otherwise). Pair it with a visible mark such\n * as a badge; the tint alone is colour.\n */\n selected?: boolean;\n /** Any additional data */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: any;\n}\n\n/**\n * List item size\n */\nexport type ListSize = 'sm' | 'md' | 'lg';\n\n/**\n * A plain click (or Enter) on an `href` row (3.16, TD-4): `item` is the row,\n * `event` the click. Call `event.preventDefault()` to navigate with your\n * router instead of a full page load.\n */\nexport interface ListLinkClickEvent {\n item: ListItem;\n event: MouseEvent;\n}\n\n/**\n * The part of a row that `focusItem()` focuses (3.34): `row`, the row's\n * button (`clickable`) or link (`href`), or `action`, its built-in action\n * button.\n */\nexport type ListItemPart = 'row' | 'action';\n\n/**\n * List component for displaying structured item collections.\n *\n * Features:\n * - Vertical and horizontal layout orientations\n * - Variant styles (default, bordered, card)\n * - Optional dividers between items\n * - Icon and avatar support per item\n * - Action buttons with click events\n * - Accessible with ARIA list role; `clickable` rows are keyboard-operable\n * - `ListItem.href` rows are real links over the full row (3.16, TD-4);\n * `linkClick` hands plain clicks to your router\n * - `focusItem(id, part)` (3.34): moves the focus to a row's button or link,\n * or to its action, e.g. when the sheet that action opened closes\n * (WCAG 2.4.3)\n * - `showChevron` (3.36): a decorative chevron at the end of every\n * `clickable` or `href` row\n *\n * @example\n * ```html\n * <lc-list\n * [items]=\"listItems\"\n * orientation=\"vertical\"\n * [showDividers]=\"true\"\n * (itemClick)=\"handleItemClick($event)\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-list',\n standalone: true,\n imports: [NgTemplateOutlet, IconComponent, ButtonComponent],\n templateUrl: './list.component.html',\n styleUrl: './list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_RIGHT])],\n})\nexport class ListComponent {\n /** Custom item template provided via lcListItem directive */\n readonly itemTemplate = contentChild(ListItemTemplateDirective);\n\n /** Array of list items to display */\n items = input<ListItem[]>([]);\n\n /** Orientation of the list */\n orientation = input<ListOrientation>('vertical');\n\n /** Visual variant */\n variant = input<ListVariant>('default');\n\n /** Size of list items */\n size = input<ListSize>('md');\n\n /** Whether to show dividers between items */\n showDividers = input<boolean>(false);\n\n /**\n * Whether rows are interactive. When set, each row is rendered as a\n * focusable button (Enter / Space emit `itemClick`) so `itemClick` is\n * reachable from the keyboard. Set this whenever you handle `itemClick`.\n */\n clickable = input<boolean>(false);\n\n /**\n * Clamp each item's `description` to two lines (3.2). Set `false` for\n * reading text that must show in full (3.3, G-11).\n * @default true\n */\n clampDescription = input<boolean>(true);\n\n /**\n * Variant of the built-in row action (`ListItem.action`).\n * @default 'ghost'\n */\n actionVariant = input<ButtonVariant>('ghost');\n\n /**\n * Size of the built-in row action: `xs` (3.2, about 25 px) … `lg` (48.5 px,\n * the 44 px touch target).\n * @default 'xs'\n */\n actionSize = input<ButtonSize>('xs');\n\n /**\n * Ends every row that opens something, a `clickable` row or an `href` row,\n * with the kit's `chevron-right` (20 px, `--color-text-tertiary`) after its\n * badge, metadata and action (3.36, quellen row 3). Decorative: hidden from\n * screen readers. Plain rows get none, and neither do rows drawn by an\n * `lcListItem` template (add the icon to the template there). Row height,\n * target size, focus ring, badge and `actionClick` don't change.\n * @default false\n */\n showChevron = input<boolean>(false);\n\n /** Event emitted when an item is clicked */\n readonly itemClick = output<ListItem>();\n\n /** Event emitted when an action button is clicked */\n readonly actionClick = output<ListItem>();\n\n /**\n * A plain click (or Enter) on an `href` row: primary button, no\n * Ctrl/Cmd/Shift/Alt (3.16, TD-4). Emitted before the browser follows the\n * link; call `event.preventDefault()` and navigate with your router for an\n * in-app route (`router.navigateByUrl(item.href)`). The kit never prevents\n * the default; modifier and middle clicks are never emitted, so \"open in new\n * tab\" keeps working.\n */\n readonly linkClick = output<ListLinkClickEvent>();\n\n private readonly injector = inject(Injector);\n\n /** The `role=\"list\"` element; each item renders exactly one child of it. */\n private readonly listEl = viewChild<ElementRef<HTMLElement>>('listEl');\n\n /** The `focusItem()` hook waiting for the next render. */\n private pendingItemFocus?: AfterRenderRef;\n\n constructor() {\n effect(() => {\n if (!isDevMode()) return;\n // `items` may be bound to undefined (tolerated since 3.2).\n for (const item of this.items() ?? []) {\n if (this.isLinkRow(item) && item.action) {\n console.warn(`[lc-list] item \"${item.label}\" has href and action: the action is not rendered inside a link`);\n }\n }\n });\n }\n\n /**\n * Moves the focus into the row of the first item with this `id` after the\n * next render (3.34). `'row'`: the row's button (`clickable`) or link\n * (`href`). `'action'`: its built-in action button. Does nothing for an\n * unknown `id`, for a row without that part (a plain row, a disabled row for\n * `'row'`, no `action`, an `lcListItem` template's own controls), or when\n * the list is destroyed first. A second call before that render replaces the\n * first.\n * @param id The `ListItem.id` of the row.\n * @param part Which part of the row gets the focus.\n */\n focusItem(id: string, part: ListItemPart = 'row'): void {\n this.pendingItemFocus?.destroy();\n this.pendingItemFocus = afterNextRender(\n () => {\n this.pendingItemFocus = undefined;\n const index = (this.items() ?? []).findIndex((item) => item.id === id);\n const row = index < 0 ? null : this.listEl()?.nativeElement.children.item(index);\n // querySelector skips the row element itself, so a plain row matches nothing.\n const target = row?.querySelector<HTMLElement>(\n part === 'action'\n ? '.lc-list__action-area button'\n : 'a.lc-list__item, .lc-list__item[role=button]:not([aria-disabled=true])',\n );\n target?.focus();\n },\n { injector: this.injector },\n );\n }\n\n /** Whether a row renders as a link: `href` set and not disabled. */\n protected isLinkRow(item: ListItem): boolean {\n return !!item.href && !item.disabled;\n }\n\n /**\n * Computed CSS classes for the list\n */\n listClasses = computed(() => {\n const classes = ['lc-list'];\n\n classes.push(`lc-list--${this.orientation()}`);\n classes.push(`lc-list--${this.variant()}`);\n classes.push(`lc-list--${this.size()}`);\n\n if (this.showDividers()) {\n classes.push('lc-list--with-dividers');\n }\n\n if (this.clickable()) {\n classes.push('lc-list--clickable');\n }\n\n if (!this.clampDescription()) {\n classes.push('lc-list--no-clamp');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Get CSS classes for a list item\n */\n getItemClasses(item: ListItem): string {\n const classes = ['lc-list__item'];\n\n if (item.disabled) {\n classes.push('lc-list__item--disabled');\n }\n\n if (item.selected) {\n classes.push('lc-list__item--selected');\n }\n\n return classes.join(' ');\n }\n\n /** CSS classes of an item's avatar (member colour, LK-7). */\n protected avatarClasses(item: ListItem): string {\n const color = item.avatarColor;\n return color && color !== 'default'\n ? `lc-list__avatar lc-list__avatar--member lc-list__avatar--${color}`\n : 'lc-list__avatar';\n }\n\n /**\n * Handle item click\n */\n onItemClick(item: ListItem): void {\n if (!item.disabled) {\n this.itemClick.emit(item);\n }\n }\n\n /**\n * Keyboard activation of a clickable row. Only reacts to keys on the row\n * itself: a nested control (e.g. the action button) handles its own Enter.\n */\n onItemKeydown(event: KeyboardEvent, item: ListItem): void {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.onItemClick(item);\n }\n }\n\n /**\n * Handle action button click\n */\n onActionClick(item: ListItem): void {\n this.actionClick.emit(item);\n }\n\n /**\n * A click on a link row: only a plain primary click reaches `linkClick`.\n * The default (following the link) is left to the browser and the consumer.\n */\n protected onLinkClick(event: MouseEvent, item: ListItem): void {\n if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;\n this.linkClick.emit({ item, event });\n }\n}\n","<!--\n Row content is shared between the plain (legacy) row and the keyboard-\n operable row rendered when `clickable` is set.\n-->\n<ng-template #rowContent let-item>\n @if (itemTemplate(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl.template; context: { $implicit: item }\"></ng-container>\n } @else {\n <!-- Leading: Avatar or Icon -->\n @if (item.avatar) {\n <div [class]=\"avatarClasses(item)\">\n @if (item.avatar.startsWith('http') || item.avatar.startsWith('/') || item.avatar.startsWith('data:')) {\n <img [src]=\"item.avatar\" [alt]=\"item.label\" class=\"lc-list__avatar-img\" />\n } @else {\n <span class=\"lc-list__avatar-initials\">{{ item.avatar }}</span>\n }\n </div>\n } @else if (item.icon) {\n <lc-icon [name]=\"item.icon\" class=\"lc-list__icon\" size=\"sm\" [decorative]=\"true\" />\n }\n\n <!-- Content: Label, Subtitle, Description -->\n <div class=\"lc-list__content\">\n <span class=\"lc-list__label\">{{ item.label }}</span>\n @if (item.subtitle) {\n <span class=\"lc-list__subtitle\">{{ item.subtitle }}</span>\n }\n @if (item.description) {\n <span class=\"lc-list__description\">{{ item.description }}</span>\n }\n </div>\n\n <!-- Trailing: Badge, Metadata, Action -->\n <div class=\"lc-list__trailing\">\n @if (item.badge) {\n <span class=\"lc-list__badge lc-list__badge--{{ item.badgeVariant || 'default' }}\">{{ item.badge }}</span>\n }\n @if (item.metadata) {\n <span class=\"lc-list__metadata\">{{ item.metadata }}</span>\n }\n @if (item.action && !isLinkRow(item)) {\n <!-- The action is its own control: its click must not also activate the row. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- only stops the action click from also activating the row; the action is a real button -->\n <span class=\"lc-list__action-area\" (click)=\"$event.stopPropagation()\">\n <lc-button\n [variant]=\"actionVariant()\"\n [size]=\"actionSize()\"\n [ariaLabel]=\"item.actionAriaLabel ?? ''\"\n class=\"lc-list__action\"\n (clicked)=\"onActionClick(item)\"\n >\n {{ item.action }}\n </lc-button>\n </span>\n }\n </div>\n\n <!-- 3.36: a decorative chevron ends a clickable or linked row. -->\n @if (showChevron() && (clickable() || !!item.href)) {\n <lc-icon name=\"chevron-right\" class=\"lc-list__chevron\" size=\"sm\" [decorative]=\"true\" />\n }\n }\n</ng-template>\n\n<div #listEl [class]=\"listClasses()\" role=\"list\">\n @for (item of items(); track item.id || $index) {\n @if (isLinkRow(item)) {\n <!-- A real link over the whole row (3.16, TD-4); the row element carries the item styles. -->\n <div class=\"lc-list__item-wrapper\" role=\"listitem\">\n <a\n [class]=\"getItemClasses(item) + ' lc-list__item--link'\"\n [href]=\"item.href\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n (click)=\"onLinkClick($event, item)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </a>\n </div>\n } @else if (clickable()) {\n <div class=\"lc-list__item-wrapper\" role=\"listitem\">\n <div\n [class]=\"getItemClasses(item)\"\n role=\"button\"\n [attr.tabindex]=\"item.disabled ? -1 : 0\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n (click)=\"onItemClick(item)\"\n (keydown)=\"onItemKeydown($event, item)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </div>\n </div>\n } @else {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- non-clickable lists keep reporting mouse clicks (backward compatible); use clickable for keyboard rows -->\n <div\n [class]=\"getItemClasses(item)\"\n role=\"listitem\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n (click)=\"onItemClick(item)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </div>\n }\n }\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-list.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/list/list-item-template.directive.ts","../../../../libs/angular-ui-kit/src/lib/list/list.component.ts","../../../../libs/angular-ui-kit/src/lib/list/list.component.html"],"sourcesContent":["import { Directive, TemplateRef, inject } from '@angular/core';\nimport { ListItem } from './list.component';\n\n/**\n * Directive for providing a custom template to render each list item.\n *\n * @example\n * ```html\n * <lc-list [items]=\"runs()\" (itemClick)=\"onRunClick($event)\">\n * <ng-template lcListItem let-item>\n * <lc-badge [variant]=\"getStatusVariant(item.status)\">{{ item.status }}</lc-badge>\n * <span>{{ item.symbol }}</span>\n * <span class=\"text-sm text-neutral-500\">{{ item.date }}</span>\n * </ng-template>\n * </lc-list>\n * ```\n */\n@Directive({\n selector: '[lcListItem]',\n standalone: true,\n})\nexport class ListItemTemplateDirective {\n public readonly template = inject(TemplateRef<{ $implicit: ListItem }>);\n}\n","import {\n AfterRenderRef,\n Component,\n ChangeDetectionStrategy,\n ElementRef,\n Injector,\n input,\n computed,\n contentChild,\n effect,\n inject,\n isDevMode,\n output,\n viewChild,\n afterNextRender,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_RIGHT } from '@lifecorner/angular-ui-kit/icons';\nimport { ButtonComponent, ButtonSize, ButtonVariant } from '@lifecorner/angular-ui-kit/button';\nimport { ListItemTemplateDirective } from './list-item-template.directive';\nimport type { AvatarColor } from '@lifecorner/angular-ui-kit/avatar';\n\n/** Counter for generated row ids: they follow creation order, so prerendered HTML is the same in every build. */\nlet nextListId = 0;\n\n/**\n * List orientation type\n */\nexport type ListOrientation = 'vertical' | 'horizontal';\n\n/**\n * List visual variant\n * - default: flat rows (original)\n * - card: each item rendered as a mini-card with border, radius and shadow on hover\n */\nexport type ListVariant = 'default' | 'card';\n\n/**\n * List item interface\n */\nexport interface ListItem {\n /** Item label text (primary line) */\n label: string;\n /** Secondary text shown below the label */\n subtitle?: string;\n /** Description text shown below subtitle (third line) */\n description?: string;\n /** Optional icon identifier */\n icon?: string;\n /** Avatar URL or initials (renders a circular avatar instead of icon) */\n avatar?: string;\n /**\n * Background of avatar initials: `default` or a member colour `member-1` …\n * `member-6` with white initials (as `lc-avatar`'s `color`).\n */\n avatarColor?: AvatarColor;\n /** Badge text (e.g. count, status label) shown on the trailing side */\n badge?: string;\n /** Badge color variant */\n badgeVariant?: 'default' | 'primary' | 'success' | 'warning' | 'error';\n /** Metadata text shown on the trailing side (e.g. date, size) */\n metadata?: string;\n /** Optional action label */\n action?: string;\n /**\n * Accessible name of this row's action when the visible label repeats on\n * every row (e.g. „Entfernen“ → „iPhone · Safari entfernen“). The visible\n * label must be part of it (WCAG 2.5.3).\n */\n actionAriaLabel?: string;\n /**\n * URL of the row link (3.16, TD-4). The row renders as an `<a>` over its\n * full area (at least 44 px high) with real link semantics (middle click,\n * \"open in new tab\"). Not rendered with `href`: `action` (no control inside\n * a link; a dev warning). Not emitted: `itemClick` (see `linkClick`). With\n * `disabled` the row is the plain disabled row, no link. A custom\n * `lcListItem` template must not hold controls in `href` rows.\n */\n href?: string;\n /** Item identifier */\n id?: string;\n /** Whether the item is disabled */\n disabled?: boolean;\n /**\n * Whether the item is the current choice: a tinted row and, since 3.33,\n * `aria-current=\"true\"` on the row element (the button with `clickable`,\n * the link with `href`, the row otherwise). Pair it with a visible mark such\n * as a badge; the tint alone is colour.\n */\n selected?: boolean;\n /** Any additional data */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: any;\n}\n\n/**\n * List item size\n */\nexport type ListSize = 'sm' | 'md' | 'lg';\n\n/**\n * A plain click (or Enter) on an `href` row (3.16, TD-4): `item` is the row,\n * `event` the click. Call `event.preventDefault()` to navigate with your\n * router instead of a full page load.\n */\nexport interface ListLinkClickEvent {\n item: ListItem;\n event: MouseEvent;\n}\n\n/**\n * The part of a row that `focusItem()` focuses (3.34): `row`, the row's\n * button (`clickable`) or link (`href`), or `action`, its built-in action\n * button.\n */\nexport type ListItemPart = 'row' | 'action';\n\n/**\n * List component for displaying structured item collections.\n *\n * Features:\n * - Vertical and horizontal layout orientations\n * - Variant styles (default, bordered, card)\n * - Optional dividers between items\n * - Icon and avatar support per item\n * - Action buttons with click events\n * - Accessible with ARIA list role; `clickable` rows are keyboard-operable\n * - `ListItem.href` rows are real links over the full row (3.16, TD-4);\n * `linkClick` hands plain clicks to your router\n * - `focusItem(id, part)` (3.34): moves the focus to a row's button or link,\n * or to its action, e.g. when the sheet that action opened closes\n * (WCAG 2.4.3)\n * - `showChevron` (3.36): a decorative chevron at the end of every\n * `clickable` or `href` row\n * - 3.37: a `clickable` row with an `action` holds a native button stretched\n * over the row (named by the row) beside its action, so no control sits\n * inside another (WCAG 4.1.2); one Tab stop for the row, then the action\n *\n * @example\n * ```html\n * <lc-list\n * [items]=\"listItems\"\n * orientation=\"vertical\"\n * [showDividers]=\"true\"\n * (itemClick)=\"handleItemClick($event)\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-list',\n standalone: true,\n imports: [NgTemplateOutlet, IconComponent, ButtonComponent],\n templateUrl: './list.component.html',\n styleUrl: './list.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_RIGHT])],\n})\nexport class ListComponent {\n /** Custom item template provided via lcListItem directive */\n readonly itemTemplate = contentChild(ListItemTemplateDirective);\n\n /** Array of list items to display */\n items = input<ListItem[]>([]);\n\n /** Orientation of the list */\n orientation = input<ListOrientation>('vertical');\n\n /** Visual variant */\n variant = input<ListVariant>('default');\n\n /** Size of list items */\n size = input<ListSize>('md');\n\n /** Whether to show dividers between items */\n showDividers = input<boolean>(false);\n\n /**\n * Whether rows are interactive. When set, each row is rendered as a\n * focusable button (Enter / Space emit `itemClick`) so `itemClick` is\n * reachable from the keyboard. Set this whenever you handle `itemClick`.\n */\n clickable = input<boolean>(false);\n\n /**\n * Clamp each item's `description` to two lines (3.2). Set `false` for\n * reading text that must show in full (3.3, G-11).\n * @default true\n */\n clampDescription = input<boolean>(true);\n\n /**\n * Variant of the built-in row action (`ListItem.action`).\n * @default 'ghost'\n */\n actionVariant = input<ButtonVariant>('ghost');\n\n /**\n * Size of the built-in row action: `xs` (3.2, about 25 px) … `lg` (48.5 px,\n * the 44 px touch target).\n * @default 'xs'\n */\n actionSize = input<ButtonSize>('xs');\n\n /**\n * Ends every row that opens something, a `clickable` row or an `href` row,\n * with the kit's `chevron-right` (20 px, `--color-text-tertiary`) after its\n * badge, metadata and action (3.36, quellen row 3). Decorative: hidden from\n * screen readers. Plain rows get none, and neither do rows drawn by an\n * `lcListItem` template (add the icon to the template there). Row height,\n * target size, focus ring, badge and `actionClick` don't change.\n * @default false\n */\n showChevron = input<boolean>(false);\n\n /** Event emitted when an item is clicked */\n readonly itemClick = output<ListItem>();\n\n /** Event emitted when an action button is clicked */\n readonly actionClick = output<ListItem>();\n\n /**\n * A plain click (or Enter) on an `href` row: primary button, no\n * Ctrl/Cmd/Shift/Alt (3.16, TD-4). Emitted before the browser follows the\n * link; call `event.preventDefault()` and navigate with your router for an\n * in-app route (`router.navigateByUrl(item.href)`). The kit never prevents\n * the default; modifier and middle clicks are never emitted, so \"open in new\n * tab\" keeps working.\n */\n readonly linkClick = output<ListLinkClickEvent>();\n\n private readonly injector = inject(Injector);\n\n /** The `role=\"list\"` element; each item renders exactly one child of it. */\n private readonly listEl = viewChild<ElementRef<HTMLElement>>('listEl');\n\n /** The `focusItem()` hook waiting for the next render. */\n private pendingItemFocus?: AfterRenderRef;\n\n constructor() {\n effect(() => {\n if (!isDevMode()) return;\n // `items` may be bound to undefined (tolerated since 3.2).\n for (const item of this.items() ?? []) {\n if (this.isLinkRow(item) && item.action) {\n console.warn(`[lc-list] item \"${item.label}\" has href and action: the action is not rendered inside a link`);\n }\n }\n });\n }\n\n /**\n * Moves the focus into the row of the first item with this `id` after the\n * next render (3.34). `'row'`: the row's button (`clickable`) or link\n * (`href`). `'action'`: its built-in action button. Does nothing for an\n * unknown `id`, for a row without that part (a plain row, a disabled row for\n * `'row'`, no `action`, an `lcListItem` template's own controls), or when\n * the list is destroyed first. A second call before that render replaces the\n * first.\n * @param id The `ListItem.id` of the row.\n * @param part Which part of the row gets the focus.\n */\n focusItem(id: string, part: ListItemPart = 'row'): void {\n this.pendingItemFocus?.destroy();\n this.pendingItemFocus = afterNextRender(\n () => {\n this.pendingItemFocus = undefined;\n const index = (this.items() ?? []).findIndex((item) => item.id === id);\n const row = index < 0 ? null : this.listEl()?.nativeElement.children.item(index);\n // querySelector skips the row element itself, so a plain row matches nothing.\n const target = row?.querySelector<HTMLElement>(\n part === 'action'\n ? '.lc-list__action-area button'\n : 'a.lc-list__item, .lc-list__item[role=button]:not([aria-disabled=true]), .lc-list__item-button:not([aria-disabled=true])',\n );\n target?.focus();\n },\n { injector: this.injector },\n );\n }\n\n /** Generated id prefix, taken once per instance (module counter, the same in every prerender). */\n private readonly uid = `lc-list-${nextListId++}`;\n\n /**\n * Id of row `index` of a `clickable` row with an `action` (3.37): the\n * row's button is named by it (`aria-labelledby`).\n */\n protected rowId(index: number): string {\n return `${this.uid}-row-${index}`;\n }\n\n /** Whether a row renders as a link: `href` set and not disabled. */\n protected isLinkRow(item: ListItem): boolean {\n return !!item.href && !item.disabled;\n }\n\n /**\n * Computed CSS classes for the list\n */\n listClasses = computed(() => {\n const classes = ['lc-list'];\n\n classes.push(`lc-list--${this.orientation()}`);\n classes.push(`lc-list--${this.variant()}`);\n classes.push(`lc-list--${this.size()}`);\n\n if (this.showDividers()) {\n classes.push('lc-list--with-dividers');\n }\n\n if (this.clickable()) {\n classes.push('lc-list--clickable');\n }\n\n if (!this.clampDescription()) {\n classes.push('lc-list--no-clamp');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Get CSS classes for a list item\n */\n getItemClasses(item: ListItem): string {\n const classes = ['lc-list__item'];\n\n if (item.disabled) {\n classes.push('lc-list__item--disabled');\n }\n\n if (item.selected) {\n classes.push('lc-list__item--selected');\n }\n\n return classes.join(' ');\n }\n\n /** CSS classes of an item's avatar (member colour, LK-7). */\n protected avatarClasses(item: ListItem): string {\n const color = item.avatarColor;\n return color && color !== 'default'\n ? `lc-list__avatar lc-list__avatar--member lc-list__avatar--${color}`\n : 'lc-list__avatar';\n }\n\n /**\n * Handle item click\n */\n onItemClick(item: ListItem): void {\n if (!item.disabled) {\n this.itemClick.emit(item);\n }\n }\n\n /**\n * Keyboard activation of a clickable row. Only reacts to keys on the row\n * itself: a nested control (e.g. the action button) handles its own Enter.\n */\n onItemKeydown(event: KeyboardEvent, item: ListItem): void {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.onItemClick(item);\n }\n }\n\n /**\n * Handle action button click\n */\n onActionClick(item: ListItem): void {\n this.actionClick.emit(item);\n }\n\n /**\n * A click on a link row: only a plain primary click reaches `linkClick`.\n * The default (following the link) is left to the browser and the consumer.\n */\n protected onLinkClick(event: MouseEvent, item: ListItem): void {\n if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;\n this.linkClick.emit({ item, event });\n }\n}\n","<!--\n Row content is shared between the plain (legacy) row and the keyboard-\n operable row rendered when `clickable` is set.\n-->\n<ng-template #rowContent let-item>\n @if (itemTemplate(); as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl.template; context: { $implicit: item }\"></ng-container>\n } @else {\n <!-- Leading: Avatar or Icon -->\n @if (item.avatar) {\n <div [class]=\"avatarClasses(item)\">\n @if (item.avatar.startsWith('http') || item.avatar.startsWith('/') || item.avatar.startsWith('data:')) {\n <img [src]=\"item.avatar\" [alt]=\"item.label\" class=\"lc-list__avatar-img\" />\n } @else {\n <span class=\"lc-list__avatar-initials\">{{ item.avatar }}</span>\n }\n </div>\n } @else if (item.icon) {\n <lc-icon [name]=\"item.icon\" class=\"lc-list__icon\" size=\"sm\" [decorative]=\"true\" />\n }\n\n <!-- Content: Label, Subtitle, Description -->\n <div class=\"lc-list__content\">\n <span class=\"lc-list__label\">{{ item.label }}</span>\n @if (item.subtitle) {\n <span class=\"lc-list__subtitle\">{{ item.subtitle }}</span>\n }\n @if (item.description) {\n <span class=\"lc-list__description\">{{ item.description }}</span>\n }\n </div>\n\n <!-- Trailing: Badge, Metadata, Action -->\n <div class=\"lc-list__trailing\">\n @if (item.badge) {\n <span class=\"lc-list__badge lc-list__badge--{{ item.badgeVariant || 'default' }}\">{{ item.badge }}</span>\n }\n @if (item.metadata) {\n <span class=\"lc-list__metadata\">{{ item.metadata }}</span>\n }\n @if (item.action && !isLinkRow(item)) {\n <!-- The action is its own control: its click must not also activate the row. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- only stops the action click from also activating the row; the action is a real button -->\n <span class=\"lc-list__action-area\" (click)=\"$event.stopPropagation()\">\n <lc-button\n [variant]=\"actionVariant()\"\n [size]=\"actionSize()\"\n [ariaLabel]=\"item.actionAriaLabel ?? ''\"\n class=\"lc-list__action\"\n (clicked)=\"onActionClick(item)\"\n >\n {{ item.action }}\n </lc-button>\n </span>\n }\n </div>\n\n <!-- 3.36: a decorative chevron ends a clickable or linked row. -->\n @if (showChevron() && (clickable() || !!item.href)) {\n <lc-icon name=\"chevron-right\" class=\"lc-list__chevron\" size=\"sm\" [decorative]=\"true\" />\n }\n }\n</ng-template>\n\n<div #listEl [class]=\"listClasses()\" role=\"list\">\n @for (item of items(); track item.id || $index; let i = $index) {\n @if (isLinkRow(item)) {\n <!-- A real link over the whole row (3.16, TD-4); the row element carries the item styles. -->\n <div class=\"lc-list__item-wrapper\" role=\"listitem\">\n <a\n [class]=\"getItemClasses(item) + ' lc-list__item--link'\"\n [href]=\"item.href\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n (click)=\"onLinkClick($event, item)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </a>\n </div>\n } @else if (clickable() && item.action && !itemTemplate()) {\n <!-- 3.37 (request 057, 056 F-2): a clickable row with an action keeps the\n action out of the row's button. The row's native button is stretched\n over the row and named by it; the action area sits above it. -->\n <div class=\"lc-list__item-wrapper\" role=\"listitem\">\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- the row's native button is the control; its Enter/Space click bubbles here -->\n <div [class]=\"getItemClasses(item) + ' lc-list__item--split'\" [id]=\"rowId(i)\" (click)=\"onItemClick(item)\">\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content -- named by aria-labelledby (the row's text); text inside would repeat it -->\n <button\n type=\"button\"\n class=\"lc-list__item-button\"\n [attr.aria-labelledby]=\"rowId(i)\"\n [attr.tabindex]=\"item.disabled ? -1 : null\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n ></button>\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </div>\n </div>\n } @else if (clickable()) {\n <div class=\"lc-list__item-wrapper\" role=\"listitem\">\n <div\n [class]=\"getItemClasses(item)\"\n role=\"button\"\n [attr.tabindex]=\"item.disabled ? -1 : 0\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n (click)=\"onItemClick(item)\"\n (keydown)=\"onItemKeydown($event, item)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </div>\n </div>\n } @else {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- non-clickable lists keep reporting mouse clicks (backward compatible); use clickable for keyboard rows -->\n <div\n [class]=\"getItemClasses(item)\"\n role=\"listitem\"\n [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n [attr.aria-current]=\"item.selected ? 'true' : null\"\n (click)=\"onItemClick(item)\"\n >\n <ng-container *ngTemplateOutlet=\"rowContent; context: { $implicit: item }\"></ng-container>\n </div>\n }\n }\n</div>\n"],"mappings":";;;;;;AAqBA,IAAa,4BAAb,MAAa,0BAAyB;CACpB,WAAW,OAAO,WAAoC;CAD3D,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAyB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAzB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAyB,cAAA;EAAA,UAAA;EAAA,UAAA;CAAA,CAAA;;AAAzB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAyB,YAAA,CAAA;EAJrC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,YAAY;EACb,CAAA;;;ACID,IAAI,aAAa;AAsIjB,IAAa,gBAAb,MAAa,cAAa;CAEf,eAAe,aAAa,2BAA0B,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG/D,QAAQ,MAAkB,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAG7B,cAAc,MAAuB,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGhD,UAAU,MAAmB,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAGvC,OAAO,MAAgB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG5B,eAAe,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAOpC,YAAY,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAOjC,mBAAmB,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,mBAAA,CAAA,+BAAA,CAAA,CAAA;CAMvC,gBAAgB,MAAqB,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAO7C,aAAa,MAAkB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAWpC,cAAc,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAG1B,YAAY,OAAM;CAGlB,cAAc,OAAM;CAUpB,YAAY,OAAM;CAEV,WAAW,OAAO,QAAQ;CAG1B,SAAS,UAAmC,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAG9D;CAER,cAAA;EACE,aAAY;GACV,IAAI,CAAC,UAAS,GAAI;GAElB,KAAK,MAAM,QAAQ,KAAK,MAAK,KAAM,CAAA,GACjC,IAAI,KAAK,UAAU,IAAI,KAAK,KAAK,QAC/B,QAAQ,KAAK,mBAAmB,KAAK,MAAK,gEAAiE;EAGjH,CAAC;CACH;CAaA,UAAU,IAAY,OAAqB,OAAK;EAC9C,KAAK,kBAAkB,QAAO;EAC9B,KAAK,mBAAmB,sBACjB;GACH,KAAK,mBAAmB;GACxB,MAAM,SAAS,KAAK,MAAK,KAAM,CAAA,EAAA,CAAI,WAAW,SAAS,KAAK,OAAO,EAAE;GAQrE,EAPY,QAAQ,IAAI,OAAO,KAAK,OAAM,CAAE,EAAE,cAAc,SAAS,KAAK,KAAK,EAEhE,EAAK,cAClB,SAAS,WACL,iCACA,yHAAyH,EAE/H,EAAQ,MAAK;EACf,GACA,EAAE,UAAU,KAAK,SAAQ,CAAE;CAE/B;CAGiB,MAAM,WAAW;CAMxB,MAAM,OAAa;EAC3B,OAAO,GAAG,KAAK,IAAG,OAAQ;CAC5B;CAGU,UAAU,MAAc;EAChC,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK;CAC9B;CAKA,cAAc,eAAc;EAC1B,MAAM,UAAU,CAAC,SAAS;EAE1B,QAAQ,KAAK,YAAY,KAAK,YAAW,GAAI;EAC7C,QAAQ,KAAK,YAAY,KAAK,QAAO,GAAI;EACzC,QAAQ,KAAK,YAAY,KAAK,KAAI,GAAI;EAEtC,IAAI,KAAK,aAAY,GACnB,QAAQ,KAAK,wBAAwB;EAGvC,IAAI,KAAK,UAAS,GAChB,QAAQ,KAAK,oBAAoB;EAGnC,IAAI,CAAC,KAAK,iBAAgB,GACxB,QAAQ,KAAK,mBAAmB;EAGlC,OAAO,QAAQ,KAAK,GAAG;CACzB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAKF,eAAe,MAAc;EAC3B,MAAM,UAAU,CAAC,eAAe;EAEhC,IAAI,KAAK,UACP,QAAQ,KAAK,yBAAyB;EAGxC,IAAI,KAAK,UACP,QAAQ,KAAK,yBAAyB;EAGxC,OAAO,QAAQ,KAAK,GAAG;CACzB;CAGU,cAAc,MAAc;EACpC,MAAM,QAAQ,KAAK;EACnB,OAAO,SAAS,UAAU,YACtB,4DAA4D,UAC5D;CACN;CAKA,YAAY,MAAc;EACxB,IAAI,CAAC,KAAK,UACR,KAAK,UAAU,KAAK,IAAI;CAE5B;CAMA,cAAc,OAAsB,MAAc;EAChD,IAAI,MAAM,WAAW,MAAM,eAAe;EAC1C,IAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;GAC9C,MAAM,eAAc;GACpB,KAAK,YAAY,IAAI;EACvB;CACF;CAKA,cAAc,MAAc;EAC1B,KAAK,YAAY,KAAK,IAAI;CAC5B;CAMU,YAAY,OAAmB,MAAc;EACrD,IAAI,MAAM,WAAW,KAAK,MAAM,WAAW,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ;EAC5F,KAAK,UAAU,KAAK;GAAE;GAAM;EAAK,CAAE;CACrC;CAhOW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAa,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAb,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAa,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,SAAA;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,cAAA;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,kBAAA;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,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,WAAA;GAAA,aAAA;GAAA,WAAA;EAAA;EAAA,SAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAEa;GAAyB,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,QAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UChKhE;EA6HA,QAAA,CAAA,s6OAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MD2BY;IAAgB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAa,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAe,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;IAAA;GAAA;EAAA;EAAA,eAI3C,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAE5C,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAa,YAAA,CAAA;EATzB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAS,YACP;GAAI,SACP;IAAC;IAAkB;IAAe;GAAe;GAAC,iBAG1C,wBAAwB;GAAM,eAChC,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;GAAC,UAAA;GAAA,QAAA,CAAA,s6OAAA;EAAA,CAAA;CAIlB,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAAA,yBAAyB,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,kBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,WAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,WAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CA0ED,UAAQ,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
|
|
@@ -106,9 +106,9 @@ var SelectComponent = class SelectComponent {
|
|
|
106
106
|
this.isOpen.set(true);
|
|
107
107
|
this.opened.emit();
|
|
108
108
|
}
|
|
109
|
-
close() {
|
|
109
|
+
close(returnFocus = false) {
|
|
110
110
|
if (!this.isOpen()) return;
|
|
111
|
-
this.restoreFocusFromPanel();
|
|
111
|
+
this.restoreFocusFromPanel(returnFocus);
|
|
112
112
|
this.isOpen.set(false);
|
|
113
113
|
this.searchQuery.set("");
|
|
114
114
|
this.highlightedIndex.set(-1);
|
|
@@ -127,7 +127,7 @@ var SelectComponent = class SelectComponent {
|
|
|
127
127
|
this.value.set(option.value);
|
|
128
128
|
this.onChange(option.value);
|
|
129
129
|
this.selectionChange.emit(option.value);
|
|
130
|
-
this.close();
|
|
130
|
+
this.close(true);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
isSelected(option) {
|
|
@@ -160,7 +160,7 @@ var SelectComponent = class SelectComponent {
|
|
|
160
160
|
break;
|
|
161
161
|
case "Escape":
|
|
162
162
|
if (this.isOpen()) {
|
|
163
|
-
this.close();
|
|
163
|
+
this.close(true);
|
|
164
164
|
event.preventDefault();
|
|
165
165
|
event.stopPropagation();
|
|
166
166
|
}
|
|
@@ -202,6 +202,9 @@ var SelectComponent = class SelectComponent {
|
|
|
202
202
|
onClickOutside() {
|
|
203
203
|
this.close();
|
|
204
204
|
}
|
|
205
|
+
onOverlayKeydown(event) {
|
|
206
|
+
if (event.key === "Escape" && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) this.close(true);
|
|
207
|
+
}
|
|
205
208
|
writeValue(value) {
|
|
206
209
|
this.value.set(value);
|
|
207
210
|
}
|
|
@@ -217,10 +220,12 @@ var SelectComponent = class SelectComponent {
|
|
|
217
220
|
isSearchInput(target) {
|
|
218
221
|
return target instanceof HTMLInputElement;
|
|
219
222
|
}
|
|
220
|
-
restoreFocusFromPanel() {
|
|
223
|
+
restoreFocusFromPanel(fromBody = false) {
|
|
221
224
|
const panel = this.dropdownPanel()?.nativeElement;
|
|
222
225
|
const active = this.document.activeElement;
|
|
223
|
-
|
|
226
|
+
const inPanel = !!panel && !!active && panel.contains(active);
|
|
227
|
+
const lost = !active || active === this.document.body || active === this.document.documentElement;
|
|
228
|
+
if (inPanel || fromBody && lost) this.selectTrigger()?.nativeElement.focus();
|
|
224
229
|
}
|
|
225
230
|
findOptionByValue(value, options) {
|
|
226
231
|
return this.flattenOptions(options).find((opt) => opt.value === value);
|
|
@@ -390,8 +395,8 @@ var SelectComponent = class SelectComponent {
|
|
|
390
395
|
isSignal: true
|
|
391
396
|
}],
|
|
392
397
|
ngImport: i0,
|
|
393
|
-
template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
|
|
394
|
-
styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"],
|
|
398
|
+
template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (overlayKeydown)=\"onOverlayKeydown($event)\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
|
|
399
|
+
styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--lg:has(.lc-select__clear){padding-right:calc(var(--spacing-3) + 1.25rem + var(--spacing-1) + max(-.125rem,(44px - 1.5rem) / 2 - .25rem) + 1.5rem + max(0px,(44px - 1.5rem) / 2))}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select--lg .lc-select__clear{margin-right:max(-.125rem,(44px - 1.5rem) / 2 - .25rem)}.lc-select--lg .lc-select__clear:before{content:\"\";position:absolute;inset:min(0px,(100% - 44px) / 2)}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"],
|
|
395
400
|
dependencies: [
|
|
396
401
|
{
|
|
397
402
|
kind: "ngmodule",
|
|
@@ -486,8 +491,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
486
491
|
useExisting: forwardRef(() => SelectComponent),
|
|
487
492
|
multi: true
|
|
488
493
|
}],
|
|
489
|
-
template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
|
|
490
|
-
styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"]
|
|
494
|
+
template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (overlayKeydown)=\"onOverlayKeydown($event)\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
|
|
495
|
+
styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--lg:has(.lc-select__clear){padding-right:calc(var(--spacing-3) + 1.25rem + var(--spacing-1) + max(-.125rem,(44px - 1.5rem) / 2 - .25rem) + 1.5rem + max(0px,(44px - 1.5rem) / 2))}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select--lg .lc-select__clear{margin-right:max(-.125rem,(44px - 1.5rem) / 2 - .25rem)}.lc-select--lg .lc-select__clear:before{content:\"\";position:absolute;inset:min(0px,(100% - 44px) / 2)}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"]
|
|
491
496
|
}]
|
|
492
497
|
}],
|
|
493
498
|
propDecorators: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-select.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/select/select.component.ts","../../../../libs/angular-ui-kit/src/lib/select/select.component.html"],"sourcesContent":["import {\n Component,\n input,\n output,\n signal,\n computed,\n linkedSignal,\n ChangeDetectionStrategy,\n forwardRef,\n ElementRef,\n viewChild,\n inject,\n DOCUMENT,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { closestLang } from '@lifecorner/angular-ui-kit/internal/a11y';\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectOptionGroup {\n label: string;\n options: SelectOption[];\n}\n\nexport type SelectValue = string | number | string[] | number[] | null;\n\n/**\n * Select component for dropdown option selection.\n *\n * Features:\n * - Single and multiple selection modes\n * - Searchable/filterable option list\n * - Option groups with headers\n * - Loading state indicator\n * - Variant styles (outline, filled)\n * - Size presets (xs, sm, md, lg)\n * - Keyboard navigation support\n * - CDK overlay positioning\n * - ControlValueAccessor integration for reactive forms\n *\n * The dropdown takes the `lang` of the host's closest `[lang]` (3.31).\n *\n * @example\n * ```html\n * <lc-select [options]=\"options\" placeholder=\"Choose\" [(ngModel)]=\"selected\" />\n * ```\n */\n@Component({\n selector: 'lc-select',\n standalone: true,\n imports: [FormsModule, OverlayModule],\n templateUrl: './select.component.html',\n styleUrl: './select.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SelectComponent),\n multi: true,\n },\n ],\n})\nexport class SelectComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance id base; the listbox and its options derive their ids from it. */\n readonly selectId = `lc-select-${++SelectComponent.nextId}`;\n readonly listboxId = `${this.selectId}-listbox`;\n\n private readonly selectTrigger = viewChild<ElementRef<HTMLElement>>('selectTrigger');\n private readonly dropdownPanel = viewChild<ElementRef<HTMLElement>>('dropdownPanel');\n\n /**\n * Visual variant of the select\n */\n readonly variant = input<'outline' | 'filled'>('outline');\n\n /**\n * Size of the select\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the select is disabled\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the select is in error state\n */\n readonly error = input<boolean>(false);\n\n /**\n * Whether the select is required\n */\n readonly required = input<boolean>(false);\n\n /**\n * Whether the select is in loading state\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Whether the select allows searching\n */\n readonly searchable = input<boolean>(false);\n\n /**\n * Whether multiple options can be selected\n */\n readonly multiple = input<boolean>(false);\n\n /**\n * Placeholder text when no option is selected\n */\n readonly placeholder = input<string>('Select an option');\n\n /**\n * Helper text displayed below the select\n */\n readonly helperText = input<string>('');\n\n /**\n * Error message displayed when error is true\n */\n readonly errorMessage = input<string>('');\n\n /**\n * ARIA label for accessibility\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Select options (flat list or groups)\n */\n readonly options = input<SelectOption[] | SelectOptionGroup[]>([]);\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear selection');\n\n /** Placeholder of the search field (`searchable`). */\n readonly searchPlaceholder = input('Search...');\n\n /** Accessible name of the search field. Empty: \"Search \" + `ariaLabel`, or \"Search options\". */\n readonly searchAriaLabel = input('');\n\n /** Shown when the search matches no option. */\n readonly noOptionsLabel = input('No options found');\n\n /**\n * Emitted when selection changes\n */\n readonly selectionChange = output<SelectValue>();\n\n /**\n * Emitted when dropdown opens\n */\n readonly opened = output<void>();\n\n /**\n * Emitted when dropdown closes\n */\n readonly closed = output<void>();\n\n // Internal state\n readonly value = signal<SelectValue>(null);\n readonly isOpen = signal<boolean>(false);\n readonly searchQuery = signal<string>('');\n\n /** Disabled state pushed in by the forms API (setDisabledState). */\n private readonly formDisabled = signal<boolean>(false);\n\n /** Effective disabled state: the `disabled` input OR the form control's disabled state. */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * `lang` of the open dropdown (3.31, request 050, WCAG 3.1.2): the overlay\n * renders under `<body>`, so it takes the host's closest `[lang]`, else none.\n * Read at every opening; `null` while closed, so the server never reads the DOM.\n */\n protected readonly panelLang = computed(() => (this.isOpen() ? closestLang(this.host.nativeElement) : null));\n\n /**\n * The trigger's width in px while the dropdown is open (3.33, request 052,\n * 050-n4), read at every opening: the panel's floor next to its 200 px\n * (`min-width`), so it is at least as wide as the field and wider content\n * still grows it. `null` while closed.\n */\n protected readonly fieldWidth = computed(() =>\n this.isOpen() ? (this.selectTrigger()?.nativeElement.getBoundingClientRect().width ?? null) : null,\n );\n\n // Computed values\n readonly filteredOptions = computed(() => {\n const query = this.searchQuery().toLowerCase();\n const opts = this.options();\n\n if (!query || !this.searchable()) {\n return this.flattenOptions(opts);\n }\n\n return this.flattenOptions(opts).filter((opt) => opt.label.toLowerCase().includes(query));\n });\n\n /** Keyboard highlight; resets whenever the visible option list changes so it never points at a stale row. */\n readonly highlightedIndex = linkedSignal<SelectOption[], number>({\n source: this.filteredOptions,\n computation: () => -1,\n });\n\n readonly activeDescendantId = computed(() => {\n const index = this.highlightedIndex();\n return this.isOpen() && index >= 0 ? this.optionId(index) : null;\n });\n\n readonly selectedLabel = computed(() => {\n const currentValue = this.value();\n const opts = this.options();\n\n if (currentValue === null) {\n return '';\n }\n\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [currentValue];\n return values\n .map((val) => this.findOptionByValue(val, opts)?.label)\n .filter(Boolean)\n .join(', ');\n }\n\n const val = Array.isArray(currentValue) ? currentValue[0] : currentValue;\n return this.findOptionByValue(val ?? '', opts)?.label || '';\n });\n\n readonly displayValue = computed(() => {\n const label = this.selectedLabel();\n if (label) {\n const currentValue = this.value();\n if (this.multiple() && Array.isArray(currentValue)) {\n const count = currentValue.length;\n return count > 1 ? `${count} selected` : label;\n }\n return label;\n }\n return this.placeholder();\n });\n\n /** id of the helper/error text the trigger is described by (aria-describedby) */\n readonly describedBy = computed(() => {\n if (this.error() && this.errorMessage()) {\n return `${this.selectId}-error`;\n }\n return this.helperText() ? `${this.selectId}-helper` : null;\n });\n\n /**\n * Computed classes for the select trigger element\n */\n readonly selectClasses = computed(() => {\n const classes = ['lc-select', `lc-select--${this.variant()}`, `lc-select--${this.size()}`];\n\n if (this.isDisabled()) {\n classes.push('lc-select--disabled');\n }\n\n if (this.error()) {\n classes.push('lc-select--error');\n }\n\n if (this.loading()) {\n classes.push('lc-select--loading');\n }\n\n if (this.isOpen()) {\n classes.push('lc-select--open');\n }\n\n return classes.join(' ');\n });\n\n // Private properties\n private readonly document = inject(DOCUMENT);\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: SelectValue) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n // Public methods\n /**\n * Id of the option at `index` in the filtered list (used for aria-activedescendant)\n */\n optionId(index: number): string {\n return `${this.selectId}-option-${index}`;\n }\n\n /**\n * Toggle dropdown open/closed\n */\n toggle(): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n if (this.isOpen()) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Open dropdown\n */\n open(): void {\n if (this.isDisabled() || this.loading() || this.isOpen()) {\n return;\n }\n this.isOpen.set(true);\n this.opened.emit();\n }\n\n /**\n * Close dropdown. Idempotent: the overlay's `detach` re-enters here after\n * `isOpen` flips, and that second call must not emit `closed` again.\n */\n close(): void {\n if (!this.isOpen()) {\n return;\n }\n // If focus is inside the panel (search input) it would fall to <body> when the\n // overlay is torn down; hand it back to the trigger first.\n this.restoreFocusFromPanel();\n this.isOpen.set(false);\n this.searchQuery.set('');\n this.highlightedIndex.set(-1);\n this.closed.emit();\n }\n\n /**\n * Select an option\n */\n selectOption(option: SelectOption): void {\n if (option.disabled) {\n return;\n }\n\n if (this.multiple()) {\n const currentValue = this.value() || [];\n const currentArray = (Array.isArray(currentValue) ? currentValue : []) as (string | number)[];\n const index = currentArray.indexOf(option.value);\n\n const newValue = (\n index > -1\n ? currentArray.filter((v) => v !== option.value) // Deselect\n : [...currentArray, option.value] // Select\n ) as SelectValue;\n this.value.set(newValue);\n this.onChange(newValue);\n this.selectionChange.emit(newValue);\n } else {\n this.value.set(option.value);\n this.onChange(option.value);\n this.selectionChange.emit(option.value);\n this.close();\n }\n }\n\n /**\n * Check if an option is selected\n */\n isSelected(option: SelectOption): boolean {\n const currentValue = this.value();\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [];\n return values.some((v) => v === option.value);\n }\n return currentValue === option.value;\n }\n\n /**\n * Clear selection\n */\n clear(): void {\n if (this.multiple()) {\n this.value.set([]);\n this.onChange([]);\n } else {\n this.value.set(null);\n this.onChange(null);\n }\n this.selectionChange.emit(this.value());\n }\n\n /**\n * Handle keyboard navigation (trigger and search input share this handler)\n */\n onKeyDown(event: KeyboardEvent): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n\n switch (event.key) {\n case ' ':\n case 'Enter':\n // Space must keep typing into the search box.\n if (event.key === ' ' && this.isSearchInput(event.target)) {\n return;\n }\n if (!this.isOpen()) {\n this.open();\n } else {\n const option = this.filteredOptions()[this.highlightedIndex()];\n if (option) {\n this.selectOption(option);\n }\n }\n event.preventDefault();\n break;\n\n case 'Escape':\n if (this.isOpen()) {\n this.close();\n event.preventDefault();\n // Only this select should react; keep the key from reaching enclosing overlays.\n event.stopPropagation();\n }\n break;\n\n case 'ArrowDown':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n this.highlightedIndex.set((this.highlightedIndex() + 1) % count);\n }\n }\n event.preventDefault();\n break;\n\n case 'ArrowUp':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n const current = this.highlightedIndex();\n this.highlightedIndex.set(current <= 0 ? count - 1 : current - 1);\n }\n }\n event.preventDefault();\n break;\n\n case 'Home':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(0);\n event.preventDefault();\n }\n break;\n\n case 'End':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(this.filteredOptions().length - 1);\n event.preventDefault();\n }\n break;\n }\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n /**\n * Handle click outside\n */\n onClickOutside(): void {\n this.close();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: SelectValue): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: (value: SelectValue) => 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 // Private helper methods\n private isSearchInput(target: EventTarget | null): boolean {\n return target instanceof HTMLInputElement;\n }\n\n private restoreFocusFromPanel(): void {\n const panel = this.dropdownPanel()?.nativeElement;\n const active = this.document.activeElement;\n if (panel && active && panel.contains(active)) {\n this.selectTrigger()?.nativeElement.focus();\n }\n }\n\n private findOptionByValue(\n value: string | number,\n options: SelectOption[] | SelectOptionGroup[],\n ): SelectOption | undefined {\n const flatOptions = this.flattenOptions(options);\n return flatOptions.find((opt) => opt.value === value);\n }\n\n private flattenOptions(options: SelectOption[] | SelectOptionGroup[]): SelectOption[] {\n if (!options || options.length === 0) {\n return [];\n }\n\n // Check if first item is a group\n const firstItem = options[0];\n if (firstItem && 'options' in firstItem) {\n return (options as SelectOptionGroup[]).flatMap((group) => group.options);\n }\n\n return options as SelectOption[];\n }\n}\n","<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n"],"mappings":";;;;;;;AAmEA,IAAa,kBAAb,MAAa,gBAAe;CAClB,OAAO,SAAS;CAGf,WAAW,aAAa,EAAE,gBAAgB;CAC1C,YAAY,GAAG,KAAK,SAAQ;CAEpB,gBAAgB,UAAmC,iBAAgB,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CACnE,gBAAgB,UAAmC,iBAAgB,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAK3E,UAAU,MAA4B,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAKhD,OAAO,MAAiC,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAK7C,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAKhC,QAAQ,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAK7B,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAKhC,UAAU,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAK/B,aAAa,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAKlC,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAKhC,cAAc,MAAc,oBAAmB,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAK/C,aAAa,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAK9B,eAAe,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAKhC,YAAY,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAKhD,UAAU,MAA4C,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAGzD,iBAAiB,MAAM,mBAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAGzC,oBAAoB,MAAM,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAGtC,kBAAkB,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAG3B,iBAAiB,MAAM,oBAAmB,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAK1C,kBAAkB,OAAM;CAKxB,SAAS,OAAM;CAKf,SAAS,OAAM;CAGf,QAAQ,OAAoB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACjC,SAAS,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAC/B,cAAc,OAAe,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGxB,eAAe,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG7C,aAAa,eAAe,KAAK,SAAQ,KAAM,KAAK,aAAY,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAE3D,OAAO,OAAgC,UAAU;CAO/C,YAAY,eAAgB,KAAK,OAAM,IAAK,YAAY,KAAK,KAAK,aAAa,IAAI,MAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAQzF,aAAa,eAC9B,KAAK,OAAM,IAAM,KAAK,cAAa,CAAE,EAAE,cAAc,sBAAqB,CAAE,CAAC,SAAS,OAAQ,MAC/F,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGQ,kBAAkB,eAAc;EACvC,MAAM,QAAQ,KAAK,YAAW,CAAE,CAAC,YAAW;EAC5C,MAAM,OAAO,KAAK,QAAO;EAEzB,IAAI,CAAC,SAAS,CAAC,KAAK,WAAU,GAC5B,OAAO,KAAK,eAAe,IAAI;EAGjC,OAAO,KAAK,eAAe,IAAI,CAAC,CAAC,QAAQ,QAAQ,IAAI,MAAM,YAAW,CAAE,CAAC,SAAS,KAAK,CAAC;CAC1F,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAGO,mBAAmB,aAAY;EAAA,GAAA,YAAA,EAAA,WAAA,mBAAA,+BAAA,CAAA;EACtC,QAAQ,KAAK;EACb,mBAAmB;CAAE,CAAA;CAGd,qBAAqB,eAAc;EAC1C,MAAM,QAAQ,KAAK,iBAAgB;EACnC,OAAO,KAAK,OAAM,KAAM,SAAS,IAAI,KAAK,SAAS,KAAK,IAAI;CAC9D,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,qBAAA,CAAA,+BAAA,CAAA,CAAA;CAEO,gBAAgB,eAAc;EACrC,MAAM,eAAe,KAAK,MAAK;EAC/B,MAAM,OAAO,KAAK,QAAO;EAEzB,IAAI,iBAAiB,MACnB,OAAO;EAGT,IAAI,KAAK,SAAQ,GAEf,QADe,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,YAAY,EAClE,CACJ,KAAK,QAAQ,KAAK,kBAAkB,KAAK,IAAI,CAAC,EAAE,KAAK,CAAC,CACtD,OAAO,OAAO,CAAC,CACf,KAAK,IAAI;EAGd,MAAM,MAAM,MAAM,QAAQ,YAAY,IAAI,aAAa,KAAK;EAC5D,OAAO,KAAK,kBAAkB,OAAO,IAAI,IAAI,CAAC,EAAE,SAAS;CAC3D,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAEO,eAAe,eAAc;EACpC,MAAM,QAAQ,KAAK,cAAa;EAChC,IAAI,OAAO;GACT,MAAM,eAAe,KAAK,MAAK;GAC/B,IAAI,KAAK,SAAQ,KAAM,MAAM,QAAQ,YAAY,GAAG;IAClD,MAAM,QAAQ,aAAa;IAC3B,OAAO,QAAQ,IAAI,GAAG,MAAK,aAAc;GAC3C;GACA,OAAO;EACT;EACA,OAAO,KAAK,YAAW;CACzB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGO,cAAc,eAAc;EACnC,IAAI,KAAK,MAAK,KAAM,KAAK,aAAY,GACnC,OAAO,GAAG,KAAK,SAAQ;EAEzB,OAAO,KAAK,WAAU,IAAK,GAAG,KAAK,SAAQ,WAAY;CACzD,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAKO,gBAAgB,eAAc;EACrC,MAAM,UAAU;GAAC;GAAa,cAAc,KAAK,QAAO;GAAM,cAAc,KAAK,KAAI;EAAI;EAEzF,IAAI,KAAK,WAAU,GACjB,QAAQ,KAAK,qBAAqB;EAGpC,IAAI,KAAK,MAAK,GACZ,QAAQ,KAAK,kBAAkB;EAGjC,IAAI,KAAK,QAAO,GACd,QAAQ,KAAK,oBAAoB;EAGnC,IAAI,KAAK,OAAM,GACb,QAAQ,KAAK,iBAAiB;EAGhC,OAAO,QAAQ,KAAK,GAAG;CACzB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAGe,WAAW,OAAO,QAAQ;CAEnC,iBAA8C,CAAE;CAEhD,kBAA6B,CAAE;CAMvC,SAAS,OAAa;EACpB,OAAO,GAAG,KAAK,SAAQ,UAAW;CACpC;CAKA,SAAM;EACJ,IAAI,KAAK,WAAU,KAAM,KAAK,QAAO,GACnC;EAEF,IAAI,KAAK,OAAM,GACb,KAAK,MAAK;OAEV,KAAK,KAAI;CAEb;CAKA,OAAI;EACF,IAAI,KAAK,WAAU,KAAM,KAAK,QAAO,KAAM,KAAK,OAAM,GACpD;EAEF,KAAK,OAAO,IAAI,IAAI;EACpB,KAAK,OAAO,KAAI;CAClB;CAMA,QAAK;EACH,IAAI,CAAC,KAAK,OAAM,GACd;EAIF,KAAK,sBAAqB;EAC1B,KAAK,OAAO,IAAI,KAAK;EACrB,KAAK,YAAY,IAAI,EAAE;EACvB,KAAK,iBAAiB,IAAI,EAAE;EAC5B,KAAK,OAAO,KAAI;CAClB;CAKA,aAAa,QAAoB;EAC/B,IAAI,OAAO,UACT;EAGF,IAAI,KAAK,SAAQ,GAAI;GACnB,MAAM,eAAe,KAAK,MAAK,KAAM,CAAA;GACrC,MAAM,eAAgB,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAA;GAGnE,MAAM,WAFQ,aAAa,QAAQ,OAAO,KAGxC,IAAQ,KACJ,aAAa,QAAQ,MAAM,MAAM,OAAO,KAAK,IAC7C,CAAC,GAAG,cAAc,OAAO,KAAK;GAEpC,KAAK,MAAM,IAAI,QAAQ;GACvB,KAAK,SAAS,QAAQ;GACtB,KAAK,gBAAgB,KAAK,QAAQ;EACpC,OAAO;GACL,KAAK,MAAM,IAAI,OAAO,KAAK;GAC3B,KAAK,SAAS,OAAO,KAAK;GAC1B,KAAK,gBAAgB,KAAK,OAAO,KAAK;GACtC,KAAK,MAAK;EACZ;CACF;CAKA,WAAW,QAAoB;EAC7B,MAAM,eAAe,KAAK,MAAK;EAC/B,IAAI,KAAK,SAAQ,GAEf,QADe,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAA,EACrD,CAAO,MAAM,MAAM,MAAM,OAAO,KAAK;EAE9C,OAAO,iBAAiB,OAAO;CACjC;CAKA,QAAK;EACH,IAAI,KAAK,SAAQ,GAAI;GACnB,KAAK,MAAM,IAAI,CAAA,CAAE;GACjB,KAAK,SAAS,CAAA,CAAE;EAClB,OAAO;GACL,KAAK,MAAM,IAAI,IAAI;GACnB,KAAK,SAAS,IAAI;EACpB;EACA,KAAK,gBAAgB,KAAK,KAAK,MAAK,CAAE;CACxC;CAKA,UAAU,OAAoB;EAC5B,IAAI,KAAK,WAAU,KAAM,KAAK,QAAO,GACnC;EAGF,QAAQ,MAAM,KAAd;GACE,KAAK;GACL,KAAK;IAEH,IAAI,MAAM,QAAQ,OAAO,KAAK,cAAc,MAAM,MAAM,GACtD;IAEF,IAAI,CAAC,KAAK,OAAM,GACd,KAAK,KAAI;SACJ;KACL,MAAM,SAAS,KAAK,gBAAe,CAAE,CAAC,KAAK,iBAAgB;KAC3D,IAAI,QACF,KAAK,aAAa,MAAM;IAE5B;IACA,MAAM,eAAc;IACpB;GAEF,KAAK;IACH,IAAI,KAAK,OAAM,GAAI;KACjB,KAAK,MAAK;KACV,MAAM,eAAc;KAEpB,MAAM,gBAAe;IACvB;IACA;GAEF,KAAK;IACH,IAAI,CAAC,KAAK,OAAM,GACd,KAAK,KAAI;SACJ;KACL,MAAM,QAAQ,KAAK,gBAAe,CAAE,CAAC;KACrC,IAAI,QAAQ,GACV,KAAK,iBAAiB,KAAK,KAAK,iBAAgB,IAAK,KAAK,KAAK;IAEnE;IACA,MAAM,eAAc;IACpB;GAEF,KAAK;IACH,IAAI,CAAC,KAAK,OAAM,GACd,KAAK,KAAI;SACJ;KACL,MAAM,QAAQ,KAAK,gBAAe,CAAE,CAAC;KACrC,IAAI,QAAQ,GAAG;MACb,MAAM,UAAU,KAAK,iBAAgB;MACrC,KAAK,iBAAiB,IAAI,WAAW,IAAI,QAAQ,IAAI,UAAU,CAAC;KAClE;IACF;IACA,MAAM,eAAc;IACpB;GAEF,KAAK;IACH,IAAI,KAAK,OAAM,KAAM,KAAK,gBAAe,CAAE,CAAC,SAAS,GAAG;KACtD,KAAK,iBAAiB,IAAI,CAAC;KAC3B,MAAM,eAAc;IACtB;IACA;GAEF,KAAK,OACH,IAAI,KAAK,OAAM,KAAM,KAAK,gBAAe,CAAE,CAAC,SAAS,GAAG;IACtD,KAAK,iBAAiB,IAAI,KAAK,gBAAe,CAAE,CAAC,SAAS,CAAC;IAC3D,MAAM,eAAc;GACtB;EAEJ;CACF;CAKA,SAAM;EACJ,KAAK,UAAS;CAChB;CAKA,iBAAc;EACZ,KAAK,MAAK;CACZ;CAGA,WAAW,OAAkB;EAC3B,KAAK,MAAM,IAAI,KAAK;CACtB;CAEA,iBAAiB,IAAgC;EAC/C,KAAK,WAAW;CAClB;CAEA,kBAAkB,IAAc;EAC9B,KAAK,YAAY;CACnB;CAEA,iBAAiB,YAAmB;EAClC,KAAK,aAAa,IAAI,UAAU;CAClC;CAGQ,cAAc,QAA0B;EAC9C,OAAO,kBAAkB;CAC3B;CAEQ,wBAAqB;EAC3B,MAAM,QAAQ,KAAK,cAAa,CAAE,EAAE;EACpC,MAAM,SAAS,KAAK,SAAS;EAC7B,IAAI,SAAS,UAAU,MAAM,SAAS,MAAM,GAC1C,KAAK,cAAa,CAAE,EAAE,cAAc,MAAK;CAE7C;CAEQ,kBACN,OACA,SAA6C;EAG7C,OADoB,KAAK,eAAe,OACjC,CAAA,CAAY,MAAM,QAAQ,IAAI,UAAU,KAAK;CACtD;CAEQ,eAAe,SAA6C;EAClE,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO,CAAA;EAIT,MAAM,YAAY,QAAQ;EAC1B,IAAI,aAAa,aAAa,WAC5B,OAAQ,QAAgC,SAAS,UAAU,MAAM,OAAO;EAG1E,OAAO;CACT;CAzdW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAe,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAf,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAe,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;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,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;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,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,mBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,iBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,iBAAA;GAAA,QAAA;GAAA,QAAA;EAAA;EAAA,WARf,CACT;GACE,SAAS;GACT,aAAa,iBAAiB,eAAe;GAC7C,OAAO;EACR,CACF;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,eAAA;GAAA,aAAA;GAAA,UAAA;EAAA,GAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,eAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UCjEH;EAoMA,QAAA,CAAA,quOAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MD7IY;GAAW;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,eAAA;IAAA,UAAA,CAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAa;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,qBAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAYzB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAe,YAAA,CAAA;EAf3B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAW,YACT;GAAI,SACP,CAAC,aAAa,aAAa;GAAC,iBAGpB,wBAAwB;GAAM,WACpC,CACT;IACE,SAAS;IACT,aAAa,iBAAgB,eAAgB;IAC7C,OAAO;GACR,CACF;GAAA,UAAA;GAAA,QAAA,CAAA,quOAAA;EAAA,CAAA;CASmE,CAAA;CAAA,gBAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,iBAAe,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACf,iBAAe,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,gBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,mBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,iBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,gBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,iBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,iBAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,QAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,QAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-select.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/select/select.component.ts","../../../../libs/angular-ui-kit/src/lib/select/select.component.html"],"sourcesContent":["import {\n Component,\n input,\n output,\n signal,\n computed,\n linkedSignal,\n ChangeDetectionStrategy,\n forwardRef,\n ElementRef,\n viewChild,\n inject,\n DOCUMENT,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { closestLang } from '@lifecorner/angular-ui-kit/internal/a11y';\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectOptionGroup {\n label: string;\n options: SelectOption[];\n}\n\nexport type SelectValue = string | number | string[] | number[] | null;\n\n/**\n * Select component for dropdown option selection.\n *\n * Features:\n * - Single and multiple selection modes\n * - Searchable/filterable option list\n * - Option groups with headers\n * - Loading state indicator\n * - Variant styles (outline, filled)\n * - Size presets (xs, sm, md, lg)\n * - Keyboard navigation support\n * - CDK overlay positioning\n * - ControlValueAccessor integration for reactive forms\n *\n * The dropdown takes the `lang` of the host's closest `[lang]` (3.31).\n *\n * @example\n * ```html\n * <lc-select [options]=\"options\" placeholder=\"Choose\" [(ngModel)]=\"selected\" />\n * ```\n */\n@Component({\n selector: 'lc-select',\n standalone: true,\n imports: [FormsModule, OverlayModule],\n templateUrl: './select.component.html',\n styleUrl: './select.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SelectComponent),\n multi: true,\n },\n ],\n})\nexport class SelectComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance id base; the listbox and its options derive their ids from it. */\n readonly selectId = `lc-select-${++SelectComponent.nextId}`;\n readonly listboxId = `${this.selectId}-listbox`;\n\n private readonly selectTrigger = viewChild<ElementRef<HTMLElement>>('selectTrigger');\n private readonly dropdownPanel = viewChild<ElementRef<HTMLElement>>('dropdownPanel');\n\n /**\n * Visual variant of the select\n */\n readonly variant = input<'outline' | 'filled'>('outline');\n\n /**\n * Size of the select\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the select is disabled\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the select is in error state\n */\n readonly error = input<boolean>(false);\n\n /**\n * Whether the select is required\n */\n readonly required = input<boolean>(false);\n\n /**\n * Whether the select is in loading state\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Whether the select allows searching\n */\n readonly searchable = input<boolean>(false);\n\n /**\n * Whether multiple options can be selected\n */\n readonly multiple = input<boolean>(false);\n\n /**\n * Placeholder text when no option is selected\n */\n readonly placeholder = input<string>('Select an option');\n\n /**\n * Helper text displayed below the select\n */\n readonly helperText = input<string>('');\n\n /**\n * Error message displayed when error is true\n */\n readonly errorMessage = input<string>('');\n\n /**\n * ARIA label for accessibility\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Select options (flat list or groups)\n */\n readonly options = input<SelectOption[] | SelectOptionGroup[]>([]);\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear selection');\n\n /** Placeholder of the search field (`searchable`). */\n readonly searchPlaceholder = input('Search...');\n\n /** Accessible name of the search field. Empty: \"Search \" + `ariaLabel`, or \"Search options\". */\n readonly searchAriaLabel = input('');\n\n /** Shown when the search matches no option. */\n readonly noOptionsLabel = input('No options found');\n\n /**\n * Emitted when selection changes\n */\n readonly selectionChange = output<SelectValue>();\n\n /**\n * Emitted when dropdown opens\n */\n readonly opened = output<void>();\n\n /**\n * Emitted when dropdown closes\n */\n readonly closed = output<void>();\n\n // Internal state\n readonly value = signal<SelectValue>(null);\n readonly isOpen = signal<boolean>(false);\n readonly searchQuery = signal<string>('');\n\n /** Disabled state pushed in by the forms API (setDisabledState). */\n private readonly formDisabled = signal<boolean>(false);\n\n /** Effective disabled state: the `disabled` input OR the form control's disabled state. */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * `lang` of the open dropdown (3.31, request 050, WCAG 3.1.2): the overlay\n * renders under `<body>`, so it takes the host's closest `[lang]`, else none.\n * Read at every opening; `null` while closed, so the server never reads the DOM.\n */\n protected readonly panelLang = computed(() => (this.isOpen() ? closestLang(this.host.nativeElement) : null));\n\n /**\n * The trigger's width in px while the dropdown is open (3.33, request 052,\n * 050-n4), read at every opening: the panel's floor next to its 200 px\n * (`min-width`), so it is at least as wide as the field and wider content\n * still grows it. `null` while closed.\n */\n protected readonly fieldWidth = computed(() =>\n this.isOpen() ? (this.selectTrigger()?.nativeElement.getBoundingClientRect().width ?? null) : null,\n );\n\n // Computed values\n readonly filteredOptions = computed(() => {\n const query = this.searchQuery().toLowerCase();\n const opts = this.options();\n\n if (!query || !this.searchable()) {\n return this.flattenOptions(opts);\n }\n\n return this.flattenOptions(opts).filter((opt) => opt.label.toLowerCase().includes(query));\n });\n\n /** Keyboard highlight; resets whenever the visible option list changes so it never points at a stale row. */\n readonly highlightedIndex = linkedSignal<SelectOption[], number>({\n source: this.filteredOptions,\n computation: () => -1,\n });\n\n readonly activeDescendantId = computed(() => {\n const index = this.highlightedIndex();\n return this.isOpen() && index >= 0 ? this.optionId(index) : null;\n });\n\n readonly selectedLabel = computed(() => {\n const currentValue = this.value();\n const opts = this.options();\n\n if (currentValue === null) {\n return '';\n }\n\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [currentValue];\n return values\n .map((val) => this.findOptionByValue(val, opts)?.label)\n .filter(Boolean)\n .join(', ');\n }\n\n const val = Array.isArray(currentValue) ? currentValue[0] : currentValue;\n return this.findOptionByValue(val ?? '', opts)?.label || '';\n });\n\n readonly displayValue = computed(() => {\n const label = this.selectedLabel();\n if (label) {\n const currentValue = this.value();\n if (this.multiple() && Array.isArray(currentValue)) {\n const count = currentValue.length;\n return count > 1 ? `${count} selected` : label;\n }\n return label;\n }\n return this.placeholder();\n });\n\n /** id of the helper/error text the trigger is described by (aria-describedby) */\n readonly describedBy = computed(() => {\n if (this.error() && this.errorMessage()) {\n return `${this.selectId}-error`;\n }\n return this.helperText() ? `${this.selectId}-helper` : null;\n });\n\n /**\n * Computed classes for the select trigger element\n */\n readonly selectClasses = computed(() => {\n const classes = ['lc-select', `lc-select--${this.variant()}`, `lc-select--${this.size()}`];\n\n if (this.isDisabled()) {\n classes.push('lc-select--disabled');\n }\n\n if (this.error()) {\n classes.push('lc-select--error');\n }\n\n if (this.loading()) {\n classes.push('lc-select--loading');\n }\n\n if (this.isOpen()) {\n classes.push('lc-select--open');\n }\n\n return classes.join(' ');\n });\n\n // Private properties\n private readonly document = inject(DOCUMENT);\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: SelectValue) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n // Public methods\n /**\n * Id of the option at `index` in the filtered list (used for aria-activedescendant)\n */\n optionId(index: number): string {\n return `${this.selectId}-option-${index}`;\n }\n\n /**\n * Toggle dropdown open/closed\n */\n toggle(): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n if (this.isOpen()) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Open dropdown\n */\n open(): void {\n if (this.isDisabled() || this.loading() || this.isOpen()) {\n return;\n }\n this.isOpen.set(true);\n this.opened.emit();\n }\n\n /**\n * Close dropdown. Idempotent: the overlay's `detach` re-enters here after\n * `isOpen` flips, and that second call must not emit `closed` again.\n *\n * `returnFocus` (3.37, request 057, services row 2): a single-select choice\n * and `Escape` also hand the focus back to the trigger when it is on `<body>`\n * (a tap on an option, which has no `tabindex`, moves it there). Without it\n * (backdrop click, detach) only focus inside the panel returns, as before.\n */\n close(returnFocus = false): void {\n if (!this.isOpen()) {\n return;\n }\n // If focus is inside the panel (search input) it would fall to <body> when the\n // overlay is torn down; hand it back to the trigger first.\n this.restoreFocusFromPanel(returnFocus);\n this.isOpen.set(false);\n this.searchQuery.set('');\n this.highlightedIndex.set(-1);\n this.closed.emit();\n }\n\n /**\n * Select an option\n */\n selectOption(option: SelectOption): void {\n if (option.disabled) {\n return;\n }\n\n if (this.multiple()) {\n const currentValue = this.value() || [];\n const currentArray = (Array.isArray(currentValue) ? currentValue : []) as (string | number)[];\n const index = currentArray.indexOf(option.value);\n\n const newValue = (\n index > -1\n ? currentArray.filter((v) => v !== option.value) // Deselect\n : [...currentArray, option.value] // Select\n ) as SelectValue;\n this.value.set(newValue);\n this.onChange(newValue);\n this.selectionChange.emit(newValue);\n } else {\n this.value.set(option.value);\n this.onChange(option.value);\n this.selectionChange.emit(option.value);\n this.close(true);\n }\n }\n\n /**\n * Check if an option is selected\n */\n isSelected(option: SelectOption): boolean {\n const currentValue = this.value();\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [];\n return values.some((v) => v === option.value);\n }\n return currentValue === option.value;\n }\n\n /**\n * Clear selection\n */\n clear(): void {\n if (this.multiple()) {\n this.value.set([]);\n this.onChange([]);\n } else {\n this.value.set(null);\n this.onChange(null);\n }\n this.selectionChange.emit(this.value());\n }\n\n /**\n * Handle keyboard navigation (trigger and search input share this handler)\n */\n onKeyDown(event: KeyboardEvent): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n\n switch (event.key) {\n case ' ':\n case 'Enter':\n // Space must keep typing into the search box.\n if (event.key === ' ' && this.isSearchInput(event.target)) {\n return;\n }\n if (!this.isOpen()) {\n this.open();\n } else {\n const option = this.filteredOptions()[this.highlightedIndex()];\n if (option) {\n this.selectOption(option);\n }\n }\n event.preventDefault();\n break;\n\n case 'Escape':\n if (this.isOpen()) {\n this.close(true);\n event.preventDefault();\n // Only this select should react; keep the key from reaching enclosing overlays.\n event.stopPropagation();\n }\n break;\n\n case 'ArrowDown':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n this.highlightedIndex.set((this.highlightedIndex() + 1) % count);\n }\n }\n event.preventDefault();\n break;\n\n case 'ArrowUp':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n const current = this.highlightedIndex();\n this.highlightedIndex.set(current <= 0 ? count - 1 : current - 1);\n }\n }\n event.preventDefault();\n break;\n\n case 'Home':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(0);\n event.preventDefault();\n }\n break;\n\n case 'End':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(this.filteredOptions().length - 1);\n event.preventDefault();\n }\n break;\n }\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n /**\n * Handle click outside\n */\n onClickOutside(): void {\n this.close();\n }\n\n /**\n * Keys that reach the open overlay while the focus is outside the field and\n * the panel, e.g. on `<body>` after a tap (3.37, request 057): `Escape`\n * closes and returns the focus. It runs before the overlay's own `Escape`\n * detach, which then finds the select closed.\n */\n onOverlayKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {\n this.close(true);\n }\n }\n\n // ControlValueAccessor implementation\n writeValue(value: SelectValue): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: (value: SelectValue) => 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 // Private helper methods\n private isSearchInput(target: EventTarget | null): boolean {\n return target instanceof HTMLInputElement;\n }\n\n private restoreFocusFromPanel(fromBody = false): void {\n const panel = this.dropdownPanel()?.nativeElement;\n const active = this.document.activeElement;\n const inPanel = !!panel && !!active && panel.contains(active);\n // Never take the focus from an element a `selectionChange` handler focused.\n const lost = !active || active === this.document.body || active === this.document.documentElement;\n if (inPanel || (fromBody && lost)) {\n this.selectTrigger()?.nativeElement.focus();\n }\n }\n\n private findOptionByValue(\n value: string | number,\n options: SelectOption[] | SelectOptionGroup[],\n ): SelectOption | undefined {\n const flatOptions = this.flattenOptions(options);\n return flatOptions.find((opt) => opt.value === value);\n }\n\n private flattenOptions(options: SelectOption[] | SelectOptionGroup[]): SelectOption[] {\n if (!options || options.length === 0) {\n return [];\n }\n\n // Check if first item is a group\n const firstItem = options[0];\n if (firstItem && 'options' in firstItem) {\n return (options as SelectOptionGroup[]).flatMap((group) => group.options);\n }\n\n return options as SelectOption[];\n }\n}\n","<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (overlayKeydown)=\"onOverlayKeydown($event)\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n"],"mappings":"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"}
|