@lifecorner/angular-ui-kit 3.35.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/README.md +1 -0
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs +6 -6
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs +82 -16
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +18 -2
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +43 -5
- package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs +45 -8
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-hero.mjs +78 -5
- 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 +30 -6
- 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-stacked-bar-chart.mjs +47 -5
- package/fesm2022/lifecorner-angular-ui-kit-stacked-bar-chart.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs +226 -0
- package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs.map +1 -0
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs +25 -4
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit.mjs +1 -0
- package/package.json +5 -1
- package/src/styles/_theme-light.scss +8 -1
- package/src/styles/theme-contrast.spec.ts +116 -0
- package/src/styles/theme-modes.spec.ts +20 -5
- package/types/lifecorner-angular-ui-kit-button.d.ts +7 -3
- package/types/lifecorner-angular-ui-kit-card.d.ts +51 -3
- package/types/lifecorner-angular-ui-kit-combobox.d.ts +15 -0
- package/types/lifecorner-angular-ui-kit-description-list.d.ts +40 -0
- package/types/lifecorner-angular-ui-kit-header.d.ts +44 -2
- package/types/lifecorner-angular-ui-kit-hero.d.ts +50 -1
- package/types/lifecorner-angular-ui-kit-internal-chart.d.ts +6 -0
- package/types/lifecorner-angular-ui-kit-list.d.ts +27 -0
- package/types/lifecorner-angular-ui-kit-select.d.ts +13 -1
- package/types/lifecorner-angular-ui-kit-stacked-bar-chart.d.ts +36 -0
- package/types/lifecorner-angular-ui-kit-state-strip.d.ts +125 -0
- package/types/lifecorner-angular-ui-kit-table.d.ts +46 -1
- package/types/lifecorner-angular-ui-kit.d.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/card/card.component.ts","../../../../libs/angular-ui-kit/src/lib/card/card.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n HostListener,\n computed,\n effect,\n isDevMode,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterLink, type Params } from '@angular/router';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/** Visual variant of `lc-card`. */\nexport type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';\n\n/** Heading level of `lc-card`'s built-in title. */\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\n/** Layout of `lc-card`'s `[card-footer]` slot: `default` (a flex row, end-aligned) or `equal` (children at equal width). */\nexport type CardFooterLayout = 'default' | 'equal';\n\n/** Whether a `route` value is a link: `''`, `null` and `undefined` are not. */\nfunction hasRoute(route: string | readonly unknown[] | null | undefined): boolean {\n return route !== '' && route !== null && route !== undefined;\n}\n\n/**\n * Card component for content grouping and visual hierarchy.\n *\n * Features:\n * - Variant styles (elevated, outlined, flat, raised, dashed)\n * - Configurable padding (none, xs, sm, md, lg)\n * - Border radius options (none, sm, md, lg, xl, full)\n * - Optional title display, with an optional leading icon tile\n * - Content projection for flexible body content\n * - Link mode (3.24): `href` or `route` make the whole card a real link,\n * the title stays a heading\n * - Equal footer (3.29): `footerLayout=\"equal\"` gives two answers of equal\n * weight in the `[card-footer]` slot the same width\n *\n * @example\n * ```html\n * <lc-card title=\"Card Title\" variant=\"elevated\" padding=\"md\">\n * <p>Card body content</p>\n * </lc-card>\n *\n * <!-- With a leading brand-tile icon in the header -->\n * <lc-card title=\"Repository\" subtitle=\"Checkout & profile\" icon=\"git-branch\">\n * ...\n * </lc-card>\n *\n * <!-- An overview card that opens a page: heading h2 with a link inside -->\n * <lc-card title=\"SAP-Ist\" variant=\"outlined\" [headingLevel]=\"2\"\n * [route]=\"['/quellen', 'sap-ist']\" [queryParams]=\"{ ansicht: 'laeufe' }\">\n * ...\n * </lc-card>\n *\n * <!-- Two answers of equal weight (3.29): primary answer first -->\n * <lc-card title=\"Netflix\" variant=\"outlined\" footerLayout=\"equal\">\n * ...\n * <div card-footer>\n * <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">Stimmt</lc-button>\n * <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">Kein Vertrag</lc-button>\n * </div>\n * </lc-card>\n * ```\n */\n@Component({\n selector: 'lc-card',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet, RouterLink],\n templateUrl: './card.component.html',\n styleUrls: ['./card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class.lc-card--footer-equal]': \"footerLayout() === 'equal'\" },\n})\nexport class CardComponent {\n /**\n * Optional title rendered as a styled header.\n * For complex headers (e.g. title + action button), use content projection instead.\n */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional subtitle rendered below the title.\n */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a teal brand tile to\n * the left of the title. Only shown when `title` is set.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * - md: 2.25rem tile (default)\n * - sm: compact ~1.75rem chip for dense card headers\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /**\n * Optional badge text/count shown next to the title (e.g. item count).\n * For a richer status badge (e.g. a leading status dot), project an\n * `lc-badge` into the `card-badge` slot instead — the slot takes precedence.\n */\n badge = input<string | undefined>(undefined);\n\n /**\n * Badge color variant.\n * @default 'default'\n */\n badgeVariant = input<'default' | 'primary' | 'success' | 'warning' | 'error'>('default');\n\n /**\n * Visual variant of the card\n * - elevated: Box shadow (default)\n * - outlined: Border only (the hairline `--color-border`, no shadow) — the\n * content card on the \"Weite\" sky with `borderRadius=\"xl\"`\n * - flat: No shadow or border\n * - raised: plain surface, hairline border and the long, soft\n * `--shadow-auth-card`, no accent line — the sign-in card\n * - dashed: the muted \"coming soon\" tile — `--color-muted-tile` fill, 1 px\n * dashed `--color-muted-tile-border`, secondary ink, icons in\n * `--color-muted-tile-icon`. Never clickable: `clickable` is ignored;\n * `href` and `route` are ignored too\n * @default 'elevated'\n */\n variant = input<CardVariant>('elevated');\n\n /**\n * Padding inside the card\n * @default 'md'\n */\n padding = input<'none' | 'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the card is clickable (hover effects, cursor pointer). Has no\n * effect on a link card (`href`/`route`).\n * @default false\n */\n clickable = input<boolean>(false);\n\n /**\n * Whether the card is selected (if clickable)\n * @default false\n */\n selected = input<boolean>(false);\n\n /**\n * Border radius size: `none`, `sm`, `md`, `lg` (18 px), `xl` (24 px, the\n * \"Weite\" content card) or `full`.\n * @default 'md'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'>('md');\n\n /**\n * Heading level of the built-in `title` (`h2`–`h6`), so the card fits the\n * page's outline. Semantics only, the look stays the same.\n * @default 3\n */\n headingLevel = input<CardHeadingLevel>(3);\n\n /**\n * Accessible name. On a link card (`href`/`route`) it names the link\n * instead of the card; it must contain the visible title (WCAG 2.5.3) and\n * the heading reads it too, so keep it short. Without it the link is named\n * by the title, and the card's content (badge, list) is read as normal\n * content. On other cards it labels the card.\n * @default undefined\n */\n ariaLabel = input<string | undefined>(undefined);\n\n /**\n * URL the card links to (3.24, quellen row 1). The card becomes a real\n * link: the built-in `title` is an `<a href>` inside its heading, and the\n * link covers the whole card (click, Enter, middle click, \"open in new\n * tab\", \"copy link\"). No router needed; works in prerendered HTML. Wins\n * over `route`. Ignored by `dashed`. On a link card `clickable` has no\n * effect and `cardClick` is not emitted.\n * @default undefined\n */\n href = input<string | undefined>(undefined);\n\n /**\n * In-app route the card links to through the Angular router (`RouterLink`\n * commands: `'/quellen/usm'` or `['/quellen', id]`), relative to the\n * consumer's route, rendered as a real `<a href>` like `href`. Needs the\n * app's router (`provideRouter`); without one, use `href`. `''` and `null`\n * mean no link.\n * @default undefined\n */\n route = input<string | readonly unknown[] | null | undefined>(undefined);\n\n /**\n * Query parameters of `route`, as `RouterLink`'s `queryParams`\n * (`{ ansicht: 'laeufe' }`). Ignored with `href` (write the query into the\n * URL).\n * @default undefined\n */\n queryParams = input<Params | null | undefined>(undefined);\n\n /**\n * Layout of the `[card-footer]` slot (3.29, CC-2). `equal`: its children\n * share the row at equal width once the card is at least 480 px (30rem)\n * wide, and stack full width, 8 px apart, below that (the card's own width,\n * at any viewport; long labels wrap inside the buttons, which get the\n * row's height). For two answers of equal weight, put\n * `<lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">` in it,\n * primary answer first; the card adds layout only (gap, divider and\n * spacing as `default`) and never restyles the buttons. The card becomes a\n * size container and takes its parent's full width. Keep the default\n * `padding` (with `none` the buttons' focus ring is clipped). Works in link\n * cards (the buttons stay clickable above the link) and in prerendered\n * HTML (CSS only).\n * @default 'default'\n */\n footerLayout = input<CardFooterLayout>('default');\n\n /**\n * Emitted when a `clickable` card is clicked or activated with Enter or\n * Space. Not emitted on a link card (`href`/`route`): the link navigates by\n * itself (one navigation); bind the native `(click)` for analytics.\n */\n readonly cardClick = output<MouseEvent>();\n\n /**\n * How the card links (3.24): `href` wins over `route`; `''`/`null` routes\n * are no link, and the dashed \"coming soon\" tile never links.\n */\n protected readonly linkMode = computed<'href' | 'route' | null>(() => {\n if (this.variant() === 'dashed') return null;\n if (this.href()) return 'href';\n return hasRoute(this.route()) ? 'route' : null;\n });\n\n /**\n * Whether the card acts as a button: `clickable`, except for the dashed\n * \"coming soon\" tile, which is never interactive, and a link card, whose\n * anchor is the only interactive element.\n */\n protected isClickable = computed(\n () => this.clickable() && this.variant() !== 'dashed' && !this.linkMode(),\n );\n\n /** `ariaLabel` on the card; on a link card it names the anchor instead. */\n protected readonly cardAriaLabel = computed(() => (this.linkMode() ? undefined : this.ariaLabel()));\n\n constructor() {\n if (isDevMode()) {\n effect(() => {\n if (!this.linkMode()) return;\n if (this.href() && hasRoute(this.route())) {\n console.warn('[lc-card] href and route are both set: href wins');\n }\n if (!this.title() && !this.ariaLabel()) {\n console.warn('[lc-card] a link card without title needs an ariaLabel to name its link');\n }\n });\n }\n }\n\n /**\n * Compute card CSS classes based on inputs\n */\n protected cardClasses = computed(() => {\n const classes = [\n 'card',\n `card-${this.variant()}`,\n `card-padding-${this.padding()}`,\n `card-radius-${this.borderRadius()}`,\n ];\n\n if (this.isClickable()) {\n classes.push('card-clickable');\n }\n\n // A link card looks and reacts like a clickable one (3.24).\n if (this.linkMode()) {\n classes.push('card-linked');\n }\n\n // The dashed tile is never interactive, so it is never selected either.\n if (this.selected() && this.variant() !== 'dashed') {\n classes.push('card-selected');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Compute ARIA role based on clickable state\n */\n protected role = computed(() => (this.isClickable() ? 'button' : 'article'));\n\n /**\n * Handle card click events\n */\n protected handleClick(event: MouseEvent): void {\n if (this.isClickable()) {\n this.cardClick.emit(event);\n }\n }\n\n /**\n * Handle keyboard events for accessibility\n */\n @HostListener('keydown', ['$event'])\n protected handleKeydown(event: KeyboardEvent): void {\n if (this.isClickable() && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n const mouseEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n });\n this.cardClick.emit(mouseEvent);\n }\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"cardAriaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (linkMode() && !title()) {\n <!-- Link card without a title (3.24): an empty anchor, named by ariaLabel, covers the card. -->\n <ng-container [ngTemplateOutlet]=\"cardLink\" [ngTemplateOutletContext]=\"{ bare: true }\" />\n }\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @if (linkMode()) {\n <!-- Link card (3.24): the heading stays a heading and holds the link. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h3>\n }\n }\n } @else {\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n\n<!-- The link of a link card (3.24): `href` (plain, no router) or `route`\n (RouterLink, created only here, so other cards never need a router).\n Its ::after covers the card. -->\n<ng-template #cardLink let-bare=\"bare\">\n @if (linkMode() === 'href') {\n <a class=\"card__link\" [class.card__link--bare]=\"bare\" [href]=\"href()\" [attr.aria-label]=\"ariaLabel()\">{{ bare ? '' : title() }}</a>\n } @else {\n <a\n class=\"card__link\"\n [class.card__link--bare]=\"bare\"\n [routerLink]=\"route()\"\n [queryParams]=\"queryParams()\"\n [attr.aria-label]=\"ariaLabel()\"\n >{{ bare ? '' : title() }}</a\n >\n }\n</ng-template>\n"],"mappings":";;;;;AAwBA,SAAS,SAAS,OAAqD;CACrE,OAAO,UAAU,MAAM,UAAU,QAAQ,UAAU;AACrD;AAoDA,IAAa,gBAAb,MAAa,cAAa;CAKxB,QAAQ,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAK5C,WAAW,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAM/C,OAAO,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAQ3C,cAAc,MAA0B,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAQhD,WAAW,MAAmB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAOnC,QAAQ,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAM5C,eAAe,MAA+D,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAgBxF,UAAU,MAAmB,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAMxC,UAAU,MAA0C,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAOzD,YAAY,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAMjC,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAOhC,eAAe,MAAmD,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAOvE,eAAe,MAAwB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAUzC,YAAY,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAWhD,OAAO,MAA0B,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAU3C,QAAQ,MAAsD,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAQxE,cAAc,MAAiC,QAAU,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAiBzD,eAAe,MAAwB,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAOxC,YAAY,OAAM;CAMR,WAAW,eAAuC;EACnE,IAAI,KAAK,QAAO,MAAO,UAAU,OAAO;EACxC,IAAI,KAAK,KAAI,GAAI,OAAO;EACxB,OAAO,SAAS,KAAK,MAAK,CAAE,IAAI,UAAU;CAC5C,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAOQ,cAAc,eAChB,KAAK,UAAS,KAAM,KAAK,QAAO,MAAO,YAAY,CAAC,KAAK,SAAQ,GACxE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGkB,gBAAgB,eAAgB,KAAK,SAAQ,IAAK,SAAY,KAAK,UAAS,GAAI,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAEnG,cAAA;EACE,IAAI,UAAS,GACX,aAAY;GACV,IAAI,CAAC,KAAK,SAAQ,GAAI;GACtB,IAAI,KAAK,KAAI,KAAM,SAAS,KAAK,MAAK,CAAE,GACtC,QAAQ,KAAK,kDAAkD;GAEjE,IAAI,CAAC,KAAK,MAAK,KAAM,CAAC,KAAK,UAAS,GAClC,QAAQ,KAAK,yEAAyE;EAE1F,CAAC;CAEL;CAKU,cAAc,eAAc;EACpC,MAAM,UAAU;GACd;GACA,QAAQ,KAAK,QAAO;GACpB,gBAAgB,KAAK,QAAO;GAC5B,eAAe,KAAK,aAAY;EACjC;EAED,IAAI,KAAK,YAAW,GAClB,QAAQ,KAAK,gBAAgB;EAI/B,IAAI,KAAK,SAAQ,GACf,QAAQ,KAAK,aAAa;EAI5B,IAAI,KAAK,SAAQ,KAAM,KAAK,QAAO,MAAO,UACxC,QAAQ,KAAK,eAAe;EAG9B,OAAO,QAAQ,KAAK,GAAG;CACzB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAKQ,OAAO,eAAgB,KAAK,YAAW,IAAK,WAAW,WAAW,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAKlE,YAAY,OAAiB;EACrC,IAAI,KAAK,YAAW,GAClB,KAAK,UAAU,KAAK,KAAK;CAE7B;CAMU,cAAc,OAAoB;EAC1C,IAAI,KAAK,YAAW,MAAO,MAAM,QAAQ,WAAW,MAAM,QAAQ,MAAM;GACtE,MAAM,eAAc;GACpB,MAAM,aAAa,IAAI,WAAW,SAAS;IACzC,SAAS;IACT,YAAY;GACb,CAAA;GACD,KAAK,UAAU,KAAK,UAAU;EAChC;CACF;CA1PW,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,UAAA;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,aAAA;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,cAAA;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,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,WAAA,YAAA;EAAA,MAAA;GAAA,WAAA,EAAA,WAAA,wBAAA;GAAA,YAAA,EAAA,+BAAA,6BAAA;EAAA;EAAA,UAAA;EAAA,UC9E1B;EAsGA,QAAA,CAAA,q2MAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MD9BY;IAAa,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAgB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAU,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAM1C,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;IAAe;IAAkB;GAAU;GAAC,iBAGrC,wBAAwB;GAAM,MACzC,EAAE,iCAAiC,6BAA4B;GAAE,UAAA;GAAA,QAAA,CAAA,q2MAAA;EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkPtE,MAAA;GAAa,MAAA,CAAA,WAAW,CAAC,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/card/card.component.ts","../../../../libs/angular-ui-kit/src/lib/card/card.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n model,\n output,\n HostListener,\n computed,\n effect,\n isDevMode,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterLink, type Params } from '@angular/router';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHEVRON_DOWN } from '@lifecorner/angular-ui-kit/icons';\n\n/** Visual variant of `lc-card`. */\nexport type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';\n\n/** Heading level of `lc-card`'s built-in title. */\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\n/** Layout of `lc-card`'s `[card-footer]` slot: `default` (a flex row, end-aligned) or `equal` (children at equal width). */\nexport type CardFooterLayout = 'default' | 'equal';\n\n/** Counter for generated ids (3.36): creation order, so prerendered HTML is the same in every build. */\nlet nextCardId = 0;\n\n/** Whether a `route` value is a link: `''`, `null` and `undefined` are not. */\nfunction hasRoute(route: string | readonly unknown[] | null | undefined): boolean {\n return route !== '' && route !== null && route !== undefined;\n}\n\n/**\n * Card component for content grouping and visual hierarchy.\n *\n * Features:\n * - Variant styles (elevated, outlined, flat, raised, dashed)\n * - Configurable padding (none, xs, sm, md, lg)\n * - Border radius options (none, sm, md, lg, xl, full)\n * - Optional title display, with an optional leading icon tile\n * - Content projection for flexible body content\n * - Link mode (3.24): `href` or `route` make the whole card a real link,\n * the title stays a heading\n * - Equal footer (3.29): `footerLayout=\"equal\"` gives two answers of equal\n * weight in the `[card-footer]` slot the same width\n * - Collapsible (3.36): `collapsible` with `[(expanded)]` turns the head into\n * one toggle that opens and closes the body and footer\n *\n * @example\n * ```html\n * <lc-card title=\"Card Title\" variant=\"elevated\" padding=\"md\">\n * <p>Card body content</p>\n * </lc-card>\n *\n * <!-- With a leading brand-tile icon in the header -->\n * <lc-card title=\"Repository\" subtitle=\"Checkout & profile\" icon=\"git-branch\">\n * ...\n * </lc-card>\n *\n * <!-- An overview card that opens a page: heading h2 with a link inside -->\n * <lc-card title=\"SAP-Ist\" variant=\"outlined\" [headingLevel]=\"2\"\n * [route]=\"['/quellen', 'sap-ist']\" [queryParams]=\"{ ansicht: 'laeufe' }\">\n * ...\n * </lc-card>\n *\n * <!-- Two answers of equal weight (3.29): primary answer first -->\n * <lc-card title=\"Netflix\" variant=\"outlined\" footerLayout=\"equal\">\n * ...\n * <div card-footer>\n * <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">Stimmt</lc-button>\n * <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">Kein Vertrag</lc-button>\n * </div>\n * </lc-card>\n * ```\n */\n@Component({\n selector: 'lc-card',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet, RouterLink],\n templateUrl: './card.component.html',\n styleUrls: ['./card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.lc-card--footer-equal]': \"footerLayout() === 'equal'\",\n '[class.lc-card--collapsed]': 'isCollapsible() && !expanded()',\n },\n viewProviders: [ɵprovideKitIcons([ICON_CHEVRON_DOWN])],\n})\nexport class CardComponent {\n /**\n * Optional title rendered as a styled header.\n * For complex headers (e.g. title + action button), use content projection instead.\n */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional subtitle rendered below the title.\n */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a teal brand tile to\n * the left of the title. Only shown when `title` is set.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * - md: 2.25rem tile (default)\n * - sm: compact ~1.75rem chip for dense card headers\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /**\n * Optional badge text/count shown next to the title (e.g. item count).\n * For a richer status badge (e.g. a leading status dot), project an\n * `lc-badge` into the `card-badge` slot instead — the slot takes precedence.\n */\n badge = input<string | undefined>(undefined);\n\n /**\n * Badge color variant.\n * @default 'default'\n */\n badgeVariant = input<'default' | 'primary' | 'success' | 'warning' | 'error'>('default');\n\n /**\n * Visual variant of the card\n * - elevated: Box shadow (default)\n * - outlined: Border only (the hairline `--color-border`, no shadow) — the\n * content card on the \"Weite\" sky with `borderRadius=\"xl\"`\n * - flat: No shadow or border\n * - raised: plain surface, hairline border and the long, soft\n * `--shadow-auth-card`, no accent line — the sign-in card\n * - dashed: the muted \"coming soon\" tile — `--color-muted-tile` fill, 1 px\n * dashed `--color-muted-tile-border`, secondary ink, icons in\n * `--color-muted-tile-icon`. Never clickable: `clickable` is ignored;\n * `href` and `route` are ignored too\n * @default 'elevated'\n */\n variant = input<CardVariant>('elevated');\n\n /**\n * Padding inside the card\n * @default 'md'\n */\n padding = input<'none' | 'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the card is clickable (hover effects, cursor pointer). Has no\n * effect on a link card (`href`/`route`).\n * @default false\n */\n clickable = input<boolean>(false);\n\n /**\n * Whether the card is selected (if clickable)\n * @default false\n */\n selected = input<boolean>(false);\n\n /**\n * Border radius size: `none`, `sm`, `md`, `lg` (18 px), `xl` (24 px, the\n * \"Weite\" content card) or `full`.\n * @default 'md'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'>('md');\n\n /**\n * Heading level of the built-in `title` (`h2`–`h6`), so the card fits the\n * page's outline. Semantics only, the look stays the same.\n * @default 3\n */\n headingLevel = input<CardHeadingLevel>(3);\n\n /**\n * Accessible name. On a link card (`href`/`route`) it names the link\n * instead of the card; it must contain the visible title (WCAG 2.5.3) and\n * the heading reads it too, so keep it short. Without it the link is named\n * by the title, and the card's content (badge, list) is read as normal\n * content. On other cards it labels the card.\n * @default undefined\n */\n ariaLabel = input<string | undefined>(undefined);\n\n /**\n * URL the card links to (3.24, quellen row 1). The card becomes a real\n * link: the built-in `title` is an `<a href>` inside its heading, and the\n * link covers the whole card (click, Enter, middle click, \"open in new\n * tab\", \"copy link\"). No router needed; works in prerendered HTML. Wins\n * over `route`. Ignored by `dashed`. On a link card `clickable` has no\n * effect and `cardClick` is not emitted.\n * @default undefined\n */\n href = input<string | undefined>(undefined);\n\n /**\n * In-app route the card links to through the Angular router (`RouterLink`\n * commands: `'/quellen/usm'` or `['/quellen', id]`), relative to the\n * consumer's route, rendered as a real `<a href>` like `href`. Needs the\n * app's router (`provideRouter`); without one, use `href`. `''` and `null`\n * mean no link.\n * @default undefined\n */\n route = input<string | readonly unknown[] | null | undefined>(undefined);\n\n /**\n * Query parameters of `route`, as `RouterLink`'s `queryParams`\n * (`{ ansicht: 'laeufe' }`). Ignored with `href` (write the query into the\n * URL).\n * @default undefined\n */\n queryParams = input<Params | null | undefined>(undefined);\n\n /**\n * Layout of the `[card-footer]` slot (3.29, CC-2). `equal`: its children\n * share the row at equal width once the card is at least 480 px (30rem)\n * wide, and stack full width, 8 px apart, below that (the card's own width,\n * at any viewport; long labels wrap inside the buttons, which get the\n * row's height). For two answers of equal weight, put\n * `<lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">` in it,\n * primary answer first; the card adds layout only (gap, divider and\n * spacing as `default`) and never restyles the buttons. The card becomes a\n * size container and takes its parent's full width. Keep the default\n * `padding` (with `none` the buttons' focus ring is clipped). Works in link\n * cards (the buttons stay clickable above the link) and in prerendered\n * HTML (CSS only).\n * @default 'default'\n */\n footerLayout = input<CardFooterLayout>('default');\n\n /**\n * The card can close (3.36, regeln row 1): the head (icon, title, subtitle)\n * becomes one toggle, a button inside the title's heading with\n * `aria-expanded` and `aria-controls`, whose hit area is the whole head (at\n * least 44 px high); Enter, Space or a click open and close the body and the\n * `[card-footer]`. A chevron at the end of the head shows the state.\n * `[card-header-action]` stays a separate control beside it. Needs the\n * built-in `title` (otherwise ignored); wins over `clickable`, `href` and\n * `route`, which are then ignored; ignored on `dashed`. Bind `[(expanded)]`.\n * @default false\n */\n collapsible = input<boolean>(false);\n\n /**\n * Open state of a `collapsible` card (3.36), two-way: `[(expanded)]`. Open\n * by default, so a plain `collapsible` hides nothing; set `false` for a card\n * that starts closed (also in prerendered HTML). Closed, only the head\n * shows: the body is `hidden` and the footer is not displayed.\n * @default true\n */\n readonly expanded = model<boolean>(true);\n\n /**\n * Emitted when a `clickable` card is clicked or activated with Enter or\n * Space. Not emitted on a link card (`href`/`route`): the link navigates by\n * itself (one navigation); bind the native `(click)` for analytics.\n */\n readonly cardClick = output<MouseEvent>();\n\n /** Generated id prefix (3.36): creation order, so prerendered HTML is the same in every build. */\n private readonly uid = `lc-card-${nextCardId++}`;\n\n /** Id of the body a collapsible card's toggle controls. */\n protected readonly bodyId = `${this.uid}-body`;\n\n /** Id of the subtitle that describes a collapsible card's toggle. */\n protected readonly subtitleId = `${this.uid}-subtitle`;\n\n /**\n * Whether the card collapses (3.36): `collapsible` with a built-in title,\n * except for the dashed tile.\n */\n protected readonly isCollapsible = computed(\n () => this.collapsible() && !!this.title() && this.variant() !== 'dashed',\n );\n\n /**\n * How the card links (3.24): `href` wins over `route`; `''`/`null` routes\n * are no link, and the dashed \"coming soon\" tile never links. A\n * collapsible card (3.36) never links.\n */\n protected readonly linkMode = computed<'href' | 'route' | null>(() => {\n if (this.variant() === 'dashed' || this.isCollapsible()) return null;\n if (this.href()) return 'href';\n return hasRoute(this.route()) ? 'route' : null;\n });\n\n /**\n * Whether the card acts as a button: `clickable`, except for the dashed\n * \"coming soon\" tile, which is never interactive, a link card, whose\n * anchor is the only interactive element, and a collapsible card (3.36),\n * whose toggle is.\n */\n protected isClickable = computed(\n () => this.clickable() && this.variant() !== 'dashed' && !this.linkMode() && !this.isCollapsible(),\n );\n\n /** Opens or closes a collapsible card; `expandedChange` emits the new state. */\n protected toggle(): void {\n this.expanded.set(!this.expanded());\n }\n\n /** `ariaLabel` on the card; on a link card it names the anchor instead. */\n protected readonly cardAriaLabel = computed(() => (this.linkMode() ? undefined : this.ariaLabel()));\n\n constructor() {\n if (isDevMode()) {\n effect(() => {\n if (!this.collapsible() || this.variant() === 'dashed') return;\n if (!this.title()) {\n console.warn('[lc-card] collapsible needs the built-in title: ignored');\n } else if (this.clickable() || this.href() || hasRoute(this.route())) {\n console.warn('[lc-card] collapsible wins over clickable, href and route: they are ignored');\n }\n });\n effect(() => {\n if (!this.linkMode()) return;\n if (this.href() && hasRoute(this.route())) {\n console.warn('[lc-card] href and route are both set: href wins');\n }\n if (!this.title() && !this.ariaLabel()) {\n console.warn('[lc-card] a link card without title needs an ariaLabel to name its link');\n }\n });\n }\n }\n\n /**\n * Compute card CSS classes based on inputs\n */\n protected cardClasses = computed(() => {\n const classes = [\n 'card',\n `card-${this.variant()}`,\n `card-padding-${this.padding()}`,\n `card-radius-${this.borderRadius()}`,\n ];\n\n if (this.isClickable()) {\n classes.push('card-clickable');\n }\n\n // A link card looks and reacts like a clickable one (3.24).\n if (this.linkMode()) {\n classes.push('card-linked');\n }\n\n // The dashed tile is never interactive, so it is never selected either.\n if (this.selected() && this.variant() !== 'dashed') {\n classes.push('card-selected');\n }\n\n // A collapsible card (3.36), and its closed state.\n if (this.isCollapsible()) {\n classes.push('card-collapsible');\n if (!this.expanded()) classes.push('card-collapsed');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Compute ARIA role based on clickable state\n */\n protected role = computed(() => (this.isClickable() ? 'button' : 'article'));\n\n /**\n * Handle card click events\n */\n protected handleClick(event: MouseEvent): void {\n if (this.isClickable()) {\n this.cardClick.emit(event);\n }\n }\n\n /**\n * Handle keyboard events for accessibility\n */\n @HostListener('keydown', ['$event'])\n protected handleKeydown(event: KeyboardEvent): void {\n if (this.isClickable() && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n const mouseEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n });\n this.cardClick.emit(mouseEvent);\n }\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"cardAriaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (linkMode() && !title()) {\n <!-- Link card without a title (3.24): an empty anchor, named by ariaLabel, covers the card. -->\n <ng-container [ngTemplateOutlet]=\"cardLink\" [ngTemplateOutletContext]=\"{ bare: true }\" />\n }\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @if (isCollapsible()) {\n <!-- Collapsible card (3.36): the heading stays a heading and holds the toggle. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardToggle\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardToggle\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardToggle\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardToggle\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardToggle\" /></h3>\n }\n }\n } @else if (linkMode()) {\n <!-- Link card (3.24): the heading stays a heading and holds the link. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h3>\n }\n }\n } @else {\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\" [attr.id]=\"isCollapsible() ? subtitleId : null\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n @if (isCollapsible()) {\n <lc-icon name=\"chevron-down\" class=\"card__chevron\" size=\"sm\" [decorative]=\"true\" />\n }\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div\n class=\"card__body\"\n [attr.id]=\"isCollapsible() ? bodyId : null\"\n [attr.hidden]=\"isCollapsible() && !expanded() ? '' : null\"\n >\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n\n<!-- The toggle of a collapsible card (3.36): a button inside the heading,\n named by the title and described by the subtitle. Its ::after covers the\n head, so the icon tile, subtitle and chevron toggle too. -->\n<ng-template #cardToggle>\n <button\n type=\"button\"\n class=\"card__toggle\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-controls]=\"bodyId\"\n [attr.aria-describedby]=\"subtitle() ? subtitleId : null\"\n (click)=\"toggle()\"\n >{{ title() }}</button>\n</ng-template>\n\n<!-- The link of a link card (3.24): `href` (plain, no router) or `route`\n (RouterLink, created only here, so other cards never need a router).\n Its ::after covers the card. -->\n<ng-template #cardLink let-bare=\"bare\">\n @if (linkMode() === 'href') {\n <a class=\"card__link\" [class.card__link--bare]=\"bare\" [href]=\"href()\" [attr.aria-label]=\"ariaLabel()\">{{ bare ? '' : title() }}</a>\n } @else {\n <a\n class=\"card__link\"\n [class.card__link--bare]=\"bare\"\n [routerLink]=\"route()\"\n [queryParams]=\"queryParams()\"\n [attr.aria-label]=\"ariaLabel()\"\n >{{ bare ? '' : title() }}</a\n >\n }\n</ng-template>\n"],"mappings":"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"}
|
|
@@ -28,6 +28,7 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
28
28
|
label = input(...ngDevMode ? [void 0, { debugName: "label" }] : /* istanbul ignore next */ []);
|
|
29
29
|
helperText = input(...ngDevMode ? [void 0, { debugName: "helperText" }] : /* istanbul ignore next */ []);
|
|
30
30
|
error = input(...ngDevMode ? [void 0, { debugName: "error" }] : /* istanbul ignore next */ []);
|
|
31
|
+
announceError = input(true, ...ngDevMode ? [{ debugName: "announceError" }] : /* istanbul ignore next */ []);
|
|
31
32
|
disabled = input(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
|
|
32
33
|
multiple = input(false, ...ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []);
|
|
33
34
|
allowCreate = input(false, ...ngDevMode ? [{ debugName: "allowCreate" }] : /* istanbul ignore next */ []);
|
|
@@ -418,6 +419,13 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
418
419
|
isRequired: false,
|
|
419
420
|
transformFunction: null
|
|
420
421
|
},
|
|
422
|
+
announceError: {
|
|
423
|
+
classPropertyName: "announceError",
|
|
424
|
+
publicName: "announceError",
|
|
425
|
+
isSignal: true,
|
|
426
|
+
isRequired: false,
|
|
427
|
+
transformFunction: null
|
|
428
|
+
},
|
|
421
429
|
disabled: {
|
|
422
430
|
classPropertyName: "disabled",
|
|
423
431
|
publicName: "disabled",
|
|
@@ -526,7 +534,7 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
526
534
|
}
|
|
527
535
|
],
|
|
528
536
|
ngImport: i0,
|
|
529
|
-
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n [style.--_lc-combobox-field-width.px]=\"fieldWidth()\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n [attr.lang]=\"panelLang()\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n",
|
|
537
|
+
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n [style.--_lc-combobox-field-width.px]=\"fieldWidth()\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n [attr.lang]=\"panelLang()\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n @if (announceError()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n } @else {\n <!-- 3.36 (announceError=false): read with the field through aria-describedby, not announced. -->\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\">{{ error() }}</span>\n }\n }\n</div>\n",
|
|
530
538
|
styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);outline:2px solid var(--color-primary-500);outline-offset:0;box-shadow:none}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.75rem;margin-block:-.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;border:none;background:transparent;background-clip:content-box;cursor:pointer;color:var(--color-text-tertiary);border-radius:calc(var(--border-radius-sm) + .125rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-combobox--lg .lc-combobox__clear{margin-inline-start:.625rem}.lc-combobox--lg .lc-combobox__clear:before{content:\"\";position:absolute;inset:-.625rem -.375rem -.625rem -.875rem}.lc-combobox__dropdown{flex:1 1 auto;min-width:max(80px,var(--_lc-combobox-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}@media(forced-colors:active){.lc-combobox__input-wrapper:focus-within,.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline-color:Highlight}.lc-combobox__clear:focus-visible{outline-color:Highlight}}\n"],
|
|
531
539
|
dependencies: [
|
|
532
540
|
{
|
|
@@ -641,7 +649,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
641
649
|
useExisting: forwardRef(() => ComboboxComponent),
|
|
642
650
|
multi: true
|
|
643
651
|
}],
|
|
644
|
-
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n [style.--_lc-combobox-field-width.px]=\"fieldWidth()\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n [attr.lang]=\"panelLang()\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n",
|
|
652
|
+
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n [style.--_lc-combobox-field-width.px]=\"fieldWidth()\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n [attr.lang]=\"panelLang()\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n @if (announceError()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n } @else {\n <!-- 3.36 (announceError=false): read with the field through aria-describedby, not announced. -->\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\">{{ error() }}</span>\n }\n }\n</div>\n",
|
|
645
653
|
styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);outline:2px solid var(--color-primary-500);outline-offset:0;box-shadow:none}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.75rem;margin-block:-.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;border:none;background:transparent;background-clip:content-box;cursor:pointer;color:var(--color-text-tertiary);border-radius:calc(var(--border-radius-sm) + .125rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-combobox--lg .lc-combobox__clear{margin-inline-start:.625rem}.lc-combobox--lg .lc-combobox__clear:before{content:\"\";position:absolute;inset:-.625rem -.375rem -.625rem -.875rem}.lc-combobox__dropdown{flex:1 1 auto;min-width:max(80px,var(--_lc-combobox-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}@media(forced-colors:active){.lc-combobox__input-wrapper:focus-within,.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline-color:Highlight}.lc-combobox__clear:focus-visible{outline-color:Highlight}}\n"]
|
|
646
654
|
}]
|
|
647
655
|
}],
|
|
@@ -723,6 +731,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
723
731
|
required: false
|
|
724
732
|
}]
|
|
725
733
|
}],
|
|
734
|
+
announceError: [{
|
|
735
|
+
type: i0.Input,
|
|
736
|
+
args: [{
|
|
737
|
+
isSignal: true,
|
|
738
|
+
alias: "announceError",
|
|
739
|
+
required: false
|
|
740
|
+
}]
|
|
741
|
+
}],
|
|
726
742
|
disabled: [{
|
|
727
743
|
type: i0.Input,
|
|
728
744
|
args: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-combobox.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.ts","../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n afterRenderEffect,\n effect,\n forwardRef,\n ElementRef,\n viewChild,\n HostListener,\n inject,\n isDevMode,\n model,\n untracked,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { OverlayModule, type ConnectedPosition } from '@angular/cdk/overlay';\nimport { Observable, Subject, catchError, debounce, defer, of, switchMap, timer } from 'rxjs';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS, ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { ChipComponent } from '@lifecorner/angular-ui-kit/chip';\nimport { openPanelLang } from '@lifecorner/angular-ui-kit/internal/a11y';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n description?: string;\n icon?: string;\n group?: string;\n disabled?: boolean;\n}\n\nexport type ComboboxValue = ComboboxOption | ComboboxOption[] | null;\n\nexport type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg';\n\n/**\n * Combobox / async autocomplete component.\n *\n * Supports free-text input with sync/async option suggestions,\n * single/multiple selection, create new entries, and keyboard navigation.\n * The suggestion list is rendered in a CDK overlay so it is never clipped\n * by `overflow: hidden` ancestors (cards, modals, table cells). The dropdown\n * takes the `lang` of the host's closest `[lang]` (3.30.2).\n *\n * The selection is two-way bindable by an option's `value` string\n * (`[(selectedValue)]`, single mode), next to the `valueChange` output with\n * the option object and the forms binding.\n *\n * `focus()` (3.35) moves the focus into the field's `<input>` when a form\n * opens or after a rejected save (WCAG 2.4.3); it opens the list as a user's\n * focus does (`openOnFocus`).\n *\n * @example\n * ```html\n * <lc-combobox\n * label=\"Assign to user\"\n * [loadOptions]=\"searchUsers\"\n * [(selectedValue)]=\"assigneeId\"\n * allowCreate\n * />\n * ```\n */\n@Component({\n selector: 'lc-combobox',\n standalone: true,\n imports: [OverlayModule, IconComponent, SpinnerComponent, ChipComponent],\n viewProviders: [ɵprovideKitIcons([ICON_X_MARK, ICON_PLUS])],\n templateUrl: './combobox.component.html',\n styleUrls: ['./combobox.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ComboboxComponent),\n multi: true,\n },\n ],\n})\nexport class ComboboxComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance ids so label/listbox/options can be linked via ARIA */\n readonly inputId = `lc-combobox-${++ComboboxComponent.nextId}`;\n readonly listboxId = `${this.inputId}-listbox`;\n protected readonly createOptionId = `${this.listboxId}-create`;\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n private readonly dropdownEl = viewChild<ElementRef<HTMLElement>>('dropdownEl');\n private readonly inputWrapper = viewChild<ElementRef<HTMLElement>>('inputWrapper');\n\n private readonly elRef = inject(ElementRef);\n private readonly querySubject = new Subject<string>();\n\n /** Sync options */\n readonly options = input<ComboboxOption[]>([]);\n\n /** Async option loader */\n readonly loadOptions = input<((query: string) => Observable<ComboboxOption[]>)>();\n\n /** Debounce for async queries */\n readonly debounceMs = input<number>(250);\n\n /** Minimum characters before triggering search */\n readonly minChars = input<number>(1);\n\n /** Placeholder text */\n readonly placeholder = input<string>('Search…');\n\n /** Label */\n readonly label = input<string>();\n\n /** Helper text */\n readonly helperText = input<string>();\n\n /** Error message */\n readonly error = input<string>();\n\n /** Disabled state */\n readonly disabled = input<boolean>(false);\n\n /** Allow multiple selections */\n readonly multiple = input<boolean>(false);\n\n /** Allow creating new entries */\n readonly allowCreate = input<boolean>(false);\n\n /** Show loading spinner (controlled) */\n readonly loading = input<boolean>(false);\n\n /** Empty state message */\n readonly emptyMessage = input<string>('No results');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear');\n\n /** Label before the query in the \"create\" option (`allowCreate`): `Create \"query\"`. */\n readonly createOptionLabel = input('Create');\n\n /** Size */\n readonly size = input<ComboboxSize>('md');\n\n /**\n * Whether the list opens when the field gets focus (3.32, request 051).\n * With `false`, focus alone (`focus()` from the page, Tab) leaves the list\n * closed and `aria-expanded=\"false\"`; a click or tap on the field, typing or\n * ArrowDown opens it. Use it in forms that move focus into the field\n * themselves, on open or to the first invalid field after a rejected save.\n * Escape and choosing an option work as before.\n * @default true\n */\n readonly openOnFocus = input<boolean>(true);\n\n /**\n * The `value` of the selected option, two-way (3.29, regeln row 2):\n * `[(selectedValue)]=\"month\"`. Shows the matching option's label (from\n * `options`, or the current selection); an unknown value shows an empty\n * field and is filled in when `options` with it arrive. Page writes\n * (including back to `null`) select or clear silently: no\n * `selectedValueChange`, no `valueChange`, no forms `onChange`. Every user\n * choice or clear emits `selectedValueChange`, next to the unchanged\n * `valueChange` (the option object). Single mode only: ignored with\n * `multiple` (a dev warning). Use it or a forms binding, not both.\n * @default null\n */\n readonly selectedValue = model<string | null>(null);\n\n /** Value change event */\n readonly valueChange = output<ComboboxValue>();\n\n /** Query change event */\n readonly queryChange = output<string>();\n\n /** Option selected event */\n readonly optionSelected = output<ComboboxOption>();\n\n /** Created event (allowCreate) */\n readonly created = output<string>();\n\n /** Internal state */\n protected query = signal('');\n protected isOpen = signal(false);\n protected highlightedIndex = signal(-1);\n protected asyncOptions = signal<ComboboxOption[]>([]);\n protected isLoading = signal(false);\n\n /** Disabled via `setDisabledState` (reactive forms) */\n private readonly formDisabled = signal(false);\n\n /** Effective disabled state: `disabled` input OR form control disabled */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /**\n * `lang` of the open dropdown (3.30.2, WCAG 3.1.2): the overlay renders under\n * `<body>`, so it takes the host's closest `[lang]`, else none. Read at every\n * opening and, since 3.33, again when a `lang` changes while the list is\n * open; `null` while closed, so the server never reads the DOM.\n */\n protected readonly panelLang = openPanelLang(this.isOpen);\n\n /**\n * The field's width in px while the list is open (3.33, request 052,\n * 049-n2), read at every opening: the list's floor (`min-width`), so it is\n * at least as wide as the field and wider content still grows it. `null`\n * while closed.\n */\n protected readonly fieldWidth = computed(() =>\n this.isOpen() ? (this.inputWrapper()?.nativeElement.getBoundingClientRect().width ?? null) : null,\n );\n\n /**\n * Below the field, else above it, start- then end-aligned (the CDK's\n * defaults), 4 px from the field on either side (3.33, 047-n1: the gap was\n * a margin inside the pane, so it was 0 above). The list is never pushed,\n * so the offset can't move it off the field.\n */\n protected readonly positions: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },\n { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },\n { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },\n ];\n\n /** Selected values */\n protected selectedSingle = signal<ComboboxOption | null>(null);\n protected selectedMultiple = signal<ComboboxOption[]>([]);\n\n /** The last `selectedValue` the component applied or emitted: a different one is a page write. */\n private seenSelectedValue: string | null = null;\n\n /** A page-written `selectedValue` no option matched yet: selected silently once `options` hold it. */\n private pendingSelectedValue: string | null = null;\n\n /** Resolved visible options */\n protected visibleOptions = computed(() => {\n const q = this.query().toLowerCase();\n const loader = this.loadOptions();\n\n let opts: ComboboxOption[];\n if (loader) {\n opts = this.asyncOptions();\n } else {\n opts = this.options();\n if (q.length >= this.minChars()) {\n opts = opts.filter(\n (o) =>\n o.label.toLowerCase().includes(q) ||\n o.value.toLowerCase().includes(q) ||\n (o.description && o.description.toLowerCase().includes(q))\n );\n }\n }\n\n // Remove already selected in multiple mode\n if (this.multiple()) {\n const selected = new Set(this.selectedMultiple().map((s) => s.value));\n opts = opts.filter((o) => !selected.has(o.value));\n }\n\n return opts;\n });\n\n /** Grouped options */\n protected groupedOptions = computed(() => {\n const opts = this.visibleOptions();\n const groups = new Map<string, ComboboxOption[]>();\n\n for (const opt of opts) {\n const group = opt.group || '';\n if (!groups.has(group)) groups.set(group, []);\n groups.get(group)!.push(opt);\n }\n\n return Array.from(groups.entries()).map(([label, items]) => ({\n label,\n items,\n }));\n });\n\n /** Flat list for keyboard nav */\n protected flatOptions = computed(() => {\n return this.groupedOptions().flatMap((g) => g.items);\n });\n\n /** Whether to show \"Create\" option */\n protected showCreateOption = computed(() => {\n if (!this.allowCreate()) return false;\n const q = this.query().trim();\n if (!q) return false;\n const exact = this.visibleOptions().some(\n (o) => o.label.toLowerCase() === q.toLowerCase()\n );\n return !exact;\n });\n\n /** id of the highlighted option — announced via aria-activedescendant */\n protected readonly activeDescendantId = computed(() => {\n if (!this.isOpen()) return null;\n const i = this.highlightedIndex();\n if (i < 0) return null;\n if (i === this.flatOptions().length) {\n return this.showCreateOption() ? this.createOptionId : null;\n }\n return i < this.flatOptions().length ? this.optionId(i) : null;\n });\n\n /** ids referenced by aria-describedby (helper / error) */\n protected readonly describedBy = computed(() => {\n if (this.error()) return `${this.inputId}-error`;\n if (this.helperText()) return `${this.inputId}-helper`;\n return null;\n });\n\n /** Container classes */\n protected containerClasses = computed(() => {\n const classes = [`lc-combobox`, `lc-combobox--${this.size()}`];\n if (this.isDisabled()) classes.push('lc-combobox--disabled');\n if (this.error()) classes.push('lc-combobox--error');\n if (this.isOpen()) classes.push('lc-combobox--open');\n return classes.join(' ');\n });\n\n protected optionId(index: number): string {\n return `${this.listboxId}-opt-${index}`;\n }\n\n // ControlValueAccessor\n private onChange: (value: ComboboxValue) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n constructor() {\n // Async loading pipe. `debounce` (not `debounceTime`) reads the signal per\n // emission, so `debounceMs` is honoured after inputs are populated.\n this.querySubject\n .pipe(\n debounce(() => timer(this.debounceMs())),\n switchMap((q) => {\n const loader = this.loadOptions();\n if (!loader || q.length < this.minChars()) {\n return of([] as ComboboxOption[]);\n }\n this.isLoading.set(true);\n // `defer` turns a synchronously throwing loader into an error\n // notification; `catchError` keeps the pipe alive for the next query.\n return defer(() => loader(q)).pipe(catchError(() => of([] as ComboboxOption[])));\n }),\n takeUntilDestroyed()\n )\n .subscribe((results) => {\n this.asyncOptions.set(results);\n this.isLoading.set(false);\n });\n\n // Keep the highlighted option visible while navigating with the keyboard\n // (after render, so the option element exists in the overlay pane).\n afterRenderEffect(() => {\n const id = this.activeDescendantId();\n if (!id) return;\n untracked(() => {\n // ids are generated from a counter, so no CSS escaping is needed\n this.dropdownEl()\n ?.nativeElement.querySelector<HTMLElement>(`#${id}`)\n ?.scrollIntoView?.({ block: 'nearest' });\n });\n });\n\n // `selectedValue` (3.29): a page write selects or clears silently; an\n // unknown value waits for `options` that hold it. The component's own\n // writes (user choice, clear) are already applied, so they are skipped.\n effect(() => {\n const value = this.selectedValue();\n const options = this.options();\n untracked(() => {\n if (value !== this.seenSelectedValue) {\n this.seenSelectedValue = value;\n if (!this.multiple()) {\n this.applySelectedValue(value, options);\n } else if (value !== null && isDevMode()) {\n console.warn('[lc-combobox] selectedValue is ignored with multiple');\n }\n } else if (this.pendingSelectedValue !== null && !this.multiple()) {\n this.applySelectedValue(this.pendingSelectedValue, options);\n }\n });\n });\n }\n\n /** Shows the option with `value` (or clears for `null`) without emitting anything. */\n private applySelectedValue(value: string | null, options: ComboboxOption[]): void {\n this.pendingSelectedValue = null;\n if (value === null) {\n this.selectedSingle.set(null);\n this.query.set('');\n return;\n }\n if (this.selectedSingle()?.value === value) return;\n const match = options.find((o) => o.value === value) ?? null;\n if (!match) this.pendingSelectedValue = value;\n this.selectedSingle.set(match);\n this.query.set(match ? match.label : '');\n }\n\n /** Emits a user's single choice or clear through `selectedValueChange` (not in `multiple` mode). */\n private emitSelectedValue(value: string | null): void {\n if (this.multiple()) return;\n this.pendingSelectedValue = null;\n this.seenSelectedValue = value;\n this.selectedValue.set(value);\n }\n\n writeValue(value: ComboboxValue): void {\n if (this.multiple()) {\n this.selectedMultiple.set(Array.isArray(value) ? value : []);\n } else {\n const single = value && !Array.isArray(value) ? value : null;\n this.pendingSelectedValue = null;\n this.selectedSingle.set(single);\n this.query.set(single ? single.label : '');\n }\n }\n\n registerOnChange(fn: (value: ComboboxValue) => 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 if (isDisabled) this.isOpen.set(false);\n }\n\n /**\n * Moves the focus into the field's `<input>` (3.35), e.g. when a form opens\n * with this field first, or to the field with the error after a rejected\n * save (422) (WCAG 2.4.3). `options` as for `HTMLElement.focus()`\n * (`{ preventScroll: true }`). Shows the same focus ring as a user's focus.\n * Like a user's focus it opens the list when `openOnFocus` is `true` and\n * there is something to show; with `false` the list stays closed\n * (`aria-expanded=\"false\"`). A disabled field takes no focus (it stays where\n * it is). Call it once the field has rendered, e.g.\n * `afterNextRender(() => this.month().focus(), { injector })` with\n * `month = viewChild.required(ComboboxComponent)` and the page component's\n * `injector = inject(Injector)`. In an `lc-modal` that opens with the field,\n * that call runs after the dialog moved the focus to its first control.\n * Does nothing before the component has rendered.\n * @param options Passed to the native `focus()`.\n */\n focus(options?: FocusOptions): void {\n this.inputEl()?.nativeElement.focus(options);\n }\n\n /**\n * Clear the current selection and query. Emits `null` (single) or `[]`\n * (multiple) exactly once, like a user-initiated deselect.\n */\n clear(): void {\n this.query.set('');\n this.highlightedIndex.set(-1);\n this.isOpen.set(false);\n const value: ComboboxValue = this.multiple() ? [] : null;\n if (this.multiple()) {\n this.selectedMultiple.set([]);\n } else {\n this.selectedSingle.set(null);\n }\n this.onChange(value);\n this.valueChange.emit(value);\n this.emitSelectedValue(null);\n }\n\n protected onClearClick(): void {\n if (this.isDisabled()) return;\n this.clear();\n this.inputEl()?.nativeElement.focus();\n }\n\n /** Whether a pointer event on the frame hit the input or a button in it (chip ×, clear). */\n private hitsFrameControl(event: MouseEvent): boolean {\n const target = event.target as Element | null;\n if (!target || target === this.inputEl()?.nativeElement) return true;\n const button = target.closest('button');\n return !!button && (event.currentTarget as Element).contains(button);\n }\n\n /**\n * 3.30 (request 047, regeln row 6): a press on the frame beside the input\n * and its buttons keeps focus where it is (no blur, so the list stays open).\n */\n protected onFrameMousedown(event: MouseEvent): void {\n if (!this.hitsFrameControl(event)) event.preventDefault();\n }\n\n /**\n * A click on the frame beside the input and its buttons focuses the input.\n * With `openOnFocus` off (3.32, request 051), a click or tap on the input or\n * the frame also opens the list, which focus alone no longer does.\n */\n protected onFrameClick(event: MouseEvent): void {\n if (this.isDisabled()) return;\n const onInput = event.target === this.inputEl()?.nativeElement;\n if (!onInput) {\n if (this.hitsFrameControl(event)) return;\n this.inputEl()?.nativeElement.focus();\n }\n if (!this.openOnFocus()) this.openIfAny();\n }\n\n protected onInputChange(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.query.set(value);\n this.queryChange.emit(value);\n this.highlightedIndex.set(-1);\n\n if (this.loadOptions()) {\n this.querySubject.next(value);\n }\n\n if (value.length >= this.minChars()) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputFocus(): void {\n if (this.openOnFocus()) this.openIfAny();\n }\n\n /** Opens the list when there is something to show: a long enough query, or options. */\n private openIfAny(): void {\n if (this.query().length >= this.minChars() || this.options().length > 0) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputBlur(): void {\n this.onTouched();\n // Delay to allow click on dropdown\n setTimeout(() => this.isOpen.set(false), 200);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const flat = this.flatOptions();\n const total = flat.length + (this.showCreateOption() ? 1 : 0);\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.isOpen.set(true);\n if (total > 0) this.highlightedIndex.update((i) => (i + 1) % total);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (total > 0) this.highlightedIndex.update((i) => (i - 1 + total) % total);\n break;\n case 'Enter':\n event.preventDefault();\n if (this.highlightedIndex() >= 0) {\n if (this.highlightedIndex() === flat.length && this.showCreateOption()) {\n this.onCreateNew();\n } else {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n } else if (this.showCreateOption()) {\n this.onCreateNew();\n }\n break;\n case 'Escape':\n if (this.isOpen()) {\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n // Escape consumed by the list — an enclosing modal must not close too.\n event.stopPropagation();\n }\n break;\n case 'Tab':\n if (this.highlightedIndex() >= 0 && this.isOpen()) {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n this.isOpen.set(false);\n break;\n case 'Backspace':\n if (this.multiple() && !this.query() && this.selectedMultiple().length > 0) {\n this.removeSelected(this.selectedMultiple()[this.selectedMultiple().length - 1]);\n }\n break;\n }\n }\n\n protected selectOption(option: ComboboxOption): void {\n if (this.multiple()) {\n this.selectedMultiple.update((sel) => [...sel, option]);\n this.query.set('');\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n } else {\n this.selectedSingle.set(option);\n this.query.set(option.label);\n this.onChange(option);\n this.valueChange.emit(option);\n this.emitSelectedValue(option.value);\n }\n this.optionSelected.emit(option);\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n }\n\n protected removeSelected(option: ComboboxOption): void {\n if (this.isDisabled()) return;\n this.selectedMultiple.update((sel) =>\n sel.filter((s) => s.value !== option.value)\n );\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n this.inputEl()?.nativeElement.focus();\n }\n\n protected onCreateNew(): void {\n const q = this.query().trim();\n if (!q) return;\n const newOption: ComboboxOption = { value: q, label: q };\n this.created.emit(q);\n this.selectOption(newOption);\n }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: MouseEvent): void {\n const target = event.target as Node | null;\n // The dropdown lives in the CDK overlay container, outside the host element.\n if (\n !this.elRef.nativeElement.contains(target) &&\n !this.dropdownEl()?.nativeElement.contains(target)\n ) {\n this.isOpen.set(false);\n }\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n [style.--_lc-combobox-field-width.px]=\"fieldWidth()\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n [attr.lang]=\"panelLang()\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-combobox.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.ts","../../../../libs/angular-ui-kit/src/lib/combobox/combobox.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n afterRenderEffect,\n effect,\n forwardRef,\n ElementRef,\n viewChild,\n HostListener,\n inject,\n isDevMode,\n model,\n untracked,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { OverlayModule, type ConnectedPosition } from '@angular/cdk/overlay';\nimport { Observable, Subject, catchError, debounce, defer, of, switchMap, timer } from 'rxjs';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_PLUS, ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { ChipComponent } from '@lifecorner/angular-ui-kit/chip';\nimport { openPanelLang } from '@lifecorner/angular-ui-kit/internal/a11y';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n description?: string;\n icon?: string;\n group?: string;\n disabled?: boolean;\n}\n\nexport type ComboboxValue = ComboboxOption | ComboboxOption[] | null;\n\nexport type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg';\n\n/**\n * Combobox / async autocomplete component.\n *\n * Supports free-text input with sync/async option suggestions,\n * single/multiple selection, create new entries, and keyboard navigation.\n * The suggestion list is rendered in a CDK overlay so it is never clipped\n * by `overflow: hidden` ancestors (cards, modals, table cells). The dropdown\n * takes the `lang` of the host's closest `[lang]` (3.30.2).\n *\n * The selection is two-way bindable by an option's `value` string\n * (`[(selectedValue)]`, single mode), next to the `valueChange` output with\n * the option object and the forms binding.\n *\n * `focus()` (3.35) moves the focus into the field's `<input>` when a form\n * opens or after a rejected save (WCAG 2.4.3); it opens the list as a user's\n * focus does (`openOnFocus`).\n *\n * @example\n * ```html\n * <lc-combobox\n * label=\"Assign to user\"\n * [loadOptions]=\"searchUsers\"\n * [(selectedValue)]=\"assigneeId\"\n * allowCreate\n * />\n * ```\n */\n@Component({\n selector: 'lc-combobox',\n standalone: true,\n imports: [OverlayModule, IconComponent, SpinnerComponent, ChipComponent],\n viewProviders: [ɵprovideKitIcons([ICON_X_MARK, ICON_PLUS])],\n templateUrl: './combobox.component.html',\n styleUrls: ['./combobox.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ComboboxComponent),\n multi: true,\n },\n ],\n})\nexport class ComboboxComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance ids so label/listbox/options can be linked via ARIA */\n readonly inputId = `lc-combobox-${++ComboboxComponent.nextId}`;\n readonly listboxId = `${this.inputId}-listbox`;\n protected readonly createOptionId = `${this.listboxId}-create`;\n\n private readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputEl');\n private readonly dropdownEl = viewChild<ElementRef<HTMLElement>>('dropdownEl');\n private readonly inputWrapper = viewChild<ElementRef<HTMLElement>>('inputWrapper');\n\n private readonly elRef = inject(ElementRef);\n private readonly querySubject = new Subject<string>();\n\n /** Sync options */\n readonly options = input<ComboboxOption[]>([]);\n\n /** Async option loader */\n readonly loadOptions = input<((query: string) => Observable<ComboboxOption[]>)>();\n\n /** Debounce for async queries */\n readonly debounceMs = input<number>(250);\n\n /** Minimum characters before triggering search */\n readonly minChars = input<number>(1);\n\n /** Placeholder text */\n readonly placeholder = input<string>('Search…');\n\n /** Label */\n readonly label = input<string>();\n\n /** Helper text */\n readonly helperText = input<string>();\n\n /** Error message */\n readonly error = input<string>();\n\n /**\n * Announce the error message the moment it appears (`role=\"alert\"` on the\n * message), as `lc-textarea`. Set `false` when an `lc-alert` error summary\n * already announces the errors, e.g. a page that moves focus to this field\n * after a failed submit (422): the error is then read once, with the field,\n * not twice (alert and description). The message stays visible, stays linked\n * through `aria-describedby`, and the input keeps `aria-invalid`.\n * @default true\n */\n readonly announceError = input<boolean>(true);\n\n /** Disabled state */\n readonly disabled = input<boolean>(false);\n\n /** Allow multiple selections */\n readonly multiple = input<boolean>(false);\n\n /** Allow creating new entries */\n readonly allowCreate = input<boolean>(false);\n\n /** Show loading spinner (controlled) */\n readonly loading = input<boolean>(false);\n\n /** Empty state message */\n readonly emptyMessage = input<string>('No results');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear');\n\n /** Label before the query in the \"create\" option (`allowCreate`): `Create \"query\"`. */\n readonly createOptionLabel = input('Create');\n\n /** Size */\n readonly size = input<ComboboxSize>('md');\n\n /**\n * Whether the list opens when the field gets focus (3.32, request 051).\n * With `false`, focus alone (`focus()` from the page, Tab) leaves the list\n * closed and `aria-expanded=\"false\"`; a click or tap on the field, typing or\n * ArrowDown opens it. Use it in forms that move focus into the field\n * themselves, on open or to the first invalid field after a rejected save.\n * Escape and choosing an option work as before.\n * @default true\n */\n readonly openOnFocus = input<boolean>(true);\n\n /**\n * The `value` of the selected option, two-way (3.29, regeln row 2):\n * `[(selectedValue)]=\"month\"`. Shows the matching option's label (from\n * `options`, or the current selection); an unknown value shows an empty\n * field and is filled in when `options` with it arrive. Page writes\n * (including back to `null`) select or clear silently: no\n * `selectedValueChange`, no `valueChange`, no forms `onChange`. Every user\n * choice or clear emits `selectedValueChange`, next to the unchanged\n * `valueChange` (the option object). Single mode only: ignored with\n * `multiple` (a dev warning). Use it or a forms binding, not both.\n * @default null\n */\n readonly selectedValue = model<string | null>(null);\n\n /** Value change event */\n readonly valueChange = output<ComboboxValue>();\n\n /** Query change event */\n readonly queryChange = output<string>();\n\n /** Option selected event */\n readonly optionSelected = output<ComboboxOption>();\n\n /** Created event (allowCreate) */\n readonly created = output<string>();\n\n /** Internal state */\n protected query = signal('');\n protected isOpen = signal(false);\n protected highlightedIndex = signal(-1);\n protected asyncOptions = signal<ComboboxOption[]>([]);\n protected isLoading = signal(false);\n\n /** Disabled via `setDisabledState` (reactive forms) */\n private readonly formDisabled = signal(false);\n\n /** Effective disabled state: `disabled` input OR form control disabled */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n /**\n * `lang` of the open dropdown (3.30.2, WCAG 3.1.2): the overlay renders under\n * `<body>`, so it takes the host's closest `[lang]`, else none. Read at every\n * opening and, since 3.33, again when a `lang` changes while the list is\n * open; `null` while closed, so the server never reads the DOM.\n */\n protected readonly panelLang = openPanelLang(this.isOpen);\n\n /**\n * The field's width in px while the list is open (3.33, request 052,\n * 049-n2), read at every opening: the list's floor (`min-width`), so it is\n * at least as wide as the field and wider content still grows it. `null`\n * while closed.\n */\n protected readonly fieldWidth = computed(() =>\n this.isOpen() ? (this.inputWrapper()?.nativeElement.getBoundingClientRect().width ?? null) : null,\n );\n\n /**\n * Below the field, else above it, start- then end-aligned (the CDK's\n * defaults), 4 px from the field on either side (3.33, 047-n1: the gap was\n * a margin inside the pane, so it was 0 above). The list is never pushed,\n * so the offset can't move it off the field.\n */\n protected readonly positions: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },\n { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },\n { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },\n ];\n\n /** Selected values */\n protected selectedSingle = signal<ComboboxOption | null>(null);\n protected selectedMultiple = signal<ComboboxOption[]>([]);\n\n /** The last `selectedValue` the component applied or emitted: a different one is a page write. */\n private seenSelectedValue: string | null = null;\n\n /** A page-written `selectedValue` no option matched yet: selected silently once `options` hold it. */\n private pendingSelectedValue: string | null = null;\n\n /** Resolved visible options */\n protected visibleOptions = computed(() => {\n const q = this.query().toLowerCase();\n const loader = this.loadOptions();\n\n let opts: ComboboxOption[];\n if (loader) {\n opts = this.asyncOptions();\n } else {\n opts = this.options();\n if (q.length >= this.minChars()) {\n opts = opts.filter(\n (o) =>\n o.label.toLowerCase().includes(q) ||\n o.value.toLowerCase().includes(q) ||\n (o.description && o.description.toLowerCase().includes(q))\n );\n }\n }\n\n // Remove already selected in multiple mode\n if (this.multiple()) {\n const selected = new Set(this.selectedMultiple().map((s) => s.value));\n opts = opts.filter((o) => !selected.has(o.value));\n }\n\n return opts;\n });\n\n /** Grouped options */\n protected groupedOptions = computed(() => {\n const opts = this.visibleOptions();\n const groups = new Map<string, ComboboxOption[]>();\n\n for (const opt of opts) {\n const group = opt.group || '';\n if (!groups.has(group)) groups.set(group, []);\n groups.get(group)!.push(opt);\n }\n\n return Array.from(groups.entries()).map(([label, items]) => ({\n label,\n items,\n }));\n });\n\n /** Flat list for keyboard nav */\n protected flatOptions = computed(() => {\n return this.groupedOptions().flatMap((g) => g.items);\n });\n\n /** Whether to show \"Create\" option */\n protected showCreateOption = computed(() => {\n if (!this.allowCreate()) return false;\n const q = this.query().trim();\n if (!q) return false;\n const exact = this.visibleOptions().some(\n (o) => o.label.toLowerCase() === q.toLowerCase()\n );\n return !exact;\n });\n\n /** id of the highlighted option — announced via aria-activedescendant */\n protected readonly activeDescendantId = computed(() => {\n if (!this.isOpen()) return null;\n const i = this.highlightedIndex();\n if (i < 0) return null;\n if (i === this.flatOptions().length) {\n return this.showCreateOption() ? this.createOptionId : null;\n }\n return i < this.flatOptions().length ? this.optionId(i) : null;\n });\n\n /** ids referenced by aria-describedby (helper / error) */\n protected readonly describedBy = computed(() => {\n if (this.error()) return `${this.inputId}-error`;\n if (this.helperText()) return `${this.inputId}-helper`;\n return null;\n });\n\n /** Container classes */\n protected containerClasses = computed(() => {\n const classes = [`lc-combobox`, `lc-combobox--${this.size()}`];\n if (this.isDisabled()) classes.push('lc-combobox--disabled');\n if (this.error()) classes.push('lc-combobox--error');\n if (this.isOpen()) classes.push('lc-combobox--open');\n return classes.join(' ');\n });\n\n protected optionId(index: number): string {\n return `${this.listboxId}-opt-${index}`;\n }\n\n // ControlValueAccessor\n private onChange: (value: ComboboxValue) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n constructor() {\n // Async loading pipe. `debounce` (not `debounceTime`) reads the signal per\n // emission, so `debounceMs` is honoured after inputs are populated.\n this.querySubject\n .pipe(\n debounce(() => timer(this.debounceMs())),\n switchMap((q) => {\n const loader = this.loadOptions();\n if (!loader || q.length < this.minChars()) {\n return of([] as ComboboxOption[]);\n }\n this.isLoading.set(true);\n // `defer` turns a synchronously throwing loader into an error\n // notification; `catchError` keeps the pipe alive for the next query.\n return defer(() => loader(q)).pipe(catchError(() => of([] as ComboboxOption[])));\n }),\n takeUntilDestroyed()\n )\n .subscribe((results) => {\n this.asyncOptions.set(results);\n this.isLoading.set(false);\n });\n\n // Keep the highlighted option visible while navigating with the keyboard\n // (after render, so the option element exists in the overlay pane).\n afterRenderEffect(() => {\n const id = this.activeDescendantId();\n if (!id) return;\n untracked(() => {\n // ids are generated from a counter, so no CSS escaping is needed\n this.dropdownEl()\n ?.nativeElement.querySelector<HTMLElement>(`#${id}`)\n ?.scrollIntoView?.({ block: 'nearest' });\n });\n });\n\n // `selectedValue` (3.29): a page write selects or clears silently; an\n // unknown value waits for `options` that hold it. The component's own\n // writes (user choice, clear) are already applied, so they are skipped.\n effect(() => {\n const value = this.selectedValue();\n const options = this.options();\n untracked(() => {\n if (value !== this.seenSelectedValue) {\n this.seenSelectedValue = value;\n if (!this.multiple()) {\n this.applySelectedValue(value, options);\n } else if (value !== null && isDevMode()) {\n console.warn('[lc-combobox] selectedValue is ignored with multiple');\n }\n } else if (this.pendingSelectedValue !== null && !this.multiple()) {\n this.applySelectedValue(this.pendingSelectedValue, options);\n }\n });\n });\n }\n\n /** Shows the option with `value` (or clears for `null`) without emitting anything. */\n private applySelectedValue(value: string | null, options: ComboboxOption[]): void {\n this.pendingSelectedValue = null;\n if (value === null) {\n this.selectedSingle.set(null);\n this.query.set('');\n return;\n }\n if (this.selectedSingle()?.value === value) return;\n const match = options.find((o) => o.value === value) ?? null;\n if (!match) this.pendingSelectedValue = value;\n this.selectedSingle.set(match);\n this.query.set(match ? match.label : '');\n }\n\n /** Emits a user's single choice or clear through `selectedValueChange` (not in `multiple` mode). */\n private emitSelectedValue(value: string | null): void {\n if (this.multiple()) return;\n this.pendingSelectedValue = null;\n this.seenSelectedValue = value;\n this.selectedValue.set(value);\n }\n\n writeValue(value: ComboboxValue): void {\n if (this.multiple()) {\n this.selectedMultiple.set(Array.isArray(value) ? value : []);\n } else {\n const single = value && !Array.isArray(value) ? value : null;\n this.pendingSelectedValue = null;\n this.selectedSingle.set(single);\n this.query.set(single ? single.label : '');\n }\n }\n\n registerOnChange(fn: (value: ComboboxValue) => 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 if (isDisabled) this.isOpen.set(false);\n }\n\n /**\n * Moves the focus into the field's `<input>` (3.35), e.g. when a form opens\n * with this field first, or to the field with the error after a rejected\n * save (422) (WCAG 2.4.3). `options` as for `HTMLElement.focus()`\n * (`{ preventScroll: true }`). Shows the same focus ring as a user's focus.\n * Like a user's focus it opens the list when `openOnFocus` is `true` and\n * there is something to show; with `false` the list stays closed\n * (`aria-expanded=\"false\"`). A disabled field takes no focus (it stays where\n * it is). Call it once the field has rendered, e.g.\n * `afterNextRender(() => this.month().focus(), { injector })` with\n * `month = viewChild.required(ComboboxComponent)` and the page component's\n * `injector = inject(Injector)`. In an `lc-modal` that opens with the field,\n * that call runs after the dialog moved the focus to its first control.\n * Does nothing before the component has rendered.\n * @param options Passed to the native `focus()`.\n */\n focus(options?: FocusOptions): void {\n this.inputEl()?.nativeElement.focus(options);\n }\n\n /**\n * Clear the current selection and query. Emits `null` (single) or `[]`\n * (multiple) exactly once, like a user-initiated deselect.\n */\n clear(): void {\n this.query.set('');\n this.highlightedIndex.set(-1);\n this.isOpen.set(false);\n const value: ComboboxValue = this.multiple() ? [] : null;\n if (this.multiple()) {\n this.selectedMultiple.set([]);\n } else {\n this.selectedSingle.set(null);\n }\n this.onChange(value);\n this.valueChange.emit(value);\n this.emitSelectedValue(null);\n }\n\n protected onClearClick(): void {\n if (this.isDisabled()) return;\n this.clear();\n this.inputEl()?.nativeElement.focus();\n }\n\n /** Whether a pointer event on the frame hit the input or a button in it (chip ×, clear). */\n private hitsFrameControl(event: MouseEvent): boolean {\n const target = event.target as Element | null;\n if (!target || target === this.inputEl()?.nativeElement) return true;\n const button = target.closest('button');\n return !!button && (event.currentTarget as Element).contains(button);\n }\n\n /**\n * 3.30 (request 047, regeln row 6): a press on the frame beside the input\n * and its buttons keeps focus where it is (no blur, so the list stays open).\n */\n protected onFrameMousedown(event: MouseEvent): void {\n if (!this.hitsFrameControl(event)) event.preventDefault();\n }\n\n /**\n * A click on the frame beside the input and its buttons focuses the input.\n * With `openOnFocus` off (3.32, request 051), a click or tap on the input or\n * the frame also opens the list, which focus alone no longer does.\n */\n protected onFrameClick(event: MouseEvent): void {\n if (this.isDisabled()) return;\n const onInput = event.target === this.inputEl()?.nativeElement;\n if (!onInput) {\n if (this.hitsFrameControl(event)) return;\n this.inputEl()?.nativeElement.focus();\n }\n if (!this.openOnFocus()) this.openIfAny();\n }\n\n protected onInputChange(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.query.set(value);\n this.queryChange.emit(value);\n this.highlightedIndex.set(-1);\n\n if (this.loadOptions()) {\n this.querySubject.next(value);\n }\n\n if (value.length >= this.minChars()) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputFocus(): void {\n if (this.openOnFocus()) this.openIfAny();\n }\n\n /** Opens the list when there is something to show: a long enough query, or options. */\n private openIfAny(): void {\n if (this.query().length >= this.minChars() || this.options().length > 0) {\n this.isOpen.set(true);\n }\n }\n\n protected onInputBlur(): void {\n this.onTouched();\n // Delay to allow click on dropdown\n setTimeout(() => this.isOpen.set(false), 200);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const flat = this.flatOptions();\n const total = flat.length + (this.showCreateOption() ? 1 : 0);\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.isOpen.set(true);\n if (total > 0) this.highlightedIndex.update((i) => (i + 1) % total);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (total > 0) this.highlightedIndex.update((i) => (i - 1 + total) % total);\n break;\n case 'Enter':\n event.preventDefault();\n if (this.highlightedIndex() >= 0) {\n if (this.highlightedIndex() === flat.length && this.showCreateOption()) {\n this.onCreateNew();\n } else {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n } else if (this.showCreateOption()) {\n this.onCreateNew();\n }\n break;\n case 'Escape':\n if (this.isOpen()) {\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n // Escape consumed by the list — an enclosing modal must not close too.\n event.stopPropagation();\n }\n break;\n case 'Tab':\n if (this.highlightedIndex() >= 0 && this.isOpen()) {\n const opt = flat[this.highlightedIndex()];\n if (opt && !opt.disabled) {\n this.selectOption(opt);\n }\n }\n this.isOpen.set(false);\n break;\n case 'Backspace':\n if (this.multiple() && !this.query() && this.selectedMultiple().length > 0) {\n this.removeSelected(this.selectedMultiple()[this.selectedMultiple().length - 1]);\n }\n break;\n }\n }\n\n protected selectOption(option: ComboboxOption): void {\n if (this.multiple()) {\n this.selectedMultiple.update((sel) => [...sel, option]);\n this.query.set('');\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n } else {\n this.selectedSingle.set(option);\n this.query.set(option.label);\n this.onChange(option);\n this.valueChange.emit(option);\n this.emitSelectedValue(option.value);\n }\n this.optionSelected.emit(option);\n this.isOpen.set(false);\n this.highlightedIndex.set(-1);\n }\n\n protected removeSelected(option: ComboboxOption): void {\n if (this.isDisabled()) return;\n this.selectedMultiple.update((sel) =>\n sel.filter((s) => s.value !== option.value)\n );\n const value = this.selectedMultiple();\n this.onChange(value);\n this.valueChange.emit(value);\n this.inputEl()?.nativeElement.focus();\n }\n\n protected onCreateNew(): void {\n const q = this.query().trim();\n if (!q) return;\n const newOption: ComboboxOption = { value: q, label: q };\n this.created.emit(q);\n this.selectOption(newOption);\n }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: MouseEvent): void {\n const target = event.target as Node | null;\n // The dropdown lives in the CDK overlay container, outside the host element.\n if (\n !this.elRef.nativeElement.contains(target) &&\n !this.dropdownEl()?.nativeElement.contains(target)\n ) {\n this.isOpen.set(false);\n }\n }\n}\n","<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n [style.--_lc-combobox-field-width.px]=\"fieldWidth()\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n [attr.lang]=\"panelLang()\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n @if (announceError()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n } @else {\n <!-- 3.36 (announceError=false): read with the field through aria-describedby, not announced. -->\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\">{{ error() }}</span>\n }\n }\n</div>\n"],"mappings":"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"}
|