@skra/ui 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +201 -201
  2. package/NOTICE +7 -7
  3. package/README.md +39 -39
  4. package/consumer-guide/README.md +84 -84
  5. package/consumer-guide/forms-and-binding.md +19 -3
  6. package/consumer-guide/layout.md +19 -0
  7. package/consumer-guide/overlays.md +100 -100
  8. package/consumer-guide/setup-and-gotchas.md +165 -103
  9. package/consumer-guide/theming-and-encapsulation.md +129 -115
  10. package/fesm2022/skra-ui-accordion.mjs +61 -43
  11. package/fesm2022/skra-ui-accordion.mjs.map +1 -1
  12. package/fesm2022/skra-ui-alert.mjs +26 -18
  13. package/fesm2022/skra-ui-alert.mjs.map +1 -1
  14. package/fesm2022/skra-ui-avatar.mjs +12 -8
  15. package/fesm2022/skra-ui-avatar.mjs.map +1 -1
  16. package/fesm2022/skra-ui-badge.mjs +19 -13
  17. package/fesm2022/skra-ui-badge.mjs.map +1 -1
  18. package/fesm2022/skra-ui-button.mjs +28 -19
  19. package/fesm2022/skra-ui-button.mjs.map +1 -1
  20. package/fesm2022/skra-ui-checkbox.mjs +3 -3
  21. package/fesm2022/skra-ui-checkbox.mjs.map +1 -1
  22. package/fesm2022/skra-ui-chip.mjs +15 -11
  23. package/fesm2022/skra-ui-chip.mjs.map +1 -1
  24. package/fesm2022/skra-ui-combobox.mjs +135 -130
  25. package/fesm2022/skra-ui-combobox.mjs.map +1 -1
  26. package/fesm2022/skra-ui-core.mjs +95 -73
  27. package/fesm2022/skra-ui-core.mjs.map +1 -1
  28. package/fesm2022/skra-ui-counter.mjs +9 -6
  29. package/fesm2022/skra-ui-counter.mjs.map +1 -1
  30. package/fesm2022/skra-ui-datepicker.mjs +334 -307
  31. package/fesm2022/skra-ui-datepicker.mjs.map +1 -1
  32. package/fesm2022/skra-ui-dialog.mjs +5 -4
  33. package/fesm2022/skra-ui-dialog.mjs.map +1 -1
  34. package/fesm2022/skra-ui-field.mjs +65 -43
  35. package/fesm2022/skra-ui-field.mjs.map +1 -1
  36. package/fesm2022/skra-ui-icon.mjs +5 -4
  37. package/fesm2022/skra-ui-icon.mjs.map +1 -1
  38. package/fesm2022/skra-ui-input.mjs +53 -12
  39. package/fesm2022/skra-ui-input.mjs.map +1 -1
  40. package/fesm2022/skra-ui-kbd.mjs +3 -3
  41. package/fesm2022/skra-ui-kbd.mjs.map +1 -1
  42. package/fesm2022/skra-ui-layout.mjs +33 -21
  43. package/fesm2022/skra-ui-layout.mjs.map +1 -1
  44. package/fesm2022/skra-ui-link.mjs +3 -3
  45. package/fesm2022/skra-ui-link.mjs.map +1 -1
  46. package/fesm2022/skra-ui-loading-indicator.mjs +3 -3
  47. package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -1
  48. package/fesm2022/skra-ui-menu.mjs +8 -7
  49. package/fesm2022/skra-ui-menu.mjs.map +1 -1
  50. package/fesm2022/skra-ui-multi-select.mjs +102 -98
  51. package/fesm2022/skra-ui-multi-select.mjs.map +1 -1
  52. package/fesm2022/skra-ui-native-select.mjs +35 -17
  53. package/fesm2022/skra-ui-native-select.mjs.map +1 -1
  54. package/fesm2022/skra-ui-popover.mjs +5 -4
  55. package/fesm2022/skra-ui-popover.mjs.map +1 -1
  56. package/fesm2022/skra-ui-progress.mjs +7 -5
  57. package/fesm2022/skra-ui-progress.mjs.map +1 -1
  58. package/fesm2022/skra-ui-radio.mjs +3 -3
  59. package/fesm2022/skra-ui-radio.mjs.map +1 -1
  60. package/fesm2022/skra-ui-rating-display.mjs +14 -9
  61. package/fesm2022/skra-ui-rating-display.mjs.map +1 -1
  62. package/fesm2022/skra-ui-select.mjs +98 -95
  63. package/fesm2022/skra-ui-select.mjs.map +1 -1
  64. package/fesm2022/skra-ui-skeleton.mjs +16 -13
  65. package/fesm2022/skra-ui-skeleton.mjs.map +1 -1
  66. package/fesm2022/skra-ui-star-rating.mjs +18 -11
  67. package/fesm2022/skra-ui-star-rating.mjs.map +1 -1
  68. package/fesm2022/skra-ui-status-dot.mjs +7 -5
  69. package/fesm2022/skra-ui-status-dot.mjs.map +1 -1
  70. package/fesm2022/skra-ui-switch.mjs +3 -3
  71. package/fesm2022/skra-ui-switch.mjs.map +1 -1
  72. package/fesm2022/skra-ui-tabs.mjs +43 -29
  73. package/fesm2022/skra-ui-tabs.mjs.map +1 -1
  74. package/fesm2022/skra-ui-textarea.mjs +41 -15
  75. package/fesm2022/skra-ui-textarea.mjs.map +1 -1
  76. package/fesm2022/skra-ui-toast.mjs +35 -23
  77. package/fesm2022/skra-ui-toast.mjs.map +1 -1
  78. package/fesm2022/skra-ui-tooltip.mjs +12 -9
  79. package/fesm2022/skra-ui-tooltip.mjs.map +1 -1
  80. package/fesm2022/skra-ui-typography.mjs +9 -6
  81. package/fesm2022/skra-ui-typography.mjs.map +1 -1
  82. package/package.json +6 -6
  83. package/types/skra-ui-field.d.ts +3 -3
  84. package/types/skra-ui-field.d.ts.map +1 -1
  85. package/types/skra-ui-input.d.ts +24 -1
  86. package/types/skra-ui-input.d.ts.map +1 -1
  87. package/types/skra-ui-native-select.d.ts +9 -1
  88. package/types/skra-ui-native-select.d.ts.map +1 -1
  89. package/types/skra-ui-textarea.d.ts +10 -1
  90. package/types/skra-ui-textarea.d.ts.map +1 -1
@@ -33,7 +33,8 @@ class SkraCombobox extends _SkraListboxWidgetBase {
33
33
  /** Combobox panel token namespace — `--skra-comp-combobox-panel-*`. */
34
34
  panelTokenPrefix = 'combobox';
35
35
  /** Consumer-owned value channel (ADR 037) — single, option-bound `T`. */
36
- value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
36
+ value = input(/* @ts-ignore */
37
+ ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
37
38
  /** Fires ONLY on user selection of an option — never on a programmatic `value()` change (ADR 037). */
38
39
  valueChange = output();
39
40
  /** The typed text, emitted on EVERY input change. Consumer-driven projection; NO Forge debounce (ADR 0049). */
@@ -43,8 +44,10 @@ class SkraCombobox extends _SkraListboxWidgetBase {
43
44
  /** Cardinality hint (ADR 0046) — single-select. */
44
45
  multiple = false;
45
46
  /** Internal editable-text state; emitted out via `filterChange`. NOT the value (ADR 0049). */
46
- query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
47
- isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
47
+ query = signal('', /* @ts-ignore */
48
+ ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
49
+ isFocused = signal(false, /* @ts-ignore */
50
+ ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
48
51
  hostEl = inject(ElementRef);
49
52
  /**
50
53
  * Position the panel against — and match its width to — the HOST box, not the inner input. The input
@@ -55,13 +58,15 @@ class SkraCombobox extends _SkraListboxWidgetBase {
55
58
  return this.hostEl.nativeElement;
56
59
  }
57
60
  /** The option whose value matches `value()` (ADR 037 selection model) — feeds the displayed text + active seed. */
58
- selectedOption = computed(() => this.options().find((o) => this.isSelected(o.value())) ?? null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
61
+ selectedOption = computed(() => this.options().find((o) => this.isSelected(o.value())) ?? null, /* @ts-ignore */
62
+ ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
59
63
  /**
60
64
  * The text shown in the input: the live query while editing (focused), otherwise the selected option's
61
65
  * label. This is the controlled-input seam that enforces the option-bound value (ADR 0049) — on blur the
62
66
  * input snaps back to the selected label, so unmatched typed text never persists.
63
67
  */
64
- displayValue = computed(() => this.isFocused() ? this.query() : (this.selectedOption()?.getLabel() ?? ''), ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
68
+ displayValue = computed(() => this.isFocused() ? this.query() : (this.selectedOption()?.getLabel() ?? ''), /* @ts-ignore */
69
+ ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
65
70
  constructor() {
66
71
  super();
67
72
  if (typeof ngDevMode === 'undefined' || ngDevMode) {
@@ -171,131 +176,131 @@ class SkraCombobox extends _SkraListboxWidgetBase {
171
176
  this.open();
172
177
  }
173
178
  }
174
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraCombobox, deps: [], target: i0.ɵɵFactoryTarget.Component });
175
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", 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: `
176
- <input
177
- #trigger
178
- class="skra-combobox-input"
179
- type="text"
180
- role="combobox"
181
- autocomplete="off"
182
- aria-autocomplete="list"
183
- aria-haspopup="listbox"
184
- [attr.id]="triggerId()"
185
- [attr.aria-controls]="panelId"
186
- [attr.aria-expanded]="isOpen()"
187
- [attr.aria-activedescendant]="activeOptionId()"
188
- [attr.aria-describedby]="describedBy()"
189
- [attr.aria-invalid]="hasError() ? 'true' : null"
190
- [attr.aria-required]="isRequired() ? 'true' : null"
191
- [attr.aria-busy]="loading() ? 'true' : null"
192
- [attr.placeholder]="placeholder()"
193
- [disabled]="isDisabled()"
194
- [readOnly]="isReadonly()"
195
- [value]="displayValue()"
196
- (focus)="onFocus()"
197
- (input)="onInput($event)"
198
- (keydown)="onTriggerKeydown($event)"
199
- (blur)="onBlur()"
200
- />
201
- <button
202
- class="skra-combobox-toggle"
203
- type="button"
204
- tabindex="-1"
205
- aria-hidden="true"
206
- [disabled]="isDisabled()"
207
- (mousedown)="$event.preventDefault()"
208
- (click)="toggle()"
209
- >
210
- <span class="skra-combobox-arrow" aria-hidden="true"></span>
211
- </button>
212
-
213
- <div
214
- #panel
215
- class="skra-combobox-listbox"
216
- [id]="panelId"
217
- role="listbox"
218
- popover="auto"
219
- (mousedown)="$event.preventDefault()"
220
- (toggle)="onPanelToggle($event)"
221
- >
222
- <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
223
- a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
224
- :empty in SCSS. -->
225
- <div class="skra-combobox-status" role="status" aria-live="polite">
226
- @if (loading()) {
227
- <ng-content select="[skraLoadingIndicator]"></ng-content>
228
- <span class="skra-combobox-spinner" aria-hidden="true"></span>
229
- } @else if (options().length === 0) {
230
- <ng-content select="[skraComboboxEmpty]"></ng-content>
231
- }
232
- </div>
233
- <ng-content></ng-content>
234
- </div>
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>
235
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 });
236
241
  }
237
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraCombobox, decorators: [{
242
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraCombobox, decorators: [{
238
243
  type: Component,
239
- args: [{ selector: 'skra-combobox', standalone: true, exportAs: 'skraCombobox', template: `
240
- <input
241
- #trigger
242
- class="skra-combobox-input"
243
- type="text"
244
- role="combobox"
245
- autocomplete="off"
246
- aria-autocomplete="list"
247
- aria-haspopup="listbox"
248
- [attr.id]="triggerId()"
249
- [attr.aria-controls]="panelId"
250
- [attr.aria-expanded]="isOpen()"
251
- [attr.aria-activedescendant]="activeOptionId()"
252
- [attr.aria-describedby]="describedBy()"
253
- [attr.aria-invalid]="hasError() ? 'true' : null"
254
- [attr.aria-required]="isRequired() ? 'true' : null"
255
- [attr.aria-busy]="loading() ? 'true' : null"
256
- [attr.placeholder]="placeholder()"
257
- [disabled]="isDisabled()"
258
- [readOnly]="isReadonly()"
259
- [value]="displayValue()"
260
- (focus)="onFocus()"
261
- (input)="onInput($event)"
262
- (keydown)="onTriggerKeydown($event)"
263
- (blur)="onBlur()"
264
- />
265
- <button
266
- class="skra-combobox-toggle"
267
- type="button"
268
- tabindex="-1"
269
- aria-hidden="true"
270
- [disabled]="isDisabled()"
271
- (mousedown)="$event.preventDefault()"
272
- (click)="toggle()"
273
- >
274
- <span class="skra-combobox-arrow" aria-hidden="true"></span>
275
- </button>
276
-
277
- <div
278
- #panel
279
- class="skra-combobox-listbox"
280
- [id]="panelId"
281
- role="listbox"
282
- popover="auto"
283
- (mousedown)="$event.preventDefault()"
284
- (toggle)="onPanelToggle($event)"
285
- >
286
- <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
287
- a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
288
- :empty in SCSS. -->
289
- <div class="skra-combobox-status" role="status" aria-live="polite">
290
- @if (loading()) {
291
- <ng-content select="[skraLoadingIndicator]"></ng-content>
292
- <span class="skra-combobox-spinner" aria-hidden="true"></span>
293
- } @else if (options().length === 0) {
294
- <ng-content select="[skraComboboxEmpty]"></ng-content>
295
- }
296
- </div>
297
- <ng-content></ng-content>
298
- </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>
299
304
  `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
300
305
  '[attr.data-skra-loading]': 'loading() ? "" : null',
301
306
  '[attr.data-skra-empty]': '!loading() && options().length === 0 ? "" : null',
@@ -316,10 +321,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
316
321
  * the projection selector, so no logic lives here.
317
322
  */
318
323
  class SkraComboboxEmpty {
319
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraComboboxEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
320
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SkraComboboxEmpty, isStandalone: true, selector: "[skraComboboxEmpty]", ngImport: i0 });
324
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraComboboxEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
325
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.1", type: SkraComboboxEmpty, isStandalone: true, selector: "[skraComboboxEmpty]", ngImport: i0 });
321
326
  }
322
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraComboboxEmpty, decorators: [{
327
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraComboboxEmpty, decorators: [{
323
328
  type: Directive,
324
329
  args: [{
325
330
  selector: '[skraComboboxEmpty]',
@@ -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 {\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":"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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 {\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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"}