@skra/ui 0.3.1 → 0.5.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 (66) hide show
  1. package/consumer-guide/README.md +2 -1
  2. package/consumer-guide/layout.md +79 -0
  3. package/consumer-guide/setup-and-gotchas.md +2 -2
  4. package/fesm2022/skra-ui-accordion.mjs +2 -2
  5. package/fesm2022/skra-ui-accordion.mjs.map +1 -1
  6. package/fesm2022/skra-ui-alert.mjs +2 -2
  7. package/fesm2022/skra-ui-alert.mjs.map +1 -1
  8. package/fesm2022/skra-ui-avatar.mjs.map +1 -1
  9. package/fesm2022/skra-ui-badge.mjs.map +1 -1
  10. package/fesm2022/skra-ui-button.mjs +2 -2
  11. package/fesm2022/skra-ui-button.mjs.map +1 -1
  12. package/fesm2022/skra-ui-checkbox.mjs +2 -2
  13. package/fesm2022/skra-ui-checkbox.mjs.map +1 -1
  14. package/fesm2022/skra-ui-chip.mjs +2 -2
  15. package/fesm2022/skra-ui-chip.mjs.map +1 -1
  16. package/fesm2022/skra-ui-combobox.mjs +120 -120
  17. package/fesm2022/skra-ui-combobox.mjs.map +1 -1
  18. package/fesm2022/skra-ui-core.mjs +36 -36
  19. package/fesm2022/skra-ui-core.mjs.map +1 -1
  20. package/fesm2022/skra-ui-counter.mjs.map +1 -1
  21. package/fesm2022/skra-ui-datepicker.mjs +605 -190
  22. package/fesm2022/skra-ui-datepicker.mjs.map +1 -1
  23. package/fesm2022/skra-ui-dialog.mjs.map +1 -1
  24. package/fesm2022/skra-ui-field.mjs.map +1 -1
  25. package/fesm2022/skra-ui-icon.mjs.map +1 -1
  26. package/fesm2022/skra-ui-input.mjs +2 -2
  27. package/fesm2022/skra-ui-input.mjs.map +1 -1
  28. package/fesm2022/skra-ui-kbd.mjs.map +1 -1
  29. package/fesm2022/skra-ui-layout.mjs +40 -1
  30. package/fesm2022/skra-ui-layout.mjs.map +1 -1
  31. package/fesm2022/skra-ui-link.mjs +2 -2
  32. package/fesm2022/skra-ui-link.mjs.map +1 -1
  33. package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -1
  34. package/fesm2022/skra-ui-menu.mjs +2 -2
  35. package/fesm2022/skra-ui-menu.mjs.map +1 -1
  36. package/fesm2022/skra-ui-multi-select.mjs +94 -94
  37. package/fesm2022/skra-ui-multi-select.mjs.map +1 -1
  38. package/fesm2022/skra-ui-native-select.mjs +2 -2
  39. package/fesm2022/skra-ui-native-select.mjs.map +1 -1
  40. package/fesm2022/skra-ui-popover.mjs.map +1 -1
  41. package/fesm2022/skra-ui-progress.mjs.map +1 -1
  42. package/fesm2022/skra-ui-radio.mjs +2 -2
  43. package/fesm2022/skra-ui-radio.mjs.map +1 -1
  44. package/fesm2022/skra-ui-rating-display.mjs.map +1 -1
  45. package/fesm2022/skra-ui-select.mjs +92 -92
  46. package/fesm2022/skra-ui-select.mjs.map +1 -1
  47. package/fesm2022/skra-ui-skeleton.mjs +8 -8
  48. package/fesm2022/skra-ui-skeleton.mjs.map +1 -1
  49. package/fesm2022/skra-ui-star-rating.mjs +2 -2
  50. package/fesm2022/skra-ui-star-rating.mjs.map +1 -1
  51. package/fesm2022/skra-ui-status-dot.mjs.map +1 -1
  52. package/fesm2022/skra-ui-switch.mjs +2 -2
  53. package/fesm2022/skra-ui-switch.mjs.map +1 -1
  54. package/fesm2022/skra-ui-tabs.mjs +2 -2
  55. package/fesm2022/skra-ui-tabs.mjs.map +1 -1
  56. package/fesm2022/skra-ui-textarea.mjs +2 -2
  57. package/fesm2022/skra-ui-textarea.mjs.map +1 -1
  58. package/fesm2022/skra-ui-toast.mjs +2 -2
  59. package/fesm2022/skra-ui-toast.mjs.map +1 -1
  60. package/fesm2022/skra-ui-tooltip.mjs.map +1 -1
  61. package/fesm2022/skra-ui-typography.mjs.map +1 -1
  62. package/package.json +2 -2
  63. package/types/skra-ui-datepicker.d.ts +146 -46
  64. package/types/skra-ui-datepicker.d.ts.map +1 -1
  65. package/types/skra-ui-layout.d.ts +33 -2
  66. package/types/skra-ui-layout.d.ts.map +1 -1
@@ -177,130 +177,130 @@ class SkraCombobox extends _SkraListboxWidgetBase {
177
177
  }
178
178
  }
179
179
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraCombobox, deps: [], target: i0.ɵɵFactoryTarget.Component });
180
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraCombobox, isStandalone: true, selector: "skra-combobox", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", filterChange: "filterChange" }, host: { properties: { "attr.data-skra-loading": "loading() ? \"\" : null", "attr.data-skra-empty": "!loading() && options().length === 0 ? \"\" : null" } }, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }], exportAs: ["skraCombobox"], usesInheritance: true, ngImport: i0, template: `
181
- <input
182
- #trigger
183
- class="skra-combobox-input"
184
- type="text"
185
- role="combobox"
186
- autocomplete="off"
187
- aria-autocomplete="list"
188
- aria-haspopup="listbox"
189
- [attr.id]="triggerId()"
190
- [attr.aria-controls]="panelId"
191
- [attr.aria-expanded]="isOpen()"
192
- [attr.aria-activedescendant]="activeOptionId()"
193
- [attr.aria-describedby]="describedBy()"
194
- [attr.aria-invalid]="hasError() ? 'true' : null"
195
- [attr.aria-required]="isRequired() ? 'true' : null"
196
- [attr.aria-busy]="loading() ? 'true' : null"
197
- [attr.placeholder]="placeholder()"
198
- [disabled]="isDisabled()"
199
- [readOnly]="isReadonly()"
200
- [value]="displayValue()"
201
- (focus)="onFocus()"
202
- (input)="onInput($event)"
203
- (keydown)="onTriggerKeydown($event)"
204
- (blur)="onBlur()"
205
- />
206
- <button
207
- class="skra-combobox-toggle"
208
- type="button"
209
- tabindex="-1"
210
- aria-hidden="true"
211
- [disabled]="isDisabled()"
212
- (mousedown)="$event.preventDefault()"
213
- (click)="toggle()"
214
- >
215
- <span class="skra-combobox-arrow" aria-hidden="true"></span>
216
- </button>
217
-
218
- <div
219
- #panel
220
- class="skra-combobox-listbox"
221
- [id]="panelId"
222
- role="listbox"
223
- popover="auto"
224
- (mousedown)="$event.preventDefault()"
225
- (toggle)="onPanelToggle($event)"
226
- >
227
- <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
228
- a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
229
- :empty in SCSS. -->
230
- <div class="skra-combobox-status" role="status" aria-live="polite">
231
- @if (loading()) {
232
- <ng-content select="[skraLoadingIndicator]"></ng-content>
233
- <span class="skra-combobox-spinner" aria-hidden="true"></span>
234
- } @else if (options().length === 0) {
235
- <ng-content select="[skraComboboxEmpty]"></ng-content>
236
- }
237
- </div>
238
- <ng-content></ng-content>
239
- </div>
180
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraCombobox, isStandalone: true, selector: "skra-combobox", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", filterChange: "filterChange" }, host: { properties: { "attr.data-skra-loading": "loading() ? \"\" : null", "attr.data-skra-empty": "!loading() && options().length === 0 ? \"\" : null" } }, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }], exportAs: ["skraCombobox"], usesInheritance: true, ngImport: i0, template: `
181
+ <input
182
+ #trigger
183
+ class="skra-combobox-input"
184
+ type="text"
185
+ role="combobox"
186
+ autocomplete="off"
187
+ aria-autocomplete="list"
188
+ aria-haspopup="listbox"
189
+ [attr.id]="triggerId()"
190
+ [attr.aria-controls]="panelId"
191
+ [attr.aria-expanded]="isOpen()"
192
+ [attr.aria-activedescendant]="activeOptionId()"
193
+ [attr.aria-describedby]="describedBy()"
194
+ [attr.aria-invalid]="hasError() ? 'true' : null"
195
+ [attr.aria-required]="isRequired() ? 'true' : null"
196
+ [attr.aria-busy]="loading() ? 'true' : null"
197
+ [attr.placeholder]="placeholder()"
198
+ [disabled]="isDisabled()"
199
+ [readOnly]="isReadonly()"
200
+ [value]="displayValue()"
201
+ (focus)="onFocus()"
202
+ (input)="onInput($event)"
203
+ (keydown)="onTriggerKeydown($event)"
204
+ (blur)="onBlur()"
205
+ />
206
+ <button
207
+ class="skra-combobox-toggle"
208
+ type="button"
209
+ tabindex="-1"
210
+ aria-hidden="true"
211
+ [disabled]="isDisabled()"
212
+ (mousedown)="$event.preventDefault()"
213
+ (click)="toggle()"
214
+ >
215
+ <span class="skra-combobox-arrow" aria-hidden="true"></span>
216
+ </button>
217
+
218
+ <div
219
+ #panel
220
+ class="skra-combobox-listbox"
221
+ [id]="panelId"
222
+ role="listbox"
223
+ popover="auto"
224
+ (mousedown)="$event.preventDefault()"
225
+ (toggle)="onPanelToggle($event)"
226
+ >
227
+ <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
228
+ a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
229
+ :empty in SCSS. -->
230
+ <div class="skra-combobox-status" role="status" aria-live="polite">
231
+ @if (loading()) {
232
+ <ng-content select="[skraLoadingIndicator]"></ng-content>
233
+ <span class="skra-combobox-spinner" aria-hidden="true"></span>
234
+ } @else if (options().length === 0) {
235
+ <ng-content select="[skraComboboxEmpty]"></ng-content>
236
+ }
237
+ </div>
238
+ <ng-content></ng-content>
239
+ </div>
240
240
  `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-combobox-gap);inline-size:100%;min-block-size:var(--skra-comp-combobox-min-block-size);box-sizing:border-box;margin:0;padding-inline:var(--skra-comp-combobox-padding-inline);background:var(--skra-comp-combobox-background);color:var(--skra-comp-combobox-foreground);border-color:var(--skra-comp-combobox-border-color);border-width:var(--skra-comp-combobox-border-width);border-style:var(--skra-comp-combobox-border-style);border-radius:var(--skra-comp-combobox-radius);box-shadow:var(--skra-comp-combobox-shadow);transition:background-color var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing),border-color var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing)}:host:has(.skra-combobox-input:focus-visible){outline:var(--skra-comp-combobox-focus-ring-width) solid var(--skra-comp-combobox-focus-ring-color);outline-offset:.125rem}:host([data-skra-disabled]){background:var(--skra-comp-combobox-background-disabled);color:var(--skra-comp-combobox-foreground-disabled)}:host([data-skra-readonly]){background:var(--skra-comp-combobox-background-readonly)}:host([data-skra-validation=invalid]){border-color:var(--skra-comp-combobox-border-color-validation-invalid)}.skra-combobox-input{flex:1 1 auto;min-inline-size:0;box-sizing:border-box;margin:0;padding-block:var(--skra-comp-combobox-padding-block);padding-inline:0;border:none;background:transparent;color:inherit;appearance:none;outline:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing)}.skra-combobox-input::placeholder{color:var(--skra-comp-combobox-foreground-disabled)}.skra-combobox-input:disabled{color:var(--skra-comp-combobox-foreground-disabled);cursor:not-allowed}.skra-combobox-toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-combobox-arrow-icon-size);block-size:var(--skra-comp-combobox-arrow-icon-size);margin:0;padding:0;border:none;background:transparent;cursor:pointer}.skra-combobox-toggle:disabled{cursor:not-allowed}.skra-combobox-arrow{inline-size:100%;block-size:100%;background-color:var(--skra-comp-combobox-arrow-color);mask:url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M6%209l6%206%206-6'%20fill='none'%20stroke='%23000'%20stroke-width='2'/%3E%3C/svg%3E\") no-repeat center/contain;-webkit-mask:url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M6%209l6%206%206-6'%20fill='none'%20stroke='%23000'%20stroke-width='2'/%3E%3C/svg%3E\") no-repeat center/contain;transition:transform var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing)}:host:has(.skra-combobox-input[aria-expanded=true]) .skra-combobox-arrow{transform:rotate(180deg)}.skra-combobox-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-combobox-panel-padding-block);padding-inline:var(--skra-comp-combobox-panel-padding-inline);background:var(--skra-comp-combobox-panel-background);color:var(--skra-comp-combobox-panel-foreground);border:var(--skra-comp-combobox-border-width) solid var(--skra-comp-combobox-panel-border-color);border-radius:var(--skra-comp-combobox-panel-radius);box-shadow:var(--skra-comp-combobox-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-combobox-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing),display var(--skra-comp-combobox-motion-duration) allow-discrete,overlay var(--skra-comp-combobox-motion-duration) allow-discrete}.skra-combobox-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-combobox-listbox:popover-open{opacity:0}}.skra-combobox-status{display:flex;align-items:center;gap:var(--skra-comp-combobox-gap);padding-block:var(--skra-comp-combobox-status-padding-block);padding-inline:var(--skra-comp-combobox-status-padding-inline);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-combobox-status:empty{display:none}:host([data-skra-empty]) .skra-combobox-status{color:var(--skra-comp-combobox-empty-foreground)}.skra-combobox-status:has([skraLoadingIndicator]) .skra-combobox-spinner{display:none}.skra-combobox-spinner{flex:none;inline-size:1em;block-size:1em;border:.125rem solid var(--skra-comp-combobox-loading-indicator-color);border-block-start-color:transparent;border-radius:50%;animation:skra-combobox-spin .6s linear infinite}@keyframes skra-combobox-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){:host,.skra-combobox-arrow,.skra-combobox-listbox{transition-duration:0ms}.skra-combobox-spinner{animation-duration:0ms}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
241
241
  }
242
242
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraCombobox, decorators: [{
243
243
  type: Component,
244
- args: [{ selector: 'skra-combobox', standalone: true, exportAs: 'skraCombobox', template: `
245
- <input
246
- #trigger
247
- class="skra-combobox-input"
248
- type="text"
249
- role="combobox"
250
- autocomplete="off"
251
- aria-autocomplete="list"
252
- aria-haspopup="listbox"
253
- [attr.id]="triggerId()"
254
- [attr.aria-controls]="panelId"
255
- [attr.aria-expanded]="isOpen()"
256
- [attr.aria-activedescendant]="activeOptionId()"
257
- [attr.aria-describedby]="describedBy()"
258
- [attr.aria-invalid]="hasError() ? 'true' : null"
259
- [attr.aria-required]="isRequired() ? 'true' : null"
260
- [attr.aria-busy]="loading() ? 'true' : null"
261
- [attr.placeholder]="placeholder()"
262
- [disabled]="isDisabled()"
263
- [readOnly]="isReadonly()"
264
- [value]="displayValue()"
265
- (focus)="onFocus()"
266
- (input)="onInput($event)"
267
- (keydown)="onTriggerKeydown($event)"
268
- (blur)="onBlur()"
269
- />
270
- <button
271
- class="skra-combobox-toggle"
272
- type="button"
273
- tabindex="-1"
274
- aria-hidden="true"
275
- [disabled]="isDisabled()"
276
- (mousedown)="$event.preventDefault()"
277
- (click)="toggle()"
278
- >
279
- <span class="skra-combobox-arrow" aria-hidden="true"></span>
280
- </button>
281
-
282
- <div
283
- #panel
284
- class="skra-combobox-listbox"
285
- [id]="panelId"
286
- role="listbox"
287
- popover="auto"
288
- (mousedown)="$event.preventDefault()"
289
- (toggle)="onPanelToggle($event)"
290
- >
291
- <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
292
- a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
293
- :empty in SCSS. -->
294
- <div class="skra-combobox-status" role="status" aria-live="polite">
295
- @if (loading()) {
296
- <ng-content select="[skraLoadingIndicator]"></ng-content>
297
- <span class="skra-combobox-spinner" aria-hidden="true"></span>
298
- } @else if (options().length === 0) {
299
- <ng-content select="[skraComboboxEmpty]"></ng-content>
300
- }
301
- </div>
302
- <ng-content></ng-content>
303
- </div>
244
+ args: [{ selector: 'skra-combobox', standalone: true, exportAs: 'skraCombobox', template: `
245
+ <input
246
+ #trigger
247
+ class="skra-combobox-input"
248
+ type="text"
249
+ role="combobox"
250
+ autocomplete="off"
251
+ aria-autocomplete="list"
252
+ aria-haspopup="listbox"
253
+ [attr.id]="triggerId()"
254
+ [attr.aria-controls]="panelId"
255
+ [attr.aria-expanded]="isOpen()"
256
+ [attr.aria-activedescendant]="activeOptionId()"
257
+ [attr.aria-describedby]="describedBy()"
258
+ [attr.aria-invalid]="hasError() ? 'true' : null"
259
+ [attr.aria-required]="isRequired() ? 'true' : null"
260
+ [attr.aria-busy]="loading() ? 'true' : null"
261
+ [attr.placeholder]="placeholder()"
262
+ [disabled]="isDisabled()"
263
+ [readOnly]="isReadonly()"
264
+ [value]="displayValue()"
265
+ (focus)="onFocus()"
266
+ (input)="onInput($event)"
267
+ (keydown)="onTriggerKeydown($event)"
268
+ (blur)="onBlur()"
269
+ />
270
+ <button
271
+ class="skra-combobox-toggle"
272
+ type="button"
273
+ tabindex="-1"
274
+ aria-hidden="true"
275
+ [disabled]="isDisabled()"
276
+ (mousedown)="$event.preventDefault()"
277
+ (click)="toggle()"
278
+ >
279
+ <span class="skra-combobox-arrow" aria-hidden="true"></span>
280
+ </button>
281
+
282
+ <div
283
+ #panel
284
+ class="skra-combobox-listbox"
285
+ [id]="panelId"
286
+ role="listbox"
287
+ popover="auto"
288
+ (mousedown)="$event.preventDefault()"
289
+ (toggle)="onPanelToggle($event)"
290
+ >
291
+ <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
292
+ a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
293
+ :empty in SCSS. -->
294
+ <div class="skra-combobox-status" role="status" aria-live="polite">
295
+ @if (loading()) {
296
+ <ng-content select="[skraLoadingIndicator]"></ng-content>
297
+ <span class="skra-combobox-spinner" aria-hidden="true"></span>
298
+ } @else if (options().length === 0) {
299
+ <ng-content select="[skraComboboxEmpty]"></ng-content>
300
+ }
301
+ </div>
302
+ <ng-content></ng-content>
303
+ </div>
304
304
  `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
305
305
  '[attr.data-skra-loading]': 'loading() ? "" : null',
306
306
  '[attr.data-skra-empty]': '!loading() && options().length === 0 ? "" : null',
@@ -1 +1 @@
1
- {"version":3,"file":"skra-ui-combobox.mjs","sources":["../../../../libs/ui/combobox/src/lib/combobox/combobox.ts","../../../../libs/ui/combobox/src/lib/combobox/combobox-empty.ts","../../../../libs/ui/combobox/src/skra-ui-combobox.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n ViewEncapsulation,\r\n afterRenderEffect,\r\n booleanAttribute,\r\n computed,\r\n inject,\r\n input,\r\n output,\r\n signal,\r\n} from '@angular/core';\r\n\r\nimport { _SkraListboxWidgetBase } from '@skra/ui/core';\r\nimport {\r\n SKRA_LISTBOX_PANEL,\r\n type SkraListboxKeyboardItem,\r\n} from '@skra/ui/core';\r\n\r\ndeclare const ngDevMode: boolean | undefined;\r\n\r\n/**\r\n * SkraCombobox — the WAI-ARIA APG **Editable Combobox** (list autocomplete, manual selection): an\r\n * editable `<input role=\"combobox\" aria-autocomplete=\"list\">` whose top-layer listbox (Popover API,\r\n * `popover=\"auto\"`, ADR 0044) of projected `skra-option` children NARROWS as the user types. The first\r\n * Tier-3 / higher-order component. Single-cardinality (ADR 0046); the multi sibling is future work.\r\n *\r\n * - **Filtering + option set are CONSUMER-OWNED (ADR 0049):** Forge emits the typed text via\r\n * `filterChange` (no debounce — timing is the consumer's); the consumer projects the matching\r\n * `skra-option` subset (sync computed OR async fetch). There is NO `[items]` input and NO internal\r\n * matcher — Forge renders exactly what is projected.\r\n * - **Value is OPTION-BOUND (ADR 0049):** `value = input<T>()` + `valueChange` (consumer-owned, ADR 037);\r\n * a committed value is always an existing option's `T`. On cancel/blur the input text reverts to the\r\n * selected option's label — typed text that matched nothing NEVER leaks as the value (free-text commit\r\n * is the separate `skra-autocomplete`).\r\n * - **Async (ADR 0049/028):** `loading` reflects `aria-busy` + drives a panel loading branch\r\n * (`[skraLoadingIndicator]` slot + fallback); an empty projected set drives the `[skraComboboxEmpty]`\r\n * branch; both are announced via `aria-live`.\r\n *\r\n * The trigger-shape-agnostic substrate (field cascade, panel toggle/positioner, keyboard wiring,\r\n * open/close, the `SkraListboxPanel` selection seam) lives in {@link _SkraListboxWidgetBase}, shared\r\n * verbatim with the select family (ADR 0046 amendment). `skra-option` is reused verbatim — combobox\r\n * provides the `SKRA_LISTBOX_PANEL` token so the option's selection/keyboard/indicator wiring works\r\n * unchanged.\r\n *\r\n * Full contract: adr/specs/component-combobox-spec.md.\r\n */\r\n@Component({\r\n selector: 'skra-combobox',\r\n standalone: true,\r\n exportAs: 'skraCombobox',\r\n template: `\r\n <input\r\n #trigger\r\n class=\"skra-combobox-input\"\r\n type=\"text\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n aria-autocomplete=\"list\"\r\n aria-haspopup=\"listbox\"\r\n [attr.id]=\"triggerId()\"\r\n [attr.aria-controls]=\"panelId\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-activedescendant]=\"activeOptionId()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.aria-invalid]=\"hasError() ? 'true' : null\"\r\n [attr.aria-required]=\"isRequired() ? 'true' : null\"\r\n [attr.aria-busy]=\"loading() ? 'true' : null\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [disabled]=\"isDisabled()\"\r\n [readOnly]=\"isReadonly()\"\r\n [value]=\"displayValue()\"\r\n (focus)=\"onFocus()\"\r\n (input)=\"onInput($event)\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n (blur)=\"onBlur()\"\r\n />\r\n <button\r\n class=\"skra-combobox-toggle\"\r\n type=\"button\"\r\n tabindex=\"-1\"\r\n aria-hidden=\"true\"\r\n [disabled]=\"isDisabled()\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (click)=\"toggle()\"\r\n >\r\n <span class=\"skra-combobox-arrow\" aria-hidden=\"true\"></span>\r\n </button>\r\n\r\n <div\r\n #panel\r\n class=\"skra-combobox-listbox\"\r\n [id]=\"panelId\"\r\n role=\"listbox\"\r\n popover=\"auto\"\r\n (mousedown)=\"$event.preventDefault()\"\r\n (toggle)=\"onPanelToggle($event)\"\r\n >\r\n <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —\r\n a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via\r\n :empty in SCSS. -->\r\n <div class=\"skra-combobox-status\" role=\"status\" aria-live=\"polite\">\r\n @if (loading()) {\r\n <ng-content select=\"[skraLoadingIndicator]\"></ng-content>\r\n <span class=\"skra-combobox-spinner\" aria-hidden=\"true\"></span>\r\n } @else if (options().length === 0) {\r\n <ng-content select=\"[skraComboboxEmpty]\"></ng-content>\r\n }\r\n </div>\r\n <ng-content></ng-content>\r\n </div>\r\n `,\r\n styleUrl: './combobox.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n host: {\r\n '[attr.data-skra-loading]': 'loading() ? \"\" : null',\r\n '[attr.data-skra-empty]':\r\n '!loading() && options().length === 0 ? \"\" : null',\r\n },\r\n providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }],\r\n})\r\nexport class SkraCombobox<T = unknown> extends _SkraListboxWidgetBase<T> {\r\n /** Combobox panel token namespace — `--skra-comp-combobox-panel-*`. */\r\n protected readonly panelTokenPrefix = 'combobox';\r\n\r\n /** Consumer-owned value channel (ADR 037) — single, option-bound `T`. */\r\n readonly value = input<T>();\r\n /** Fires ONLY on user selection of an option — never on a programmatic `value()` change (ADR 037). */\r\n readonly valueChange = output<T>();\r\n /** The typed text, emitted on EVERY input change. Consumer-driven projection; NO Forge debounce (ADR 0049). */\r\n readonly filterChange = output<string>();\r\n /** Async hint (ADR 0049/028) — reflects `aria-busy` + drives the panel loading branch. */\r\n readonly loading = input(false, { transform: booleanAttribute });\r\n /** Cardinality hint (ADR 0046) — single-select. */\r\n readonly multiple = false;\r\n\r\n /** Internal editable-text state; emitted out via `filterChange`. NOT the value (ADR 0049). */\r\n protected readonly query = signal('');\r\n private readonly isFocused = signal(false);\r\n\r\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\r\n\r\n /**\r\n * Position the panel against — and match its width to — the HOST box, not the inner input. The input\r\n * is inset (host padding + chevron), so anchoring to it would make a narrower, misaligned panel; the\r\n * host gives full-width parity with `skra-select`.\r\n */\r\n protected override positioningAnchor(): HTMLElement {\r\n return this.hostEl.nativeElement;\r\n }\r\n\r\n /** The option whose value matches `value()` (ADR 037 selection model) — feeds the displayed text + active seed. */\r\n protected readonly selectedOption = computed(\r\n () => this.options().find((o) => this.isSelected(o.value())) ?? null,\r\n );\r\n\r\n /**\r\n * The text shown in the input: the live query while editing (focused), otherwise the selected option's\r\n * label. This is the controlled-input seam that enforces the option-bound value (ADR 0049) — on blur the\r\n * input snaps back to the selected label, so unmatched typed text never persists.\r\n */\r\n protected readonly displayValue = computed(() =>\r\n this.isFocused() ? this.query() : (this.selectedOption()?.getLabel() ?? ''),\r\n );\r\n\r\n constructor() {\r\n super();\r\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\r\n // Dev-only consumer warning. Uses afterRenderEffect (NOT effect) so the projected\r\n // options' required `value` inputs are read only AFTER each render applies their\r\n // bindings — an eager read during the initial CD (SSR / first render) OR a runtime\r\n // re-projection (consumer filtering adds fresh <skra-option>s) would throw NG0950.\r\n // afterRender hooks never run on the server, so this stays SSR-safe. Covers both\r\n // F-13 (initial render) and F-16 (runtime re-projection, surfaced in dogfood).\r\n afterRenderEffect(() => {\r\n const v = this.value();\r\n if (v === undefined) return;\r\n const matched = this.options().some((o) =>\r\n this.compareWith()(o.value() as T, v),\r\n );\r\n if (!matched && this.options().length) {\r\n console.warn(\r\n '[skra-combobox] value has no matching <skra-option> in the projected set — the input ' +\r\n 'shows empty (likely a compareWith mismatch, or the selected option was filtered out).',\r\n );\r\n }\r\n });\r\n }\r\n }\r\n\r\n // ── SkraListboxPanel seam ──\r\n isSelected(value: unknown): boolean {\r\n const current = this.value();\r\n return current !== undefined && this.compareWith()(current, value as T);\r\n }\r\n\r\n selectValue(value: unknown): void {\r\n const opt =\r\n this.options().find((o) =>\r\n this.compareWith()(o.value() as T, value as T),\r\n ) ?? null;\r\n this.valueChange.emit(value as T);\r\n // Optimistic display: show the picked option's label immediately (focus is retained).\r\n this.query.set(opt?.getLabel() ?? '');\r\n this.close(); // single-select: pick + close\r\n }\r\n\r\n protected activeSeedItem(): SkraListboxKeyboardItem | null {\r\n return this.selectedOption();\r\n }\r\n\r\n // ── Editable-input trigger model (the divergence from the select-family button trigger) ──\r\n protected onFocus(): void {\r\n // Start editing from the current value's label (APG: the textbox shows the value, editable).\r\n this.query.set(this.selectedOption()?.getLabel() ?? '');\r\n this.isFocused.set(true);\r\n }\r\n\r\n protected onInput(event: Event): void {\r\n if (this.isReadonly()) return;\r\n const text = (event.target as HTMLInputElement).value;\r\n this.query.set(text);\r\n this.filterChange.emit(text); // ADR 0049 — consumer owns the matching + projection\r\n if (!this.isOpen()) this.open();\r\n }\r\n\r\n protected onBlur(): void {\r\n this.isFocused.set(false); // displayValue() snaps back to the selected label (option-bound, ADR 0049)\r\n if (this.isOpen()) this.close();\r\n }\r\n\r\n protected onTriggerKeydown(event: KeyboardEvent): void {\r\n if (this.isDisabled() || this.isReadonly()) return;\r\n const key = event.key;\r\n\r\n if (key === 'ArrowDown' || key === 'ArrowUp') {\r\n event.preventDefault(); // do not move the text caret\r\n if (event.altKey) {\r\n // APG: Alt+Down opens (no active move); Alt+Up closes + reverts (keeps value).\r\n if (key === 'ArrowDown') {\r\n if (!this.isOpen()) this.open();\r\n } else if (this.isOpen()) {\r\n this.query.set(this.selectedOption()?.getLabel() ?? '');\r\n this.close();\r\n }\r\n return;\r\n }\r\n if (!this.isOpen()) this.open();\r\n else this.keyboard.onKeydown(event); // move the active descendant\r\n return;\r\n }\r\n if (key === 'Enter') {\r\n if (this.isOpen()) {\r\n event.preventDefault();\r\n this.selectActive(); // select the active option (no-op if none / soft-disabled)\r\n }\r\n return;\r\n }\r\n if (key === 'Escape') {\r\n if (this.isOpen()) {\r\n event.preventDefault();\r\n this.query.set(this.selectedOption()?.getLabel() ?? ''); // revert (keep value)\r\n this.close();\r\n }\r\n return;\r\n }\r\n // Home/End/printable/Backspace: native text editing — NOT routed to the listbox (no type-ahead).\r\n }\r\n\r\n protected toggle(): void {\r\n if (this.isDisabled() || this.isReadonly()) return; // pointer parity with the keydown/input guards (M1)\r\n const el = this.triggerRef()?.nativeElement;\r\n if (this.isOpen()) {\r\n this.close();\r\n } else {\r\n el?.focus(); // fires onFocus → seeds the editable text\r\n this.open();\r\n }\r\n }\r\n}\r\n","import { Directive } from '@angular/core';\r\n\r\n/**\r\n * SkraComboboxEmpty — the no-results marker (ADR 0049). Place on the element that should show inside the\r\n * combobox listbox when the consumer's filter yields zero `skra-option` children:\r\n *\r\n * <skra-combobox (filterChange)=\"query.set($event)\" [value]=\"city()\" (valueChange)=\"city.set($event)\">\r\n * @for (c of visible(); track c.id) { <skra-option [value]=\"c.id\">{{ c.name }}</skra-option> }\r\n * <span skraComboboxEmpty>No matches</span>\r\n * </skra-combobox>\r\n *\r\n * The text is consumer-supplied (i18n is the consumer's concern — Forge ships no hardcoded string).\r\n * Forge toggles its visibility via `@if (!loading() && options().length === 0)`; the marker only carries\r\n * the projection selector, so no logic lives here.\r\n */\r\n@Directive({\r\n selector: '[skraComboboxEmpty]',\r\n standalone: true,\r\n})\r\nexport class SkraComboboxEmpty {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skra-ui-combobox.mjs","sources":["../../../../libs/ui/combobox/src/lib/combobox/combobox.ts","../../../../libs/ui/combobox/src/lib/combobox/combobox-empty.ts","../../../../libs/ui/combobox/src/skra-ui-combobox.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n afterRenderEffect,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { _SkraListboxWidgetBase } from '@skra/ui/core';\nimport {\n SKRA_LISTBOX_PANEL,\n type SkraListboxKeyboardItem,\n} from '@skra/ui/core';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * SkraCombobox — the WAI-ARIA APG **Editable Combobox** (list autocomplete, manual selection): an\n * editable `<input role=\"combobox\" aria-autocomplete=\"list\">` whose top-layer listbox (Popover API,\n * `popover=\"auto\"`, ADR 0044) of projected `skra-option` children NARROWS as the user types. The first\n * Tier-3 / higher-order component. Single-cardinality (ADR 0046); the multi sibling is future work.\n *\n * - **Filtering + option set are CONSUMER-OWNED (ADR 0049):** Forge emits the typed text via\n * `filterChange` (no debounce — timing is the consumer's); the consumer projects the matching\n * `skra-option` subset (sync computed OR async fetch). There is NO `[items]` input and NO internal\n * matcher — Forge renders exactly what is projected.\n * - **Value is OPTION-BOUND (ADR 0049):** `value = input<T>()` + `valueChange` (consumer-owned, ADR 037);\n * a committed value is always an existing option's `T`. On cancel/blur the input text reverts to the\n * selected option's label — typed text that matched nothing NEVER leaks as the value (free-text commit\n * is the separate `skra-autocomplete`).\n * - **Async (ADR 0049/028):** `loading` reflects `aria-busy` + drives a panel loading branch\n * (`[skraLoadingIndicator]` slot + fallback); an empty projected set drives the `[skraComboboxEmpty]`\n * branch; both are announced via `aria-live`.\n *\n * The trigger-shape-agnostic substrate (field cascade, panel toggle/positioner, keyboard wiring,\n * open/close, the `SkraListboxPanel` selection seam) lives in {@link _SkraListboxWidgetBase}, shared\n * verbatim with the select family (ADR 0046 amendment). `skra-option` is reused verbatim — combobox\n * provides the `SKRA_LISTBOX_PANEL` token so the option's selection/keyboard/indicator wiring works\n * unchanged.\n *\n * Full contract: adr/specs/component-combobox-spec.md.\n */\n@Component({\n selector: 'skra-combobox',\n standalone: true,\n exportAs: 'skraCombobox',\n template: `\n <input\n #trigger\n class=\"skra-combobox-input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n [attr.id]=\"triggerId()\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"hasError() ? 'true' : null\"\n [attr.aria-required]=\"isRequired() ? 'true' : null\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readOnly]=\"isReadonly()\"\n [value]=\"displayValue()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n class=\"skra-combobox-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggle()\"\n >\n <span class=\"skra-combobox-arrow\" aria-hidden=\"true\"></span>\n </button>\n\n <div\n #panel\n class=\"skra-combobox-listbox\"\n [id]=\"panelId\"\n role=\"listbox\"\n popover=\"auto\"\n (mousedown)=\"$event.preventDefault()\"\n (toggle)=\"onPanelToggle($event)\"\n >\n <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —\n a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via\n :empty in SCSS. -->\n <div class=\"skra-combobox-status\" role=\"status\" aria-live=\"polite\">\n @if (loading()) {\n <ng-content select=\"[skraLoadingIndicator]\"></ng-content>\n <span class=\"skra-combobox-spinner\" aria-hidden=\"true\"></span>\n } @else if (options().length === 0) {\n <ng-content select=\"[skraComboboxEmpty]\"></ng-content>\n }\n </div>\n <ng-content></ng-content>\n </div>\n `,\n styleUrl: './combobox.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n '[attr.data-skra-loading]': 'loading() ? \"\" : null',\n '[attr.data-skra-empty]':\n '!loading() && options().length === 0 ? \"\" : null',\n },\n providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }],\n})\nexport class SkraCombobox<T = unknown> extends _SkraListboxWidgetBase<T> {\n /** Combobox panel token namespace — `--skra-comp-combobox-panel-*`. */\n protected readonly panelTokenPrefix = 'combobox';\n\n /** Consumer-owned value channel (ADR 037) — single, option-bound `T`. */\n readonly value = input<T>();\n /** Fires ONLY on user selection of an option — never on a programmatic `value()` change (ADR 037). */\n readonly valueChange = output<T>();\n /** The typed text, emitted on EVERY input change. Consumer-driven projection; NO Forge debounce (ADR 0049). */\n readonly filterChange = output<string>();\n /** Async hint (ADR 0049/028) — reflects `aria-busy` + drives the panel loading branch. */\n readonly loading = input(false, { transform: booleanAttribute });\n /** Cardinality hint (ADR 0046) — single-select. */\n readonly multiple = false;\n\n /** Internal editable-text state; emitted out via `filterChange`. NOT the value (ADR 0049). */\n protected readonly query = signal('');\n private readonly isFocused = signal(false);\n\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * Position the panel against — and match its width to — the HOST box, not the inner input. The input\n * is inset (host padding + chevron), so anchoring to it would make a narrower, misaligned panel; the\n * host gives full-width parity with `skra-select`.\n */\n protected override positioningAnchor(): HTMLElement {\n return this.hostEl.nativeElement;\n }\n\n /** The option whose value matches `value()` (ADR 037 selection model) — feeds the displayed text + active seed. */\n protected readonly selectedOption = computed(\n () => this.options().find((o) => this.isSelected(o.value())) ?? null,\n );\n\n /**\n * The text shown in the input: the live query while editing (focused), otherwise the selected option's\n * label. This is the controlled-input seam that enforces the option-bound value (ADR 0049) — on blur the\n * input snaps back to the selected label, so unmatched typed text never persists.\n */\n protected readonly displayValue = computed(() =>\n this.isFocused() ? this.query() : (this.selectedOption()?.getLabel() ?? ''),\n );\n\n constructor() {\n super();\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n // Dev-only consumer warning. Uses afterRenderEffect (NOT effect) so the projected\n // options' required `value` inputs are read only AFTER each render applies their\n // bindings — an eager read during the initial CD (SSR / first render) OR a runtime\n // re-projection (consumer filtering adds fresh <skra-option>s) would throw NG0950.\n // afterRender hooks never run on the server, so this stays SSR-safe. Covers both\n // F-13 (initial render) and F-16 (runtime re-projection, surfaced in dogfood).\n afterRenderEffect(() => {\n const v = this.value();\n if (v === undefined) return;\n const matched = this.options().some((o) =>\n this.compareWith()(o.value() as T, v),\n );\n if (!matched && this.options().length) {\n console.warn(\n '[skra-combobox] value has no matching <skra-option> in the projected set — the input ' +\n 'shows empty (likely a compareWith mismatch, or the selected option was filtered out).',\n );\n }\n });\n }\n }\n\n // ── SkraListboxPanel seam ──\n isSelected(value: unknown): boolean {\n const current = this.value();\n return current !== undefined && this.compareWith()(current, value as T);\n }\n\n selectValue(value: unknown): void {\n const opt =\n this.options().find((o) =>\n this.compareWith()(o.value() as T, value as T),\n ) ?? null;\n this.valueChange.emit(value as T);\n // Optimistic display: show the picked option's label immediately (focus is retained).\n this.query.set(opt?.getLabel() ?? '');\n this.close(); // single-select: pick + close\n }\n\n protected activeSeedItem(): SkraListboxKeyboardItem | null {\n return this.selectedOption();\n }\n\n // ── Editable-input trigger model (the divergence from the select-family button trigger) ──\n protected onFocus(): void {\n // Start editing from the current value's label (APG: the textbox shows the value, editable).\n this.query.set(this.selectedOption()?.getLabel() ?? '');\n this.isFocused.set(true);\n }\n\n protected onInput(event: Event): void {\n if (this.isReadonly()) return;\n const text = (event.target as HTMLInputElement).value;\n this.query.set(text);\n this.filterChange.emit(text); // ADR 0049 — consumer owns the matching + projection\n if (!this.isOpen()) this.open();\n }\n\n protected onBlur(): void {\n this.isFocused.set(false); // displayValue() snaps back to the selected label (option-bound, ADR 0049)\n if (this.isOpen()) this.close();\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.isDisabled() || this.isReadonly()) return;\n const key = event.key;\n\n if (key === 'ArrowDown' || key === 'ArrowUp') {\n event.preventDefault(); // do not move the text caret\n if (event.altKey) {\n // APG: Alt+Down opens (no active move); Alt+Up closes + reverts (keeps value).\n if (key === 'ArrowDown') {\n if (!this.isOpen()) this.open();\n } else if (this.isOpen()) {\n this.query.set(this.selectedOption()?.getLabel() ?? '');\n this.close();\n }\n return;\n }\n if (!this.isOpen()) this.open();\n else this.keyboard.onKeydown(event); // move the active descendant\n return;\n }\n if (key === 'Enter') {\n if (this.isOpen()) {\n event.preventDefault();\n this.selectActive(); // select the active option (no-op if none / soft-disabled)\n }\n return;\n }\n if (key === 'Escape') {\n if (this.isOpen()) {\n event.preventDefault();\n this.query.set(this.selectedOption()?.getLabel() ?? ''); // revert (keep value)\n this.close();\n }\n return;\n }\n // Home/End/printable/Backspace: native text editing — NOT routed to the listbox (no type-ahead).\n }\n\n protected toggle(): void {\n if (this.isDisabled() || this.isReadonly()) return; // pointer parity with the keydown/input guards (M1)\n const el = this.triggerRef()?.nativeElement;\n if (this.isOpen()) {\n this.close();\n } else {\n el?.focus(); // fires onFocus → seeds the editable text\n this.open();\n }\n }\n}\n","import { Directive } from '@angular/core';\n\n/**\n * SkraComboboxEmpty — the no-results marker (ADR 0049). Place on the element that should show inside the\n * combobox listbox when the consumer's filter yields zero `skra-option` children:\n *\n * <skra-combobox (filterChange)=\"query.set($event)\" [value]=\"city()\" (valueChange)=\"city.set($event)\">\n * @for (c of visible(); track c.id) { <skra-option [value]=\"c.id\">{{ c.name }}</skra-option> }\n * <span skraComboboxEmpty>No matches</span>\n * </skra-combobox>\n *\n * The text is consumer-supplied (i18n is the consumer's concern — Forge ships no hardcoded string).\n * Forge toggles its visibility via `@if (!loading() && options().length === 0)`; the marker only carries\n * the projection selector, so no logic lives here.\n */\n@Directive({\n selector: '[skraComboboxEmpty]',\n standalone: true,\n})\nexport class SkraComboboxEmpty {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAsBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AA4EG,MAAO,YAA0B,SAAQ,sBAAyB,CAAA;;IAEnD,gBAAgB,GAAG,UAAU;;AAGvC,IAAA,KAAK,GAAG,KAAK;yFAAK;;IAElB,WAAW,GAAG,MAAM,EAAK;;IAEzB,YAAY,GAAG,MAAM,EAAU;;IAE/B,OAAO,GAAG,KAAK,CAAC,KAAK,+EAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;IAEvD,QAAQ,GAAG,KAAK;;IAGN,KAAK,GAAG,MAAM,CAAC,EAAE;8EAAC;IACpB,SAAS,GAAG,MAAM,CAAC,KAAK;kFAAC;AAEzB,IAAA,MAAM,GAAG,MAAM,CAA0B,UAAU,CAAC;AAErE;;;;AAIG;IACgB,iBAAiB,GAAA;AAClC,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa;IAClC;;AAGmB,IAAA,cAAc,GAAG,QAAQ,CAC1C,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,IAAI;uFACrE;AAED;;;;AAIG;AACgB,IAAA,YAAY,GAAG,QAAQ,CAAC,MACzC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;qFAC5E;AAED,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,EAAE;;;;;;;YAOjD,iBAAiB,CAAC,MAAK;AACrB,gBAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;gBACtB,IAAI,CAAC,KAAK,SAAS;oBAAE;gBACrB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KACpC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,EAAO,EAAE,CAAC,CAAC,CACtC;gBACD,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;oBACrC,OAAO,CAAC,IAAI,CACV,uFAAuF;AACrF,wBAAA,uFAAuF,CAC1F;gBACH;AACF,YAAA,CAAC,CAAC;QACJ;IACF;;AAGA,IAAA,UAAU,CAAC,KAAc,EAAA;AACvB,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE;AAC5B,QAAA,OAAO,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,KAAU,CAAC;IACzE;AAEA,IAAA,WAAW,CAAC,KAAc,EAAA;AACxB,QAAA,MAAM,GAAG,GACP,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KACpB,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,EAAO,EAAE,KAAU,CAAC,CAC/C,IAAI,IAAI;AACX,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAU,CAAC;;AAEjC,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACrC,QAAA,IAAI,CAAC,KAAK,EAAE,CAAC;IACf;IAEU,cAAc,GAAA;AACtB,QAAA,OAAO,IAAI,CAAC,cAAc,EAAE;IAC9B;;IAGU,OAAO,GAAA;;AAEf,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACvD,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;IAC1B;AAEU,IAAA,OAAO,CAAC,KAAY,EAAA;QAC5B,IAAI,IAAI,CAAC,UAAU,EAAE;YAAE;AACvB,QAAA,MAAM,IAAI,GAAI,KAAK,CAAC,MAA2B,CAAC,KAAK;AACrD,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAAE,IAAI,CAAC,IAAI,EAAE;IACjC;IAEU,MAAM,GAAA;QACd,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,IAAI,CAAC,MAAM,EAAE;YAAE,IAAI,CAAC,KAAK,EAAE;IACjC;AAEU,IAAA,gBAAgB,CAAC,KAAoB,EAAA;QAC7C,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE;YAAE;AAC5C,QAAA,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG;QAErB,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,SAAS,EAAE;AAC5C,YAAA,KAAK,CAAC,cAAc,EAAE,CAAC;AACvB,YAAA,IAAI,KAAK,CAAC,MAAM,EAAE;;AAEhB,gBAAA,IAAI,GAAG,KAAK,WAAW,EAAE;AACvB,oBAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;wBAAE,IAAI,CAAC,IAAI,EAAE;gBACjC;AAAO,qBAAA,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;AACxB,oBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;oBACvD,IAAI,CAAC,KAAK,EAAE;gBACd;gBACA;YACF;AACA,YAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;gBAAE,IAAI,CAAC,IAAI,EAAE;;gBAC1B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACpC;QACF;AACA,QAAA,IAAI,GAAG,KAAK,OAAO,EAAE;AACnB,YAAA,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;gBACjB,KAAK,CAAC,cAAc,EAAE;AACtB,gBAAA,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB;YACA;QACF;AACA,QAAA,IAAI,GAAG,KAAK,QAAQ,EAAE;AACpB,YAAA,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;gBACjB,KAAK,CAAC,cAAc,EAAE;AACtB,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxD,IAAI,CAAC,KAAK,EAAE;YACd;YACA;QACF;;IAEF;IAEU,MAAM,GAAA;QACd,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE;AAAE,YAAA,OAAO;QACnD,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa;AAC3C,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;YACjB,IAAI,CAAC,KAAK,EAAE;QACd;aAAO;AACL,YAAA,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,IAAI,CAAC,IAAI,EAAE;QACb;IACF;uGA7JW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,WAAA,EAAA,aAAA,EAAA,YAAA,EAAA,cAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,wBAAA,EAAA,yBAAA,EAAA,sBAAA,EAAA,oDAAA,EAAA,EAAA,EAAA,SAAA,EAFZ,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,EAAA,QAAA,EAAA,CAAA,cAAA,CAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EArE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,mwKAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAWU,YAAY,EAAA,UAAA,EAAA,CAAA;kBA3ExB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,EAAA,UAAA,EACb,IAAI,EAAA,QAAA,EACN,cAAc,EAAA,QAAA,EACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DT,EAAA,CAAA,EAAA,eAAA,EAEgB,uBAAuB,CAAC,MAAM,iBAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;AACJ,wBAAA,0BAA0B,EAAE,uBAAuB;AACnD,wBAAA,wBAAwB,EACtB,kDAAkD;qBACrD,EAAA,SAAA,EACU,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAA,YAAc,EAAE,CAAC,EAAA,MAAA,EAAA,CAAA,mwKAAA,CAAA,EAAA;;;ACvHzE;;;;;;;;;;;;AAYG;MAKU,iBAAiB,CAAA;uGAAjB,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAjB,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAJ7B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,qBAAqB;AAC/B,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA;;;AClBD;;AAEG;;;;"}
@@ -142,44 +142,44 @@ class SkraOption {
142
142
  this.select?.selectValue(this.value());
143
143
  }
144
144
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraOption, deps: [], target: i0.ɵɵFactoryTarget.Component });
145
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraOption, isStandalone: true, selector: "skra-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "onActivate()" }, properties: { "id": "optionId", "attr.aria-selected": "selected() ? \"true\" : \"false\"", "attr.data-skra-active": "active() ? \"\" : null" } }, queries: [{ propertyName: "leadingSlot", first: true, predicate: SkraOptionLeading, descendants: true, isSignal: true }, { propertyName: "detailSlot", first: true, predicate: SkraOptionDetail, descendants: true, isSignal: true }], exportAs: ["skraOption"], ngImport: i0, template: `
146
- @if (hasLeading()) {
147
- <span class="skra-select-option-leading"
148
- ><ng-content select="[skra-option-leading]"></ng-content
149
- ></span>
150
- }
151
- <span class="skra-select-option-label"><ng-content></ng-content></span>
152
- @if (hasDetail()) {
153
- <span class="skra-select-option-detail"
154
- ><ng-content select="[skra-option-detail]"></ng-content
155
- ></span>
156
- }
157
- <span
158
- class="skra-select-option-indicator"
159
- [class.skra-select-option-indicator--checkbox]="multiple()"
160
- aria-hidden="true"
161
- ></span>
162
- `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-select-option-gap);inline-size:100%;min-block-size:var(--skra-comp-select-option-min-block-size);margin:0;box-sizing:border-box;padding-block:var(--skra-comp-select-option-padding-block);padding-inline:var(--skra-comp-select-option-padding-inline);color:var(--skra-comp-select-option-foreground);cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-option-leading{display:inline-flex;align-items:center;flex:none;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-option-label{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skra-select-option-detail{flex:none;color:var(--skra-comp-select-option-detail-foreground);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-select-option-indicator{flex:none;inline-size:1em;display:inline-flex;align-items:center;justify-content:center}:host([aria-selected=true]) .skra-select-option-indicator:not(.skra-select-option-indicator--checkbox):before{content:\"\\2713\";color:var(--skra-comp-select-indicator-color)}.skra-select-option-indicator--checkbox{inline-size:var(--skra-comp-select-option-checkbox-size);block-size:var(--skra-comp-select-option-checkbox-size);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-option-checkbox-border-color);border-radius:var(--skra-comp-select-option-checkbox-radius);box-sizing:border-box}:host([aria-selected=true]) .skra-select-option-indicator--checkbox{background-color:var(--skra-comp-select-option-checkbox-background-selected);border-color:var(--skra-comp-select-option-checkbox-background-selected)}:host([aria-selected=true]) .skra-select-option-indicator--checkbox:before{content:\"\\2713\";color:var(--skra-comp-select-option-checkbox-check-color);font-size:.75em;line-height:1}:host(:hover){background-color:var(--skra-comp-select-option-background-hover)}:host([data-skra-active]){background-color:var(--skra-comp-select-option-background-active);outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:calc(-1 * var(--skra-comp-select-focus-ring-width))}:host([aria-selected=true]){background-color:var(--skra-comp-select-option-background-selected);color:var(--skra-comp-select-option-foreground-selected)}:host([aria-disabled=true]){color:var(--skra-comp-select-option-foreground-disabled);cursor:default}@media(prefers-reduced-motion:reduce){:host{transition-duration:0ms}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
145
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraOption, isStandalone: true, selector: "skra-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "onActivate()" }, properties: { "id": "optionId", "attr.aria-selected": "selected() ? \"true\" : \"false\"", "attr.data-skra-active": "active() ? \"\" : null" } }, queries: [{ propertyName: "leadingSlot", first: true, predicate: SkraOptionLeading, descendants: true, isSignal: true }, { propertyName: "detailSlot", first: true, predicate: SkraOptionDetail, descendants: true, isSignal: true }], exportAs: ["skraOption"], ngImport: i0, template: `
146
+ @if (hasLeading()) {
147
+ <span class="skra-select-option-leading"
148
+ ><ng-content select="[skra-option-leading]"></ng-content
149
+ ></span>
150
+ }
151
+ <span class="skra-select-option-label"><ng-content></ng-content></span>
152
+ @if (hasDetail()) {
153
+ <span class="skra-select-option-detail"
154
+ ><ng-content select="[skra-option-detail]"></ng-content
155
+ ></span>
156
+ }
157
+ <span
158
+ class="skra-select-option-indicator"
159
+ [class.skra-select-option-indicator--checkbox]="multiple()"
160
+ aria-hidden="true"
161
+ ></span>
162
+ `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-select-option-gap);inline-size:100%;min-block-size:var(--skra-comp-select-option-min-block-size);margin:0;box-sizing:border-box;padding-block:var(--skra-comp-select-option-padding-block);padding-inline:var(--skra-comp-select-option-padding-inline);color:var(--skra-comp-select-option-foreground);cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-option-leading{display:inline-flex;align-items:center;flex:none;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-option-label{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skra-select-option-detail{flex:none;color:var(--skra-comp-select-option-detail-foreground);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-select-option-indicator{flex:none;inline-size:1em;display:inline-flex;align-items:center;justify-content:center}:host([aria-selected=true]) .skra-select-option-indicator:not(.skra-select-option-indicator--checkbox):before{content:\"\\2713\";color:var(--skra-comp-select-indicator-color)}.skra-select-option-indicator--checkbox{inline-size:var(--skra-comp-select-option-checkbox-size);block-size:var(--skra-comp-select-option-checkbox-size);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-option-checkbox-border-color);border-radius:var(--skra-comp-select-option-checkbox-radius);box-sizing:border-box}:host([aria-selected=true]) .skra-select-option-indicator--checkbox{background-color:var(--skra-comp-select-option-checkbox-background-selected);border-color:var(--skra-comp-select-option-checkbox-background-selected)}:host([aria-selected=true]) .skra-select-option-indicator--checkbox:before{content:\"\\2713\";color:var(--skra-comp-select-option-checkbox-check-color);font-size:.75em;line-height:1}@media(hover:hover)and (pointer:fine){:host(:hover){background-color:var(--skra-comp-select-option-background-hover)}}:host([data-skra-active]){background-color:var(--skra-comp-select-option-background-active);outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:calc(-1 * var(--skra-comp-select-focus-ring-width))}:host([aria-selected=true]){background-color:var(--skra-comp-select-option-background-selected);color:var(--skra-comp-select-option-foreground-selected)}:host([aria-disabled=true]){color:var(--skra-comp-select-option-foreground-disabled);cursor:default}@media(prefers-reduced-motion:reduce){:host{transition-duration:0ms}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
163
163
  }
164
164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraOption, decorators: [{
165
165
  type: Component,
166
- args: [{ selector: 'skra-option', standalone: true, exportAs: 'skraOption', template: `
167
- @if (hasLeading()) {
168
- <span class="skra-select-option-leading"
169
- ><ng-content select="[skra-option-leading]"></ng-content
170
- ></span>
171
- }
172
- <span class="skra-select-option-label"><ng-content></ng-content></span>
173
- @if (hasDetail()) {
174
- <span class="skra-select-option-detail"
175
- ><ng-content select="[skra-option-detail]"></ng-content
176
- ></span>
177
- }
178
- <span
179
- class="skra-select-option-indicator"
180
- [class.skra-select-option-indicator--checkbox]="multiple()"
181
- aria-hidden="true"
182
- ></span>
166
+ args: [{ selector: 'skra-option', standalone: true, exportAs: 'skraOption', template: `
167
+ @if (hasLeading()) {
168
+ <span class="skra-select-option-leading"
169
+ ><ng-content select="[skra-option-leading]"></ng-content
170
+ ></span>
171
+ }
172
+ <span class="skra-select-option-label"><ng-content></ng-content></span>
173
+ @if (hasDetail()) {
174
+ <span class="skra-select-option-detail"
175
+ ><ng-content select="[skra-option-detail]"></ng-content
176
+ ></span>
177
+ }
178
+ <span
179
+ class="skra-select-option-indicator"
180
+ [class.skra-select-option-indicator--checkbox]="multiple()"
181
+ aria-hidden="true"
182
+ ></span>
183
183
  `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
184
184
  role: 'option',
185
185
  '[id]': 'optionId',
@@ -187,7 +187,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
187
187
  '[attr.data-skra-active]': 'active() ? "" : null',
188
188
  '(click)': 'onActivate()',
189
189
  // Disabled = the consumer's native `aria-disabled="true"` (observed live) — no Forge binding.
190
- }, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-select-option-gap);inline-size:100%;min-block-size:var(--skra-comp-select-option-min-block-size);margin:0;box-sizing:border-box;padding-block:var(--skra-comp-select-option-padding-block);padding-inline:var(--skra-comp-select-option-padding-inline);color:var(--skra-comp-select-option-foreground);cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-option-leading{display:inline-flex;align-items:center;flex:none;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-option-label{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skra-select-option-detail{flex:none;color:var(--skra-comp-select-option-detail-foreground);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-select-option-indicator{flex:none;inline-size:1em;display:inline-flex;align-items:center;justify-content:center}:host([aria-selected=true]) .skra-select-option-indicator:not(.skra-select-option-indicator--checkbox):before{content:\"\\2713\";color:var(--skra-comp-select-indicator-color)}.skra-select-option-indicator--checkbox{inline-size:var(--skra-comp-select-option-checkbox-size);block-size:var(--skra-comp-select-option-checkbox-size);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-option-checkbox-border-color);border-radius:var(--skra-comp-select-option-checkbox-radius);box-sizing:border-box}:host([aria-selected=true]) .skra-select-option-indicator--checkbox{background-color:var(--skra-comp-select-option-checkbox-background-selected);border-color:var(--skra-comp-select-option-checkbox-background-selected)}:host([aria-selected=true]) .skra-select-option-indicator--checkbox:before{content:\"\\2713\";color:var(--skra-comp-select-option-checkbox-check-color);font-size:.75em;line-height:1}:host(:hover){background-color:var(--skra-comp-select-option-background-hover)}:host([data-skra-active]){background-color:var(--skra-comp-select-option-background-active);outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:calc(-1 * var(--skra-comp-select-focus-ring-width))}:host([aria-selected=true]){background-color:var(--skra-comp-select-option-background-selected);color:var(--skra-comp-select-option-foreground-selected)}:host([aria-disabled=true]){color:var(--skra-comp-select-option-foreground-disabled);cursor:default}@media(prefers-reduced-motion:reduce){:host{transition-duration:0ms}}\n"] }]
190
+ }, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-select-option-gap);inline-size:100%;min-block-size:var(--skra-comp-select-option-min-block-size);margin:0;box-sizing:border-box;padding-block:var(--skra-comp-select-option-padding-block);padding-inline:var(--skra-comp-select-option-padding-inline);color:var(--skra-comp-select-option-foreground);cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing);transition:background-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-option-leading{display:inline-flex;align-items:center;flex:none;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-option-label{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.skra-select-option-detail{flex:none;color:var(--skra-comp-select-option-detail-foreground);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-select-option-indicator{flex:none;inline-size:1em;display:inline-flex;align-items:center;justify-content:center}:host([aria-selected=true]) .skra-select-option-indicator:not(.skra-select-option-indicator--checkbox):before{content:\"\\2713\";color:var(--skra-comp-select-indicator-color)}.skra-select-option-indicator--checkbox{inline-size:var(--skra-comp-select-option-checkbox-size);block-size:var(--skra-comp-select-option-checkbox-size);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-option-checkbox-border-color);border-radius:var(--skra-comp-select-option-checkbox-radius);box-sizing:border-box}:host([aria-selected=true]) .skra-select-option-indicator--checkbox{background-color:var(--skra-comp-select-option-checkbox-background-selected);border-color:var(--skra-comp-select-option-checkbox-background-selected)}:host([aria-selected=true]) .skra-select-option-indicator--checkbox:before{content:\"\\2713\";color:var(--skra-comp-select-option-checkbox-check-color);font-size:.75em;line-height:1}@media(hover:hover)and (pointer:fine){:host(:hover){background-color:var(--skra-comp-select-option-background-hover)}}:host([data-skra-active]){background-color:var(--skra-comp-select-option-background-active);outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:calc(-1 * var(--skra-comp-select-focus-ring-width))}:host([aria-selected=true]){background-color:var(--skra-comp-select-option-background-selected);color:var(--skra-comp-select-option-foreground-selected)}:host([aria-disabled=true]){color:var(--skra-comp-select-option-foreground-disabled);cursor:default}@media(prefers-reduced-motion:reduce){:host{transition-duration:0ms}}\n"] }]
191
191
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], leadingSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraOptionLeading), { isSignal: true }] }], detailSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraOptionDetail), { isSignal: true }] }] } });
192
192
 
193
193
  /**