@skra/ui 0.4.0 → 0.6.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 (72) hide show
  1. package/consumer-guide/README.md +88 -85
  2. package/consumer-guide/custom-components.md +154 -0
  3. package/consumer-guide/wrapping-patterns.md +98 -90
  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 +64 -39
  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.map +1 -1
  30. package/fesm2022/skra-ui-link.mjs +2 -2
  31. package/fesm2022/skra-ui-link.mjs.map +1 -1
  32. package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -1
  33. package/fesm2022/skra-ui-menu.mjs +40 -16
  34. package/fesm2022/skra-ui-menu.mjs.map +1 -1
  35. package/fesm2022/skra-ui-multi-select.mjs +94 -94
  36. package/fesm2022/skra-ui-multi-select.mjs.map +1 -1
  37. package/fesm2022/skra-ui-native-select.mjs +2 -2
  38. package/fesm2022/skra-ui-native-select.mjs.map +1 -1
  39. package/fesm2022/skra-ui-popover.mjs +25 -9
  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 +2 -2
  45. package/fesm2022/skra-ui-rating-display.mjs.map +1 -1
  46. package/fesm2022/skra-ui-select.mjs +92 -92
  47. package/fesm2022/skra-ui-select.mjs.map +1 -1
  48. package/fesm2022/skra-ui-skeleton.mjs +8 -8
  49. package/fesm2022/skra-ui-skeleton.mjs.map +1 -1
  50. package/fesm2022/skra-ui-star-rating.mjs +2 -2
  51. package/fesm2022/skra-ui-star-rating.mjs.map +1 -1
  52. package/fesm2022/skra-ui-status-dot.mjs.map +1 -1
  53. package/fesm2022/skra-ui-switch.mjs +2 -2
  54. package/fesm2022/skra-ui-switch.mjs.map +1 -1
  55. package/fesm2022/skra-ui-tabs.mjs +2 -2
  56. package/fesm2022/skra-ui-tabs.mjs.map +1 -1
  57. package/fesm2022/skra-ui-textarea.mjs +2 -2
  58. package/fesm2022/skra-ui-textarea.mjs.map +1 -1
  59. package/fesm2022/skra-ui-toast.mjs +2 -2
  60. package/fesm2022/skra-ui-toast.mjs.map +1 -1
  61. package/fesm2022/skra-ui-tooltip.mjs +7 -2
  62. package/fesm2022/skra-ui-tooltip.mjs.map +1 -1
  63. package/fesm2022/skra-ui-typography.mjs.map +1 -1
  64. package/package.json +3 -3
  65. package/types/skra-ui-core.d.ts.map +1 -1
  66. package/types/skra-ui-datepicker.d.ts +146 -46
  67. package/types/skra-ui-datepicker.d.ts.map +1 -1
  68. package/types/skra-ui-menu.d.ts +18 -6
  69. package/types/skra-ui-menu.d.ts.map +1 -1
  70. package/types/skra-ui-popover.d.ts +13 -4
  71. package/types/skra-ui-popover.d.ts.map +1 -1
  72. package/types/skra-ui-tooltip.d.ts.map +1 -1
@@ -76,103 +76,103 @@ class SkraMultiSelect extends _SkraSelectBase {
76
76
  return this.selectedOptions()[0] ?? null;
77
77
  }
78
78
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraMultiSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
79
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraMultiSelect, isStandalone: true, selector: "skra-multi-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraMultiSelect }], exportAs: ["skraMultiSelect"], usesInheritance: true, ngImport: i0, template: `
80
- <button
81
- #trigger
82
- class="skra-select-trigger"
83
- type="button"
84
- role="combobox"
85
- aria-haspopup="listbox"
86
- [attr.id]="triggerId()"
87
- [attr.popovertarget]="panelId"
88
- [attr.aria-controls]="panelId"
89
- [attr.aria-expanded]="isOpen()"
90
- [attr.aria-activedescendant]="activeOptionId()"
91
- [attr.aria-describedby]="describedBy()"
92
- [attr.aria-invalid]="hasError() ? 'true' : null"
93
- [attr.aria-required]="isRequired() ? 'true' : null"
94
- [attr.aria-readonly]="isReadonly() ? 'true' : null"
95
- [disabled]="isDisabled()"
96
- (click)="onTriggerClick($event)"
97
- (keydown)="onTriggerKeydown($event)"
98
- >
99
- <span class="skra-select-trigger-value">
100
- @if (triggerTemplate(); as tpl) {
101
- <ng-container
102
- [ngTemplateOutlet]="tpl"
103
- [ngTemplateOutletContext]="triggerContext()"
104
- ></ng-container>
105
- } @else if (selectedOptions().length) {
106
- {{ summary() }}
107
- } @else {
108
- <span class="skra-select-placeholder">{{ placeholder() }}</span>
109
- }
110
- </span>
111
- <span class="skra-select-arrow" aria-hidden="true"></span>
112
- </button>
113
-
114
- <div
115
- #panel
116
- class="skra-select-listbox"
117
- [id]="panelId"
118
- role="listbox"
119
- aria-multiselectable="true"
120
- popover="auto"
121
- (toggle)="onPanelToggle($event)"
122
- >
123
- <ng-content></ng-content>
124
- </div>
125
- `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:block;box-sizing:border-box;margin:0}.skra-select-trigger{display:flex;align-items:center;gap:var(--skra-comp-select-gap);inline-size:100%;min-block-size:var(--skra-comp-select-min-block-size);box-sizing:border-box;margin:0;padding-block:var(--skra-comp-select-padding-block);padding-inline:var(--skra-comp-select-padding-inline);text-align:start;cursor:pointer;background:var(--skra-comp-select-background);color:var(--skra-comp-select-foreground);border-color:var(--skra-comp-select-border-color);border-width:var(--skra-comp-select-border-width);border-style:var(--skra-comp-select-border-style);border-radius:var(--skra-comp-select-radius);box-shadow:var(--skra-comp-select-shadow);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),border-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-trigger:hover:not(:disabled):not([aria-readonly=true]){background:var(--skra-comp-select-background-hover)}.skra-select-trigger:focus-visible{outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:.125rem}.skra-select-trigger:disabled{background:var(--skra-comp-select-background-disabled);color:var(--skra-comp-select-foreground-disabled);cursor:not-allowed}.skra-select-trigger[aria-readonly=true]{background:var(--skra-comp-select-background-readonly);cursor:default}.skra-select-trigger[aria-invalid=true]{border-color:var(--skra-comp-select-border-color-validation-invalid)}.skra-select-trigger-value{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-placeholder{color:var(--skra-comp-select-foreground-disabled)}.skra-select-arrow{flex:none;inline-size:var(--skra-comp-select-arrow-icon-size);block-size:var(--skra-comp-select-arrow-icon-size);background-color:var(--skra-comp-select-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-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-trigger[aria-expanded=true] .skra-select-arrow{transform:rotate(180deg)}.skra-select-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-select-panel-padding-block);padding-inline:var(--skra-comp-select-panel-padding-inline);background:var(--skra-comp-select-panel-background);color:var(--skra-comp-select-panel-foreground);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-panel-border-color);border-radius:var(--skra-comp-select-panel-radius);box-shadow:var(--skra-comp-select-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-select-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing),display var(--skra-comp-select-motion-duration) allow-discrete,overlay var(--skra-comp-select-motion-duration) allow-discrete}.skra-select-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-select-listbox:popover-open{opacity:0}}@media(prefers-reduced-motion:reduce){.skra-select-trigger,.skra-select-arrow,.skra-select-listbox{transition-duration:0ms}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
79
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: SkraMultiSelect, isStandalone: true, selector: "skra-multi-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraMultiSelect }], exportAs: ["skraMultiSelect"], usesInheritance: true, ngImport: i0, template: `
80
+ <button
81
+ #trigger
82
+ class="skra-select-trigger"
83
+ type="button"
84
+ role="combobox"
85
+ aria-haspopup="listbox"
86
+ [attr.id]="triggerId()"
87
+ [attr.popovertarget]="panelId"
88
+ [attr.aria-controls]="panelId"
89
+ [attr.aria-expanded]="isOpen()"
90
+ [attr.aria-activedescendant]="activeOptionId()"
91
+ [attr.aria-describedby]="describedBy()"
92
+ [attr.aria-invalid]="hasError() ? 'true' : null"
93
+ [attr.aria-required]="isRequired() ? 'true' : null"
94
+ [attr.aria-readonly]="isReadonly() ? 'true' : null"
95
+ [disabled]="isDisabled()"
96
+ (click)="onTriggerClick($event)"
97
+ (keydown)="onTriggerKeydown($event)"
98
+ >
99
+ <span class="skra-select-trigger-value">
100
+ @if (triggerTemplate(); as tpl) {
101
+ <ng-container
102
+ [ngTemplateOutlet]="tpl"
103
+ [ngTemplateOutletContext]="triggerContext()"
104
+ ></ng-container>
105
+ } @else if (selectedOptions().length) {
106
+ {{ summary() }}
107
+ } @else {
108
+ <span class="skra-select-placeholder">{{ placeholder() }}</span>
109
+ }
110
+ </span>
111
+ <span class="skra-select-arrow" aria-hidden="true"></span>
112
+ </button>
113
+
114
+ <div
115
+ #panel
116
+ class="skra-select-listbox"
117
+ [id]="panelId"
118
+ role="listbox"
119
+ aria-multiselectable="true"
120
+ popover="auto"
121
+ (toggle)="onPanelToggle($event)"
122
+ >
123
+ <ng-content></ng-content>
124
+ </div>
125
+ `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:block;box-sizing:border-box;margin:0}.skra-select-trigger{display:flex;align-items:center;gap:var(--skra-comp-select-gap);inline-size:100%;min-block-size:var(--skra-comp-select-min-block-size);box-sizing:border-box;margin:0;padding-block:var(--skra-comp-select-padding-block);padding-inline:var(--skra-comp-select-padding-inline);text-align:start;cursor:pointer;background:var(--skra-comp-select-background);color:var(--skra-comp-select-foreground);border-color:var(--skra-comp-select-border-color);border-width:var(--skra-comp-select-border-width);border-style:var(--skra-comp-select-border-style);border-radius:var(--skra-comp-select-radius);box-shadow:var(--skra-comp-select-shadow);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),border-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}@media(hover:hover)and (pointer:fine){.skra-select-trigger:hover:not(:disabled):not([aria-readonly=true]){background:var(--skra-comp-select-background-hover)}}.skra-select-trigger:focus-visible{outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:.125rem}.skra-select-trigger:disabled{background:var(--skra-comp-select-background-disabled);color:var(--skra-comp-select-foreground-disabled);cursor:not-allowed}.skra-select-trigger[aria-readonly=true]{background:var(--skra-comp-select-background-readonly);cursor:default}.skra-select-trigger[aria-invalid=true]{border-color:var(--skra-comp-select-border-color-validation-invalid)}.skra-select-trigger-value{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-placeholder{color:var(--skra-comp-select-foreground-disabled)}.skra-select-arrow{flex:none;inline-size:var(--skra-comp-select-arrow-icon-size);block-size:var(--skra-comp-select-arrow-icon-size);background-color:var(--skra-comp-select-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-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-trigger[aria-expanded=true] .skra-select-arrow{transform:rotate(180deg)}.skra-select-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-select-panel-padding-block);padding-inline:var(--skra-comp-select-panel-padding-inline);background:var(--skra-comp-select-panel-background);color:var(--skra-comp-select-panel-foreground);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-panel-border-color);border-radius:var(--skra-comp-select-panel-radius);box-shadow:var(--skra-comp-select-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-select-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing),display var(--skra-comp-select-motion-duration) allow-discrete,overlay var(--skra-comp-select-motion-duration) allow-discrete}.skra-select-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-select-listbox:popover-open{opacity:0}}@media(prefers-reduced-motion:reduce){.skra-select-trigger,.skra-select-arrow,.skra-select-listbox{transition-duration:0ms}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
126
126
  }
127
127
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraMultiSelect, decorators: [{
128
128
  type: Component,
129
- args: [{ selector: 'skra-multi-select', standalone: true, exportAs: 'skraMultiSelect', imports: [NgTemplateOutlet], template: `
130
- <button
131
- #trigger
132
- class="skra-select-trigger"
133
- type="button"
134
- role="combobox"
135
- aria-haspopup="listbox"
136
- [attr.id]="triggerId()"
137
- [attr.popovertarget]="panelId"
138
- [attr.aria-controls]="panelId"
139
- [attr.aria-expanded]="isOpen()"
140
- [attr.aria-activedescendant]="activeOptionId()"
141
- [attr.aria-describedby]="describedBy()"
142
- [attr.aria-invalid]="hasError() ? 'true' : null"
143
- [attr.aria-required]="isRequired() ? 'true' : null"
144
- [attr.aria-readonly]="isReadonly() ? 'true' : null"
145
- [disabled]="isDisabled()"
146
- (click)="onTriggerClick($event)"
147
- (keydown)="onTriggerKeydown($event)"
148
- >
149
- <span class="skra-select-trigger-value">
150
- @if (triggerTemplate(); as tpl) {
151
- <ng-container
152
- [ngTemplateOutlet]="tpl"
153
- [ngTemplateOutletContext]="triggerContext()"
154
- ></ng-container>
155
- } @else if (selectedOptions().length) {
156
- {{ summary() }}
157
- } @else {
158
- <span class="skra-select-placeholder">{{ placeholder() }}</span>
159
- }
160
- </span>
161
- <span class="skra-select-arrow" aria-hidden="true"></span>
162
- </button>
163
-
164
- <div
165
- #panel
166
- class="skra-select-listbox"
167
- [id]="panelId"
168
- role="listbox"
169
- aria-multiselectable="true"
170
- popover="auto"
171
- (toggle)="onPanelToggle($event)"
172
- >
173
- <ng-content></ng-content>
174
- </div>
175
- `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraMultiSelect }], styles: ["@charset \"UTF-8\";:host{display:block;box-sizing:border-box;margin:0}.skra-select-trigger{display:flex;align-items:center;gap:var(--skra-comp-select-gap);inline-size:100%;min-block-size:var(--skra-comp-select-min-block-size);box-sizing:border-box;margin:0;padding-block:var(--skra-comp-select-padding-block);padding-inline:var(--skra-comp-select-padding-inline);text-align:start;cursor:pointer;background:var(--skra-comp-select-background);color:var(--skra-comp-select-foreground);border-color:var(--skra-comp-select-border-color);border-width:var(--skra-comp-select-border-width);border-style:var(--skra-comp-select-border-style);border-radius:var(--skra-comp-select-radius);box-shadow:var(--skra-comp-select-shadow);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),border-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-trigger:hover:not(:disabled):not([aria-readonly=true]){background:var(--skra-comp-select-background-hover)}.skra-select-trigger:focus-visible{outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:.125rem}.skra-select-trigger:disabled{background:var(--skra-comp-select-background-disabled);color:var(--skra-comp-select-foreground-disabled);cursor:not-allowed}.skra-select-trigger[aria-readonly=true]{background:var(--skra-comp-select-background-readonly);cursor:default}.skra-select-trigger[aria-invalid=true]{border-color:var(--skra-comp-select-border-color-validation-invalid)}.skra-select-trigger-value{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-placeholder{color:var(--skra-comp-select-foreground-disabled)}.skra-select-arrow{flex:none;inline-size:var(--skra-comp-select-arrow-icon-size);block-size:var(--skra-comp-select-arrow-icon-size);background-color:var(--skra-comp-select-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-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-trigger[aria-expanded=true] .skra-select-arrow{transform:rotate(180deg)}.skra-select-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-select-panel-padding-block);padding-inline:var(--skra-comp-select-panel-padding-inline);background:var(--skra-comp-select-panel-background);color:var(--skra-comp-select-panel-foreground);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-panel-border-color);border-radius:var(--skra-comp-select-panel-radius);box-shadow:var(--skra-comp-select-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-select-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing),display var(--skra-comp-select-motion-duration) allow-discrete,overlay var(--skra-comp-select-motion-duration) allow-discrete}.skra-select-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-select-listbox:popover-open{opacity:0}}@media(prefers-reduced-motion:reduce){.skra-select-trigger,.skra-select-arrow,.skra-select-listbox{transition-duration:0ms}}\n"] }]
129
+ args: [{ selector: 'skra-multi-select', standalone: true, exportAs: 'skraMultiSelect', imports: [NgTemplateOutlet], template: `
130
+ <button
131
+ #trigger
132
+ class="skra-select-trigger"
133
+ type="button"
134
+ role="combobox"
135
+ aria-haspopup="listbox"
136
+ [attr.id]="triggerId()"
137
+ [attr.popovertarget]="panelId"
138
+ [attr.aria-controls]="panelId"
139
+ [attr.aria-expanded]="isOpen()"
140
+ [attr.aria-activedescendant]="activeOptionId()"
141
+ [attr.aria-describedby]="describedBy()"
142
+ [attr.aria-invalid]="hasError() ? 'true' : null"
143
+ [attr.aria-required]="isRequired() ? 'true' : null"
144
+ [attr.aria-readonly]="isReadonly() ? 'true' : null"
145
+ [disabled]="isDisabled()"
146
+ (click)="onTriggerClick($event)"
147
+ (keydown)="onTriggerKeydown($event)"
148
+ >
149
+ <span class="skra-select-trigger-value">
150
+ @if (triggerTemplate(); as tpl) {
151
+ <ng-container
152
+ [ngTemplateOutlet]="tpl"
153
+ [ngTemplateOutletContext]="triggerContext()"
154
+ ></ng-container>
155
+ } @else if (selectedOptions().length) {
156
+ {{ summary() }}
157
+ } @else {
158
+ <span class="skra-select-placeholder">{{ placeholder() }}</span>
159
+ }
160
+ </span>
161
+ <span class="skra-select-arrow" aria-hidden="true"></span>
162
+ </button>
163
+
164
+ <div
165
+ #panel
166
+ class="skra-select-listbox"
167
+ [id]="panelId"
168
+ role="listbox"
169
+ aria-multiselectable="true"
170
+ popover="auto"
171
+ (toggle)="onPanelToggle($event)"
172
+ >
173
+ <ng-content></ng-content>
174
+ </div>
175
+ `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraMultiSelect }], styles: ["@charset \"UTF-8\";:host{display:block;box-sizing:border-box;margin:0}.skra-select-trigger{display:flex;align-items:center;gap:var(--skra-comp-select-gap);inline-size:100%;min-block-size:var(--skra-comp-select-min-block-size);box-sizing:border-box;margin:0;padding-block:var(--skra-comp-select-padding-block);padding-inline:var(--skra-comp-select-padding-inline);text-align:start;cursor:pointer;background:var(--skra-comp-select-background);color:var(--skra-comp-select-foreground);border-color:var(--skra-comp-select-border-color);border-width:var(--skra-comp-select-border-width);border-style:var(--skra-comp-select-border-style);border-radius:var(--skra-comp-select-radius);box-shadow:var(--skra-comp-select-shadow);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),border-color var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing)}@media(hover:hover)and (pointer:fine){.skra-select-trigger:hover:not(:disabled):not([aria-readonly=true]){background:var(--skra-comp-select-background-hover)}}.skra-select-trigger:focus-visible{outline:var(--skra-comp-select-focus-ring-width) solid var(--skra-comp-select-focus-ring-color);outline-offset:.125rem}.skra-select-trigger:disabled{background:var(--skra-comp-select-background-disabled);color:var(--skra-comp-select-foreground-disabled);cursor:not-allowed}.skra-select-trigger[aria-readonly=true]{background:var(--skra-comp-select-background-readonly);cursor:default}.skra-select-trigger[aria-invalid=true]{border-color:var(--skra-comp-select-border-color-validation-invalid)}.skra-select-trigger-value{flex:1 1 auto;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--skra-comp-icon-size: var(--skra-comp-select-icon-size)}.skra-select-placeholder{color:var(--skra-comp-select-foreground-disabled)}.skra-select-arrow{flex:none;inline-size:var(--skra-comp-select-arrow-icon-size);block-size:var(--skra-comp-select-arrow-icon-size);background-color:var(--skra-comp-select-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-select-motion-duration) var(--skra-comp-select-motion-easing)}.skra-select-trigger[aria-expanded=true] .skra-select-arrow{transform:rotate(180deg)}.skra-select-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-select-panel-padding-block);padding-inline:var(--skra-comp-select-panel-padding-inline);background:var(--skra-comp-select-panel-background);color:var(--skra-comp-select-panel-foreground);border:var(--skra-comp-select-border-width) solid var(--skra-comp-select-panel-border-color);border-radius:var(--skra-comp-select-panel-radius);box-shadow:var(--skra-comp-select-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-select-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-select-motion-duration) var(--skra-comp-select-motion-easing),display var(--skra-comp-select-motion-duration) allow-discrete,overlay var(--skra-comp-select-motion-duration) allow-discrete}.skra-select-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-select-listbox:popover-open{opacity:0}}@media(prefers-reduced-motion:reduce){.skra-select-trigger,.skra-select-arrow,.skra-select-listbox{transition-duration:0ms}}\n"] }]
176
176
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
177
177
 
178
178
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"skra-ui-multi-select.mjs","sources":["../../../../libs/ui/multi-select/src/lib/multi-select/multi-select.ts","../../../../libs/ui/multi-select/src/skra-ui-multi-select.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ViewEncapsulation,\r\n afterRenderEffect,\r\n computed,\r\n input,\r\n output,\r\n} from '@angular/core';\r\n\r\nimport { _SkraSelectBase } 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 * SkraMultiSelect — a custom MULTI-select listbox: the WAI-ARIA APG multi-selectable listbox combobox.\r\n * The multi-cardinality sibling of `skra-select` over the shared `_SkraSelectBase` (**ADR 0046** —\r\n * cardinality split: a separate component with an HONEST `readonly T[]` value contract, NOT a `multiple`\r\n * flag). The trigger (`role=\"combobox\"`) opens a top-layer listbox panel (`popover=\"auto\"`, ADR 0044,\r\n * `aria-multiselectable=\"true\"`) of the SAME projected `skra-option` children — reused verbatim; the\r\n * option swaps its indicator to a checkbox via the `SkraListboxPanel.multiple` hint.\r\n *\r\n * - **Value (ADR 037):** `value = input<readonly T[]>([])` + `valueChange = output<readonly T[]>()` —\r\n * consumer-owned, no `@angular/forms`. Selection is membership (`compareWith` per element).\r\n * - **Toggle (ADR 0046):** picking an option toggles its membership and KEEPS THE PANEL OPEN (the one\r\n * behavioural divergence from single-select, which closes).\r\n * - **Trigger summary (ADR 0045 / 033):** comma-joined `getLabel()` of selected options — the VALUE\r\n * display, never the accessible name. Count-style / chips → the opt-in `[skraSelectTrigger]` recipe.\r\n *\r\n * Full contract: adr/specs/component-multi-select-spec.md.\r\n */\r\n@Component({\r\n selector: 'skra-multi-select',\r\n standalone: true,\r\n exportAs: 'skraMultiSelect',\r\n imports: [NgTemplateOutlet],\r\n template: `\r\n <button\r\n #trigger\r\n class=\"skra-select-trigger\"\r\n type=\"button\"\r\n role=\"combobox\"\r\n aria-haspopup=\"listbox\"\r\n [attr.id]=\"triggerId()\"\r\n [attr.popovertarget]=\"panelId\"\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-readonly]=\"isReadonly() ? 'true' : null\"\r\n [disabled]=\"isDisabled()\"\r\n (click)=\"onTriggerClick($event)\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n >\r\n <span class=\"skra-select-trigger-value\">\r\n @if (triggerTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"triggerContext()\"\r\n ></ng-container>\r\n } @else if (selectedOptions().length) {\r\n {{ summary() }}\r\n } @else {\r\n <span class=\"skra-select-placeholder\">{{ placeholder() }}</span>\r\n }\r\n </span>\r\n <span class=\"skra-select-arrow\" aria-hidden=\"true\"></span>\r\n </button>\r\n\r\n <div\r\n #panel\r\n class=\"skra-select-listbox\"\r\n [id]=\"panelId\"\r\n role=\"listbox\"\r\n aria-multiselectable=\"true\"\r\n popover=\"auto\"\r\n (toggle)=\"onPanelToggle($event)\"\r\n >\r\n <ng-content></ng-content>\r\n </div>\r\n `,\r\n styleUrl: './multi-select.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraMultiSelect }],\r\n})\r\nexport class SkraMultiSelect<T = unknown> extends _SkraSelectBase<T> {\r\n /** Consumer-owned ARRAY value channel (ADR 037) — default `[]` (never undefined). */\r\n readonly value = input<readonly T[]>([]);\r\n /** Fires ONLY on user toggle — emits a NEW array; never on a programmatic `value()` change (ADR 037). */\r\n readonly valueChange = output<readonly T[]>();\r\n /** Cardinality hint (ADR 0046) — multi-select (drives the option checkbox indicator + aria-multiselectable). */\r\n readonly multiple = true;\r\n\r\n /** The options whose value is a member of `value()` — feeds the trigger summary + seed. */\r\n protected readonly selectedOptions = computed(() =>\r\n this.options().filter((o) => this.isSelected(o.value())),\r\n );\r\n\r\n /** Comma-joined identity labels of the selected options (value display, ADR 0045 — language-neutral). */\r\n protected readonly summary = computed(() =>\r\n this.selectedOptions()\r\n .map((o) => o.getLabel())\r\n .join(', '),\r\n );\r\n\r\n protected readonly triggerContext = computed(() => ({\r\n $implicit: this.value(),\r\n options: this.selectedOptions(),\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 would throw NG0950. afterRender hooks never run on the server, so\r\n // this stays SSR-safe. Covers F-13 (initial render) + F-16 (runtime re-projection).\r\n afterRenderEffect(() => {\r\n const opts = this.options();\r\n const unmatched = this.value().filter(\r\n (v) => !opts.some((o) => this.compareWith()(o.value() as T, v)),\r\n );\r\n if (unmatched.length) {\r\n console.warn(\r\n '[skra-multi-select] value contains entries with no matching <skra-option> — they are ' +\r\n 'dropped from the trigger summary (likely a compareWith mismatch for object values).',\r\n );\r\n }\r\n });\r\n }\r\n }\r\n\r\n // ── SkraListboxPanel seam ──\r\n isSelected(value: unknown): boolean {\r\n return this.value().some((x) => this.compareWith()(x, value as T));\r\n }\r\n\r\n selectValue(value: unknown): void {\r\n const present = this.isSelected(value);\r\n const next = present\r\n ? this.value().filter((x) => !this.compareWith()(x, value as T))\r\n : [...this.value(), value as T];\r\n this.valueChange.emit(next);\r\n // NO close() — multi-select keeps the panel open for further toggling (ADR 0046).\r\n }\r\n\r\n protected activeSeedItem(): SkraListboxKeyboardItem | null {\r\n return this.selectedOptions()[0] ?? null;\r\n }\r\n}\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-multi-select.mjs","sources":["../../../../libs/ui/multi-select/src/lib/multi-select/multi-select.ts","../../../../libs/ui/multi-select/src/skra-ui-multi-select.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n afterRenderEffect,\n computed,\n input,\n output,\n} from '@angular/core';\n\nimport { _SkraSelectBase } 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 * SkraMultiSelect — a custom MULTI-select listbox: the WAI-ARIA APG multi-selectable listbox combobox.\n * The multi-cardinality sibling of `skra-select` over the shared `_SkraSelectBase` (**ADR 0046** —\n * cardinality split: a separate component with an HONEST `readonly T[]` value contract, NOT a `multiple`\n * flag). The trigger (`role=\"combobox\"`) opens a top-layer listbox panel (`popover=\"auto\"`, ADR 0044,\n * `aria-multiselectable=\"true\"`) of the SAME projected `skra-option` children — reused verbatim; the\n * option swaps its indicator to a checkbox via the `SkraListboxPanel.multiple` hint.\n *\n * - **Value (ADR 037):** `value = input<readonly T[]>([])` + `valueChange = output<readonly T[]>()` —\n * consumer-owned, no `@angular/forms`. Selection is membership (`compareWith` per element).\n * - **Toggle (ADR 0046):** picking an option toggles its membership and KEEPS THE PANEL OPEN (the one\n * behavioural divergence from single-select, which closes).\n * - **Trigger summary (ADR 0045 / 033):** comma-joined `getLabel()` of selected options — the VALUE\n * display, never the accessible name. Count-style / chips → the opt-in `[skraSelectTrigger]` recipe.\n *\n * Full contract: adr/specs/component-multi-select-spec.md.\n */\n@Component({\n selector: 'skra-multi-select',\n standalone: true,\n exportAs: 'skraMultiSelect',\n imports: [NgTemplateOutlet],\n template: `\n <button\n #trigger\n class=\"skra-select-trigger\"\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.id]=\"triggerId()\"\n [attr.popovertarget]=\"panelId\"\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-readonly]=\"isReadonly() ? 'true' : null\"\n [disabled]=\"isDisabled()\"\n (click)=\"onTriggerClick($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <span class=\"skra-select-trigger-value\">\n @if (triggerTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"triggerContext()\"\n ></ng-container>\n } @else if (selectedOptions().length) {\n {{ summary() }}\n } @else {\n <span class=\"skra-select-placeholder\">{{ placeholder() }}</span>\n }\n </span>\n <span class=\"skra-select-arrow\" aria-hidden=\"true\"></span>\n </button>\n\n <div\n #panel\n class=\"skra-select-listbox\"\n [id]=\"panelId\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n popover=\"auto\"\n (toggle)=\"onPanelToggle($event)\"\n >\n <ng-content></ng-content>\n </div>\n `,\n styleUrl: './multi-select.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraMultiSelect }],\n})\nexport class SkraMultiSelect<T = unknown> extends _SkraSelectBase<T> {\n /** Consumer-owned ARRAY value channel (ADR 037) — default `[]` (never undefined). */\n readonly value = input<readonly T[]>([]);\n /** Fires ONLY on user toggle — emits a NEW array; never on a programmatic `value()` change (ADR 037). */\n readonly valueChange = output<readonly T[]>();\n /** Cardinality hint (ADR 0046) — multi-select (drives the option checkbox indicator + aria-multiselectable). */\n readonly multiple = true;\n\n /** The options whose value is a member of `value()` — feeds the trigger summary + seed. */\n protected readonly selectedOptions = computed(() =>\n this.options().filter((o) => this.isSelected(o.value())),\n );\n\n /** Comma-joined identity labels of the selected options (value display, ADR 0045 — language-neutral). */\n protected readonly summary = computed(() =>\n this.selectedOptions()\n .map((o) => o.getLabel())\n .join(', '),\n );\n\n protected readonly triggerContext = computed(() => ({\n $implicit: this.value(),\n options: this.selectedOptions(),\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 would throw NG0950. afterRender hooks never run on the server, so\n // this stays SSR-safe. Covers F-13 (initial render) + F-16 (runtime re-projection).\n afterRenderEffect(() => {\n const opts = this.options();\n const unmatched = this.value().filter(\n (v) => !opts.some((o) => this.compareWith()(o.value() as T, v)),\n );\n if (unmatched.length) {\n console.warn(\n '[skra-multi-select] value contains entries with no matching <skra-option> — they are ' +\n 'dropped from the trigger summary (likely a compareWith mismatch for object values).',\n );\n }\n });\n }\n }\n\n // ── SkraListboxPanel seam ──\n isSelected(value: unknown): boolean {\n return this.value().some((x) => this.compareWith()(x, value as T));\n }\n\n selectValue(value: unknown): void {\n const present = this.isSelected(value);\n const next = present\n ? this.value().filter((x) => !this.compareWith()(x, value as T))\n : [...this.value(), value as T];\n this.valueChange.emit(next);\n // NO close() — multi-select keeps the panel open for further toggling (ADR 0046).\n }\n\n protected activeSeedItem(): SkraListboxKeyboardItem | null {\n return this.selectedOptions()[0] ?? null;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -113,7 +113,7 @@ class SkraNativeSelect {
113
113
  }
114
114
  }
115
115
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraNativeSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
116
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: SkraNativeSelect, isStandalone: true, selector: "select[skraSelect]", host: { listeners: { "mousedown": "blockWhenReadonly($event)", "keydown": "blockKeyWhenReadonly($event)" }, properties: { "attr.aria-describedby": "context?.describedByIds() ?? null", "attr.aria-invalid": "context?.hasError() ? \"true\" : null", "attr.aria-required": "context?.isRequired() ? \"true\" : null", "attr.aria-readonly": "context?.isReadonly() ? \"true\" : null" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{background-color:var(--skra-comp-field-background);color:var(--skra-comp-field-foreground);border-color:var(--skra-comp-field-border-color);border-width:var(--skra-comp-field-border-width);border-style:var(--skra-comp-field-border-style);border-radius:var(--skra-comp-field-radius);padding-block:var(--skra-comp-field-padding-block);padding-inline:var(--skra-comp-field-padding-inline);box-shadow:var(--skra-comp-field-shadow);font:inherit;outline:none;cursor:pointer;transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:not([size])){appearance:none;padding-inline-end:var(--skra-comp-field-select-arrow-padding-inline-end);background-image:var(--skra-comp-field-select-chevron-glyph);background-repeat:no-repeat;background-position:right var(--skra-comp-field-padding-inline) center;background-size:var(--skra-comp-field-select-arrow-icon-size)}:host([size]){background-image:none;cursor:default}:host(:not([size]):hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-background-hover);border-color:var(--skra-comp-field-border-color-hover)}:host(:focus-visible){border-color:var(--skra-comp-field-focus-ring-color);box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
116
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: SkraNativeSelect, isStandalone: true, selector: "select[skraSelect]", host: { listeners: { "mousedown": "blockWhenReadonly($event)", "keydown": "blockKeyWhenReadonly($event)" }, properties: { "attr.aria-describedby": "context?.describedByIds() ?? null", "attr.aria-invalid": "context?.hasError() ? \"true\" : null", "attr.aria-required": "context?.isRequired() ? \"true\" : null", "attr.aria-readonly": "context?.isReadonly() ? \"true\" : null" } }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [":host{background-color:var(--skra-comp-field-background);color:var(--skra-comp-field-foreground);border-color:var(--skra-comp-field-border-color);border-width:var(--skra-comp-field-border-width);border-style:var(--skra-comp-field-border-style);border-radius:var(--skra-comp-field-radius);padding-block:var(--skra-comp-field-padding-block);padding-inline:var(--skra-comp-field-padding-inline);box-shadow:var(--skra-comp-field-shadow);font:inherit;outline:none;cursor:pointer;transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:not([size])){appearance:none;padding-inline-end:var(--skra-comp-field-select-arrow-padding-inline-end);background-image:var(--skra-comp-field-select-chevron-glyph);background-repeat:no-repeat;background-position:right var(--skra-comp-field-padding-inline) center;background-size:var(--skra-comp-field-select-arrow-icon-size)}:host([size]){background-image:none;cursor:default}@media(hover:hover)and (pointer:fine){:host(:not([size]):hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-background-hover);border-color:var(--skra-comp-field-border-color-hover)}}:host(:focus-visible){border-color:var(--skra-comp-field-focus-ring-color);box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
117
117
  }
118
118
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraNativeSelect, decorators: [{
119
119
  type: Component,
@@ -128,7 +128,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
128
128
  // in `_form-controls.scss` (ARIA-only branch, no data-attr companion).
129
129
  '(mousedown)': 'blockWhenReadonly($event)',
130
130
  '(keydown)': 'blockKeyWhenReadonly($event)',
131
- }, styles: [":host{background-color:var(--skra-comp-field-background);color:var(--skra-comp-field-foreground);border-color:var(--skra-comp-field-border-color);border-width:var(--skra-comp-field-border-width);border-style:var(--skra-comp-field-border-style);border-radius:var(--skra-comp-field-radius);padding-block:var(--skra-comp-field-padding-block);padding-inline:var(--skra-comp-field-padding-inline);box-shadow:var(--skra-comp-field-shadow);font:inherit;outline:none;cursor:pointer;transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:not([size])){appearance:none;padding-inline-end:var(--skra-comp-field-select-arrow-padding-inline-end);background-image:var(--skra-comp-field-select-chevron-glyph);background-repeat:no-repeat;background-position:right var(--skra-comp-field-padding-inline) center;background-size:var(--skra-comp-field-select-arrow-icon-size)}:host([size]){background-image:none;cursor:default}:host(:not([size]):hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-background-hover);border-color:var(--skra-comp-field-border-color-hover)}:host(:focus-visible){border-color:var(--skra-comp-field-focus-ring-color);box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
131
+ }, styles: [":host{background-color:var(--skra-comp-field-background);color:var(--skra-comp-field-foreground);border-color:var(--skra-comp-field-border-color);border-width:var(--skra-comp-field-border-width);border-style:var(--skra-comp-field-border-style);border-radius:var(--skra-comp-field-radius);padding-block:var(--skra-comp-field-padding-block);padding-inline:var(--skra-comp-field-padding-inline);box-shadow:var(--skra-comp-field-shadow);font:inherit;outline:none;cursor:pointer;transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:not([size])){appearance:none;padding-inline-end:var(--skra-comp-field-select-arrow-padding-inline-end);background-image:var(--skra-comp-field-select-chevron-glyph);background-repeat:no-repeat;background-position:right var(--skra-comp-field-padding-inline) center;background-size:var(--skra-comp-field-select-arrow-icon-size)}:host([size]){background-image:none;cursor:default}@media(hover:hover)and (pointer:fine){:host(:not([size]):hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-background-hover);border-color:var(--skra-comp-field-border-color-hover)}}:host(:focus-visible){border-color:var(--skra-comp-field-focus-ring-color);box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
132
132
  }], ctorParameters: () => [] });
133
133
 
134
134
  // select[skraSelect] native form-control directive (SkraNativeSelect — ADR 0054 §4.3).
@@ -1 +1 @@
1
- {"version":3,"file":"skra-ui-native-select.mjs","sources":["../../../../libs/ui/native-select/src/lib/select/select.ts","../../../../libs/ui/native-select/src/index.ts","../../../../libs/ui/native-select/src/skra-ui-native-select.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n effect,\n inject,\n isDevMode,\n} from '@angular/core';\nimport { SkraFieldContext } from '@skra/ui/field';\n\n/**\n * Wires a native `<select>` inside `<skra-field>` to SkraFieldContext (ADR 013).\n * Tier 1 Foundations primitive — **single-select only**: a dropdown, or a\n * single-select scrolling listbox via the native `[size]` attribute.\n *\n * Native `[multiple]` is deliberately NOT supported (the directive rejects it,\n * see below). Native `<select multiple>` UX is not production-grade and cannot\n * be fixed at the native-`<select>` tier: selection requires modifier keys or\n * drag, the plain-click-toggle affordance exists only for a bare `<select\n * multiple>` in some engines (and breaks with an explicit `[size]`), and the\n * option markers are UA-drawn / unstylable cross-browser. Multi-select — chip\n * display, search, custom option templates, and a styleable per-row checkbox —\n * is wholly a Tier 2 `skra-select` (CDK listbox) concern per\n * `adr/specs/0039-select-directive-spec.md` §1.1 / §4.1.\n *\n * <skra-field>\n * <label skraLabelFor>Country</label>\n * <select skraSelect [value]=\"country()\" (change)=\"country.set($any($event.target).value)\">\n * <option value=\"pl\">Poland</option>\n * <option value=\"de\">Germany</option>\n * </select>\n * </skra-field>\n *\n * <!-- standalone: native implicit label (wrapping) — no id/for needed -->\n * <label>Country <select skraSelect><option value=\"pl\">Poland</option></select></label>\n *\n * `SkraFieldContext` is injected `{ optional: true }` (ADR 013 §3.7) — same\n * posture as SkraInput / SkraCheckbox. Standalone, the atom renders the skin\n * only, the readonly guards are inert, and the consumer owns every attribute\n * including the accessible name.\n *\n * Native `<select>` is the only web-platform widget whose dropdown renders in\n * the OS / browser top-layer, above modal `<dialog>`, above Shadow DOM\n * boundaries, above z-index stacking contexts. By staying native at Tier 1 we\n * inherit that for free — Tier 2 custom listbox will solve the same problem\n * via CDK overlay + Top Layer API.\n *\n * Value channel stays consumer-owned per ADR 037: bind via native `[value]` +\n * `(change)`, Reactive Forms `formControlName`, Signal Forms `[formField]`,\n * or `[(ngModel)]` — all work orthogonally.\n *\n * `readonly` is ARIA-only because HTML spec does not apply native `[readonly]`\n * to `<select>` (same gotcha as `<input type=\"checkbox|radio\">`). Unlike a text\n * input, though, a native `<select>` is an *uncontrolled* widget: if the listbox\n * opens and the user picks an option, the displayed value changes regardless of\n * the consumer's binding — so \"consumer prevents change\" cannot actually hold a\n * native select readonly. The directive therefore enforces readonly behaviourally\n * (blocks the picker from opening / the value from changing — see\n * `blockWhenReadonly` / `blockKeyWhenReadonly`), mirroring the imperative\n * `el.disabled` enforcement below. Both states are made non-interactive; only\n * readonly stays focusable + un-greyed and presents as the bare value. This is\n * interaction integrity for the control's own state — NOT a value channel, so the\n * ADR 037 no-`@angular/forms` / no-CVA boundary is untouched.\n */\n@Component({\n selector: 'select[skraSelect]',\n standalone: true,\n // Content-bearing host: <ng-content> projects the consumer's\n // <option>/<optgroup> children through. `template: ''` (as used for the void\n // <input> host) would discard them — see ADR 040 §4.\n template: '<ng-content></ng-content>',\n styleUrl: './select.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n '[attr.aria-describedby]': 'context?.describedByIds() ?? null',\n '[attr.aria-invalid]': 'context?.hasError() ? \"true\" : null',\n '[attr.aria-required]': 'context?.isRequired() ? \"true\" : null',\n '[attr.aria-readonly]': 'context?.isReadonly() ? \"true\" : null',\n // No `data-skra-readonly` — slice consumes `[aria-readonly=\"true\"]`\n // directly; emitting a parallel data-attr would be dead surface\n // (no slice selector reads it). Matches checkbox/radio readonly pattern\n // in `_form-controls.scss` (ARIA-only branch, no data-attr companion).\n '(mousedown)': 'blockWhenReadonly($event)',\n '(keydown)': 'blockKeyWhenReadonly($event)',\n },\n})\nexport class SkraNativeSelect {\n private readonly el = inject<ElementRef<HTMLSelectElement>>(ElementRef);\n readonly context = inject(SkraFieldContext, { optional: true });\n\n constructor() {\n const el = this.el.nativeElement;\n\n // Reject native multi-select loudly in dev — Forge fails on contract\n // violations rather than degrading silently. This throw is unrelated to the\n // (now optional, ADR 013 §3.7) field context: `[multiple]` is a capability\n // this atom genuinely cannot honour, whereas a missing field is a supported\n // standalone mode. Dev-only: never crashes a production bundle. A `<select\n // multiple>` that truly wants native UX can drop `skraSelect` and stay raw\n // HTML; for a real multi-select, use the Tier 2 <skra-select>.\n if (isDevMode() && el.multiple) {\n throw new Error(\n '[skraSelect] does not support the native [multiple] attribute at ' +\n 'Tier 1. Native multi-select UX is not production-grade (modifier-key/' +\n 'drag selection, UA-drawn unstylable markers, browser-fragile) and ' +\n 'cannot be fixed at the native <select> tier. Use a single-select ' +\n '<select skraSelect> (optionally with [size] for a listbox), or the ' +\n 'Tier 2 <skra-select> (CDK listbox) for real multi-select. See ' +\n 'adr/specs/0039-select-directive-spec.md §4.1.',\n );\n }\n\n // Field-cascaded native properties — only bind when context is present so\n // standalone consumers retain full control of their static attributes.\n // Mirrors SkraInput / SkraCheckbox.\n if (this.context) {\n const ctx = this.context;\n effect(() => {\n el.id = ctx.fieldId();\n });\n effect(() => {\n el.disabled = ctx.isDisabled();\n });\n }\n }\n\n /**\n * Readonly: suppress the pointer affordance that opens the native listbox.\n * `preventDefault` on `mousedown` stops the picker from opening (and, on a\n * native select, focus-on-click — Tab focus still works, which is the right\n * trade-off for a value-display control).\n */\n protected blockWhenReadonly(event: Event): void {\n if (this.context?.isReadonly()) event.preventDefault();\n }\n\n /**\n * Readonly: suppress every keyboard affordance that opens or mutates the\n * select, while letting Tab through for focus traversal. Space / Enter /\n * Alt+ArrowDown open the listbox; bare Arrows + first-letter typeahead change\n * the value inline without opening it — all are value-changing on a native\n * select, so all are blocked in readonly.\n */\n protected blockKeyWhenReadonly(event: KeyboardEvent): void {\n if (this.context?.isReadonly() && event.key !== 'Tab') {\n event.preventDefault();\n }\n }\n}\n","// select[skraSelect] native form-control directive (SkraNativeSelect — ADR 0054 §4.3).\r\nexport * from './lib/select/select';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDG;MAwBU,gBAAgB,CAAA;AACV,IAAA,EAAE,GAAG,MAAM,CAAgC,UAAU,CAAC;IAC9D,OAAO,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAE/D,IAAA,WAAA,GAAA;AACE,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa;;;;;;;;AAShC,QAAA,IAAI,SAAS,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE;YAC9B,MAAM,IAAI,KAAK,CACb,mEAAmE;gBACjE,uEAAuE;gBACvE,oEAAoE;gBACpE,mEAAmE;gBACnE,qEAAqE;gBACrE,gEAAgE;AAChE,gBAAA,+CAA+C,CAClD;QACH;;;;AAKA,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;AAChB,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO;YACxB,MAAM,CAAC,MAAK;AACV,gBAAA,EAAE,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE;AACvB,YAAA,CAAC,CAAC;YACF,MAAM,CAAC,MAAK;AACV,gBAAA,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,EAAE;AAChC,YAAA,CAAC,CAAC;QACJ;IACF;AAEA;;;;;AAKG;AACO,IAAA,iBAAiB,CAAC,KAAY,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE;YAAE,KAAK,CAAC,cAAc,EAAE;IACxD;AAEA;;;;;;AAMG;AACO,IAAA,oBAAoB,CAAC,KAAoB,EAAA;AACjD,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE;YACrD,KAAK,CAAC,cAAc,EAAE;QACxB;IACF;uGA7DW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,gBAAgB,0cAjBjB,2BAA2B,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,26DAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAiB1B,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAvB5B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,oBAAoB,EAAA,UAAA,EAClB,IAAI,EAAA,QAAA,EAIN,2BAA2B,EAAA,eAAA,EAEpB,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;AACJ,wBAAA,yBAAyB,EAAE,mCAAmC;AAC9D,wBAAA,qBAAqB,EAAE,qCAAqC;AAC5D,wBAAA,sBAAsB,EAAE,uCAAuC;AAC/D,wBAAA,sBAAsB,EAAE,uCAAuC;;;;;AAK/D,wBAAA,aAAa,EAAE,2BAA2B;AAC1C,wBAAA,WAAW,EAAE,8BAA8B;AAC5C,qBAAA,EAAA,MAAA,EAAA,CAAA,26DAAA,CAAA,EAAA;;;ACtFH;;ACAA;;AAEG;;;;"}
1
+ {"version":3,"file":"skra-ui-native-select.mjs","sources":["../../../../libs/ui/native-select/src/lib/select/select.ts","../../../../libs/ui/native-select/src/index.ts","../../../../libs/ui/native-select/src/skra-ui-native-select.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n effect,\n inject,\n isDevMode,\n} from '@angular/core';\nimport { SkraFieldContext } from '@skra/ui/field';\n\n/**\n * Wires a native `<select>` inside `<skra-field>` to SkraFieldContext (ADR 013).\n * Tier 1 Foundations primitive — **single-select only**: a dropdown, or a\n * single-select scrolling listbox via the native `[size]` attribute.\n *\n * Native `[multiple]` is deliberately NOT supported (the directive rejects it,\n * see below). Native `<select multiple>` UX is not production-grade and cannot\n * be fixed at the native-`<select>` tier: selection requires modifier keys or\n * drag, the plain-click-toggle affordance exists only for a bare `<select\n * multiple>` in some engines (and breaks with an explicit `[size]`), and the\n * option markers are UA-drawn / unstylable cross-browser. Multi-select — chip\n * display, search, custom option templates, and a styleable per-row checkbox —\n * is wholly a Tier 2 `skra-select` (CDK listbox) concern per\n * `adr/specs/0039-select-directive-spec.md` §1.1 / §4.1.\n *\n * <skra-field>\n * <label skraLabelFor>Country</label>\n * <select skraSelect [value]=\"country()\" (change)=\"country.set($any($event.target).value)\">\n * <option value=\"pl\">Poland</option>\n * <option value=\"de\">Germany</option>\n * </select>\n * </skra-field>\n *\n * <!-- standalone: native implicit label (wrapping) — no id/for needed -->\n * <label>Country <select skraSelect><option value=\"pl\">Poland</option></select></label>\n *\n * `SkraFieldContext` is injected `{ optional: true }` (ADR 013 §3.7) — same\n * posture as SkraInput / SkraCheckbox. Standalone, the atom renders the skin\n * only, the readonly guards are inert, and the consumer owns every attribute\n * including the accessible name.\n *\n * Native `<select>` is the only web-platform widget whose dropdown renders in\n * the OS / browser top-layer, above modal `<dialog>`, above Shadow DOM\n * boundaries, above z-index stacking contexts. By staying native at Tier 1 we\n * inherit that for free — Tier 2 custom listbox will solve the same problem\n * via CDK overlay + Top Layer API.\n *\n * Value channel stays consumer-owned per ADR 037: bind via native `[value]` +\n * `(change)`, Reactive Forms `formControlName`, Signal Forms `[formField]`,\n * or `[(ngModel)]` — all work orthogonally.\n *\n * `readonly` is ARIA-only because HTML spec does not apply native `[readonly]`\n * to `<select>` (same gotcha as `<input type=\"checkbox|radio\">`). Unlike a text\n * input, though, a native `<select>` is an *uncontrolled* widget: if the listbox\n * opens and the user picks an option, the displayed value changes regardless of\n * the consumer's binding — so \"consumer prevents change\" cannot actually hold a\n * native select readonly. The directive therefore enforces readonly behaviourally\n * (blocks the picker from opening / the value from changing — see\n * `blockWhenReadonly` / `blockKeyWhenReadonly`), mirroring the imperative\n * `el.disabled` enforcement below. Both states are made non-interactive; only\n * readonly stays focusable + un-greyed and presents as the bare value. This is\n * interaction integrity for the control's own state — NOT a value channel, so the\n * ADR 037 no-`@angular/forms` / no-CVA boundary is untouched.\n */\n@Component({\n selector: 'select[skraSelect]',\n standalone: true,\n // Content-bearing host: <ng-content> projects the consumer's\n // <option>/<optgroup> children through. `template: ''` (as used for the void\n // <input> host) would discard them — see ADR 040 §4.\n template: '<ng-content></ng-content>',\n styleUrl: './select.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n '[attr.aria-describedby]': 'context?.describedByIds() ?? null',\n '[attr.aria-invalid]': 'context?.hasError() ? \"true\" : null',\n '[attr.aria-required]': 'context?.isRequired() ? \"true\" : null',\n '[attr.aria-readonly]': 'context?.isReadonly() ? \"true\" : null',\n // No `data-skra-readonly` — slice consumes `[aria-readonly=\"true\"]`\n // directly; emitting a parallel data-attr would be dead surface\n // (no slice selector reads it). Matches checkbox/radio readonly pattern\n // in `_form-controls.scss` (ARIA-only branch, no data-attr companion).\n '(mousedown)': 'blockWhenReadonly($event)',\n '(keydown)': 'blockKeyWhenReadonly($event)',\n },\n})\nexport class SkraNativeSelect {\n private readonly el = inject<ElementRef<HTMLSelectElement>>(ElementRef);\n readonly context = inject(SkraFieldContext, { optional: true });\n\n constructor() {\n const el = this.el.nativeElement;\n\n // Reject native multi-select loudly in dev — Forge fails on contract\n // violations rather than degrading silently. This throw is unrelated to the\n // (now optional, ADR 013 §3.7) field context: `[multiple]` is a capability\n // this atom genuinely cannot honour, whereas a missing field is a supported\n // standalone mode. Dev-only: never crashes a production bundle. A `<select\n // multiple>` that truly wants native UX can drop `skraSelect` and stay raw\n // HTML; for a real multi-select, use the Tier 2 <skra-select>.\n if (isDevMode() && el.multiple) {\n throw new Error(\n '[skraSelect] does not support the native [multiple] attribute at ' +\n 'Tier 1. Native multi-select UX is not production-grade (modifier-key/' +\n 'drag selection, UA-drawn unstylable markers, browser-fragile) and ' +\n 'cannot be fixed at the native <select> tier. Use a single-select ' +\n '<select skraSelect> (optionally with [size] for a listbox), or the ' +\n 'Tier 2 <skra-select> (CDK listbox) for real multi-select. See ' +\n 'adr/specs/0039-select-directive-spec.md §4.1.',\n );\n }\n\n // Field-cascaded native properties — only bind when context is present so\n // standalone consumers retain full control of their static attributes.\n // Mirrors SkraInput / SkraCheckbox.\n if (this.context) {\n const ctx = this.context;\n effect(() => {\n el.id = ctx.fieldId();\n });\n effect(() => {\n el.disabled = ctx.isDisabled();\n });\n }\n }\n\n /**\n * Readonly: suppress the pointer affordance that opens the native listbox.\n * `preventDefault` on `mousedown` stops the picker from opening (and, on a\n * native select, focus-on-click — Tab focus still works, which is the right\n * trade-off for a value-display control).\n */\n protected blockWhenReadonly(event: Event): void {\n if (this.context?.isReadonly()) event.preventDefault();\n }\n\n /**\n * Readonly: suppress every keyboard affordance that opens or mutates the\n * select, while letting Tab through for focus traversal. Space / Enter /\n * Alt+ArrowDown open the listbox; bare Arrows + first-letter typeahead change\n * the value inline without opening it — all are value-changing on a native\n * select, so all are blocked in readonly.\n */\n protected blockKeyWhenReadonly(event: KeyboardEvent): void {\n if (this.context?.isReadonly() && event.key !== 'Tab') {\n event.preventDefault();\n }\n }\n}\n","// select[skraSelect] native form-control directive (SkraNativeSelect — ADR 0054 §4.3).\nexport * from './lib/select/select';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDG;MAwBU,gBAAgB,CAAA;AACV,IAAA,EAAE,GAAG,MAAM,CAAgC,UAAU,CAAC;IAC9D,OAAO,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAE/D,IAAA,WAAA,GAAA;AACE,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa;;;;;;;;AAShC,QAAA,IAAI,SAAS,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE;YAC9B,MAAM,IAAI,KAAK,CACb,mEAAmE;gBACjE,uEAAuE;gBACvE,oEAAoE;gBACpE,mEAAmE;gBACnE,qEAAqE;gBACrE,gEAAgE;AAChE,gBAAA,+CAA+C,CAClD;QACH;;;;AAKA,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;AAChB,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO;YACxB,MAAM,CAAC,MAAK;AACV,gBAAA,EAAE,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE;AACvB,YAAA,CAAC,CAAC;YACF,MAAM,CAAC,MAAK;AACV,gBAAA,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,EAAE;AAChC,YAAA,CAAC,CAAC;QACJ;IACF;AAEA;;;;;AAKG;AACO,IAAA,iBAAiB,CAAC,KAAY,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE;YAAE,KAAK,CAAC,cAAc,EAAE;IACxD;AAEA;;;;;;AAMG;AACO,IAAA,oBAAoB,CAAC,KAAoB,EAAA;AACjD,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE;YACrD,KAAK,CAAC,cAAc,EAAE;QACxB;IACF;uGA7DW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,gBAAgB,0cAjBjB,2BAA2B,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,k9DAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAiB1B,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAvB5B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,oBAAoB,EAAA,UAAA,EAClB,IAAI,EAAA,QAAA,EAIN,2BAA2B,EAAA,eAAA,EAEpB,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;AACJ,wBAAA,yBAAyB,EAAE,mCAAmC;AAC9D,wBAAA,qBAAqB,EAAE,qCAAqC;AAC5D,wBAAA,sBAAsB,EAAE,uCAAuC;AAC/D,wBAAA,sBAAsB,EAAE,uCAAuC;;;;;AAK/D,wBAAA,aAAa,EAAE,2BAA2B;AAC1C,wBAAA,WAAW,EAAE,8BAA8B;AAC5C,qBAAA,EAAA,MAAA,EAAA,CAAA,k9DAAA,CAAA,EAAA;;;ACtFH;;ACAA;;AAEG;;;;"}
@@ -33,9 +33,10 @@ class SkraPopover {
33
33
  ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
34
34
  host = inject(ElementRef);
35
35
  renderer = inject(Renderer2);
36
- /** The consumer's `[popovertarget]` button, resolved once (lazily-idempotent via `resolved`). */
36
+ /** The consumer's `[popovertarget]` button; resolution is retried until it succeeds. */
37
37
  trigger = null;
38
- resolved = false;
38
+ /** Latches the dev warning, NOT the resolution — see `ensureTrigger()`. */
39
+ warned = false;
39
40
  positioner;
40
41
  constructor() {
41
42
  // All imperative DOM wiring lives in afterNextRender — a browser-only hook, so the native listener
@@ -80,16 +81,24 @@ class SkraPopover {
80
81
  }
81
82
  /**
82
83
  * Resolve the `[popovertarget]` trigger and establish the disclosure ARIA on it (sanctioned a11y
83
- * wiring, ADR 013-style — not style mutation). Idempotent: runs at most once. Returns the trigger
84
- * (or null for an orphan popover, dev-warned once).
84
+ * wiring, ADR 013-style — not style mutation). Returns the trigger (or null for an orphan
85
+ * popover, dev-warned once).
86
+ *
87
+ * Idempotent on SUCCESS only. It deliberately does NOT latch on failure: a trigger behind an
88
+ * `@if`, a lazy route or a `@defer` block is simply not in the DOM at the `afterNextRender` tick,
89
+ * and latching there would strand it forever — no disclosure ARIA and, because `onOpen()` bails
90
+ * on a null anchor, an unpositioned panel at viewport 0,0. Retrying on each open is cheap (one
91
+ * `querySelectorAll` per open, only while still unresolved) and makes the `onOpen()` call a real
92
+ * safety net rather than the dead code it was. The dev warning latches separately via `warned`
93
+ * so a late-rendered trigger does not spam the console once per open.
85
94
  */
86
95
  ensureTrigger() {
87
- if (this.resolved)
96
+ if (this.trigger)
88
97
  return this.trigger;
89
- this.resolved = true;
90
98
  const id = this.host.nativeElement.id;
91
99
  if (!id) {
92
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
100
+ if (!this.warned && (typeof ngDevMode === 'undefined' || ngDevMode)) {
101
+ this.warned = true;
93
102
  console.warn('[skra-popover] has no `id` — it cannot be targeted by a `popovertarget` ' +
94
103
  'trigger and the disclosure ARIA (aria-expanded/aria-controls) cannot be wired. ' +
95
104
  'Give the popover an id and point the trigger at it: ' +
@@ -97,12 +106,19 @@ class SkraPopover {
97
106
  }
98
107
  return null;
99
108
  }
109
+ // Scope the lookup to this node's own root, not `document`: under the ADR 012 §5 delivery
110
+ // contract both trigger and panel live inside a shadow root, where a document-rooted query
111
+ // finds nothing and the disclosure ARIA + positioning silently fail to wire. `getRootNode()`
112
+ // returns `document` in the light-DOM case, so this is strictly a widening.
113
+ //
100
114
  // Match by attribute value rather than a built selector — no escaping needed, robust to ids
101
115
  // with special characters (and `CSS.escape` is not universally available).
116
+ const root = this.host.nativeElement.getRootNode();
102
117
  this.trigger =
103
- Array.from(document.querySelectorAll('[popovertarget]')).find((el) => el.getAttribute('popovertarget') === id) ?? null;
118
+ Array.from(root.querySelectorAll('[popovertarget]')).find((el) => el.getAttribute('popovertarget') === id) ?? null;
104
119
  if (!this.trigger) {
105
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
120
+ if (!this.warned && (typeof ngDevMode === 'undefined' || ngDevMode)) {
121
+ this.warned = true;
106
122
  console.warn(`[skra-popover] no trigger found with [popovertarget="${id}"] — the popover ` +
107
123
  'is unreachable and has no element to carry aria-expanded/aria-controls. ' +
108
124
  'Add a focusable trigger button with the matching popovertarget.');
@@ -1 +1 @@
1
- {"version":3,"file":"skra-ui-popover.mjs","sources":["../../../../libs/ui/popover/src/lib/popover/popover.ts","../../../../libs/ui/popover/src/skra-ui-popover.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n DestroyRef,\r\n ElementRef,\r\n Renderer2,\r\n ViewEncapsulation,\r\n afterNextRender,\r\n inject,\r\n input,\r\n} from '@angular/core';\r\n\r\nimport {\r\n _attachPositioner,\r\n type SkraPlacement,\r\n type _PositionerHandle,\r\n} from '@skra/ui/core';\r\n\r\ndeclare const ngDevMode: boolean | undefined;\r\n\r\n/**\r\n * SkraPopover — a NON-MODAL, anchored panel of arbitrary (interactive) content, rendered in the\r\n * browser TOP LAYER via the native Popover API (`popover=\"auto\"`), per ADR 0044. It is the middle\r\n * ground between Tooltip (supplementary, non-interactive, hover/focus) and the page-blocking modal\r\n * Dialog.\r\n *\r\n * SHAPE (scope 2026-06-06): a Component on the PANEL with projected content — NOT a directive on the\r\n * trigger + `TemplateRef`. The trigger is the consumer's own button linked by the native\r\n * `popovertarget` attribute (zero Forge trigger API); toggle, light-dismiss (pointer-outside),\r\n * Escape and invocation-order stacking are ALL platform-owned (contrast Tooltip's `popover=\"manual\"`,\r\n * which hand-wires Escape). The consumer MUST give `<skra-popover>` an `id` so `popovertarget` can\r\n * reference it.\r\n *\r\n * <button skraButton popovertarget=\"user-menu\">Account</button>\r\n * <skra-popover id=\"user-menu\"> …interactive content… </skra-popover>\r\n *\r\n * Forge owns only: the token-driven surface (see `popover.scss`), positioning via `SkraPositioner`\r\n * (geometry-only inline; ADR 0044 §3 rules 4–5), and the disclosure ARIA on the trigger\r\n * (`aria-expanded` / `aria-controls`). Focus is NOT trapped and NOT force-moved (non-modal — ADR 0044\r\n * §3 rule 8); reachability + focus-return are native to the Popover API. Panel naming is the\r\n * consumer's `aria-labelledby` → projected heading (ADR 033 container carve-out — no `ariaLabel`\r\n * input). A right-click context menu is NOT a Popover concern (→ `skra-menu` + recipe; SRP + WCAG).\r\n *\r\n * Full contract: adr/specs/component-popover-spec.md.\r\n */\r\n@Component({\r\n selector: 'skra-popover',\r\n standalone: true,\r\n exportAs: 'skraPopover',\r\n template: '<ng-content></ng-content>',\r\n styleUrl: './popover.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n host: {\r\n // Native top layer + platform-owned toggle / light-dismiss / Escape / stacking (ADR 0044).\r\n popover: 'auto',\r\n // NOTE: `toggle` is wired IMPERATIVELY (addEventListener) in the constructor, NOT as a host listener.\r\n // A consumer template binding `(toggle)` on <skra-popover> (a documented side-effect path) SHADOWS a\r\n // host-metadata `(toggle)` listener for the same event — which would silently break positioning +\r\n // aria-expanded. A raw `addEventListener` fires alongside the consumer's binding. (Found via the\r\n // skra-menu browser probe, 2026-06-07; same fix applied there.)\r\n },\r\n})\r\nexport class SkraPopover {\r\n /** Placement intent; `flip`/`shift` adjust at runtime. */\r\n readonly placement = input<SkraPlacement>('bottom');\r\n\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\r\n private readonly renderer = inject(Renderer2);\r\n\r\n /** The consumer's `[popovertarget]` button, resolved once (lazily-idempotent via `resolved`). */\r\n private trigger: HTMLElement | null = null;\r\n private resolved = false;\r\n private positioner?: _PositionerHandle;\r\n\r\n constructor() {\r\n // All imperative DOM wiring lives in afterNextRender — a browser-only hook, so the native listener\r\n // and the trigger resolution never run on the server (SSR-safe by construction; O-18).\r\n afterNextRender(() => {\r\n // Imperative (NOT host-metadata) so a consumer `(toggle)` binding does not shadow it — see host {}.\r\n this.host.nativeElement.addEventListener('toggle', this.onToggleNative);\r\n // Resolve + wire the trigger after the view exists (the trigger may be a later sibling), so the\r\n // disclosure relationship (aria-expanded=\"false\" + aria-controls) is present before first open.\r\n this.ensureTrigger();\r\n });\r\n inject(DestroyRef).onDestroy(() => this.teardown());\r\n }\r\n\r\n // ── Toggle (imperative native listener — see host {} note) ────────────────────\r\n private readonly onToggleNative = (event: Event): void => {\r\n if ((event as ToggleEvent).newState === 'open') this.onOpen();\r\n else this.onClose();\r\n };\r\n\r\n private onOpen(): void {\r\n const anchor = this.ensureTrigger(); // safety net if afterNextRender has not yet run\r\n if (anchor) {\r\n this.renderer.setAttribute(anchor, 'aria-expanded', 'true');\r\n } else {\r\n return; // orphan popover (dev-warned at init); nothing to anchor to.\r\n }\r\n\r\n this.positioner?.destroy();\r\n this.positioner = _attachPositioner(\r\n anchor,\r\n this.host.nativeElement,\r\n () => ({\r\n placement: this.placement(),\r\n offset: this.cssLenPx('--skra-comp-popover-offset'),\r\n viewportPadding: this.cssLenPx('--skra-comp-popover-viewport-padding'),\r\n }),\r\n );\r\n }\r\n\r\n private onClose(): void {\r\n if (this.trigger) {\r\n this.renderer.setAttribute(this.trigger, 'aria-expanded', 'false');\r\n }\r\n this.positioner?.destroy();\r\n this.positioner = undefined;\r\n }\r\n\r\n /**\r\n * Resolve the `[popovertarget]` trigger and establish the disclosure ARIA on it (sanctioned a11y\r\n * wiring, ADR 013-style — not style mutation). Idempotent: runs at most once. Returns the trigger\r\n * (or null for an orphan popover, dev-warned once).\r\n */\r\n private ensureTrigger(): HTMLElement | null {\r\n if (this.resolved) return this.trigger;\r\n this.resolved = true;\r\n\r\n const id = this.host.nativeElement.id;\r\n if (!id) {\r\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\r\n console.warn(\r\n '[skra-popover] has no `id` — it cannot be targeted by a `popovertarget` ' +\r\n 'trigger and the disclosure ARIA (aria-expanded/aria-controls) cannot be wired. ' +\r\n 'Give the popover an id and point the trigger at it: ' +\r\n '<button popovertarget=\"my-id\">…</button> <skra-popover id=\"my-id\">…',\r\n );\r\n }\r\n return null;\r\n }\r\n\r\n // Match by attribute value rather than a built selector — no escaping needed, robust to ids\r\n // with special characters (and `CSS.escape` is not universally available).\r\n this.trigger =\r\n Array.from(\r\n document.querySelectorAll<HTMLElement>('[popovertarget]'),\r\n ).find((el) => el.getAttribute('popovertarget') === id) ?? null;\r\n if (!this.trigger) {\r\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\r\n console.warn(\r\n `[skra-popover] no trigger found with [popovertarget=\"${id}\"] — the popover ` +\r\n 'is unreachable and has no element to carry aria-expanded/aria-controls. ' +\r\n 'Add a focusable trigger button with the matching popovertarget.',\r\n );\r\n }\r\n return null;\r\n }\r\n\r\n // Establish the disclosure relationship before the first open (APG Disclosure).\r\n this.renderer.setAttribute(this.trigger, 'aria-controls', id);\r\n this.renderer.setAttribute(this.trigger, 'aria-expanded', 'false');\r\n return this.trigger;\r\n }\r\n\r\n /** Read a length comp token (px/rem) off the host computed style for the positioner. */\r\n private cssLenPx(prop: string): number {\r\n const raw = getComputedStyle(this.host.nativeElement)\r\n .getPropertyValue(prop)\r\n .trim();\r\n if (raw.endsWith('px')) return parseFloat(raw);\r\n if (raw.endsWith('rem')) {\r\n const rootFontSize =\r\n parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\r\n return parseFloat(raw) * rootFontSize;\r\n }\r\n return parseFloat(raw) || 0;\r\n }\r\n\r\n private teardown(): void {\r\n this.host.nativeElement.removeEventListener('toggle', this.onToggleNative);\r\n this.positioner?.destroy();\r\n if (this.trigger) {\r\n this.renderer.removeAttribute(this.trigger, 'aria-expanded');\r\n this.renderer.removeAttribute(this.trigger, 'aria-controls');\r\n }\r\n }\r\n}\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-popover.mjs","sources":["../../../../libs/ui/popover/src/lib/popover/popover.ts","../../../../libs/ui/popover/src/skra-ui-popover.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n DestroyRef,\r\n ElementRef,\r\n Renderer2,\r\n ViewEncapsulation,\r\n afterNextRender,\r\n inject,\r\n input,\r\n} from '@angular/core';\r\n\r\nimport {\r\n _attachPositioner,\r\n type SkraPlacement,\r\n type _PositionerHandle,\r\n} from '@skra/ui/core';\r\n\r\ndeclare const ngDevMode: boolean | undefined;\r\n\r\n/**\r\n * SkraPopover — a NON-MODAL, anchored panel of arbitrary (interactive) content, rendered in the\r\n * browser TOP LAYER via the native Popover API (`popover=\"auto\"`), per ADR 0044. It is the middle\r\n * ground between Tooltip (supplementary, non-interactive, hover/focus) and the page-blocking modal\r\n * Dialog.\r\n *\r\n * SHAPE (scope 2026-06-06): a Component on the PANEL with projected content — NOT a directive on the\r\n * trigger + `TemplateRef`. The trigger is the consumer's own button linked by the native\r\n * `popovertarget` attribute (zero Forge trigger API); toggle, light-dismiss (pointer-outside),\r\n * Escape and invocation-order stacking are ALL platform-owned (contrast Tooltip's `popover=\"manual\"`,\r\n * which hand-wires Escape). The consumer MUST give `<skra-popover>` an `id` so `popovertarget` can\r\n * reference it.\r\n *\r\n * <button skraButton popovertarget=\"user-menu\">Account</button>\r\n * <skra-popover id=\"user-menu\"> …interactive content… </skra-popover>\r\n *\r\n * Forge owns only: the token-driven surface (see `popover.scss`), positioning via `SkraPositioner`\r\n * (geometry-only inline; ADR 0044 §3 rules 4–5), and the disclosure ARIA on the trigger\r\n * (`aria-expanded` / `aria-controls`). Focus is NOT trapped and NOT force-moved (non-modal — ADR 0044\r\n * §3 rule 8); reachability + focus-return are native to the Popover API. Panel naming is the\r\n * consumer's `aria-labelledby` → projected heading (ADR 033 container carve-out — no `ariaLabel`\r\n * input). A right-click context menu is NOT a Popover concern (→ `skra-menu` + recipe; SRP + WCAG).\r\n *\r\n * Full contract: adr/specs/component-popover-spec.md.\r\n */\r\n@Component({\r\n selector: 'skra-popover',\r\n standalone: true,\r\n exportAs: 'skraPopover',\r\n template: '<ng-content></ng-content>',\r\n styleUrl: './popover.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n host: {\r\n // Native top layer + platform-owned toggle / light-dismiss / Escape / stacking (ADR 0044).\r\n popover: 'auto',\r\n // NOTE: `toggle` is wired IMPERATIVELY (addEventListener) in the constructor, NOT as a host listener.\r\n // A consumer template binding `(toggle)` on <skra-popover> (a documented side-effect path) SHADOWS a\r\n // host-metadata `(toggle)` listener for the same event — which would silently break positioning +\r\n // aria-expanded. A raw `addEventListener` fires alongside the consumer's binding. (Found via the\r\n // skra-menu browser probe, 2026-06-07; same fix applied there.)\r\n },\r\n})\r\nexport class SkraPopover {\r\n /** Placement intent; `flip`/`shift` adjust at runtime. */\r\n readonly placement = input<SkraPlacement>('bottom');\r\n\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\r\n private readonly renderer = inject(Renderer2);\r\n\r\n /** The consumer's `[popovertarget]` button; resolution is retried until it succeeds. */\r\n private trigger: HTMLElement | null = null;\r\n /** Latches the dev warning, NOT the resolution — see `ensureTrigger()`. */\r\n private warned = false;\r\n private positioner?: _PositionerHandle;\r\n\r\n constructor() {\r\n // All imperative DOM wiring lives in afterNextRender — a browser-only hook, so the native listener\r\n // and the trigger resolution never run on the server (SSR-safe by construction; O-18).\r\n afterNextRender(() => {\r\n // Imperative (NOT host-metadata) so a consumer `(toggle)` binding does not shadow it — see host {}.\r\n this.host.nativeElement.addEventListener('toggle', this.onToggleNative);\r\n // Resolve + wire the trigger after the view exists (the trigger may be a later sibling), so the\r\n // disclosure relationship (aria-expanded=\"false\" + aria-controls) is present before first open.\r\n this.ensureTrigger();\r\n });\r\n inject(DestroyRef).onDestroy(() => this.teardown());\r\n }\r\n\r\n // ── Toggle (imperative native listener — see host {} note) ────────────────────\r\n private readonly onToggleNative = (event: Event): void => {\r\n if ((event as ToggleEvent).newState === 'open') this.onOpen();\r\n else this.onClose();\r\n };\r\n\r\n private onOpen(): void {\r\n const anchor = this.ensureTrigger(); // safety net if afterNextRender has not yet run\r\n if (anchor) {\r\n this.renderer.setAttribute(anchor, 'aria-expanded', 'true');\r\n } else {\r\n return; // orphan popover (dev-warned at init); nothing to anchor to.\r\n }\r\n\r\n this.positioner?.destroy();\r\n this.positioner = _attachPositioner(\r\n anchor,\r\n this.host.nativeElement,\r\n () => ({\r\n placement: this.placement(),\r\n offset: this.cssLenPx('--skra-comp-popover-offset'),\r\n viewportPadding: this.cssLenPx('--skra-comp-popover-viewport-padding'),\r\n }),\r\n );\r\n }\r\n\r\n private onClose(): void {\r\n if (this.trigger) {\r\n this.renderer.setAttribute(this.trigger, 'aria-expanded', 'false');\r\n }\r\n this.positioner?.destroy();\r\n this.positioner = undefined;\r\n }\r\n\r\n /**\r\n * Resolve the `[popovertarget]` trigger and establish the disclosure ARIA on it (sanctioned a11y\r\n * wiring, ADR 013-style — not style mutation). Returns the trigger (or null for an orphan\r\n * popover, dev-warned once).\r\n *\r\n * Idempotent on SUCCESS only. It deliberately does NOT latch on failure: a trigger behind an\r\n * `@if`, a lazy route or a `@defer` block is simply not in the DOM at the `afterNextRender` tick,\r\n * and latching there would strand it forever — no disclosure ARIA and, because `onOpen()` bails\r\n * on a null anchor, an unpositioned panel at viewport 0,0. Retrying on each open is cheap (one\r\n * `querySelectorAll` per open, only while still unresolved) and makes the `onOpen()` call a real\r\n * safety net rather than the dead code it was. The dev warning latches separately via `warned`\r\n * so a late-rendered trigger does not spam the console once per open.\r\n */\r\n private ensureTrigger(): HTMLElement | null {\r\n if (this.trigger) return this.trigger;\r\n\r\n const id = this.host.nativeElement.id;\r\n if (!id) {\r\n if (!this.warned && (typeof ngDevMode === 'undefined' || ngDevMode)) {\r\n this.warned = true;\r\n console.warn(\r\n '[skra-popover] has no `id` — it cannot be targeted by a `popovertarget` ' +\r\n 'trigger and the disclosure ARIA (aria-expanded/aria-controls) cannot be wired. ' +\r\n 'Give the popover an id and point the trigger at it: ' +\r\n '<button popovertarget=\"my-id\">…</button> <skra-popover id=\"my-id\">…',\r\n );\r\n }\r\n return null;\r\n }\r\n\r\n // Scope the lookup to this node's own root, not `document`: under the ADR 012 §5 delivery\r\n // contract both trigger and panel live inside a shadow root, where a document-rooted query\r\n // finds nothing and the disclosure ARIA + positioning silently fail to wire. `getRootNode()`\r\n // returns `document` in the light-DOM case, so this is strictly a widening.\r\n //\r\n // Match by attribute value rather than a built selector — no escaping needed, robust to ids\r\n // with special characters (and `CSS.escape` is not universally available).\r\n const root = this.host.nativeElement.getRootNode() as Document | ShadowRoot;\r\n this.trigger =\r\n Array.from(root.querySelectorAll<HTMLElement>('[popovertarget]')).find(\r\n (el) => el.getAttribute('popovertarget') === id,\r\n ) ?? null;\r\n if (!this.trigger) {\r\n if (!this.warned && (typeof ngDevMode === 'undefined' || ngDevMode)) {\r\n this.warned = true;\r\n console.warn(\r\n `[skra-popover] no trigger found with [popovertarget=\"${id}\"] — the popover ` +\r\n 'is unreachable and has no element to carry aria-expanded/aria-controls. ' +\r\n 'Add a focusable trigger button with the matching popovertarget.',\r\n );\r\n }\r\n return null;\r\n }\r\n\r\n // Establish the disclosure relationship before the first open (APG Disclosure).\r\n this.renderer.setAttribute(this.trigger, 'aria-controls', id);\r\n this.renderer.setAttribute(this.trigger, 'aria-expanded', 'false');\r\n return this.trigger;\r\n }\r\n\r\n /** Read a length comp token (px/rem) off the host computed style for the positioner. */\r\n private cssLenPx(prop: string): number {\r\n const raw = getComputedStyle(this.host.nativeElement)\r\n .getPropertyValue(prop)\r\n .trim();\r\n if (raw.endsWith('px')) return parseFloat(raw);\r\n if (raw.endsWith('rem')) {\r\n const rootFontSize =\r\n parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\r\n return parseFloat(raw) * rootFontSize;\r\n }\r\n return parseFloat(raw) || 0;\r\n }\r\n\r\n private teardown(): void {\r\n this.host.nativeElement.removeEventListener('toggle', this.onToggleNative);\r\n this.positioner?.destroy();\r\n if (this.trigger) {\r\n this.renderer.removeAttribute(this.trigger, 'aria-expanded');\r\n this.renderer.removeAttribute(this.trigger, 'aria-controls');\r\n }\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAoBA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;MAmBU,WAAW,CAAA;;IAEb,SAAS,GAAG,KAAK,CAAgB,QAAQ;kFAAC;AAElC,IAAA,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC;AAClD,IAAA,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC;;IAGrC,OAAO,GAAuB,IAAI;;IAElC,MAAM,GAAG,KAAK;AACd,IAAA,UAAU;AAElB,IAAA,WAAA,GAAA;;;QAGE,eAAe,CAAC,MAAK;;AAEnB,YAAA,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC;;;YAGvE,IAAI,CAAC,aAAa,EAAE;AACtB,QAAA,CAAC,CAAC;AACF,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACrD;;AAGiB,IAAA,cAAc,GAAG,CAAC,KAAY,KAAU;AACvD,QAAA,IAAK,KAAqB,CAAC,QAAQ,KAAK,MAAM;YAAE,IAAI,CAAC,MAAM,EAAE;;YACxD,IAAI,CAAC,OAAO,EAAE;AACrB,IAAA,CAAC;IAEO,MAAM,GAAA;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,IAAI,MAAM,EAAE;YACV,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC;QAC7D;aAAO;AACL,YAAA,OAAO;QACT;AAEA,QAAA,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE;AAC1B,QAAA,IAAI,CAAC,UAAU,GAAG,iBAAiB,CACjC,MAAM,EACN,IAAI,CAAC,IAAI,CAAC,aAAa,EACvB,OAAO;AACL,YAAA,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE;AAC3B,YAAA,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,4BAA4B,CAAC;AACnD,YAAA,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,sCAAsC,CAAC;AACvE,SAAA,CAAC,CACH;IACH;IAEQ,OAAO,GAAA;AACb,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;AAChB,YAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC;QACpE;AACA,QAAA,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE;AAC1B,QAAA,IAAI,CAAC,UAAU,GAAG,SAAS;IAC7B;AAEA;;;;;;;;;;;;AAYG;IACK,aAAa,GAAA;QACnB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO;QAErC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE;QACrC,IAAI,CAAC,EAAE,EAAE;AACP,YAAA,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,CAAC,EAAE;AACnE,gBAAA,IAAI,CAAC,MAAM,GAAG,IAAI;gBAClB,OAAO,CAAC,IAAI,CACV,0EAA0E;oBACxE,iFAAiF;oBACjF,sDAAsD;AACtD,oBAAA,qEAAqE,CACxE;YACH;AACA,YAAA,OAAO,IAAI;QACb;;;;;;;;QASA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,EAA2B;AAC3E,QAAA,IAAI,CAAC,OAAO;AACV,YAAA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAc,iBAAiB,CAAC,CAAC,CAAC,IAAI,CACpE,CAAC,EAAE,KAAK,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,KAAK,EAAE,CAChD,IAAI,IAAI;AACX,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;AACjB,YAAA,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,OAAO,SAAS,KAAK,WAAW,IAAI,SAAS,CAAC,EAAE;AACnE,gBAAA,IAAI,CAAC,MAAM,GAAG,IAAI;AAClB,gBAAA,OAAO,CAAC,IAAI,CACV,CAAA,qDAAA,EAAwD,EAAE,CAAA,iBAAA,CAAmB;oBAC3E,0EAA0E;AAC1E,oBAAA,iEAAiE,CACpE;YACH;AACA,YAAA,OAAO,IAAI;QACb;;AAGA,QAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,CAAC;AAC7D,QAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC;QAClE,OAAO,IAAI,CAAC,OAAO;IACrB;;AAGQ,IAAA,QAAQ,CAAC,IAAY,EAAA;QAC3B,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa;aACjD,gBAAgB,CAAC,IAAI;AACrB,aAAA,IAAI,EAAE;AACT,QAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;AAAE,YAAA,OAAO,UAAU,CAAC,GAAG,CAAC;AAC9C,QAAA,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;AACvB,YAAA,MAAM,YAAY,GAChB,UAAU,CAAC,gBAAgB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE;AACvE,YAAA,OAAO,UAAU,CAAC,GAAG,CAAC,GAAG,YAAY;QACvC;AACA,QAAA,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;IAC7B;IAEQ,QAAQ,GAAA;AACd,QAAA,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC;AAC1E,QAAA,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE;AAC1B,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC;YAC5D,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC;QAC9D;IACF;uGA7IW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAX,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,WAAW,gSAdZ,2BAA2B,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,m0CAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAc1B,WAAW,EAAA,UAAA,EAAA,CAAA;kBAlBvB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,cAAc,EAAA,UAAA,EACZ,IAAI,EAAA,QAAA,EACN,aAAa,YACb,2BAA2B,EAAA,eAAA,EAEpB,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;;AAEJ,wBAAA,OAAO,EAAE,MAAM;;;;;;AAMhB,qBAAA,EAAA,MAAA,EAAA,CAAA,m0CAAA,CAAA,EAAA;;;AC7DH;;AAEG;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"skra-ui-progress.mjs","sources":["../../../../libs/ui/progress/src/lib/progress/progress.ts","../../../../libs/ui/progress/src/skra-ui-progress.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ViewEncapsulation,\r\n input,\r\n} from '@angular/core';\r\n\r\n/**\r\n * `progress[skraProgress]` — a determinate, horizontal task-completion bar (Wave 2,\r\n * `libs/ui/indicators`). Shows the known progress of a finite task (file upload, wizard\r\n * \"step 3 of 5\", import job) as a fill from 0 → `value` / `max`.\r\n *\r\n * SRP-distinct from `[skraLoadingIndicator]` (ADR 028): that primitive is the\r\n * *indeterminate* \"host is busy\" spinner injected into another component's `loading()`\r\n * slot; SkraProgress is the *determinate* \"task is N% complete\" standalone bar. The two\r\n * are complementary — see `adr/specs/component-progress-spec.md` § SRP boundary.\r\n *\r\n * **Why a Component on the native `<progress>` host** (ADR 040), not a `<div role=\"progressbar\">`:\r\n * 1. ADR 021 (CSP): the fill width is a continuous `value/max` length. A custom element would\r\n * force `[style.--progress-value]` (forbidden) or a class/`data-*`-per-percent hack. Native\r\n * `<progress value max>` lets the BROWSER compute the fill width on its\r\n * `::-webkit-progress-value` / `::-moz-progress-bar` pseudo — zero author inline-style.\r\n * 2. Free a11y: the native host carries `role=\"progressbar\"` + `aria-valuenow`/`-valuemin`/\r\n * `-valuemax` with no author ARIA (native-attr-first, boundary #9).\r\n *\r\n * Non-interactive: `<progress>` is non-focusable, so the State Surface is all-N/A (no\r\n * `disabled`/`loading`/`severity` in MVP — those are deferred / belong to other atoms).\r\n *\r\n * Naming (ADR 033): the consumer associates a `<label for>` / `aria-labelledby` (passed through\r\n * to the host). No `SkraFieldContext` injection (a progress bar is a read-only display, not a\r\n * form field); no `ariaLabel` input.\r\n *\r\n * `value`/`max` are plain consumer-driven inputs (ADR 037 — agnostic; wire the source from any\r\n * signal/form strategy). Native `<progress>` clamps `value` to `[0, max]`, so no author clamp.\r\n */\r\n@Component({\r\n selector: 'progress[skraProgress]',\r\n standalone: true,\r\n template: '',\r\n styleUrl: './progress.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n host: {\r\n // The data contract — reflected to the NATIVE attributes. The browser derives\r\n // role=\"progressbar\" + aria-valuenow/-valuemin/-valuemax + the fill width from these.\r\n // No [style.*] (ADR 021 — the fill is native, never an inline style) and no data-skra-*\r\n // (State Surface all-N/A).\r\n '[attr.value]': 'value()',\r\n '[attr.max]': 'max()',\r\n },\r\n})\r\nexport class SkraProgress {\r\n /** Current completion, reflected to the native `value` attribute (native clamps to [0, max]). */\r\n readonly value = input<number>(0);\r\n\r\n /** Upper bound, reflected to the native `max` attribute. Drives native `aria-valuemax`. */\r\n readonly max = input<number>(100);\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;MAiBU,YAAY,CAAA;;IAEd,KAAK,GAAG,KAAK,CAAS,CAAC;8EAAC;;IAGxB,GAAG,GAAG,KAAK,CAAS,GAAG;4EAAC;uGALtB,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,gZAbb,EAAE,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,+7BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAaD,YAAY,EAAA,UAAA,EAAA,CAAA;kBAhBxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,wBAAwB,EAAA,UAAA,EACtB,IAAI,EAAA,QAAA,EACN,EAAE,EAAA,eAAA,EAEK,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;;;;;AAKJ,wBAAA,cAAc,EAAE,SAAS;AACzB,wBAAA,YAAY,EAAE,OAAO;AACtB,qBAAA,EAAA,MAAA,EAAA,CAAA,+7BAAA,CAAA,EAAA;;;ACjDH;;AAEG;;;;"}
1
+ {"version":3,"file":"skra-ui-progress.mjs","sources":["../../../../libs/ui/progress/src/lib/progress/progress.ts","../../../../libs/ui/progress/src/skra-ui-progress.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n input,\n} from '@angular/core';\n\n/**\n * `progress[skraProgress]` — a determinate, horizontal task-completion bar (Wave 2,\n * `libs/ui/indicators`). Shows the known progress of a finite task (file upload, wizard\n * \"step 3 of 5\", import job) as a fill from 0 → `value` / `max`.\n *\n * SRP-distinct from `[skraLoadingIndicator]` (ADR 028): that primitive is the\n * *indeterminate* \"host is busy\" spinner injected into another component's `loading()`\n * slot; SkraProgress is the *determinate* \"task is N% complete\" standalone bar. The two\n * are complementary — see `adr/specs/component-progress-spec.md` § SRP boundary.\n *\n * **Why a Component on the native `<progress>` host** (ADR 040), not a `<div role=\"progressbar\">`:\n * 1. ADR 021 (CSP): the fill width is a continuous `value/max` length. A custom element would\n * force `[style.--progress-value]` (forbidden) or a class/`data-*`-per-percent hack. Native\n * `<progress value max>` lets the BROWSER compute the fill width on its\n * `::-webkit-progress-value` / `::-moz-progress-bar` pseudo — zero author inline-style.\n * 2. Free a11y: the native host carries `role=\"progressbar\"` + `aria-valuenow`/`-valuemin`/\n * `-valuemax` with no author ARIA (native-attr-first, boundary #9).\n *\n * Non-interactive: `<progress>` is non-focusable, so the State Surface is all-N/A (no\n * `disabled`/`loading`/`severity` in MVP — those are deferred / belong to other atoms).\n *\n * Naming (ADR 033): the consumer associates a `<label for>` / `aria-labelledby` (passed through\n * to the host). No `SkraFieldContext` injection (a progress bar is a read-only display, not a\n * form field); no `ariaLabel` input.\n *\n * `value`/`max` are plain consumer-driven inputs (ADR 037 — agnostic; wire the source from any\n * signal/form strategy). Native `<progress>` clamps `value` to `[0, max]`, so no author clamp.\n */\n@Component({\n selector: 'progress[skraProgress]',\n standalone: true,\n template: '',\n styleUrl: './progress.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // The data contract — reflected to the NATIVE attributes. The browser derives\n // role=\"progressbar\" + aria-valuenow/-valuemin/-valuemax + the fill width from these.\n // No [style.*] (ADR 021 — the fill is native, never an inline style) and no data-skra-*\n // (State Surface all-N/A).\n '[attr.value]': 'value()',\n '[attr.max]': 'max()',\n },\n})\nexport class SkraProgress {\n /** Current completion, reflected to the native `value` attribute (native clamps to [0, max]). */\n readonly value = input<number>(0);\n\n /** Upper bound, reflected to the native `max` attribute. Drives native `aria-valuemax`. */\n readonly max = input<number>(100);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;MAiBU,YAAY,CAAA;;IAEd,KAAK,GAAG,KAAK,CAAS,CAAC;8EAAC;;IAGxB,GAAG,GAAG,KAAK,CAAS,GAAG;4EAAC;uGALtB,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,gZAbb,EAAE,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,+7BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAaD,YAAY,EAAA,UAAA,EAAA,CAAA;kBAhBxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,wBAAwB,EAAA,UAAA,EACtB,IAAI,EAAA,QAAA,EACN,EAAE,EAAA,eAAA,EAEK,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;;;;;AAKJ,wBAAA,cAAc,EAAE,SAAS;AACzB,wBAAA,YAAY,EAAE,OAAO;AACtB,qBAAA,EAAA,MAAA,EAAA,CAAA,+7BAAA,CAAA,EAAA;;;ACjDH;;AAEG;;;;"}
@@ -66,7 +66,7 @@ class SkraRadio {
66
66
  }
67
67
  }
68
68
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraRadio, deps: [], target: i0.ɵɵFactoryTarget.Component });
69
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: SkraRadio, isStandalone: true, selector: "input[type=\"radio\"][skraRadio]", host: { listeners: { "click": "blockWhenReadonly($event)", "keydown": "blockKeyWhenReadonly($event)" }, properties: { "attr.data-skra-disabled": "context?.isDisabled() ? \"\" : null", "attr.aria-invalid": "context?.hasError() ? \"true\" : null", "attr.aria-readonly": "context?.isReadonly() ? \"true\" : null", "attr.aria-describedby": "context?.describedByIds() ?? null" } }, ngImport: i0, template: '', isInline: true, styles: [":host{appearance:none;flex-shrink:0;cursor:pointer;outline:none;background-repeat:no-repeat;background-position:center;inline-size:var(--skra-comp-field-radio-size);block-size:var(--skra-comp-field-radio-size);border-width:var(--skra-comp-field-radio-border-width);border-style:solid;border-color:var(--skra-comp-field-radio-border-color);border-radius:50%;background-color:var(--skra-comp-field-radio-background);transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:checked){background-color:var(--skra-comp-field-radio-checked-background);border-color:var(--skra-comp-field-radio-checked-background);background-image:radial-gradient(circle,var(--skra-comp-field-radio-dot-color) 0,var(--skra-comp-field-radio-dot-color) calc(var(--skra-comp-field-radio-dot-size) / 2),transparent calc(var(--skra-comp-field-radio-dot-size) / 2 + .5px))}:host(:hover:not(:disabled):not([aria-readonly=true])){border-color:var(--skra-comp-field-radio-border-color-hover)}:host(:checked:hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-radio-checked-background-hover);border-color:var(--skra-comp-field-radio-checked-background-hover)}:host(:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled),:host([data-skra-disabled]){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default;background-color:var(--skra-comp-field-background-readonly);border-color:var(--skra-comp-field-border-color-readonly)}:host(:checked[aria-readonly=true]){background-color:var(--skra-comp-field-radio-checked-background-readonly);border-color:var(--skra-comp-field-radio-checked-background-readonly)}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
69
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.1", type: SkraRadio, isStandalone: true, selector: "input[type=\"radio\"][skraRadio]", host: { listeners: { "click": "blockWhenReadonly($event)", "keydown": "blockKeyWhenReadonly($event)" }, properties: { "attr.data-skra-disabled": "context?.isDisabled() ? \"\" : null", "attr.aria-invalid": "context?.hasError() ? \"true\" : null", "attr.aria-readonly": "context?.isReadonly() ? \"true\" : null", "attr.aria-describedby": "context?.describedByIds() ?? null" } }, ngImport: i0, template: '', isInline: true, styles: [":host{appearance:none;flex-shrink:0;cursor:pointer;outline:none;background-repeat:no-repeat;background-position:center;inline-size:var(--skra-comp-field-radio-size);block-size:var(--skra-comp-field-radio-size);border-width:var(--skra-comp-field-radio-border-width);border-style:solid;border-color:var(--skra-comp-field-radio-border-color);border-radius:50%;background-color:var(--skra-comp-field-radio-background);transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:checked){background-color:var(--skra-comp-field-radio-checked-background);border-color:var(--skra-comp-field-radio-checked-background);background-image:radial-gradient(circle,var(--skra-comp-field-radio-dot-color) 0,var(--skra-comp-field-radio-dot-color) calc(var(--skra-comp-field-radio-dot-size) / 2),transparent calc(var(--skra-comp-field-radio-dot-size) / 2 + .5px))}@media(hover:hover)and (pointer:fine){:host(:hover:not(:disabled):not([aria-readonly=true])){border-color:var(--skra-comp-field-radio-border-color-hover)}:host(:checked:hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-radio-checked-background-hover);border-color:var(--skra-comp-field-radio-checked-background-hover)}}:host(:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled),:host([data-skra-disabled]){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default;background-color:var(--skra-comp-field-background-readonly);border-color:var(--skra-comp-field-border-color-readonly)}:host(:checked[aria-readonly=true]){background-color:var(--skra-comp-field-radio-checked-background-readonly);border-color:var(--skra-comp-field-radio-checked-background-readonly);--skra-comp-field-radio-dot-color: var( --skra-comp-field-radio-dot-color-readonly )}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
70
70
  }
71
71
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: SkraRadio, decorators: [{
72
72
  type: Component,
@@ -81,7 +81,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
81
81
  '[attr.aria-describedby]': 'context?.describedByIds() ?? null',
82
82
  '(click)': 'blockWhenReadonly($event)',
83
83
  '(keydown)': 'blockKeyWhenReadonly($event)',
84
- }, styles: [":host{appearance:none;flex-shrink:0;cursor:pointer;outline:none;background-repeat:no-repeat;background-position:center;inline-size:var(--skra-comp-field-radio-size);block-size:var(--skra-comp-field-radio-size);border-width:var(--skra-comp-field-radio-border-width);border-style:solid;border-color:var(--skra-comp-field-radio-border-color);border-radius:50%;background-color:var(--skra-comp-field-radio-background);transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:checked){background-color:var(--skra-comp-field-radio-checked-background);border-color:var(--skra-comp-field-radio-checked-background);background-image:radial-gradient(circle,var(--skra-comp-field-radio-dot-color) 0,var(--skra-comp-field-radio-dot-color) calc(var(--skra-comp-field-radio-dot-size) / 2),transparent calc(var(--skra-comp-field-radio-dot-size) / 2 + .5px))}:host(:hover:not(:disabled):not([aria-readonly=true])){border-color:var(--skra-comp-field-radio-border-color-hover)}:host(:checked:hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-radio-checked-background-hover);border-color:var(--skra-comp-field-radio-checked-background-hover)}:host(:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled),:host([data-skra-disabled]){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default;background-color:var(--skra-comp-field-background-readonly);border-color:var(--skra-comp-field-border-color-readonly)}:host(:checked[aria-readonly=true]){background-color:var(--skra-comp-field-radio-checked-background-readonly);border-color:var(--skra-comp-field-radio-checked-background-readonly)}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
84
+ }, styles: [":host{appearance:none;flex-shrink:0;cursor:pointer;outline:none;background-repeat:no-repeat;background-position:center;inline-size:var(--skra-comp-field-radio-size);block-size:var(--skra-comp-field-radio-size);border-width:var(--skra-comp-field-radio-border-width);border-style:solid;border-color:var(--skra-comp-field-radio-border-color);border-radius:50%;background-color:var(--skra-comp-field-radio-background);transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:checked){background-color:var(--skra-comp-field-radio-checked-background);border-color:var(--skra-comp-field-radio-checked-background);background-image:radial-gradient(circle,var(--skra-comp-field-radio-dot-color) 0,var(--skra-comp-field-radio-dot-color) calc(var(--skra-comp-field-radio-dot-size) / 2),transparent calc(var(--skra-comp-field-radio-dot-size) / 2 + .5px))}@media(hover:hover)and (pointer:fine){:host(:hover:not(:disabled):not([aria-readonly=true])){border-color:var(--skra-comp-field-radio-border-color-hover)}:host(:checked:hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-radio-checked-background-hover);border-color:var(--skra-comp-field-radio-checked-background-hover)}}:host(:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled),:host([data-skra-disabled]){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default;background-color:var(--skra-comp-field-background-readonly);border-color:var(--skra-comp-field-border-color-readonly)}:host(:checked[aria-readonly=true]){background-color:var(--skra-comp-field-radio-checked-background-readonly);border-color:var(--skra-comp-field-radio-checked-background-readonly);--skra-comp-field-radio-dot-color: var( --skra-comp-field-radio-dot-color-readonly )}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
85
85
  }], ctorParameters: () => [] });
86
86
 
87
87
  /**