@lifecorner/angular-ui-kit 3.36.0 → 3.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/fesm2022/lifecorner-angular-ui-kit-card.mjs +2 -2
  2. package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs +4 -4
  4. package/fesm2022/lifecorner-angular-ui-kit-description-list.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-hero.mjs +24 -3
  6. package/fesm2022/lifecorner-angular-ui-kit-hero.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-internal-chart.mjs +2 -2
  8. package/fesm2022/lifecorner-angular-ui-kit-internal-chart.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-list.mjs +10 -5
  10. package/fesm2022/lifecorner-angular-ui-kit-list.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-select.mjs +15 -10
  12. package/fesm2022/lifecorner-angular-ui-kit-select.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs +27 -4
  14. package/fesm2022/lifecorner-angular-ui-kit-state-strip.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-table.mjs +2 -2
  16. package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
  17. package/package.json +1 -1
  18. package/src/styles/_theme-light.scss +8 -1
  19. package/src/styles/theme-contrast.spec.ts +44 -0
  20. package/src/styles/theme-modes.spec.ts +20 -5
  21. package/types/lifecorner-angular-ui-kit-hero.d.ts +19 -1
  22. package/types/lifecorner-angular-ui-kit-internal-chart.d.ts +6 -0
  23. package/types/lifecorner-angular-ui-kit-list.d.ts +10 -0
  24. package/types/lifecorner-angular-ui-kit-select.d.ts +13 -1
  25. package/types/lifecorner-angular-ui-kit-state-strip.d.ts +20 -2
  26. package/types/lifecorner-angular-ui-kit-table.d.ts +9 -1
@@ -106,9 +106,9 @@ var SelectComponent = class SelectComponent {
106
106
  this.isOpen.set(true);
107
107
  this.opened.emit();
108
108
  }
109
- close() {
109
+ close(returnFocus = false) {
110
110
  if (!this.isOpen()) return;
111
- this.restoreFocusFromPanel();
111
+ this.restoreFocusFromPanel(returnFocus);
112
112
  this.isOpen.set(false);
113
113
  this.searchQuery.set("");
114
114
  this.highlightedIndex.set(-1);
@@ -127,7 +127,7 @@ var SelectComponent = class SelectComponent {
127
127
  this.value.set(option.value);
128
128
  this.onChange(option.value);
129
129
  this.selectionChange.emit(option.value);
130
- this.close();
130
+ this.close(true);
131
131
  }
132
132
  }
133
133
  isSelected(option) {
@@ -160,7 +160,7 @@ var SelectComponent = class SelectComponent {
160
160
  break;
161
161
  case "Escape":
162
162
  if (this.isOpen()) {
163
- this.close();
163
+ this.close(true);
164
164
  event.preventDefault();
165
165
  event.stopPropagation();
166
166
  }
@@ -202,6 +202,9 @@ var SelectComponent = class SelectComponent {
202
202
  onClickOutside() {
203
203
  this.close();
204
204
  }
205
+ onOverlayKeydown(event) {
206
+ if (event.key === "Escape" && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) this.close(true);
207
+ }
205
208
  writeValue(value) {
206
209
  this.value.set(value);
207
210
  }
@@ -217,10 +220,12 @@ var SelectComponent = class SelectComponent {
217
220
  isSearchInput(target) {
218
221
  return target instanceof HTMLInputElement;
219
222
  }
220
- restoreFocusFromPanel() {
223
+ restoreFocusFromPanel(fromBody = false) {
221
224
  const panel = this.dropdownPanel()?.nativeElement;
222
225
  const active = this.document.activeElement;
223
- if (panel && active && panel.contains(active)) this.selectTrigger()?.nativeElement.focus();
226
+ const inPanel = !!panel && !!active && panel.contains(active);
227
+ const lost = !active || active === this.document.body || active === this.document.documentElement;
228
+ if (inPanel || fromBody && lost) this.selectTrigger()?.nativeElement.focus();
224
229
  }
225
230
  findOptionByValue(value, options) {
226
231
  return this.flattenOptions(options).find((opt) => opt.value === value);
@@ -390,8 +395,8 @@ var SelectComponent = class SelectComponent {
390
395
  isSignal: true
391
396
  }],
392
397
  ngImport: i0,
393
- template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
394
- styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"],
398
+ template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (overlayKeydown)=\"onOverlayKeydown($event)\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
399
+ styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--lg:has(.lc-select__clear){padding-right:calc(var(--spacing-3) + 1.25rem + var(--spacing-1) + max(-.125rem,(44px - 1.5rem) / 2 - .25rem) + 1.5rem + max(0px,(44px - 1.5rem) / 2))}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select--lg .lc-select__clear{margin-right:max(-.125rem,(44px - 1.5rem) / 2 - .25rem)}.lc-select--lg .lc-select__clear:before{content:\"\";position:absolute;inset:min(0px,(100% - 44px) / 2)}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"],
395
400
  dependencies: [
396
401
  {
397
402
  kind: "ngmodule",
@@ -486,8 +491,8 @@ i0.ɵɵngDeclareClassMetadata({
486
491
  useExisting: forwardRef(() => SelectComponent),
487
492
  multi: true
488
493
  }],
489
- template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
490
- styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"]
494
+ template: "<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (overlayKeydown)=\"onOverlayKeydown($event)\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n",
495
+ styles: [".lc-select-wrapper{display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%;position:relative}.lc-select{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease);cursor:pointer;-webkit-user-select:none;user-select:none}.lc-select:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){border-color:var(--color-text-secondary)}.lc-select:focus,.lc-select:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.lc-select.lc-select--xs{height:var(--size-interactive-xs-height);padding:var(--size-interactive-xs-padding);padding-right:var(--spacing-8);font-size:var(--size-interactive-xs-font-size)}.lc-select.lc-select--sm{height:var(--size-interactive-sm-height);padding:var(--size-interactive-sm-padding);padding-right:var(--spacing-9);font-size:var(--size-interactive-sm-font-size)}.lc-select.lc-select--md{height:var(--size-interactive-md-height);padding:var(--size-interactive-md-padding);padding-right:var(--spacing-10);font-size:var(--size-interactive-md-font-size)}.lc-select.lc-select--lg{height:var(--size-interactive-lg-height);padding:var(--size-interactive-lg-padding);padding-right:var(--spacing-12);font-size:var(--size-interactive-lg-font-size)}.lc-select.lc-select--lg:has(.lc-select__clear){padding-right:calc(var(--spacing-3) + 1.25rem + var(--spacing-1) + max(-.125rem,(44px - 1.5rem) / 2 - .25rem) + 1.5rem + max(0px,(44px - 1.5rem) / 2))}.lc-select.lc-select--filled{background-color:var(--color-surface-sunken);border-color:var(--color-field-border)}.lc-select.lc-select--filled:hover:not(.lc-select--disabled):not(.lc-select--error):not(:focus){background-color:var(--color-surface-hover);border-color:var(--color-text-secondary)}.lc-select.lc-select--filled:focus,.lc-select.lc-select--filled:focus-visible{background-color:var(--color-surface);border-color:var(--color-primary-500)}.lc-select.lc-select--error{border-color:var(--color-error-600)}.lc-select.lc-select--error:focus,.lc-select.lc-select--error:focus-visible{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.lc-select.lc-select--disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.lc-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-select__placeholder{color:var(--color-text-secondary)}.lc-select__icons{position:absolute;right:var(--spacing-3);display:flex;align-items:center;gap:var(--spacing-1);flex-shrink:0;pointer-events:none}.lc-select__icons button{pointer-events:auto}.lc-select__spinner{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lc-select__clear{position:relative;display:var(--lc-select-clear-display, flex);align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;background:none;background-clip:content-box;border:none;border-radius:calc(var(--border-radius-sm) + .125rem);color:var(--lc-select-clear-fg, var(--color-text-secondary));cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__clear:hover{color:var(--lc-select-clear-hover-fg, var(--color-text-primary));background-color:var(--lc-select-clear-hover-bg, var(--color-surface-hover))}.lc-select__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-select__clear svg{width:1rem;height:1rem}.lc-select--lg .lc-select__clear{margin-right:max(-.125rem,(44px - 1.5rem) / 2 - .25rem)}.lc-select--lg .lc-select__clear:before{content:\"\";position:absolute;inset:min(0px,(100% - 44px) / 2)}.lc-select__chevron{width:1.25rem;height:1.25rem;color:var(--color-text-secondary);transition:transform var(--animation-duration-base) var(--animation-timing-ease)}.lc-select__chevron.lc-select__chevron--open{transform:rotate(180deg)}.lc-select__dropdown{position:absolute;z-index:1000;margin-top:var(--spacing-1);padding:var(--lc-density-padding-xs, var(--spacing-2));background-color:var(--color-surface);background-image:var(--gradient-surface);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-lg);box-shadow:var(--elevation-4);min-width:max(200px,var(--_lc-select-field-width, 0px));max-width:calc(100vw - 2 * var(--spacing-2));max-height:320px;overflow-y:auto;opacity:0;transform:translateY(-8px);transition:all var(--animation-duration-base) var(--animation-timing-ease);pointer-events:none}.lc-select__dropdown.lc-select__dropdown--open{opacity:1;transform:translateY(0);pointer-events:auto}.lc-select__dropdown::-webkit-scrollbar{width:8px}.lc-select__dropdown::-webkit-scrollbar-track{background:var(--color-surface-sunken);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:var(--border-radius-md)}.lc-select__dropdown::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}.lc-select__search{padding:var(--lc-density-padding-xs, var(--spacing-2));border-bottom:1px solid var(--color-border);margin-bottom:var(--spacing-2)}.lc-select__search-input{width:100%;padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm);color:var(--color-text-primary);background-color:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md);outline:none;transition:all var(--animation-duration-fast) var(--animation-timing-ease)}.lc-select__search-input:focus{border-color:var(--color-primary-500)}.lc-select__search-input::placeholder{color:var(--color-text-secondary)}.lc-select__options{list-style:none;padding:0;margin:0}.lc-select__option{display:flex;align-items:center;padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--font-size-sm);color:var(--color-text-primary);background:none;border:none;border-radius:var(--border-radius-md);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-timing-ease);text-align:left;width:100%}.lc-select__option:hover:not(.lc-select__option--disabled){background-color:var(--color-surface-hover)}.lc-select__option.lc-select__option--selected{font-weight:var(--font-weight-semibold);color:var(--color-primary-600);background-color:var(--color-primary-50)}.lc-select__option.lc-select__option--highlighted{background-color:var(--color-surface-hover);outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-select__option.lc-select__option--disabled{color:var(--color-text-disabled);cursor:not-allowed;opacity:.5}.lc-select__option-label{flex:1}.lc-select__check{width:1rem;height:1rem;margin-left:var(--spacing-2);flex-shrink:0;color:var(--color-primary-600)}.lc-select__checkbox{margin-right:var(--spacing-2);width:1rem;height:1rem;pointer-events:none}.lc-select__empty{padding:var(--lc-density-padding-md, var(--spacing-4));text-align:center;font-size:var(--font-size-sm);color:var(--color-text-secondary)}.lc-select__error{font-size:var(--font-size-xs);color:var(--color-error-600);margin-top:var(--spacing-1)}.lc-select__helper{font-size:var(--font-size-xs);color:var(--color-text-secondary);margin-top:var(--spacing-1)}@media(forced-colors:active){.lc-select__option.lc-select__option--highlighted{outline-color:Highlight}.lc-select__clear:focus-visible{outline-color:Highlight}}\n"]
491
496
  }]
492
497
  }],
493
498
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-select.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/select/select.component.ts","../../../../libs/angular-ui-kit/src/lib/select/select.component.html"],"sourcesContent":["import {\n Component,\n input,\n output,\n signal,\n computed,\n linkedSignal,\n ChangeDetectionStrategy,\n forwardRef,\n ElementRef,\n viewChild,\n inject,\n DOCUMENT,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { closestLang } from '@lifecorner/angular-ui-kit/internal/a11y';\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectOptionGroup {\n label: string;\n options: SelectOption[];\n}\n\nexport type SelectValue = string | number | string[] | number[] | null;\n\n/**\n * Select component for dropdown option selection.\n *\n * Features:\n * - Single and multiple selection modes\n * - Searchable/filterable option list\n * - Option groups with headers\n * - Loading state indicator\n * - Variant styles (outline, filled)\n * - Size presets (xs, sm, md, lg)\n * - Keyboard navigation support\n * - CDK overlay positioning\n * - ControlValueAccessor integration for reactive forms\n *\n * The dropdown takes the `lang` of the host's closest `[lang]` (3.31).\n *\n * @example\n * ```html\n * <lc-select [options]=\"options\" placeholder=\"Choose\" [(ngModel)]=\"selected\" />\n * ```\n */\n@Component({\n selector: 'lc-select',\n standalone: true,\n imports: [FormsModule, OverlayModule],\n templateUrl: './select.component.html',\n styleUrl: './select.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SelectComponent),\n multi: true,\n },\n ],\n})\nexport class SelectComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance id base; the listbox and its options derive their ids from it. */\n readonly selectId = `lc-select-${++SelectComponent.nextId}`;\n readonly listboxId = `${this.selectId}-listbox`;\n\n private readonly selectTrigger = viewChild<ElementRef<HTMLElement>>('selectTrigger');\n private readonly dropdownPanel = viewChild<ElementRef<HTMLElement>>('dropdownPanel');\n\n /**\n * Visual variant of the select\n */\n readonly variant = input<'outline' | 'filled'>('outline');\n\n /**\n * Size of the select\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the select is disabled\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the select is in error state\n */\n readonly error = input<boolean>(false);\n\n /**\n * Whether the select is required\n */\n readonly required = input<boolean>(false);\n\n /**\n * Whether the select is in loading state\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Whether the select allows searching\n */\n readonly searchable = input<boolean>(false);\n\n /**\n * Whether multiple options can be selected\n */\n readonly multiple = input<boolean>(false);\n\n /**\n * Placeholder text when no option is selected\n */\n readonly placeholder = input<string>('Select an option');\n\n /**\n * Helper text displayed below the select\n */\n readonly helperText = input<string>('');\n\n /**\n * Error message displayed when error is true\n */\n readonly errorMessage = input<string>('');\n\n /**\n * ARIA label for accessibility\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Select options (flat list or groups)\n */\n readonly options = input<SelectOption[] | SelectOptionGroup[]>([]);\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear selection');\n\n /** Placeholder of the search field (`searchable`). */\n readonly searchPlaceholder = input('Search...');\n\n /** Accessible name of the search field. Empty: \"Search \" + `ariaLabel`, or \"Search options\". */\n readonly searchAriaLabel = input('');\n\n /** Shown when the search matches no option. */\n readonly noOptionsLabel = input('No options found');\n\n /**\n * Emitted when selection changes\n */\n readonly selectionChange = output<SelectValue>();\n\n /**\n * Emitted when dropdown opens\n */\n readonly opened = output<void>();\n\n /**\n * Emitted when dropdown closes\n */\n readonly closed = output<void>();\n\n // Internal state\n readonly value = signal<SelectValue>(null);\n readonly isOpen = signal<boolean>(false);\n readonly searchQuery = signal<string>('');\n\n /** Disabled state pushed in by the forms API (setDisabledState). */\n private readonly formDisabled = signal<boolean>(false);\n\n /** Effective disabled state: the `disabled` input OR the form control's disabled state. */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * `lang` of the open dropdown (3.31, request 050, WCAG 3.1.2): the overlay\n * renders under `<body>`, so it takes the host's closest `[lang]`, else none.\n * Read at every opening; `null` while closed, so the server never reads the DOM.\n */\n protected readonly panelLang = computed(() => (this.isOpen() ? closestLang(this.host.nativeElement) : null));\n\n /**\n * The trigger's width in px while the dropdown is open (3.33, request 052,\n * 050-n4), read at every opening: the panel's floor next to its 200 px\n * (`min-width`), so it is at least as wide as the field and wider content\n * still grows it. `null` while closed.\n */\n protected readonly fieldWidth = computed(() =>\n this.isOpen() ? (this.selectTrigger()?.nativeElement.getBoundingClientRect().width ?? null) : null,\n );\n\n // Computed values\n readonly filteredOptions = computed(() => {\n const query = this.searchQuery().toLowerCase();\n const opts = this.options();\n\n if (!query || !this.searchable()) {\n return this.flattenOptions(opts);\n }\n\n return this.flattenOptions(opts).filter((opt) => opt.label.toLowerCase().includes(query));\n });\n\n /** Keyboard highlight; resets whenever the visible option list changes so it never points at a stale row. */\n readonly highlightedIndex = linkedSignal<SelectOption[], number>({\n source: this.filteredOptions,\n computation: () => -1,\n });\n\n readonly activeDescendantId = computed(() => {\n const index = this.highlightedIndex();\n return this.isOpen() && index >= 0 ? this.optionId(index) : null;\n });\n\n readonly selectedLabel = computed(() => {\n const currentValue = this.value();\n const opts = this.options();\n\n if (currentValue === null) {\n return '';\n }\n\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [currentValue];\n return values\n .map((val) => this.findOptionByValue(val, opts)?.label)\n .filter(Boolean)\n .join(', ');\n }\n\n const val = Array.isArray(currentValue) ? currentValue[0] : currentValue;\n return this.findOptionByValue(val ?? '', opts)?.label || '';\n });\n\n readonly displayValue = computed(() => {\n const label = this.selectedLabel();\n if (label) {\n const currentValue = this.value();\n if (this.multiple() && Array.isArray(currentValue)) {\n const count = currentValue.length;\n return count > 1 ? `${count} selected` : label;\n }\n return label;\n }\n return this.placeholder();\n });\n\n /** id of the helper/error text the trigger is described by (aria-describedby) */\n readonly describedBy = computed(() => {\n if (this.error() && this.errorMessage()) {\n return `${this.selectId}-error`;\n }\n return this.helperText() ? `${this.selectId}-helper` : null;\n });\n\n /**\n * Computed classes for the select trigger element\n */\n readonly selectClasses = computed(() => {\n const classes = ['lc-select', `lc-select--${this.variant()}`, `lc-select--${this.size()}`];\n\n if (this.isDisabled()) {\n classes.push('lc-select--disabled');\n }\n\n if (this.error()) {\n classes.push('lc-select--error');\n }\n\n if (this.loading()) {\n classes.push('lc-select--loading');\n }\n\n if (this.isOpen()) {\n classes.push('lc-select--open');\n }\n\n return classes.join(' ');\n });\n\n // Private properties\n private readonly document = inject(DOCUMENT);\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: SelectValue) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n // Public methods\n /**\n * Id of the option at `index` in the filtered list (used for aria-activedescendant)\n */\n optionId(index: number): string {\n return `${this.selectId}-option-${index}`;\n }\n\n /**\n * Toggle dropdown open/closed\n */\n toggle(): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n if (this.isOpen()) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Open dropdown\n */\n open(): void {\n if (this.isDisabled() || this.loading() || this.isOpen()) {\n return;\n }\n this.isOpen.set(true);\n this.opened.emit();\n }\n\n /**\n * Close dropdown. Idempotent: the overlay's `detach` re-enters here after\n * `isOpen` flips, and that second call must not emit `closed` again.\n */\n close(): void {\n if (!this.isOpen()) {\n return;\n }\n // If focus is inside the panel (search input) it would fall to <body> when the\n // overlay is torn down; hand it back to the trigger first.\n this.restoreFocusFromPanel();\n this.isOpen.set(false);\n this.searchQuery.set('');\n this.highlightedIndex.set(-1);\n this.closed.emit();\n }\n\n /**\n * Select an option\n */\n selectOption(option: SelectOption): void {\n if (option.disabled) {\n return;\n }\n\n if (this.multiple()) {\n const currentValue = this.value() || [];\n const currentArray = (Array.isArray(currentValue) ? currentValue : []) as (string | number)[];\n const index = currentArray.indexOf(option.value);\n\n const newValue = (\n index > -1\n ? currentArray.filter((v) => v !== option.value) // Deselect\n : [...currentArray, option.value] // Select\n ) as SelectValue;\n this.value.set(newValue);\n this.onChange(newValue);\n this.selectionChange.emit(newValue);\n } else {\n this.value.set(option.value);\n this.onChange(option.value);\n this.selectionChange.emit(option.value);\n this.close();\n }\n }\n\n /**\n * Check if an option is selected\n */\n isSelected(option: SelectOption): boolean {\n const currentValue = this.value();\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [];\n return values.some((v) => v === option.value);\n }\n return currentValue === option.value;\n }\n\n /**\n * Clear selection\n */\n clear(): void {\n if (this.multiple()) {\n this.value.set([]);\n this.onChange([]);\n } else {\n this.value.set(null);\n this.onChange(null);\n }\n this.selectionChange.emit(this.value());\n }\n\n /**\n * Handle keyboard navigation (trigger and search input share this handler)\n */\n onKeyDown(event: KeyboardEvent): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n\n switch (event.key) {\n case ' ':\n case 'Enter':\n // Space must keep typing into the search box.\n if (event.key === ' ' && this.isSearchInput(event.target)) {\n return;\n }\n if (!this.isOpen()) {\n this.open();\n } else {\n const option = this.filteredOptions()[this.highlightedIndex()];\n if (option) {\n this.selectOption(option);\n }\n }\n event.preventDefault();\n break;\n\n case 'Escape':\n if (this.isOpen()) {\n this.close();\n event.preventDefault();\n // Only this select should react; keep the key from reaching enclosing overlays.\n event.stopPropagation();\n }\n break;\n\n case 'ArrowDown':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n this.highlightedIndex.set((this.highlightedIndex() + 1) % count);\n }\n }\n event.preventDefault();\n break;\n\n case 'ArrowUp':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n const current = this.highlightedIndex();\n this.highlightedIndex.set(current <= 0 ? count - 1 : current - 1);\n }\n }\n event.preventDefault();\n break;\n\n case 'Home':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(0);\n event.preventDefault();\n }\n break;\n\n case 'End':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(this.filteredOptions().length - 1);\n event.preventDefault();\n }\n break;\n }\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n /**\n * Handle click outside\n */\n onClickOutside(): void {\n this.close();\n }\n\n // ControlValueAccessor implementation\n writeValue(value: SelectValue): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: (value: SelectValue) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n // Private helper methods\n private isSearchInput(target: EventTarget | null): boolean {\n return target instanceof HTMLInputElement;\n }\n\n private restoreFocusFromPanel(): void {\n const panel = this.dropdownPanel()?.nativeElement;\n const active = this.document.activeElement;\n if (panel && active && panel.contains(active)) {\n this.selectTrigger()?.nativeElement.focus();\n }\n }\n\n private findOptionByValue(\n value: string | number,\n options: SelectOption[] | SelectOptionGroup[],\n ): SelectOption | undefined {\n const flatOptions = this.flattenOptions(options);\n return flatOptions.find((opt) => opt.value === value);\n }\n\n private flattenOptions(options: SelectOption[] | SelectOptionGroup[]): SelectOption[] {\n if (!options || options.length === 0) {\n return [];\n }\n\n // Check if first item is a group\n const firstItem = options[0];\n if (firstItem && 'options' in firstItem) {\n return (options as SelectOptionGroup[]).flatMap((group) => group.options);\n }\n\n return options as SelectOption[];\n }\n}\n","<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-select.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/select/select.component.ts","../../../../libs/angular-ui-kit/src/lib/select/select.component.html"],"sourcesContent":["import {\n Component,\n input,\n output,\n signal,\n computed,\n linkedSignal,\n ChangeDetectionStrategy,\n forwardRef,\n ElementRef,\n viewChild,\n inject,\n DOCUMENT,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';\nimport { OverlayModule } from '@angular/cdk/overlay';\nimport { closestLang } from '@lifecorner/angular-ui-kit/internal/a11y';\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectOptionGroup {\n label: string;\n options: SelectOption[];\n}\n\nexport type SelectValue = string | number | string[] | number[] | null;\n\n/**\n * Select component for dropdown option selection.\n *\n * Features:\n * - Single and multiple selection modes\n * - Searchable/filterable option list\n * - Option groups with headers\n * - Loading state indicator\n * - Variant styles (outline, filled)\n * - Size presets (xs, sm, md, lg)\n * - Keyboard navigation support\n * - CDK overlay positioning\n * - ControlValueAccessor integration for reactive forms\n *\n * The dropdown takes the `lang` of the host's closest `[lang]` (3.31).\n *\n * @example\n * ```html\n * <lc-select [options]=\"options\" placeholder=\"Choose\" [(ngModel)]=\"selected\" />\n * ```\n */\n@Component({\n selector: 'lc-select',\n standalone: true,\n imports: [FormsModule, OverlayModule],\n templateUrl: './select.component.html',\n styleUrl: './select.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SelectComponent),\n multi: true,\n },\n ],\n})\nexport class SelectComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance id base; the listbox and its options derive their ids from it. */\n readonly selectId = `lc-select-${++SelectComponent.nextId}`;\n readonly listboxId = `${this.selectId}-listbox`;\n\n private readonly selectTrigger = viewChild<ElementRef<HTMLElement>>('selectTrigger');\n private readonly dropdownPanel = viewChild<ElementRef<HTMLElement>>('dropdownPanel');\n\n /**\n * Visual variant of the select\n */\n readonly variant = input<'outline' | 'filled'>('outline');\n\n /**\n * Size of the select\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the select is disabled\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the select is in error state\n */\n readonly error = input<boolean>(false);\n\n /**\n * Whether the select is required\n */\n readonly required = input<boolean>(false);\n\n /**\n * Whether the select is in loading state\n */\n readonly loading = input<boolean>(false);\n\n /**\n * Whether the select allows searching\n */\n readonly searchable = input<boolean>(false);\n\n /**\n * Whether multiple options can be selected\n */\n readonly multiple = input<boolean>(false);\n\n /**\n * Placeholder text when no option is selected\n */\n readonly placeholder = input<string>('Select an option');\n\n /**\n * Helper text displayed below the select\n */\n readonly helperText = input<string>('');\n\n /**\n * Error message displayed when error is true\n */\n readonly errorMessage = input<string>('');\n\n /**\n * ARIA label for accessibility\n */\n readonly ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Select options (flat list or groups)\n */\n readonly options = input<SelectOption[] | SelectOptionGroup[]>([]);\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear selection');\n\n /** Placeholder of the search field (`searchable`). */\n readonly searchPlaceholder = input('Search...');\n\n /** Accessible name of the search field. Empty: \"Search \" + `ariaLabel`, or \"Search options\". */\n readonly searchAriaLabel = input('');\n\n /** Shown when the search matches no option. */\n readonly noOptionsLabel = input('No options found');\n\n /**\n * Emitted when selection changes\n */\n readonly selectionChange = output<SelectValue>();\n\n /**\n * Emitted when dropdown opens\n */\n readonly opened = output<void>();\n\n /**\n * Emitted when dropdown closes\n */\n readonly closed = output<void>();\n\n // Internal state\n readonly value = signal<SelectValue>(null);\n readonly isOpen = signal<boolean>(false);\n readonly searchQuery = signal<string>('');\n\n /** Disabled state pushed in by the forms API (setDisabledState). */\n private readonly formDisabled = signal<boolean>(false);\n\n /** Effective disabled state: the `disabled` input OR the form control's disabled state. */\n readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * `lang` of the open dropdown (3.31, request 050, WCAG 3.1.2): the overlay\n * renders under `<body>`, so it takes the host's closest `[lang]`, else none.\n * Read at every opening; `null` while closed, so the server never reads the DOM.\n */\n protected readonly panelLang = computed(() => (this.isOpen() ? closestLang(this.host.nativeElement) : null));\n\n /**\n * The trigger's width in px while the dropdown is open (3.33, request 052,\n * 050-n4), read at every opening: the panel's floor next to its 200 px\n * (`min-width`), so it is at least as wide as the field and wider content\n * still grows it. `null` while closed.\n */\n protected readonly fieldWidth = computed(() =>\n this.isOpen() ? (this.selectTrigger()?.nativeElement.getBoundingClientRect().width ?? null) : null,\n );\n\n // Computed values\n readonly filteredOptions = computed(() => {\n const query = this.searchQuery().toLowerCase();\n const opts = this.options();\n\n if (!query || !this.searchable()) {\n return this.flattenOptions(opts);\n }\n\n return this.flattenOptions(opts).filter((opt) => opt.label.toLowerCase().includes(query));\n });\n\n /** Keyboard highlight; resets whenever the visible option list changes so it never points at a stale row. */\n readonly highlightedIndex = linkedSignal<SelectOption[], number>({\n source: this.filteredOptions,\n computation: () => -1,\n });\n\n readonly activeDescendantId = computed(() => {\n const index = this.highlightedIndex();\n return this.isOpen() && index >= 0 ? this.optionId(index) : null;\n });\n\n readonly selectedLabel = computed(() => {\n const currentValue = this.value();\n const opts = this.options();\n\n if (currentValue === null) {\n return '';\n }\n\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [currentValue];\n return values\n .map((val) => this.findOptionByValue(val, opts)?.label)\n .filter(Boolean)\n .join(', ');\n }\n\n const val = Array.isArray(currentValue) ? currentValue[0] : currentValue;\n return this.findOptionByValue(val ?? '', opts)?.label || '';\n });\n\n readonly displayValue = computed(() => {\n const label = this.selectedLabel();\n if (label) {\n const currentValue = this.value();\n if (this.multiple() && Array.isArray(currentValue)) {\n const count = currentValue.length;\n return count > 1 ? `${count} selected` : label;\n }\n return label;\n }\n return this.placeholder();\n });\n\n /** id of the helper/error text the trigger is described by (aria-describedby) */\n readonly describedBy = computed(() => {\n if (this.error() && this.errorMessage()) {\n return `${this.selectId}-error`;\n }\n return this.helperText() ? `${this.selectId}-helper` : null;\n });\n\n /**\n * Computed classes for the select trigger element\n */\n readonly selectClasses = computed(() => {\n const classes = ['lc-select', `lc-select--${this.variant()}`, `lc-select--${this.size()}`];\n\n if (this.isDisabled()) {\n classes.push('lc-select--disabled');\n }\n\n if (this.error()) {\n classes.push('lc-select--error');\n }\n\n if (this.loading()) {\n classes.push('lc-select--loading');\n }\n\n if (this.isOpen()) {\n classes.push('lc-select--open');\n }\n\n return classes.join(' ');\n });\n\n // Private properties\n private readonly document = inject(DOCUMENT);\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onChange: (value: SelectValue) => void = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private onTouched: () => void = () => {};\n\n // Public methods\n /**\n * Id of the option at `index` in the filtered list (used for aria-activedescendant)\n */\n optionId(index: number): string {\n return `${this.selectId}-option-${index}`;\n }\n\n /**\n * Toggle dropdown open/closed\n */\n toggle(): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n if (this.isOpen()) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /**\n * Open dropdown\n */\n open(): void {\n if (this.isDisabled() || this.loading() || this.isOpen()) {\n return;\n }\n this.isOpen.set(true);\n this.opened.emit();\n }\n\n /**\n * Close dropdown. Idempotent: the overlay's `detach` re-enters here after\n * `isOpen` flips, and that second call must not emit `closed` again.\n *\n * `returnFocus` (3.37, request 057, services row 2): a single-select choice\n * and `Escape` also hand the focus back to the trigger when it is on `<body>`\n * (a tap on an option, which has no `tabindex`, moves it there). Without it\n * (backdrop click, detach) only focus inside the panel returns, as before.\n */\n close(returnFocus = false): void {\n if (!this.isOpen()) {\n return;\n }\n // If focus is inside the panel (search input) it would fall to <body> when the\n // overlay is torn down; hand it back to the trigger first.\n this.restoreFocusFromPanel(returnFocus);\n this.isOpen.set(false);\n this.searchQuery.set('');\n this.highlightedIndex.set(-1);\n this.closed.emit();\n }\n\n /**\n * Select an option\n */\n selectOption(option: SelectOption): void {\n if (option.disabled) {\n return;\n }\n\n if (this.multiple()) {\n const currentValue = this.value() || [];\n const currentArray = (Array.isArray(currentValue) ? currentValue : []) as (string | number)[];\n const index = currentArray.indexOf(option.value);\n\n const newValue = (\n index > -1\n ? currentArray.filter((v) => v !== option.value) // Deselect\n : [...currentArray, option.value] // Select\n ) as SelectValue;\n this.value.set(newValue);\n this.onChange(newValue);\n this.selectionChange.emit(newValue);\n } else {\n this.value.set(option.value);\n this.onChange(option.value);\n this.selectionChange.emit(option.value);\n this.close(true);\n }\n }\n\n /**\n * Check if an option is selected\n */\n isSelected(option: SelectOption): boolean {\n const currentValue = this.value();\n if (this.multiple()) {\n const values = Array.isArray(currentValue) ? currentValue : [];\n return values.some((v) => v === option.value);\n }\n return currentValue === option.value;\n }\n\n /**\n * Clear selection\n */\n clear(): void {\n if (this.multiple()) {\n this.value.set([]);\n this.onChange([]);\n } else {\n this.value.set(null);\n this.onChange(null);\n }\n this.selectionChange.emit(this.value());\n }\n\n /**\n * Handle keyboard navigation (trigger and search input share this handler)\n */\n onKeyDown(event: KeyboardEvent): void {\n if (this.isDisabled() || this.loading()) {\n return;\n }\n\n switch (event.key) {\n case ' ':\n case 'Enter':\n // Space must keep typing into the search box.\n if (event.key === ' ' && this.isSearchInput(event.target)) {\n return;\n }\n if (!this.isOpen()) {\n this.open();\n } else {\n const option = this.filteredOptions()[this.highlightedIndex()];\n if (option) {\n this.selectOption(option);\n }\n }\n event.preventDefault();\n break;\n\n case 'Escape':\n if (this.isOpen()) {\n this.close(true);\n event.preventDefault();\n // Only this select should react; keep the key from reaching enclosing overlays.\n event.stopPropagation();\n }\n break;\n\n case 'ArrowDown':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n this.highlightedIndex.set((this.highlightedIndex() + 1) % count);\n }\n }\n event.preventDefault();\n break;\n\n case 'ArrowUp':\n if (!this.isOpen()) {\n this.open();\n } else {\n const count = this.filteredOptions().length;\n if (count > 0) {\n const current = this.highlightedIndex();\n this.highlightedIndex.set(current <= 0 ? count - 1 : current - 1);\n }\n }\n event.preventDefault();\n break;\n\n case 'Home':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(0);\n event.preventDefault();\n }\n break;\n\n case 'End':\n if (this.isOpen() && this.filteredOptions().length > 0) {\n this.highlightedIndex.set(this.filteredOptions().length - 1);\n event.preventDefault();\n }\n break;\n }\n }\n\n /**\n * Handle blur event\n */\n onBlur(): void {\n this.onTouched();\n }\n\n /**\n * Handle click outside\n */\n onClickOutside(): void {\n this.close();\n }\n\n /**\n * Keys that reach the open overlay while the focus is outside the field and\n * the panel, e.g. on `<body>` after a tap (3.37, request 057): `Escape`\n * closes and returns the focus. It runs before the overlay's own `Escape`\n * detach, which then finds the select closed.\n */\n onOverlayKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {\n this.close(true);\n }\n }\n\n // ControlValueAccessor implementation\n writeValue(value: SelectValue): void {\n this.value.set(value);\n }\n\n registerOnChange(fn: (value: SelectValue) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n // Private helper methods\n private isSearchInput(target: EventTarget | null): boolean {\n return target instanceof HTMLInputElement;\n }\n\n private restoreFocusFromPanel(fromBody = false): void {\n const panel = this.dropdownPanel()?.nativeElement;\n const active = this.document.activeElement;\n const inPanel = !!panel && !!active && panel.contains(active);\n // Never take the focus from an element a `selectionChange` handler focused.\n const lost = !active || active === this.document.body || active === this.document.documentElement;\n if (inPanel || (fromBody && lost)) {\n this.selectTrigger()?.nativeElement.focus();\n }\n }\n\n private findOptionByValue(\n value: string | number,\n options: SelectOption[] | SelectOptionGroup[],\n ): SelectOption | undefined {\n const flatOptions = this.flattenOptions(options);\n return flatOptions.find((opt) => opt.value === value);\n }\n\n private flattenOptions(options: SelectOption[] | SelectOptionGroup[]): SelectOption[] {\n if (!options || options.length === 0) {\n return [];\n }\n\n // Check if first item is a group\n const firstItem = options[0];\n if (firstItem && 'options' in firstItem) {\n return (options as SelectOptionGroup[]).flatMap((group) => group.options);\n }\n\n return options as SelectOption[];\n }\n}\n","<div class=\"lc-select-wrapper\">\n <div\n #selectTrigger\n [class]=\"selectClasses()\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"isOpen() ? 'true' : 'false'\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-required]=\"required() ? 'true' : null\"\n [attr.aria-disabled]=\"isDisabled() ? 'true' : null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n (click)=\"toggle()\"\n (keydown)=\"onKeyDown($event)\"\n (blur)=\"onBlur()\"\n >\n <div class=\"lc-select__value\">\n <span [class.lc-select__placeholder]=\"!selectedLabel()\">\n {{ displayValue() }}\n </span>\n </div>\n\n <div class=\"lc-select__icons\">\n @if (loading()) {\n <svg\n class=\"lc-select__spinner\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n } @else if (value() && !isDisabled() && !multiple()) {\n <button\n type=\"button\"\n class=\"lc-select__clear\"\n (click)=\"clear(); $event.stopPropagation()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\"\n />\n </svg>\n </button>\n }\n\n <svg\n class=\"lc-select__chevron\"\n [class.lc-select__chevron--open]=\"isOpen()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </div>\n\n <!-- Dropdown Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectTrigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n (backdropClick)=\"onClickOutside()\"\n (overlayKeydown)=\"onOverlayKeydown($event)\"\n (detach)=\"close()\"\n >\n <div\n #dropdownPanel\n class=\"lc-select__dropdown\"\n [class.lc-select__dropdown--open]=\"isOpen()\"\n [style.--_lc-select-field-width.px]=\"fieldWidth()\"\n [attr.lang]=\"panelLang()\"\n >\n @if (searchable()) {\n <div class=\"lc-select__search\">\n <input\n type=\"text\"\n class=\"lc-select__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-label]=\"searchAriaLabel() || (ariaLabel() ? 'Search ' + ariaLabel() : 'Search options')\"\n [(ngModel)]=\"searchQuery\"\n (keydown)=\"onKeyDown($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n }\n\n <div\n class=\"lc-select__options\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n >\n @if (filteredOptions().length === 0) {\n <div\n class=\"lc-select__option lc-select__option--no-results\"\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n >\n {{ noOptionsLabel() }}\n </div>\n } @else {\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays on the trigger/search, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-select__option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"isSelected(option) ? 'true' : 'false'\"\n [attr.aria-disabled]=\"option.disabled ? 'true' : null\"\n [class.lc-select__option--selected]=\"isSelected(option)\"\n [class.lc-select__option--highlighted]=\"highlightedIndex() === i\"\n [class.lc-select__option--disabled]=\"option.disabled\"\n (click)=\"selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(i)\"\n >\n @if (multiple()) {\n <!-- Purely visual: the option itself carries the selected state (role=option\n + aria-selected); the checkbox is inert so clicks land on the option. -->\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(option)\"\n [disabled]=\"option.disabled\"\n class=\"lc-select__checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <span class=\"lc-select__option-label\">{{ option.label }}</span>\n @if (isSelected(option) && !multiple()) {\n <svg\n class=\"lc-select__check\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </div>\n }\n }\n </div>\n </div>\n </ng-template>\n\n <!-- Helper/Error Text -->\n @if (error() && errorMessage()) {\n <div class=\"lc-select__error\" [id]=\"selectId + '-error'\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n } @else if (helperText()) {\n <div class=\"lc-select__helper\" [id]=\"selectId + '-helper'\">\n {{ helperText() }}\n </div>\n }\n</div>\n"],"mappings":"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"}
@@ -65,6 +65,29 @@ var StateStripComponent = class StateStripComponent {
65
65
  onPointerLeave() {
66
66
  this.hovered.set(null);
67
67
  }
68
+ scrubbed = null;
69
+ onScrubMove(event) {
70
+ const index = this.cellAt(event.clientX);
71
+ if (index === this.scrubbed) return;
72
+ this.scrubbed = index;
73
+ this.hovered.set(index);
74
+ }
75
+ onScrubUp(event) {
76
+ const index = this.cellAt(event.clientX);
77
+ if (index !== null) this.cellEls()[index]?.nativeElement.focus();
78
+ }
79
+ onScrubLeave() {
80
+ this.scrubbed = null;
81
+ this.hovered.set(null);
82
+ }
83
+ cellAt(x) {
84
+ const cells = this.cellEls();
85
+ for (let i = 0; i < cells.length; i++) {
86
+ const box = cells[i].nativeElement.getBoundingClientRect();
87
+ if (x >= box.left && x < box.right) return i;
88
+ }
89
+ return null;
90
+ }
68
91
  onKeydown(event) {
69
92
  const last = this.cells().length - 1;
70
93
  const from = this.current();
@@ -143,8 +166,8 @@ var StateStripComponent = class StateStripComponent {
143
166
  isSignal: true
144
167
  }],
145
168
  ngImport: i0,
146
- template: "@if (cells().length) {\n <!-- An APG single-row grid: one Tab stop, arrows move between the runs. -->\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- the cells are the focusable controls (roving tabindex); their keys bubble here -->\n <div class=\"lc-state-strip__grid\" role=\"grid\" [attr.aria-label]=\"gridLabel()\" (keydown)=\"onKeydown($event)\">\n <div class=\"lc-state-strip__row\" role=\"row\">\n @for (cell of cells(); track $index; let i = $index) {\n <div\n #cell\n class=\"lc-state-strip__cell\"\n role=\"gridcell\"\n [attr.tabindex]=\"i === current() ? 0 : -1\"\n (focus)=\"onFocus(i)\"\n (blur)=\"onBlur()\"\n (pointerenter)=\"onPointerEnter(i)\"\n (pointerleave)=\"onPointerLeave()\"\n >\n <span class=\"lc-state-strip__mark lc-state-strip__mark--{{ cell.state }}\"></span>\n <span class=\"lc-state-strip__name\">{{ cell.name }}</span>\n </div>\n }\n </div>\n </div>\n @if (caption()) {\n <!-- The name of the hovered or focused run, in the tooltip look; the cell is what screen readers read. -->\n <div class=\"lc-state-strip__caption\" aria-hidden=\"true\">{{ caption() }}</div>\n }\n}\n",
147
- styles: [":host{display:block;position:relative}:host(.lc-state-strip--empty){display:none}.lc-state-strip__grid{display:block}.lc-state-strip__row{display:flex;block-size:var(--lc-state-strip-height, 2rem)}.lc-state-strip__cell{position:relative;flex:1 1 0;min-inline-size:0;max-inline-size:var(--lc-state-strip-segment-max-width, 1.5rem)}.lc-state-strip__cell:hover{background-color:var(--color-surface-hover)}.lc-state-strip__cell:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-state-strip__mark{position:absolute;inset-inline:min(3px,25%)}.lc-state-strip__mark--success{top:12.5%;height:34.375%;background-color:var(--color-text-success)}.lc-state-strip__mark--error{top:53.125%;height:34.375%;background-color:var(--color-text-error)}.lc-state-strip__mark--warning{top:25%;height:50%;background-color:var(--color-text-warning)}.lc-state-strip__mark--neutral{top:calc(50% - 1px);height:2px;background-color:var(--color-field-border)}.lc-state-strip__name{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.lc-state-strip__caption{position:absolute;top:calc(100% + 4px);inset-inline-start:0;z-index:1;inline-size:max-content;max-inline-size:100%;padding:var(--spacing-1) var(--spacing-2);border-radius:var(--border-radius-md);background-color:var(--color-text-primary);color:var(--color-surface);box-shadow:var(--elevation-2);font-size:var(--font-size-xs);line-height:1.5;overflow-wrap:break-word;hyphens:auto;pointer-events:none}@media(forced-colors:active){.lc-state-strip__mark{forced-color-adjust:none;background-color:CanvasText}.lc-state-strip__mark--neutral{background-color:GrayText}.lc-state-strip__cell:focus-visible{outline-color:Highlight}.lc-state-strip__caption{border:1px solid CanvasText}}\n"],
169
+ template: "@if (cells().length) {\n <!-- An APG single-row grid: one Tab stop, arrows move between the runs. -->\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- the cells are the focusable controls (roving tabindex); their keys bubble here -->\n <div class=\"lc-state-strip__grid\" role=\"grid\" [attr.aria-label]=\"gridLabel()\" (keydown)=\"onKeydown($event)\">\n <div class=\"lc-state-strip__row\" role=\"row\">\n @for (cell of cells(); track $index; let i = $index) {\n <div\n #cell\n class=\"lc-state-strip__cell\"\n role=\"gridcell\"\n [class.lc-state-strip__cell--hovered]=\"i === hovered()\"\n [attr.tabindex]=\"i === current() ? 0 : -1\"\n (focus)=\"onFocus(i)\"\n (blur)=\"onBlur()\"\n (pointerenter)=\"onPointerEnter(i)\"\n (pointerleave)=\"onPointerLeave()\"\n >\n <span class=\"lc-state-strip__mark lc-state-strip__mark--{{ cell.state }}\"></span>\n <span class=\"lc-state-strip__name\">{{ cell.name }}</span>\n </div>\n }\n <!-- 3.37: the pointer path. One layer over the row picks the run under the pointer, so narrow cells need no 24 px target. -->\n <div\n class=\"lc-state-strip__scrub\"\n aria-hidden=\"true\"\n (pointerdown)=\"onScrubMove($event)\"\n (pointermove)=\"onScrubMove($event)\"\n (pointerup)=\"onScrubUp($event)\"\n (pointerleave)=\"onScrubLeave()\"\n (pointercancel)=\"onScrubLeave()\"\n ></div>\n </div>\n </div>\n @if (caption()) {\n <!-- The name of the hovered or focused run, in the tooltip look; the cell is what screen readers read. -->\n <div class=\"lc-state-strip__caption\" aria-hidden=\"true\">{{ caption() }}</div>\n }\n}\n",
170
+ styles: [":host{display:block;position:relative}:host(.lc-state-strip--empty){display:none}.lc-state-strip__grid{display:block}.lc-state-strip__row{display:flex;block-size:var(--lc-state-strip-height, 2rem)}.lc-state-strip__cell{position:relative;flex:1 1 0;min-inline-size:0;max-inline-size:var(--lc-state-strip-segment-max-width, 1.5rem)}.lc-state-strip__cell:hover{background-color:var(--color-surface-hover)}.lc-state-strip__cell:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-state-strip__mark{position:absolute;inset-inline:min(3px,25%)}.lc-state-strip__mark--success{top:12.5%;height:34.375%;background-color:var(--color-text-success)}.lc-state-strip__mark--error{top:53.125%;height:34.375%;background-color:var(--color-text-error)}.lc-state-strip__mark--warning{top:25%;height:50%;background-color:var(--color-text-warning)}.lc-state-strip__mark--neutral{top:calc(50% - 1px);height:2px;background-color:var(--color-field-border)}.lc-state-strip__name{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.lc-state-strip__caption{position:absolute;top:calc(100% + 4px);inset-inline-start:0;z-index:1;inline-size:max-content;max-inline-size:100%;padding:var(--spacing-1) var(--spacing-2);border-radius:var(--border-radius-md);background-color:var(--color-text-primary);color:var(--color-surface);box-shadow:var(--elevation-2);font-size:var(--font-size-xs);line-height:1.5;overflow-wrap:break-word;hyphens:auto;pointer-events:none}.lc-state-strip__row{position:relative}.lc-state-strip__cell--hovered{background-color:var(--color-surface-hover)}.lc-state-strip__scrub{position:absolute;inset:0;touch-action:pan-y pinch-zoom}@media(forced-colors:active){.lc-state-strip__mark{forced-color-adjust:none;background-color:CanvasText}.lc-state-strip__mark--neutral{background-color:GrayText}.lc-state-strip__cell:focus-visible{outline-color:Highlight}.lc-state-strip__caption{border:1px solid CanvasText}}\n"],
148
171
  changeDetection: i0.ChangeDetectionStrategy.OnPush
149
172
  });
150
173
  };
@@ -163,8 +186,8 @@ i0.ɵɵngDeclareClassMetadata({
163
186
  class: "lc-state-strip",
164
187
  "[class.lc-state-strip--empty]": "!cells().length"
165
188
  },
166
- template: "@if (cells().length) {\n <!-- An APG single-row grid: one Tab stop, arrows move between the runs. -->\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- the cells are the focusable controls (roving tabindex); their keys bubble here -->\n <div class=\"lc-state-strip__grid\" role=\"grid\" [attr.aria-label]=\"gridLabel()\" (keydown)=\"onKeydown($event)\">\n <div class=\"lc-state-strip__row\" role=\"row\">\n @for (cell of cells(); track $index; let i = $index) {\n <div\n #cell\n class=\"lc-state-strip__cell\"\n role=\"gridcell\"\n [attr.tabindex]=\"i === current() ? 0 : -1\"\n (focus)=\"onFocus(i)\"\n (blur)=\"onBlur()\"\n (pointerenter)=\"onPointerEnter(i)\"\n (pointerleave)=\"onPointerLeave()\"\n >\n <span class=\"lc-state-strip__mark lc-state-strip__mark--{{ cell.state }}\"></span>\n <span class=\"lc-state-strip__name\">{{ cell.name }}</span>\n </div>\n }\n </div>\n </div>\n @if (caption()) {\n <!-- The name of the hovered or focused run, in the tooltip look; the cell is what screen readers read. -->\n <div class=\"lc-state-strip__caption\" aria-hidden=\"true\">{{ caption() }}</div>\n }\n}\n",
167
- styles: [":host{display:block;position:relative}:host(.lc-state-strip--empty){display:none}.lc-state-strip__grid{display:block}.lc-state-strip__row{display:flex;block-size:var(--lc-state-strip-height, 2rem)}.lc-state-strip__cell{position:relative;flex:1 1 0;min-inline-size:0;max-inline-size:var(--lc-state-strip-segment-max-width, 1.5rem)}.lc-state-strip__cell:hover{background-color:var(--color-surface-hover)}.lc-state-strip__cell:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-state-strip__mark{position:absolute;inset-inline:min(3px,25%)}.lc-state-strip__mark--success{top:12.5%;height:34.375%;background-color:var(--color-text-success)}.lc-state-strip__mark--error{top:53.125%;height:34.375%;background-color:var(--color-text-error)}.lc-state-strip__mark--warning{top:25%;height:50%;background-color:var(--color-text-warning)}.lc-state-strip__mark--neutral{top:calc(50% - 1px);height:2px;background-color:var(--color-field-border)}.lc-state-strip__name{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.lc-state-strip__caption{position:absolute;top:calc(100% + 4px);inset-inline-start:0;z-index:1;inline-size:max-content;max-inline-size:100%;padding:var(--spacing-1) var(--spacing-2);border-radius:var(--border-radius-md);background-color:var(--color-text-primary);color:var(--color-surface);box-shadow:var(--elevation-2);font-size:var(--font-size-xs);line-height:1.5;overflow-wrap:break-word;hyphens:auto;pointer-events:none}@media(forced-colors:active){.lc-state-strip__mark{forced-color-adjust:none;background-color:CanvasText}.lc-state-strip__mark--neutral{background-color:GrayText}.lc-state-strip__cell:focus-visible{outline-color:Highlight}.lc-state-strip__caption{border:1px solid CanvasText}}\n"]
189
+ template: "@if (cells().length) {\n <!-- An APG single-row grid: one Tab stop, arrows move between the runs. -->\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- the cells are the focusable controls (roving tabindex); their keys bubble here -->\n <div class=\"lc-state-strip__grid\" role=\"grid\" [attr.aria-label]=\"gridLabel()\" (keydown)=\"onKeydown($event)\">\n <div class=\"lc-state-strip__row\" role=\"row\">\n @for (cell of cells(); track $index; let i = $index) {\n <div\n #cell\n class=\"lc-state-strip__cell\"\n role=\"gridcell\"\n [class.lc-state-strip__cell--hovered]=\"i === hovered()\"\n [attr.tabindex]=\"i === current() ? 0 : -1\"\n (focus)=\"onFocus(i)\"\n (blur)=\"onBlur()\"\n (pointerenter)=\"onPointerEnter(i)\"\n (pointerleave)=\"onPointerLeave()\"\n >\n <span class=\"lc-state-strip__mark lc-state-strip__mark--{{ cell.state }}\"></span>\n <span class=\"lc-state-strip__name\">{{ cell.name }}</span>\n </div>\n }\n <!-- 3.37: the pointer path. One layer over the row picks the run under the pointer, so narrow cells need no 24 px target. -->\n <div\n class=\"lc-state-strip__scrub\"\n aria-hidden=\"true\"\n (pointerdown)=\"onScrubMove($event)\"\n (pointermove)=\"onScrubMove($event)\"\n (pointerup)=\"onScrubUp($event)\"\n (pointerleave)=\"onScrubLeave()\"\n (pointercancel)=\"onScrubLeave()\"\n ></div>\n </div>\n </div>\n @if (caption()) {\n <!-- The name of the hovered or focused run, in the tooltip look; the cell is what screen readers read. -->\n <div class=\"lc-state-strip__caption\" aria-hidden=\"true\">{{ caption() }}</div>\n }\n}\n",
190
+ styles: [":host{display:block;position:relative}:host(.lc-state-strip--empty){display:none}.lc-state-strip__grid{display:block}.lc-state-strip__row{display:flex;block-size:var(--lc-state-strip-height, 2rem)}.lc-state-strip__cell{position:relative;flex:1 1 0;min-inline-size:0;max-inline-size:var(--lc-state-strip-segment-max-width, 1.5rem)}.lc-state-strip__cell:hover{background-color:var(--color-surface-hover)}.lc-state-strip__cell:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-2px}.lc-state-strip__mark{position:absolute;inset-inline:min(3px,25%)}.lc-state-strip__mark--success{top:12.5%;height:34.375%;background-color:var(--color-text-success)}.lc-state-strip__mark--error{top:53.125%;height:34.375%;background-color:var(--color-text-error)}.lc-state-strip__mark--warning{top:25%;height:50%;background-color:var(--color-text-warning)}.lc-state-strip__mark--neutral{top:calc(50% - 1px);height:2px;background-color:var(--color-field-border)}.lc-state-strip__name{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.lc-state-strip__caption{position:absolute;top:calc(100% + 4px);inset-inline-start:0;z-index:1;inline-size:max-content;max-inline-size:100%;padding:var(--spacing-1) var(--spacing-2);border-radius:var(--border-radius-md);background-color:var(--color-text-primary);color:var(--color-surface);box-shadow:var(--elevation-2);font-size:var(--font-size-xs);line-height:1.5;overflow-wrap:break-word;hyphens:auto;pointer-events:none}.lc-state-strip__row{position:relative}.lc-state-strip__cell--hovered{background-color:var(--color-surface-hover)}.lc-state-strip__scrub{position:absolute;inset:0;touch-action:pan-y pinch-zoom}@media(forced-colors:active){.lc-state-strip__mark{forced-color-adjust:none;background-color:CanvasText}.lc-state-strip__mark--neutral{background-color:GrayText}.lc-state-strip__cell:focus-visible{outline-color:Highlight}.lc-state-strip__caption{border:1px solid CanvasText}}\n"]
168
191
  }]
169
192
  }],
170
193
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-state-strip.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/state-strip/state-strip.component.ts","../../../../libs/angular-ui-kit/src/lib/state-strip/state-strip.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n input,\n linkedSignal,\n signal,\n viewChildren,\n} from '@angular/core';\nimport { nameWithText } from '@lifecorner/angular-ui-kit/internal/a11y';\n\n/** State of one run in `lc-state-strip` (3.36). */\nexport type StateStripState = 'success' | 'warning' | 'error' | 'neutral';\n\n/** One run of `lc-state-strip`: its state, and a `label` that names it („Mo 06.10., 06:00“). */\nexport interface StateStripSegment {\n state: StateStripState;\n label: string;\n}\n\n/** The words of the four states, for the counts and the segment names. */\nexport type StateStripLabels = Record<StateStripState, string>;\n\n/** English state words of `lc-state-strip`; pass `stateLabels` for another language. */\nexport const DEFAULT_STATE_STRIP_LABELS: Readonly<StateStripLabels> = {\n success: 'succeeded',\n warning: 'with warnings',\n error: 'failed',\n neutral: 'pending',\n};\n\n/** The order of the counts in the accessible name. */\nconst STATES: readonly StateStripState[] = ['success', 'warning', 'error', 'neutral'];\n\n/**\n * The states of the last runs at a glance (3.36, quellen row 1): one segment\n * per run, oldest first. The state is a shape, not only a colour: success a\n * block in the upper half, error one in the lower half, warning a centred\n * block, neutral a flat dash, so the strip reads without hue and in forced\n * colours.\n *\n * Accessibility: an APG single-row grid. The strip is named by `ariaLabel`\n * and the count per state („Letzte 10 Ladeläufe, 9 geladen, 1\n * fehlgeschlagen“); each segment is a grid cell named by its `label` and\n * state word. One Tab stop (the newest run first), Arrow Left/Right, Home and\n * End move between the runs, Escape hides the caption. Hovering or focusing a\n * segment shows its name below the strip (hidden from screen readers, which\n * read the cell).\n *\n * Without segments it renders nothing. The segments are in the prerendered\n * HTML. CSS hooks: `--lc-state-strip-height` (default `2rem`) and\n * `--lc-state-strip-segment-max-width` (default `1.5rem`; more segments than\n * fit share the width, never overflowing).\n *\n * @example\n * ```html\n * <lc-state-strip\n * ariaLabel=\"Letzte 10 Ladeläufe\"\n * [stateLabels]=\"{ success: 'geladen', warning: 'teilweise geladen', error: 'fehlgeschlagen', neutral: 'ausstehend' }\"\n * [segments]=\"runs\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-state-strip',\n standalone: true,\n templateUrl: './state-strip.component.html',\n styleUrl: './state-strip.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'lc-state-strip',\n '[class.lc-state-strip--empty]': '!cells().length',\n },\n})\nexport class StateStripComponent {\n /**\n * The runs, in order, oldest first: one segment each. Empty renders nothing\n * (no box, no accessibility node).\n * @default []\n */\n readonly segments = input<readonly StateStripSegment[]>([]);\n\n /**\n * Start of the strip's accessible name; the count per state follows it\n * („Letzte 10 Ladeläufe, 9 geladen, 1 fehlgeschlagen“).\n * @default ''\n */\n readonly ariaLabel = input<string>('');\n\n /**\n * The state words for the counts and the segment names, over the English\n * defaults (`DEFAULT_STATE_STRIP_LABELS`: succeeded, with warnings, failed,\n * pending). German: `{ success: 'geladen', warning: 'teilweise geladen',\n * error: 'fehlgeschlagen', neutral: 'ausstehend' }`.\n * @default {}\n */\n readonly stateLabels = input<Partial<StateStripLabels>>({});\n\n private readonly cellEls = viewChildren<ElementRef<HTMLElement>>('cell');\n\n /** The state words in use. */\n private readonly words = computed<StateStripLabels>(() => ({ ...DEFAULT_STATE_STRIP_LABELS, ...this.stateLabels() }));\n\n /** One entry per segment: its state (unknown states draw as neutral) and its name. */\n protected readonly cells = computed(() => {\n const words = this.words();\n return (this.segments() ?? []).map((segment) => {\n const state: StateStripState = STATES.includes(segment.state) ? segment.state : 'neutral';\n return { state, name: nameWithText(segment.label ?? '', words[state]) };\n });\n });\n\n /** The strip's name: `ariaLabel`, then the count per state (0 left out). */\n protected readonly gridLabel = computed(() => {\n const words = this.words();\n const counts = STATES.map((state) => [state, this.cells().filter((c) => c.state === state).length] as const)\n .filter(([, count]) => count > 0)\n .map(([state, count]) => `${count} ${words[state]}`)\n .join(', ');\n const label = this.ariaLabel().trim();\n return label ? `${label}, ${counts}` : counts;\n });\n\n /** The roving Tab stop: the newest run until the user moves. */\n protected readonly current = linkedSignal(() => Math.max(0, this.cells().length - 1));\n\n /** The segment under the pointer, the one with focus, and whether Escape hid the caption. */\n private readonly hovered = signal<number | null>(null);\n private readonly focused = signal<number | null>(null);\n private readonly dismissed = signal(false);\n\n /** The segment whose name the caption shows, if any. */\n private readonly captionIndex = computed(() => {\n const hovered = this.hovered();\n if (hovered !== null) return hovered;\n return this.dismissed() ? null : this.focused();\n });\n\n /** The caption's text: the name of the hovered or focused segment ('' hides it). */\n protected readonly caption = computed(() => {\n const index = this.captionIndex();\n return index === null ? '' : (this.cells()[index]?.name ?? '');\n });\n\n protected onFocus(index: number): void {\n this.current.set(index);\n this.focused.set(index);\n this.dismissed.set(false);\n }\n\n protected onBlur(): void {\n this.focused.set(null);\n }\n\n protected onPointerEnter(index: number): void {\n this.hovered.set(index);\n }\n\n protected onPointerLeave(): void {\n this.hovered.set(null);\n }\n\n /** Arrow Left/Right, Home, End move between the runs (no wrap); Escape hides the caption. */\n protected onKeydown(event: KeyboardEvent): void {\n const last = this.cells().length - 1;\n const from = this.current();\n let to: number | null = null;\n switch (event.key) {\n case 'ArrowRight':\n to = Math.min(from + 1, last);\n break;\n case 'ArrowLeft':\n to = Math.max(from - 1, 0);\n break;\n case 'Home':\n to = 0;\n break;\n case 'End':\n to = last;\n break;\n case 'Escape':\n if (this.captionIndex() !== null) {\n this.hovered.set(null);\n this.dismissed.set(true);\n event.preventDefault();\n }\n return;\n default:\n return;\n }\n event.preventDefault();\n this.current.set(to);\n this.cellEls()[to]?.nativeElement.focus();\n }\n}\n","@if (cells().length) {\n <!-- An APG single-row grid: one Tab stop, arrows move between the runs. -->\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- the cells are the focusable controls (roving tabindex); their keys bubble here -->\n <div class=\"lc-state-strip__grid\" role=\"grid\" [attr.aria-label]=\"gridLabel()\" (keydown)=\"onKeydown($event)\">\n <div class=\"lc-state-strip__row\" role=\"row\">\n @for (cell of cells(); track $index; let i = $index) {\n <div\n #cell\n class=\"lc-state-strip__cell\"\n role=\"gridcell\"\n [attr.tabindex]=\"i === current() ? 0 : -1\"\n (focus)=\"onFocus(i)\"\n (blur)=\"onBlur()\"\n (pointerenter)=\"onPointerEnter(i)\"\n (pointerleave)=\"onPointerLeave()\"\n >\n <span class=\"lc-state-strip__mark lc-state-strip__mark--{{ cell.state }}\"></span>\n <span class=\"lc-state-strip__name\">{{ cell.name }}</span>\n </div>\n }\n </div>\n </div>\n @if (caption()) {\n <!-- The name of the hovered or focused run, in the tooltip look; the cell is what screen readers read. -->\n <div class=\"lc-state-strip__caption\" aria-hidden=\"true\">{{ caption() }}</div>\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-state-strip.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/state-strip/state-strip.component.ts","../../../../libs/angular-ui-kit/src/lib/state-strip/state-strip.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n input,\n linkedSignal,\n signal,\n viewChildren,\n} from '@angular/core';\nimport { nameWithText } from '@lifecorner/angular-ui-kit/internal/a11y';\n\n/** State of one run in `lc-state-strip` (3.36). */\nexport type StateStripState = 'success' | 'warning' | 'error' | 'neutral';\n\n/** One run of `lc-state-strip`: its state, and a `label` that names it („Mo 06.10., 06:00“). */\nexport interface StateStripSegment {\n state: StateStripState;\n label: string;\n}\n\n/** The words of the four states, for the counts and the segment names. */\nexport type StateStripLabels = Record<StateStripState, string>;\n\n/** English state words of `lc-state-strip`; pass `stateLabels` for another language. */\nexport const DEFAULT_STATE_STRIP_LABELS: Readonly<StateStripLabels> = {\n success: 'succeeded',\n warning: 'with warnings',\n error: 'failed',\n neutral: 'pending',\n};\n\n/** The order of the counts in the accessible name. */\nconst STATES: readonly StateStripState[] = ['success', 'warning', 'error', 'neutral'];\n\n/**\n * The states of the last runs at a glance (3.36, quellen row 1): one segment\n * per run, oldest first. The state is a shape, not only a colour: success a\n * block in the upper half, error one in the lower half, warning a centred\n * block, neutral a flat dash, so the strip reads without hue and in forced\n * colours.\n *\n * Accessibility: an APG single-row grid. The strip is named by `ariaLabel`\n * and the count per state („Letzte 10 Ladeläufe, 9 geladen, 1\n * fehlgeschlagen“); each segment is a grid cell named by its `label` and\n * state word. One Tab stop (the newest run first), Arrow Left/Right, Home and\n * End move between the runs, Escape hides the caption. Hovering or focusing a\n * segment shows its name below the strip (hidden from screen readers, which\n * read the cell). 3.37: the pointer uses the whole strip (one layer over the\n * row picks the run under it), so dense strips need no 24 px cells.\n *\n * Without segments it renders nothing. The segments are in the prerendered\n * HTML. CSS hooks: `--lc-state-strip-height` (default `2rem`) and\n * `--lc-state-strip-segment-max-width` (default `1.5rem`; more segments than\n * fit share the width, never overflowing).\n *\n * @example\n * ```html\n * <lc-state-strip\n * ariaLabel=\"Letzte 10 Ladeläufe\"\n * [stateLabels]=\"{ success: 'geladen', warning: 'teilweise geladen', error: 'fehlgeschlagen', neutral: 'ausstehend' }\"\n * [segments]=\"runs\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-state-strip',\n standalone: true,\n templateUrl: './state-strip.component.html',\n styleUrl: './state-strip.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'lc-state-strip',\n '[class.lc-state-strip--empty]': '!cells().length',\n },\n})\nexport class StateStripComponent {\n /**\n * The runs, in order, oldest first: one segment each. Empty renders nothing\n * (no box, no accessibility node).\n *\n * More runs than fit at 24 px (13 and more in 328 px) narrow the cells below\n * 24 px; the pointer then uses the whole strip: pointing at it, or sliding a\n * pressed finger along it, shows that run; the keyboard moves with the\n * arrows (3.37).\n * @default []\n */\n readonly segments = input<readonly StateStripSegment[]>([]);\n\n /**\n * Start of the strip's accessible name; the count per state follows it\n * („Letzte 10 Ladeläufe, 9 geladen, 1 fehlgeschlagen“).\n * @default ''\n */\n readonly ariaLabel = input<string>('');\n\n /**\n * The state words for the counts and the segment names, over the English\n * defaults (`DEFAULT_STATE_STRIP_LABELS`: succeeded, with warnings, failed,\n * pending). German: `{ success: 'geladen', warning: 'teilweise geladen',\n * error: 'fehlgeschlagen', neutral: 'ausstehend' }`.\n * @default {}\n */\n readonly stateLabels = input<Partial<StateStripLabels>>({});\n\n private readonly cellEls = viewChildren<ElementRef<HTMLElement>>('cell');\n\n /** The state words in use. */\n private readonly words = computed<StateStripLabels>(() => ({ ...DEFAULT_STATE_STRIP_LABELS, ...this.stateLabels() }));\n\n /** One entry per segment: its state (unknown states draw as neutral) and its name. */\n protected readonly cells = computed(() => {\n const words = this.words();\n return (this.segments() ?? []).map((segment) => {\n const state: StateStripState = STATES.includes(segment.state) ? segment.state : 'neutral';\n return { state, name: nameWithText(segment.label ?? '', words[state]) };\n });\n });\n\n /** The strip's name: `ariaLabel`, then the count per state (0 left out). */\n protected readonly gridLabel = computed(() => {\n const words = this.words();\n const counts = STATES.map((state) => [state, this.cells().filter((c) => c.state === state).length] as const)\n .filter(([, count]) => count > 0)\n .map(([state, count]) => `${count} ${words[state]}`)\n .join(', ');\n const label = this.ariaLabel().trim();\n return label ? `${label}, ${counts}` : counts;\n });\n\n /** The roving Tab stop: the newest run until the user moves. */\n protected readonly current = linkedSignal(() => Math.max(0, this.cells().length - 1));\n\n /** The segment under the pointer, the one with focus, and whether Escape hid the caption. */\n protected readonly hovered = signal<number | null>(null);\n private readonly focused = signal<number | null>(null);\n private readonly dismissed = signal(false);\n\n /** The segment whose name the caption shows, if any. */\n private readonly captionIndex = computed(() => {\n const hovered = this.hovered();\n if (hovered !== null) return hovered;\n return this.dismissed() ? null : this.focused();\n });\n\n /** The caption's text: the name of the hovered or focused segment ('' hides it). */\n protected readonly caption = computed(() => {\n const index = this.captionIndex();\n return index === null ? '' : (this.cells()[index]?.name ?? '');\n });\n\n protected onFocus(index: number): void {\n this.current.set(index);\n this.focused.set(index);\n this.dismissed.set(false);\n }\n\n protected onBlur(): void {\n this.focused.set(null);\n }\n\n protected onPointerEnter(index: number): void {\n this.hovered.set(index);\n }\n\n protected onPointerLeave(): void {\n this.hovered.set(null);\n }\n\n /** The run the scrub layer last picked; a move within it changes nothing (as entering a cell). */\n private scrubbed: number | null = null;\n\n /**\n * The pointer over the strip (3.37, request 057, 056 S-1): the run whose\n * cell spans the pointer's x shows in the caption; outside every cell none.\n */\n protected onScrubMove(event: PointerEvent): void {\n const index = this.cellAt(event.clientX);\n if (index === this.scrubbed) return;\n this.scrubbed = index;\n this.hovered.set(index);\n }\n\n /** Releasing the pointer (a click, a lifted finger) focuses the run under it. */\n protected onScrubUp(event: PointerEvent): void {\n const index = this.cellAt(event.clientX);\n if (index !== null) this.cellEls()[index]?.nativeElement.focus();\n }\n\n protected onScrubLeave(): void {\n this.scrubbed = null;\n this.hovered.set(null);\n }\n\n /** The index of the cell whose box spans `x`, any writing direction. */\n private cellAt(x: number): number | null {\n const cells = this.cellEls();\n for (let i = 0; i < cells.length; i++) {\n const box = cells[i].nativeElement.getBoundingClientRect();\n if (x >= box.left && x < box.right) return i;\n }\n return null;\n }\n\n /** Arrow Left/Right, Home, End move between the runs (no wrap); Escape hides the caption. */\n protected onKeydown(event: KeyboardEvent): void {\n const last = this.cells().length - 1;\n const from = this.current();\n let to: number | null = null;\n switch (event.key) {\n case 'ArrowRight':\n to = Math.min(from + 1, last);\n break;\n case 'ArrowLeft':\n to = Math.max(from - 1, 0);\n break;\n case 'Home':\n to = 0;\n break;\n case 'End':\n to = last;\n break;\n case 'Escape':\n if (this.captionIndex() !== null) {\n this.hovered.set(null);\n this.dismissed.set(true);\n event.preventDefault();\n }\n return;\n default:\n return;\n }\n event.preventDefault();\n this.current.set(to);\n this.cellEls()[to]?.nativeElement.focus();\n }\n}\n","@if (cells().length) {\n <!-- An APG single-row grid: one Tab stop, arrows move between the runs. -->\n <!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -- the cells are the focusable controls (roving tabindex); their keys bubble here -->\n <div class=\"lc-state-strip__grid\" role=\"grid\" [attr.aria-label]=\"gridLabel()\" (keydown)=\"onKeydown($event)\">\n <div class=\"lc-state-strip__row\" role=\"row\">\n @for (cell of cells(); track $index; let i = $index) {\n <div\n #cell\n class=\"lc-state-strip__cell\"\n role=\"gridcell\"\n [class.lc-state-strip__cell--hovered]=\"i === hovered()\"\n [attr.tabindex]=\"i === current() ? 0 : -1\"\n (focus)=\"onFocus(i)\"\n (blur)=\"onBlur()\"\n (pointerenter)=\"onPointerEnter(i)\"\n (pointerleave)=\"onPointerLeave()\"\n >\n <span class=\"lc-state-strip__mark lc-state-strip__mark--{{ cell.state }}\"></span>\n <span class=\"lc-state-strip__name\">{{ cell.name }}</span>\n </div>\n }\n <!-- 3.37: the pointer path. One layer over the row picks the run under the pointer, so narrow cells need no 24 px target. -->\n <div\n class=\"lc-state-strip__scrub\"\n aria-hidden=\"true\"\n (pointerdown)=\"onScrubMove($event)\"\n (pointermove)=\"onScrubMove($event)\"\n (pointerup)=\"onScrubUp($event)\"\n (pointerleave)=\"onScrubLeave()\"\n (pointercancel)=\"onScrubLeave()\"\n ></div>\n </div>\n </div>\n @if (caption()) {\n <!-- The name of the hovered or focused run, in the tooltip look; the cell is what screen readers read. -->\n <div class=\"lc-state-strip__caption\" aria-hidden=\"true\">{{ caption() }}</div>\n }\n}\n"],"mappings":"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"}