@skra/ui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +39 -0
- package/consumer-guide/README.md +84 -0
- package/consumer-guide/accessible-names.md +80 -0
- package/consumer-guide/forms-and-binding.md +149 -0
- package/consumer-guide/layout.md +86 -0
- package/consumer-guide/overlays.md +100 -0
- package/consumer-guide/setup-and-gotchas.md +103 -0
- package/consumer-guide/theming-and-encapsulation.md +115 -0
- package/consumer-guide/wrapping-patterns.md +90 -0
- package/fesm2022/skra-ui-accordion.mjs +333 -0
- package/fesm2022/skra-ui-accordion.mjs.map +1 -0
- package/fesm2022/skra-ui-alert.mjs +110 -0
- package/fesm2022/skra-ui-alert.mjs.map +1 -0
- package/fesm2022/skra-ui-avatar.mjs +83 -0
- package/fesm2022/skra-ui-avatar.mjs.map +1 -0
- package/fesm2022/skra-ui-badge.mjs +91 -0
- package/fesm2022/skra-ui-badge.mjs.map +1 -0
- package/fesm2022/skra-ui-button.mjs +67 -0
- package/fesm2022/skra-ui-button.mjs.map +1 -0
- package/fesm2022/skra-ui-checkbox.mjs +98 -0
- package/fesm2022/skra-ui-checkbox.mjs.map +1 -0
- package/fesm2022/skra-ui-chip.mjs +82 -0
- package/fesm2022/skra-ui-chip.mjs.map +1 -0
- package/fesm2022/skra-ui-combobox.mjs +335 -0
- package/fesm2022/skra-ui-combobox.mjs.map +1 -0
- package/fesm2022/skra-ui-core.mjs +529 -0
- package/fesm2022/skra-ui-core.mjs.map +1 -0
- package/fesm2022/skra-ui-counter.mjs +53 -0
- package/fesm2022/skra-ui-counter.mjs.map +1 -0
- package/fesm2022/skra-ui-datepicker.mjs +867 -0
- package/fesm2022/skra-ui-datepicker.mjs.map +1 -0
- package/fesm2022/skra-ui-dialog.mjs +133 -0
- package/fesm2022/skra-ui-dialog.mjs.map +1 -0
- package/fesm2022/skra-ui-field.mjs +293 -0
- package/fesm2022/skra-ui-field.mjs.map +1 -0
- package/fesm2022/skra-ui-icon.mjs +54 -0
- package/fesm2022/skra-ui-icon.mjs.map +1 -0
- package/fesm2022/skra-ui-input.mjs +42 -0
- package/fesm2022/skra-ui-input.mjs.map +1 -0
- package/fesm2022/skra-ui-kbd.mjs +44 -0
- package/fesm2022/skra-ui-kbd.mjs.map +1 -0
- package/fesm2022/skra-ui-layout.mjs +119 -0
- package/fesm2022/skra-ui-layout.mjs.map +1 -0
- package/fesm2022/skra-ui-link.mjs +29 -0
- package/fesm2022/skra-ui-link.mjs.map +1 -0
- package/fesm2022/skra-ui-loading-indicator.mjs +24 -0
- package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -0
- package/fesm2022/skra-ui-menu.mjs +334 -0
- package/fesm2022/skra-ui-menu.mjs.map +1 -0
- package/fesm2022/skra-ui-multi-select.mjs +179 -0
- package/fesm2022/skra-ui-multi-select.mjs.map +1 -0
- package/fesm2022/skra-ui-native-select.mjs +123 -0
- package/fesm2022/skra-ui-native-select.mjs.map +1 -0
- package/fesm2022/skra-ui-popover.mjs +158 -0
- package/fesm2022/skra-ui-popover.mjs.map +1 -0
- package/fesm2022/skra-ui-progress.mjs +57 -0
- package/fesm2022/skra-ui-progress.mjs.map +1 -0
- package/fesm2022/skra-ui-radio.mjs +92 -0
- package/fesm2022/skra-ui-radio.mjs.map +1 -0
- package/fesm2022/skra-ui-rating-display.mjs +81 -0
- package/fesm2022/skra-ui-rating-display.mjs.map +1 -0
- package/fesm2022/skra-ui-select.mjs +151 -0
- package/fesm2022/skra-ui-select.mjs.map +1 -0
- package/fesm2022/skra-ui-skeleton.mjs +65 -0
- package/fesm2022/skra-ui-skeleton.mjs.map +1 -0
- package/fesm2022/skra-ui-star-rating.mjs +110 -0
- package/fesm2022/skra-ui-star-rating.mjs.map +1 -0
- package/fesm2022/skra-ui-status-dot.mjs +61 -0
- package/fesm2022/skra-ui-status-dot.mjs.map +1 -0
- package/fesm2022/skra-ui-switch.mjs +94 -0
- package/fesm2022/skra-ui-switch.mjs.map +1 -0
- package/fesm2022/skra-ui-tabs.mjs +356 -0
- package/fesm2022/skra-ui-tabs.mjs.map +1 -0
- package/fesm2022/skra-ui-textarea.mjs +81 -0
- package/fesm2022/skra-ui-textarea.mjs.map +1 -0
- package/fesm2022/skra-ui-toast.mjs +208 -0
- package/fesm2022/skra-ui-toast.mjs.map +1 -0
- package/fesm2022/skra-ui-tooltip.mjs +252 -0
- package/fesm2022/skra-ui-tooltip.mjs.map +1 -0
- package/fesm2022/skra-ui-typography.mjs +35 -0
- package/fesm2022/skra-ui-typography.mjs.map +1 -0
- package/fesm2022/skra-ui.mjs +4 -0
- package/fesm2022/skra-ui.mjs.map +1 -0
- package/package.json +175 -0
- package/types/skra-ui-accordion.d.ts +196 -0
- package/types/skra-ui-accordion.d.ts.map +1 -0
- package/types/skra-ui-alert.d.ts +81 -0
- package/types/skra-ui-alert.d.ts.map +1 -0
- package/types/skra-ui-avatar.d.ts +71 -0
- package/types/skra-ui-avatar.d.ts.map +1 -0
- package/types/skra-ui-badge.d.ts +73 -0
- package/types/skra-ui-badge.d.ts.map +1 -0
- package/types/skra-ui-button.d.ts +45 -0
- package/types/skra-ui-button.d.ts.map +1 -0
- package/types/skra-ui-checkbox.d.ts +52 -0
- package/types/skra-ui-checkbox.d.ts.map +1 -0
- package/types/skra-ui-chip.d.ts +60 -0
- package/types/skra-ui-chip.d.ts.map +1 -0
- package/types/skra-ui-combobox.d.ts +95 -0
- package/types/skra-ui-combobox.d.ts.map +1 -0
- package/types/skra-ui-core.d.ts +314 -0
- package/types/skra-ui-core.d.ts.map +1 -0
- package/types/skra-ui-counter.d.ts +33 -0
- package/types/skra-ui-counter.d.ts.map +1 -0
- package/types/skra-ui-datepicker.d.ts +164 -0
- package/types/skra-ui-datepicker.d.ts.map +1 -0
- package/types/skra-ui-dialog.d.ts +59 -0
- package/types/skra-ui-dialog.d.ts.map +1 -0
- package/types/skra-ui-field.d.ts +181 -0
- package/types/skra-ui-field.d.ts.map +1 -0
- package/types/skra-ui-icon.d.ts +38 -0
- package/types/skra-ui-icon.d.ts.map +1 -0
- package/types/skra-ui-input.d.ts +22 -0
- package/types/skra-ui-input.d.ts.map +1 -0
- package/types/skra-ui-kbd.d.ts +33 -0
- package/types/skra-ui-kbd.d.ts.map +1 -0
- package/types/skra-ui-layout.d.ts +86 -0
- package/types/skra-ui-layout.d.ts.map +1 -0
- package/types/skra-ui-link.d.ts +20 -0
- package/types/skra-ui-link.d.ts.map +1 -0
- package/types/skra-ui-loading-indicator.d.ts +9 -0
- package/types/skra-ui-loading-indicator.d.ts.map +1 -0
- package/types/skra-ui-menu.d.ts +116 -0
- package/types/skra-ui-menu.d.ts.map +1 -0
- package/types/skra-ui-multi-select.d.ts +47 -0
- package/types/skra-ui-multi-select.d.ts.map +1 -0
- package/types/skra-ui-native-select.d.ts +74 -0
- package/types/skra-ui-native-select.d.ts.map +1 -0
- package/types/skra-ui-popover.d.ts +57 -0
- package/types/skra-ui-popover.d.ts.map +1 -0
- package/types/skra-ui-progress.d.ts +41 -0
- package/types/skra-ui-progress.d.ts.map +1 -0
- package/types/skra-ui-radio.d.ts +54 -0
- package/types/skra-ui-radio.d.ts.map +1 -0
- package/types/skra-ui-rating-display.d.ts +56 -0
- package/types/skra-ui-rating-display.d.ts.map +1 -0
- package/types/skra-ui-select.d.ts +43 -0
- package/types/skra-ui-select.d.ts.map +1 -0
- package/types/skra-ui-skeleton.d.ts +42 -0
- package/types/skra-ui-skeleton.d.ts.map +1 -0
- package/types/skra-ui-star-rating.d.ts +71 -0
- package/types/skra-ui-star-rating.d.ts.map +1 -0
- package/types/skra-ui-status-dot.d.ts +62 -0
- package/types/skra-ui-status-dot.d.ts.map +1 -0
- package/types/skra-ui-switch.d.ts +54 -0
- package/types/skra-ui-switch.d.ts.map +1 -0
- package/types/skra-ui-tabs.d.ts +191 -0
- package/types/skra-ui-tabs.d.ts.map +1 -0
- package/types/skra-ui-textarea.d.ts +55 -0
- package/types/skra-ui-textarea.d.ts.map +1 -0
- package/types/skra-ui-toast.d.ts +140 -0
- package/types/skra-ui-toast.d.ts.map +1 -0
- package/types/skra-ui-tooltip.d.ts +90 -0
- package/types/skra-ui-tooltip.d.ts.map +1 -0
- package/types/skra-ui-typography.d.ts +17 -0
- package/types/skra-ui-typography.d.ts.map +1 -0
- package/types/skra-ui.d.ts +3 -0
- package/types/skra-ui.d.ts.map +1 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Directive, input, booleanAttribute, output, contentChild, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Marker directive for the leading icon/dot/avatar slot of `<skra-chip>`.
|
|
6
|
+
* Detected via `contentChild` (ADR 020 Smart Slots v2.0) to toggle the icon
|
|
7
|
+
* wrapper without emitting an empty DOM node. Feedback-local (mirrors
|
|
8
|
+
* `SkraBadgeIcon`/`SkraAlertIcon`) to avoid a cross-lib marker dependency.
|
|
9
|
+
*/
|
|
10
|
+
class SkraChipIcon {
|
|
11
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraChipIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
12
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SkraChipIcon, isStandalone: true, selector: "[skra-chip-icon]", ngImport: i0 });
|
|
13
|
+
}
|
|
14
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraChipIcon, decorators: [{
|
|
15
|
+
type: Directive,
|
|
16
|
+
args: [{
|
|
17
|
+
selector: '[skra-chip-icon]',
|
|
18
|
+
standalone: true,
|
|
19
|
+
}]
|
|
20
|
+
}] });
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `<skra-chip>` — a small, inline, **removable** labelled pill (Feedback/Status
|
|
24
|
+
* family, Wave 2): a selected multi-select value, an applied filter, an input tag.
|
|
25
|
+
* The host is a non-interactive presentational pill (the label is plain text, no
|
|
26
|
+
* host role/focus); it contains exactly ONE interactive sub-control — the built-in
|
|
27
|
+
* remove `<button>` (the Alert dismiss precedent).
|
|
28
|
+
*
|
|
29
|
+
* SRP-distinct from `<skra-badge>` (a NON-removable status pill): the remove
|
|
30
|
+
* control is intrinsic to Chip — there is no `removable` flag, because a chip
|
|
31
|
+
* without it would be a Badge. A SELECTABLE/filter chip (a toggle) is a SEPARATE
|
|
32
|
+
* deferred atom (ARCHITECTURE_BACKLOG watch-list), NOT a `selectable` variant —
|
|
33
|
+
* one atom carrying both interaction models is an SRP smell.
|
|
34
|
+
*
|
|
35
|
+
* No `live` axis (unlike Badge/Alert): a chip is an input/selection element, not a
|
|
36
|
+
* status announcer; add/remove announcements are the consumer's own `aria-live`.
|
|
37
|
+
*
|
|
38
|
+
* Removal (ADR 037): the remove control fires `(removed)`. Forge does NOT remove
|
|
39
|
+
* itself from the DOM — the consumer owns removal (splice the bound array / `@if`).
|
|
40
|
+
* The remove control's accessible name comes from a projected
|
|
41
|
+
* `[skra-chip-remove-label]` (sr-only; ADR 033 Path 2) — the "×" glyph is `aria-hidden`.
|
|
42
|
+
*/
|
|
43
|
+
class SkraChip {
|
|
44
|
+
// --- Variant Surface (ADR 035) ---
|
|
45
|
+
intent = input('neutral', ...(ngDevMode ? [{ debugName: "intent" }] : /* istanbul ignore next */ []));
|
|
46
|
+
emphasis = input('medium', ...(ngDevMode ? [{ debugName: "emphasis" }] : /* istanbul ignore next */ []));
|
|
47
|
+
// --- Availability (ADR 027 cat 2) ---
|
|
48
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
49
|
+
// --- Removal (ADR 037 — consumer-owned presence) ---
|
|
50
|
+
removed = output();
|
|
51
|
+
// --- Smart Slots v2.0 (ADR 020) ---
|
|
52
|
+
iconSlot = contentChild(SkraChipIcon, ...(ngDevMode ? [{ debugName: "iconSlot" }] : /* istanbul ignore next */ []));
|
|
53
|
+
hasIcon = computed(() => !!this.iconSlot(), ...(ngDevMode ? [{ debugName: "hasIcon" }] : /* istanbul ignore next */ []));
|
|
54
|
+
onRemove() {
|
|
55
|
+
if (this.disabled())
|
|
56
|
+
return;
|
|
57
|
+
this.removed.emit();
|
|
58
|
+
}
|
|
59
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
60
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SkraChip, isStandalone: true, selector: "skra-chip", inputs: { intent: { classPropertyName: "intent", publicName: "intent", isSignal: true, isRequired: false, transformFunction: null }, emphasis: { classPropertyName: "emphasis", publicName: "emphasis", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removed: "removed" }, host: { properties: { "attr.data-skra-intent": "intent()", "attr.data-skra-emphasis": "emphasis()", "attr.data-skra-disabled": "disabled() ? \"\" : null", "attr.data-skra-has-icon": "hasIcon() ? \"\" : null" } }, queries: [{ propertyName: "iconSlot", first: true, predicate: SkraChipIcon, descendants: true, isSignal: true }], ngImport: i0, template: "@if (hasIcon()) {\n<span class=\"skra-chip-icon\">\n <ng-content select=\"[skra-chip-icon]\"></ng-content>\n</span>\n}\n\n<span class=\"skra-chip-label\">\n <ng-content></ng-content>\n</span>\n\n<button\n type=\"button\"\n class=\"skra-chip-remove\"\n [disabled]=\"disabled()\"\n (click)=\"onRemove()\"\n>\n <svg\n class=\"skra-chip-remove-glyph\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n <ng-content select=\"[skra-chip-remove-label]\"></ng-content>\n</button>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;box-sizing:border-box;margin:0;white-space:nowrap;min-block-size:var(--skra-comp-chip-remove-size);font-family:var(--skra-sys-typography-role-label-medium-font-family);font-size:var(--skra-sys-typography-role-label-medium-font-size);font-weight:var(--skra-sys-typography-role-label-medium-font-weight);line-height:var(--skra-sys-typography-role-label-medium-line-height);letter-spacing:var(--skra-sys-typography-role-label-medium-letter-spacing);background:var(--skra-comp-chip-background);color:var(--skra-comp-chip-foreground);border-color:var(--skra-comp-chip-border-color);border-width:var(--skra-comp-chip-border-width);border-style:var(--skra-comp-chip-border-style);border-radius:var(--skra-comp-chip-radius);padding-block:var(--skra-comp-chip-padding-block);padding-inline:var(--skra-comp-chip-padding-inline);box-shadow:var(--skra-comp-chip-shadow)}:host([data-skra-emphasis=high]){--skra-comp-chip-border-width: 0}:host([data-skra-disabled]){cursor:not-allowed;background:var(--skra-sys-color-neutral-soft);color:var(--skra-sys-color-neutral-strong);border-color:var(--skra-sys-color-neutral-border)}:host([data-skra-disabled]) .skra-chip-icon,:host([data-skra-disabled]) .skra-chip-remove{opacity:var(--skra-sys-opacity-disabled)}:host([data-skra-intent=neutral][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-neutral-high, var(--skra-sys-color-neutral-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-neutral-high, var(--skra-sys-color-neutral-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-neutral-high, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-neutral-medium, var(--skra-sys-color-neutral-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-neutral-medium, var(--skra-sys-color-neutral-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-neutral-medium, var(--skra-sys-color-neutral-border) )}:host([data-skra-intent=info][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-info-high, var(--skra-sys-color-info-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-info-high, var(--skra-sys-color-info-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-info-high, transparent )}:host([data-skra-intent=info][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-info-medium, var(--skra-sys-color-info-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-info-medium, var(--skra-sys-color-info-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-info-medium, var(--skra-sys-color-info-border) )}:host([data-skra-intent=success][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-success-high, var(--skra-sys-color-success-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-success-high, var(--skra-sys-color-success-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-success-high, transparent )}:host([data-skra-intent=success][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-success-medium, var(--skra-sys-color-success-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-success-medium, var(--skra-sys-color-success-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-success-medium, var(--skra-sys-color-success-border) )}:host([data-skra-intent=warning][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-warning-high, var(--skra-sys-color-warning-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-warning-high, var(--skra-sys-color-warning-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-warning-high, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-warning-medium, var(--skra-sys-color-warning-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-warning-medium, var(--skra-sys-color-warning-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-warning-medium, var(--skra-sys-color-warning-border) )}:host([data-skra-intent=danger][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-danger-high, var(--skra-sys-color-danger-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-danger-high, var(--skra-sys-color-danger-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-danger-high, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-danger-medium, var(--skra-sys-color-danger-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-danger-medium, var(--skra-sys-color-danger-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-danger-medium, var(--skra-sys-color-danger-border) )}.skra-chip-icon{display:inline-flex;flex-shrink:0;align-items:center;block-size:1lh;margin-inline-end:var(--skra-comp-chip-icon-gap);--skra-comp-icon-size: var(--skra-comp-chip-icon-size)}.skra-chip-label{display:inline-flex;align-items:center;min-inline-size:0}.skra-chip-remove{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-chip-remove-size);block-size:var(--skra-comp-chip-remove-size);margin:0;margin-inline-start:var(--skra-comp-chip-remove-gap);padding:0;border:0;background:transparent;color:inherit;cursor:pointer;border-radius:var(--skra-comp-chip-radius);transition:opacity var(--skra-comp-chip-motion-duration) var(--skra-comp-chip-motion-easing)}.skra-chip-remove:hover{opacity:.7}.skra-chip-remove:focus-visible{outline-style:solid;outline-color:var(--skra-comp-chip-focus-ring-color);outline-width:var(--skra-comp-chip-focus-ring-width);outline-offset:2px}.skra-chip-remove:disabled{cursor:not-allowed}.skra-chip-remove-glyph{inline-size:.875em;block-size:.875em;display:block}@media(prefers-reduced-motion:reduce){.skra-chip-remove{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
61
|
+
}
|
|
62
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraChip, decorators: [{
|
|
63
|
+
type: Component,
|
|
64
|
+
args: [{ selector: 'skra-chip', standalone: true, imports: [SkraChipIcon], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
65
|
+
// Variant Surface reflection (ADR 035 §3.5; ADR 021 — never [style.--var])
|
|
66
|
+
'[attr.data-skra-intent]': 'intent()',
|
|
67
|
+
'[attr.data-skra-emphasis]': 'emphasis()',
|
|
68
|
+
// Availability reflection (ADR 027 cat 2; presence boolean). The host is a custom
|
|
69
|
+
// element with no native `disabled`, so its reflection is the Forge-semantic
|
|
70
|
+
// data-attr; the remove <button> takes the NATIVE `disabled` (native-attr-first).
|
|
71
|
+
'[attr.data-skra-disabled]': 'disabled() ? "" : null',
|
|
72
|
+
// Smart Slot layout reflection (ADR 020 v2.0 — slot-presence, State-Surface-exempt §3.6)
|
|
73
|
+
'[attr.data-skra-has-icon]': 'hasIcon() ? "" : null',
|
|
74
|
+
}, template: "@if (hasIcon()) {\n<span class=\"skra-chip-icon\">\n <ng-content select=\"[skra-chip-icon]\"></ng-content>\n</span>\n}\n\n<span class=\"skra-chip-label\">\n <ng-content></ng-content>\n</span>\n\n<button\n type=\"button\"\n class=\"skra-chip-remove\"\n [disabled]=\"disabled()\"\n (click)=\"onRemove()\"\n>\n <svg\n class=\"skra-chip-remove-glyph\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n <ng-content select=\"[skra-chip-remove-label]\"></ng-content>\n</button>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;box-sizing:border-box;margin:0;white-space:nowrap;min-block-size:var(--skra-comp-chip-remove-size);font-family:var(--skra-sys-typography-role-label-medium-font-family);font-size:var(--skra-sys-typography-role-label-medium-font-size);font-weight:var(--skra-sys-typography-role-label-medium-font-weight);line-height:var(--skra-sys-typography-role-label-medium-line-height);letter-spacing:var(--skra-sys-typography-role-label-medium-letter-spacing);background:var(--skra-comp-chip-background);color:var(--skra-comp-chip-foreground);border-color:var(--skra-comp-chip-border-color);border-width:var(--skra-comp-chip-border-width);border-style:var(--skra-comp-chip-border-style);border-radius:var(--skra-comp-chip-radius);padding-block:var(--skra-comp-chip-padding-block);padding-inline:var(--skra-comp-chip-padding-inline);box-shadow:var(--skra-comp-chip-shadow)}:host([data-skra-emphasis=high]){--skra-comp-chip-border-width: 0}:host([data-skra-disabled]){cursor:not-allowed;background:var(--skra-sys-color-neutral-soft);color:var(--skra-sys-color-neutral-strong);border-color:var(--skra-sys-color-neutral-border)}:host([data-skra-disabled]) .skra-chip-icon,:host([data-skra-disabled]) .skra-chip-remove{opacity:var(--skra-sys-opacity-disabled)}:host([data-skra-intent=neutral][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-neutral-high, var(--skra-sys-color-neutral-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-neutral-high, var(--skra-sys-color-neutral-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-neutral-high, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-neutral-medium, var(--skra-sys-color-neutral-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-neutral-medium, var(--skra-sys-color-neutral-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-neutral-medium, var(--skra-sys-color-neutral-border) )}:host([data-skra-intent=info][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-info-high, var(--skra-sys-color-info-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-info-high, var(--skra-sys-color-info-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-info-high, transparent )}:host([data-skra-intent=info][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-info-medium, var(--skra-sys-color-info-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-info-medium, var(--skra-sys-color-info-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-info-medium, var(--skra-sys-color-info-border) )}:host([data-skra-intent=success][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-success-high, var(--skra-sys-color-success-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-success-high, var(--skra-sys-color-success-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-success-high, transparent )}:host([data-skra-intent=success][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-success-medium, var(--skra-sys-color-success-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-success-medium, var(--skra-sys-color-success-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-success-medium, var(--skra-sys-color-success-border) )}:host([data-skra-intent=warning][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-warning-high, var(--skra-sys-color-warning-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-warning-high, var(--skra-sys-color-warning-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-warning-high, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-warning-medium, var(--skra-sys-color-warning-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-warning-medium, var(--skra-sys-color-warning-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-warning-medium, var(--skra-sys-color-warning-border) )}:host([data-skra-intent=danger][data-skra-emphasis=high]){--skra-comp-chip-background: var( --skra-comp-chip-background-danger-high, var(--skra-sys-color-danger-base) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-danger-high, var(--skra-sys-color-danger-contrast) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-danger-high, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=medium]){--skra-comp-chip-background: var( --skra-comp-chip-background-danger-medium, var(--skra-sys-color-danger-soft) );--skra-comp-chip-foreground: var( --skra-comp-chip-foreground-danger-medium, var(--skra-sys-color-danger-strong) );--skra-comp-chip-border-color: var( --skra-comp-chip-border-color-danger-medium, var(--skra-sys-color-danger-border) )}.skra-chip-icon{display:inline-flex;flex-shrink:0;align-items:center;block-size:1lh;margin-inline-end:var(--skra-comp-chip-icon-gap);--skra-comp-icon-size: var(--skra-comp-chip-icon-size)}.skra-chip-label{display:inline-flex;align-items:center;min-inline-size:0}.skra-chip-remove{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-chip-remove-size);block-size:var(--skra-comp-chip-remove-size);margin:0;margin-inline-start:var(--skra-comp-chip-remove-gap);padding:0;border:0;background:transparent;color:inherit;cursor:pointer;border-radius:var(--skra-comp-chip-radius);transition:opacity var(--skra-comp-chip-motion-duration) var(--skra-comp-chip-motion-easing)}.skra-chip-remove:hover{opacity:.7}.skra-chip-remove:focus-visible{outline-style:solid;outline-color:var(--skra-comp-chip-focus-ring-color);outline-width:var(--skra-comp-chip-focus-ring-width);outline-offset:2px}.skra-chip-remove:disabled{cursor:not-allowed}.skra-chip-remove-glyph{inline-size:.875em;block-size:.875em;display:block}@media(prefers-reduced-motion:reduce){.skra-chip-remove{transition:none}}\n"] }]
|
|
75
|
+
}], propDecorators: { intent: [{ type: i0.Input, args: [{ isSignal: true, alias: "intent", required: false }] }], emphasis: [{ type: i0.Input, args: [{ isSignal: true, alias: "emphasis", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], removed: [{ type: i0.Output, args: ["removed"] }], iconSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraChipIcon), { isSignal: true }] }] } });
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generated bundle index. Do not edit.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
export { SkraChip, SkraChipIcon };
|
|
82
|
+
//# sourceMappingURL=skra-ui-chip.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skra-ui-chip.mjs","sources":["../../../../libs/ui/chip/src/lib/chip.directives.ts","../../../../libs/ui/chip/src/lib/chip/chip.ts","../../../../libs/ui/chip/src/lib/chip/chip.html","../../../../libs/ui/chip/src/skra-ui-chip.ts"],"sourcesContent":["import { Directive } from '@angular/core';\n\n/**\n * Marker directive for the leading icon/dot/avatar slot of `<skra-chip>`.\n * Detected via `contentChild` (ADR 020 Smart Slots v2.0) to toggle the icon\n * wrapper without emitting an empty DOM node. Feedback-local (mirrors\n * `SkraBadgeIcon`/`SkraAlertIcon`) to avoid a cross-lib marker dependency.\n */\n@Directive({\n selector: '[skra-chip-icon]',\n standalone: true,\n})\nexport class SkraChipIcon {}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n booleanAttribute,\n computed,\n contentChild,\n input,\n output,\n} from '@angular/core';\nimport { SkraChipIcon } from '../chip.directives';\n\n/**\n * ChipIntent — semantic meaning (ADR 035 §3.1, downward subset). Excludes the\n * action intents `primary`/`secondary` (meaningless for a status/category pill).\n * Closed enum; extension requires an ADR 035 amendment.\n */\nexport type ChipIntent = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\n/**\n * ChipEmphasis — visual loudness within the chosen intent (ADR 035 §3.1, subset).\n * `high` = filled pill, `medium` = soft/tinted. `low` deferred.\n */\nexport type ChipEmphasis = 'high' | 'medium';\n\n/**\n * `<skra-chip>` — a small, inline, **removable** labelled pill (Feedback/Status\n * family, Wave 2): a selected multi-select value, an applied filter, an input tag.\n * The host is a non-interactive presentational pill (the label is plain text, no\n * host role/focus); it contains exactly ONE interactive sub-control — the built-in\n * remove `<button>` (the Alert dismiss precedent).\n *\n * SRP-distinct from `<skra-badge>` (a NON-removable status pill): the remove\n * control is intrinsic to Chip — there is no `removable` flag, because a chip\n * without it would be a Badge. A SELECTABLE/filter chip (a toggle) is a SEPARATE\n * deferred atom (ARCHITECTURE_BACKLOG watch-list), NOT a `selectable` variant —\n * one atom carrying both interaction models is an SRP smell.\n *\n * No `live` axis (unlike Badge/Alert): a chip is an input/selection element, not a\n * status announcer; add/remove announcements are the consumer's own `aria-live`.\n *\n * Removal (ADR 037): the remove control fires `(removed)`. Forge does NOT remove\n * itself from the DOM — the consumer owns removal (splice the bound array / `@if`).\n * The remove control's accessible name comes from a projected\n * `[skra-chip-remove-label]` (sr-only; ADR 033 Path 2) — the \"×\" glyph is `aria-hidden`.\n */\n@Component({\n selector: 'skra-chip',\n standalone: true,\n imports: [SkraChipIcon],\n templateUrl: './chip.html',\n styleUrl: './chip.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // Variant Surface reflection (ADR 035 §3.5; ADR 021 — never [style.--var])\n '[attr.data-skra-intent]': 'intent()',\n '[attr.data-skra-emphasis]': 'emphasis()',\n\n // Availability reflection (ADR 027 cat 2; presence boolean). The host is a custom\n // element with no native `disabled`, so its reflection is the Forge-semantic\n // data-attr; the remove <button> takes the NATIVE `disabled` (native-attr-first).\n '[attr.data-skra-disabled]': 'disabled() ? \"\" : null',\n\n // Smart Slot layout reflection (ADR 020 v2.0 — slot-presence, State-Surface-exempt §3.6)\n '[attr.data-skra-has-icon]': 'hasIcon() ? \"\" : null',\n },\n})\nexport class SkraChip {\n // --- Variant Surface (ADR 035) ---\n readonly intent = input<ChipIntent>('neutral');\n readonly emphasis = input<ChipEmphasis>('medium');\n\n // --- Availability (ADR 027 cat 2) ---\n readonly disabled = input(false, { transform: booleanAttribute });\n\n // --- Removal (ADR 037 — consumer-owned presence) ---\n readonly removed = output<void>();\n\n // --- Smart Slots v2.0 (ADR 020) ---\n readonly iconSlot = contentChild(SkraChipIcon);\n readonly hasIcon = computed(() => !!this.iconSlot());\n\n protected onRemove(): void {\n if (this.disabled()) return;\n this.removed.emit();\n }\n}\n","@if (hasIcon()) {\n<span class=\"skra-chip-icon\">\n <ng-content select=\"[skra-chip-icon]\"></ng-content>\n</span>\n}\n\n<span class=\"skra-chip-label\">\n <ng-content></ng-content>\n</span>\n\n<button\n type=\"button\"\n class=\"skra-chip-remove\"\n [disabled]=\"disabled()\"\n (click)=\"onRemove()\"\n>\n <svg\n class=\"skra-chip-remove-glyph\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n <ng-content select=\"[skra-chip-remove-label]\"></ng-content>\n</button>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, output, booleanAttribute, signal, inject, ElementRef, computed, afterRenderEffect, ViewEncapsulation, ChangeDetectionStrategy, Component, Directive } from '@angular/core';
|
|
3
|
+
import { _SkraListboxWidgetBase, SKRA_LISTBOX_PANEL } from '@skra/ui/core';
|
|
4
|
+
export { SkraOption, SkraOptionDetail, SkraOptionLeading } from '@skra/ui/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* SkraCombobox — the WAI-ARIA APG **Editable Combobox** (list autocomplete, manual selection): an
|
|
8
|
+
* editable `<input role="combobox" aria-autocomplete="list">` whose top-layer listbox (Popover API,
|
|
9
|
+
* `popover="auto"`, ADR 0044) of projected `skra-option` children NARROWS as the user types. The first
|
|
10
|
+
* Tier-3 / higher-order component. Single-cardinality (ADR 0046); the multi sibling is future work.
|
|
11
|
+
*
|
|
12
|
+
* - **Filtering + option set are CONSUMER-OWNED (ADR 0049):** Forge emits the typed text via
|
|
13
|
+
* `filterChange` (no debounce — timing is the consumer's); the consumer projects the matching
|
|
14
|
+
* `skra-option` subset (sync computed OR async fetch). There is NO `[items]` input and NO internal
|
|
15
|
+
* matcher — Forge renders exactly what is projected.
|
|
16
|
+
* - **Value is OPTION-BOUND (ADR 0049):** `value = input<T>()` + `valueChange` (consumer-owned, ADR 037);
|
|
17
|
+
* a committed value is always an existing option's `T`. On cancel/blur the input text reverts to the
|
|
18
|
+
* selected option's label — typed text that matched nothing NEVER leaks as the value (free-text commit
|
|
19
|
+
* is the separate `skra-autocomplete`).
|
|
20
|
+
* - **Async (ADR 0049/028):** `loading` reflects `aria-busy` + drives a panel loading branch
|
|
21
|
+
* (`[skraLoadingIndicator]` slot + fallback); an empty projected set drives the `[skraComboboxEmpty]`
|
|
22
|
+
* branch; both are announced via `aria-live`.
|
|
23
|
+
*
|
|
24
|
+
* The trigger-shape-agnostic substrate (field cascade, panel toggle/positioner, keyboard wiring,
|
|
25
|
+
* open/close, the `SkraListboxPanel` selection seam) lives in {@link _SkraListboxWidgetBase}, shared
|
|
26
|
+
* verbatim with the select family (ADR 0046 amendment). `skra-option` is reused verbatim — combobox
|
|
27
|
+
* provides the `SKRA_LISTBOX_PANEL` token so the option's selection/keyboard/indicator wiring works
|
|
28
|
+
* unchanged.
|
|
29
|
+
*
|
|
30
|
+
* Full contract: adr/specs/component-combobox-spec.md.
|
|
31
|
+
*/
|
|
32
|
+
class SkraCombobox extends _SkraListboxWidgetBase {
|
|
33
|
+
/** Combobox panel token namespace — `--skra-comp-combobox-panel-*`. */
|
|
34
|
+
panelTokenPrefix = 'combobox';
|
|
35
|
+
/** Consumer-owned value channel (ADR 037) — single, option-bound `T`. */
|
|
36
|
+
value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
37
|
+
/** Fires ONLY on user selection of an option — never on a programmatic `value()` change (ADR 037). */
|
|
38
|
+
valueChange = output();
|
|
39
|
+
/** The typed text, emitted on EVERY input change. Consumer-driven projection; NO Forge debounce (ADR 0049). */
|
|
40
|
+
filterChange = output();
|
|
41
|
+
/** Async hint (ADR 0049/028) — reflects `aria-busy` + drives the panel loading branch. */
|
|
42
|
+
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
43
|
+
/** Cardinality hint (ADR 0046) — single-select. */
|
|
44
|
+
multiple = false;
|
|
45
|
+
/** Internal editable-text state; emitted out via `filterChange`. NOT the value (ADR 0049). */
|
|
46
|
+
query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
47
|
+
isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
48
|
+
hostEl = inject(ElementRef);
|
|
49
|
+
/**
|
|
50
|
+
* Position the panel against — and match its width to — the HOST box, not the inner input. The input
|
|
51
|
+
* is inset (host padding + chevron), so anchoring to it would make a narrower, misaligned panel; the
|
|
52
|
+
* host gives full-width parity with `skra-select`.
|
|
53
|
+
*/
|
|
54
|
+
positioningAnchor() {
|
|
55
|
+
return this.hostEl.nativeElement;
|
|
56
|
+
}
|
|
57
|
+
/** The option whose value matches `value()` (ADR 037 selection model) — feeds the displayed text + active seed. */
|
|
58
|
+
selectedOption = computed(() => this.options().find((o) => this.isSelected(o.value())) ?? null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
|
|
59
|
+
/**
|
|
60
|
+
* The text shown in the input: the live query while editing (focused), otherwise the selected option's
|
|
61
|
+
* label. This is the controlled-input seam that enforces the option-bound value (ADR 0049) — on blur the
|
|
62
|
+
* input snaps back to the selected label, so unmatched typed text never persists.
|
|
63
|
+
*/
|
|
64
|
+
displayValue = computed(() => this.isFocused() ? this.query() : (this.selectedOption()?.getLabel() ?? ''), ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
|
|
65
|
+
constructor() {
|
|
66
|
+
super();
|
|
67
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
68
|
+
// Dev-only consumer warning. Uses afterRenderEffect (NOT effect) so the projected
|
|
69
|
+
// options' required `value` inputs are read only AFTER each render applies their
|
|
70
|
+
// bindings — an eager read during the initial CD (SSR / first render) OR a runtime
|
|
71
|
+
// re-projection (consumer filtering adds fresh <skra-option>s) would throw NG0950.
|
|
72
|
+
// afterRender hooks never run on the server, so this stays SSR-safe. Covers both
|
|
73
|
+
// F-13 (initial render) and F-16 (runtime re-projection, surfaced in dogfood).
|
|
74
|
+
afterRenderEffect(() => {
|
|
75
|
+
const v = this.value();
|
|
76
|
+
if (v === undefined)
|
|
77
|
+
return;
|
|
78
|
+
const matched = this.options().some((o) => this.compareWith()(o.value(), v));
|
|
79
|
+
if (!matched && this.options().length) {
|
|
80
|
+
console.warn('[skra-combobox] value has no matching <skra-option> in the projected set — the input ' +
|
|
81
|
+
'shows empty (likely a compareWith mismatch, or the selected option was filtered out).');
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ── SkraListboxPanel seam ──
|
|
87
|
+
isSelected(value) {
|
|
88
|
+
const current = this.value();
|
|
89
|
+
return current !== undefined && this.compareWith()(current, value);
|
|
90
|
+
}
|
|
91
|
+
selectValue(value) {
|
|
92
|
+
const opt = this.options().find((o) => this.compareWith()(o.value(), value)) ?? null;
|
|
93
|
+
this.valueChange.emit(value);
|
|
94
|
+
// Optimistic display: show the picked option's label immediately (focus is retained).
|
|
95
|
+
this.query.set(opt?.getLabel() ?? '');
|
|
96
|
+
this.close(); // single-select: pick + close
|
|
97
|
+
}
|
|
98
|
+
activeSeedItem() {
|
|
99
|
+
return this.selectedOption();
|
|
100
|
+
}
|
|
101
|
+
// ── Editable-input trigger model (the divergence from the select-family button trigger) ──
|
|
102
|
+
onFocus() {
|
|
103
|
+
// Start editing from the current value's label (APG: the textbox shows the value, editable).
|
|
104
|
+
this.query.set(this.selectedOption()?.getLabel() ?? '');
|
|
105
|
+
this.isFocused.set(true);
|
|
106
|
+
}
|
|
107
|
+
onInput(event) {
|
|
108
|
+
if (this.isReadonly())
|
|
109
|
+
return;
|
|
110
|
+
const text = event.target.value;
|
|
111
|
+
this.query.set(text);
|
|
112
|
+
this.filterChange.emit(text); // ADR 0049 — consumer owns the matching + projection
|
|
113
|
+
if (!this.isOpen())
|
|
114
|
+
this.open();
|
|
115
|
+
}
|
|
116
|
+
onBlur() {
|
|
117
|
+
this.isFocused.set(false); // displayValue() snaps back to the selected label (option-bound, ADR 0049)
|
|
118
|
+
if (this.isOpen())
|
|
119
|
+
this.close();
|
|
120
|
+
}
|
|
121
|
+
onTriggerKeydown(event) {
|
|
122
|
+
if (this.isDisabled() || this.isReadonly())
|
|
123
|
+
return;
|
|
124
|
+
const key = event.key;
|
|
125
|
+
if (key === 'ArrowDown' || key === 'ArrowUp') {
|
|
126
|
+
event.preventDefault(); // do not move the text caret
|
|
127
|
+
if (event.altKey) {
|
|
128
|
+
// APG: Alt+Down opens (no active move); Alt+Up closes + reverts (keeps value).
|
|
129
|
+
if (key === 'ArrowDown') {
|
|
130
|
+
if (!this.isOpen())
|
|
131
|
+
this.open();
|
|
132
|
+
}
|
|
133
|
+
else if (this.isOpen()) {
|
|
134
|
+
this.query.set(this.selectedOption()?.getLabel() ?? '');
|
|
135
|
+
this.close();
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (!this.isOpen())
|
|
140
|
+
this.open();
|
|
141
|
+
else
|
|
142
|
+
this.keyboard.onKeydown(event); // move the active descendant
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (key === 'Enter') {
|
|
146
|
+
if (this.isOpen()) {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
this.selectActive(); // select the active option (no-op if none / soft-disabled)
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (key === 'Escape') {
|
|
153
|
+
if (this.isOpen()) {
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
this.query.set(this.selectedOption()?.getLabel() ?? ''); // revert (keep value)
|
|
156
|
+
this.close();
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Home/End/printable/Backspace: native text editing — NOT routed to the listbox (no type-ahead).
|
|
161
|
+
}
|
|
162
|
+
toggle() {
|
|
163
|
+
if (this.isDisabled() || this.isReadonly())
|
|
164
|
+
return; // pointer parity with the keydown/input guards (M1)
|
|
165
|
+
const el = this.triggerRef()?.nativeElement;
|
|
166
|
+
if (this.isOpen()) {
|
|
167
|
+
this.close();
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
el?.focus(); // fires onFocus → seeds the editable text
|
|
171
|
+
this.open();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraCombobox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
175
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SkraCombobox, isStandalone: true, selector: "skra-combobox", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", filterChange: "filterChange" }, host: { properties: { "attr.data-skra-loading": "loading() ? \"\" : null", "attr.data-skra-empty": "!loading() && options().length === 0 ? \"\" : null" } }, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }], exportAs: ["skraCombobox"], usesInheritance: true, ngImport: i0, template: `
|
|
176
|
+
<input
|
|
177
|
+
#trigger
|
|
178
|
+
class="skra-combobox-input"
|
|
179
|
+
type="text"
|
|
180
|
+
role="combobox"
|
|
181
|
+
autocomplete="off"
|
|
182
|
+
aria-autocomplete="list"
|
|
183
|
+
aria-haspopup="listbox"
|
|
184
|
+
[attr.id]="triggerId()"
|
|
185
|
+
[attr.aria-controls]="panelId"
|
|
186
|
+
[attr.aria-expanded]="isOpen()"
|
|
187
|
+
[attr.aria-activedescendant]="activeOptionId()"
|
|
188
|
+
[attr.aria-describedby]="describedBy()"
|
|
189
|
+
[attr.aria-invalid]="hasError() ? 'true' : null"
|
|
190
|
+
[attr.aria-required]="isRequired() ? 'true' : null"
|
|
191
|
+
[attr.aria-busy]="loading() ? 'true' : null"
|
|
192
|
+
[attr.placeholder]="placeholder()"
|
|
193
|
+
[disabled]="isDisabled()"
|
|
194
|
+
[readOnly]="isReadonly()"
|
|
195
|
+
[value]="displayValue()"
|
|
196
|
+
(focus)="onFocus()"
|
|
197
|
+
(input)="onInput($event)"
|
|
198
|
+
(keydown)="onTriggerKeydown($event)"
|
|
199
|
+
(blur)="onBlur()"
|
|
200
|
+
/>
|
|
201
|
+
<button
|
|
202
|
+
class="skra-combobox-toggle"
|
|
203
|
+
type="button"
|
|
204
|
+
tabindex="-1"
|
|
205
|
+
aria-hidden="true"
|
|
206
|
+
[disabled]="isDisabled()"
|
|
207
|
+
(mousedown)="$event.preventDefault()"
|
|
208
|
+
(click)="toggle()"
|
|
209
|
+
>
|
|
210
|
+
<span class="skra-combobox-arrow" aria-hidden="true"></span>
|
|
211
|
+
</button>
|
|
212
|
+
|
|
213
|
+
<div
|
|
214
|
+
#panel
|
|
215
|
+
class="skra-combobox-listbox"
|
|
216
|
+
[id]="panelId"
|
|
217
|
+
role="listbox"
|
|
218
|
+
popover="auto"
|
|
219
|
+
(mousedown)="$event.preventDefault()"
|
|
220
|
+
(toggle)="onPanelToggle($event)"
|
|
221
|
+
>
|
|
222
|
+
<!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
|
|
223
|
+
a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
|
|
224
|
+
:empty in SCSS. -->
|
|
225
|
+
<div class="skra-combobox-status" role="status" aria-live="polite">
|
|
226
|
+
@if (loading()) {
|
|
227
|
+
<ng-content select="[skraLoadingIndicator]"></ng-content>
|
|
228
|
+
<span class="skra-combobox-spinner" aria-hidden="true"></span>
|
|
229
|
+
} @else if (options().length === 0) {
|
|
230
|
+
<ng-content select="[skraComboboxEmpty]"></ng-content>
|
|
231
|
+
}
|
|
232
|
+
</div>
|
|
233
|
+
<ng-content></ng-content>
|
|
234
|
+
</div>
|
|
235
|
+
`, isInline: true, styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-combobox-gap);inline-size:100%;min-block-size:var(--skra-comp-combobox-min-block-size);box-sizing:border-box;margin:0;padding-inline:var(--skra-comp-combobox-padding-inline);background:var(--skra-comp-combobox-background);color:var(--skra-comp-combobox-foreground);border-color:var(--skra-comp-combobox-border-color);border-width:var(--skra-comp-combobox-border-width);border-style:var(--skra-comp-combobox-border-style);border-radius:var(--skra-comp-combobox-radius);box-shadow:var(--skra-comp-combobox-shadow);transition:background-color var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing),border-color var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing)}:host:has(.skra-combobox-input:focus-visible){outline:var(--skra-comp-combobox-focus-ring-width) solid var(--skra-comp-combobox-focus-ring-color);outline-offset:.125rem}:host([data-skra-disabled]){background:var(--skra-comp-combobox-background-disabled);color:var(--skra-comp-combobox-foreground-disabled)}:host([data-skra-readonly]){background:var(--skra-comp-combobox-background-readonly)}:host([data-skra-validation=invalid]){border-color:var(--skra-comp-combobox-border-color-validation-invalid)}.skra-combobox-input{flex:1 1 auto;min-inline-size:0;box-sizing:border-box;margin:0;padding-block:var(--skra-comp-combobox-padding-block);padding-inline:0;border:none;background:transparent;color:inherit;appearance:none;outline:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing)}.skra-combobox-input::placeholder{color:var(--skra-comp-combobox-foreground-disabled)}.skra-combobox-input:disabled{color:var(--skra-comp-combobox-foreground-disabled);cursor:not-allowed}.skra-combobox-toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-combobox-arrow-icon-size);block-size:var(--skra-comp-combobox-arrow-icon-size);margin:0;padding:0;border:none;background:transparent;cursor:pointer}.skra-combobox-toggle:disabled{cursor:not-allowed}.skra-combobox-arrow{inline-size:100%;block-size:100%;background-color:var(--skra-comp-combobox-arrow-color);mask:url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M6%209l6%206%206-6'%20fill='none'%20stroke='%23000'%20stroke-width='2'/%3E%3C/svg%3E\") no-repeat center/contain;-webkit-mask:url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M6%209l6%206%206-6'%20fill='none'%20stroke='%23000'%20stroke-width='2'/%3E%3C/svg%3E\") no-repeat center/contain;transition:transform var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing)}:host:has(.skra-combobox-input[aria-expanded=true]) .skra-combobox-arrow{transform:rotate(180deg)}.skra-combobox-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-combobox-panel-padding-block);padding-inline:var(--skra-comp-combobox-panel-padding-inline);background:var(--skra-comp-combobox-panel-background);color:var(--skra-comp-combobox-panel-foreground);border:var(--skra-comp-combobox-border-width) solid var(--skra-comp-combobox-panel-border-color);border-radius:var(--skra-comp-combobox-panel-radius);box-shadow:var(--skra-comp-combobox-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-combobox-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing),display var(--skra-comp-combobox-motion-duration) allow-discrete,overlay var(--skra-comp-combobox-motion-duration) allow-discrete}.skra-combobox-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-combobox-listbox:popover-open{opacity:0}}.skra-combobox-status{display:flex;align-items:center;gap:var(--skra-comp-combobox-gap);padding-block:var(--skra-comp-combobox-status-padding-block);padding-inline:var(--skra-comp-combobox-status-padding-inline);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-combobox-status:empty{display:none}:host([data-skra-empty]) .skra-combobox-status{color:var(--skra-comp-combobox-empty-foreground)}.skra-combobox-status:has([skraLoadingIndicator]) .skra-combobox-spinner{display:none}.skra-combobox-spinner{flex:none;inline-size:1em;block-size:1em;border:.125rem solid var(--skra-comp-combobox-loading-indicator-color);border-block-start-color:transparent;border-radius:50%;animation:skra-combobox-spin .6s linear infinite}@keyframes skra-combobox-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){:host,.skra-combobox-arrow,.skra-combobox-listbox{transition-duration:0ms}.skra-combobox-spinner{animation-duration:0ms}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
236
|
+
}
|
|
237
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraCombobox, decorators: [{
|
|
238
|
+
type: Component,
|
|
239
|
+
args: [{ selector: 'skra-combobox', standalone: true, exportAs: 'skraCombobox', template: `
|
|
240
|
+
<input
|
|
241
|
+
#trigger
|
|
242
|
+
class="skra-combobox-input"
|
|
243
|
+
type="text"
|
|
244
|
+
role="combobox"
|
|
245
|
+
autocomplete="off"
|
|
246
|
+
aria-autocomplete="list"
|
|
247
|
+
aria-haspopup="listbox"
|
|
248
|
+
[attr.id]="triggerId()"
|
|
249
|
+
[attr.aria-controls]="panelId"
|
|
250
|
+
[attr.aria-expanded]="isOpen()"
|
|
251
|
+
[attr.aria-activedescendant]="activeOptionId()"
|
|
252
|
+
[attr.aria-describedby]="describedBy()"
|
|
253
|
+
[attr.aria-invalid]="hasError() ? 'true' : null"
|
|
254
|
+
[attr.aria-required]="isRequired() ? 'true' : null"
|
|
255
|
+
[attr.aria-busy]="loading() ? 'true' : null"
|
|
256
|
+
[attr.placeholder]="placeholder()"
|
|
257
|
+
[disabled]="isDisabled()"
|
|
258
|
+
[readOnly]="isReadonly()"
|
|
259
|
+
[value]="displayValue()"
|
|
260
|
+
(focus)="onFocus()"
|
|
261
|
+
(input)="onInput($event)"
|
|
262
|
+
(keydown)="onTriggerKeydown($event)"
|
|
263
|
+
(blur)="onBlur()"
|
|
264
|
+
/>
|
|
265
|
+
<button
|
|
266
|
+
class="skra-combobox-toggle"
|
|
267
|
+
type="button"
|
|
268
|
+
tabindex="-1"
|
|
269
|
+
aria-hidden="true"
|
|
270
|
+
[disabled]="isDisabled()"
|
|
271
|
+
(mousedown)="$event.preventDefault()"
|
|
272
|
+
(click)="toggle()"
|
|
273
|
+
>
|
|
274
|
+
<span class="skra-combobox-arrow" aria-hidden="true"></span>
|
|
275
|
+
</button>
|
|
276
|
+
|
|
277
|
+
<div
|
|
278
|
+
#panel
|
|
279
|
+
class="skra-combobox-listbox"
|
|
280
|
+
[id]="panelId"
|
|
281
|
+
role="listbox"
|
|
282
|
+
popover="auto"
|
|
283
|
+
(mousedown)="$event.preventDefault()"
|
|
284
|
+
(toggle)="onPanelToggle($event)"
|
|
285
|
+
>
|
|
286
|
+
<!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —
|
|
287
|
+
a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via
|
|
288
|
+
:empty in SCSS. -->
|
|
289
|
+
<div class="skra-combobox-status" role="status" aria-live="polite">
|
|
290
|
+
@if (loading()) {
|
|
291
|
+
<ng-content select="[skraLoadingIndicator]"></ng-content>
|
|
292
|
+
<span class="skra-combobox-spinner" aria-hidden="true"></span>
|
|
293
|
+
} @else if (options().length === 0) {
|
|
294
|
+
<ng-content select="[skraComboboxEmpty]"></ng-content>
|
|
295
|
+
}
|
|
296
|
+
</div>
|
|
297
|
+
<ng-content></ng-content>
|
|
298
|
+
</div>
|
|
299
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
300
|
+
'[attr.data-skra-loading]': 'loading() ? "" : null',
|
|
301
|
+
'[attr.data-skra-empty]': '!loading() && options().length === 0 ? "" : null',
|
|
302
|
+
}, providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }], styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:var(--skra-comp-combobox-gap);inline-size:100%;min-block-size:var(--skra-comp-combobox-min-block-size);box-sizing:border-box;margin:0;padding-inline:var(--skra-comp-combobox-padding-inline);background:var(--skra-comp-combobox-background);color:var(--skra-comp-combobox-foreground);border-color:var(--skra-comp-combobox-border-color);border-width:var(--skra-comp-combobox-border-width);border-style:var(--skra-comp-combobox-border-style);border-radius:var(--skra-comp-combobox-radius);box-shadow:var(--skra-comp-combobox-shadow);transition:background-color var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing),border-color var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing)}:host:has(.skra-combobox-input:focus-visible){outline:var(--skra-comp-combobox-focus-ring-width) solid var(--skra-comp-combobox-focus-ring-color);outline-offset:.125rem}:host([data-skra-disabled]){background:var(--skra-comp-combobox-background-disabled);color:var(--skra-comp-combobox-foreground-disabled)}:host([data-skra-readonly]){background:var(--skra-comp-combobox-background-readonly)}:host([data-skra-validation=invalid]){border-color:var(--skra-comp-combobox-border-color-validation-invalid)}.skra-combobox-input{flex:1 1 auto;min-inline-size:0;box-sizing:border-box;margin:0;padding-block:var(--skra-comp-combobox-padding-block);padding-inline:0;border:none;background:transparent;color:inherit;appearance:none;outline:none;font-family:var(--skra-sys-typography-role-body-medium-font-family);font-size:var(--skra-sys-typography-role-body-medium-font-size);font-weight:var(--skra-sys-typography-role-body-medium-font-weight);line-height:var(--skra-sys-typography-role-body-medium-line-height);letter-spacing:var(--skra-sys-typography-role-body-medium-letter-spacing)}.skra-combobox-input::placeholder{color:var(--skra-comp-combobox-foreground-disabled)}.skra-combobox-input:disabled{color:var(--skra-comp-combobox-foreground-disabled);cursor:not-allowed}.skra-combobox-toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;inline-size:var(--skra-comp-combobox-arrow-icon-size);block-size:var(--skra-comp-combobox-arrow-icon-size);margin:0;padding:0;border:none;background:transparent;cursor:pointer}.skra-combobox-toggle:disabled{cursor:not-allowed}.skra-combobox-arrow{inline-size:100%;block-size:100%;background-color:var(--skra-comp-combobox-arrow-color);mask:url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M6%209l6%206%206-6'%20fill='none'%20stroke='%23000'%20stroke-width='2'/%3E%3C/svg%3E\") no-repeat center/contain;-webkit-mask:url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M6%209l6%206%206-6'%20fill='none'%20stroke='%23000'%20stroke-width='2'/%3E%3C/svg%3E\") no-repeat center/contain;transition:transform var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing)}:host:has(.skra-combobox-input[aria-expanded=true]) .skra-combobox-arrow{transform:rotate(180deg)}.skra-combobox-listbox{position:fixed;margin:0;inset:auto;box-sizing:border-box;padding-block:var(--skra-comp-combobox-panel-padding-block);padding-inline:var(--skra-comp-combobox-panel-padding-inline);background:var(--skra-comp-combobox-panel-background);color:var(--skra-comp-combobox-panel-foreground);border:var(--skra-comp-combobox-border-width) solid var(--skra-comp-combobox-panel-border-color);border-radius:var(--skra-comp-combobox-panel-radius);box-shadow:var(--skra-comp-combobox-panel-shadow);inline-size:max-content;max-block-size:var(--skra-comp-combobox-panel-max-block-size);overflow-y:auto;flex-direction:column;opacity:0;transition:opacity var(--skra-comp-combobox-motion-duration) var(--skra-comp-combobox-motion-easing),display var(--skra-comp-combobox-motion-duration) allow-discrete,overlay var(--skra-comp-combobox-motion-duration) allow-discrete}.skra-combobox-listbox:popover-open{display:flex;opacity:1}@starting-style{.skra-combobox-listbox:popover-open{opacity:0}}.skra-combobox-status{display:flex;align-items:center;gap:var(--skra-comp-combobox-gap);padding-block:var(--skra-comp-combobox-status-padding-block);padding-inline:var(--skra-comp-combobox-status-padding-inline);font-family:var(--skra-sys-typography-role-body-small-font-family);font-size:var(--skra-sys-typography-role-body-small-font-size);font-weight:var(--skra-sys-typography-role-body-small-font-weight);line-height:var(--skra-sys-typography-role-body-small-line-height);letter-spacing:var(--skra-sys-typography-role-body-small-letter-spacing)}.skra-combobox-status:empty{display:none}:host([data-skra-empty]) .skra-combobox-status{color:var(--skra-comp-combobox-empty-foreground)}.skra-combobox-status:has([skraLoadingIndicator]) .skra-combobox-spinner{display:none}.skra-combobox-spinner{flex:none;inline-size:1em;block-size:1em;border:.125rem solid var(--skra-comp-combobox-loading-indicator-color);border-block-start-color:transparent;border-radius:50%;animation:skra-combobox-spin .6s linear infinite}@keyframes skra-combobox-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){:host,.skra-combobox-arrow,.skra-combobox-listbox{transition-duration:0ms}.skra-combobox-spinner{animation-duration:0ms}}\n"] }]
|
|
303
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* SkraComboboxEmpty — the no-results marker (ADR 0049). Place on the element that should show inside the
|
|
307
|
+
* combobox listbox when the consumer's filter yields zero `skra-option` children:
|
|
308
|
+
*
|
|
309
|
+
* <skra-combobox (filterChange)="query.set($event)" [value]="city()" (valueChange)="city.set($event)">
|
|
310
|
+
* @for (c of visible(); track c.id) { <skra-option [value]="c.id">{{ c.name }}</skra-option> }
|
|
311
|
+
* <span skraComboboxEmpty>No matches</span>
|
|
312
|
+
* </skra-combobox>
|
|
313
|
+
*
|
|
314
|
+
* The text is consumer-supplied (i18n is the consumer's concern — Forge ships no hardcoded string).
|
|
315
|
+
* Forge toggles its visibility via `@if (!loading() && options().length === 0)`; the marker only carries
|
|
316
|
+
* the projection selector, so no logic lives here.
|
|
317
|
+
*/
|
|
318
|
+
class SkraComboboxEmpty {
|
|
319
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraComboboxEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
320
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SkraComboboxEmpty, isStandalone: true, selector: "[skraComboboxEmpty]", ngImport: i0 });
|
|
321
|
+
}
|
|
322
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraComboboxEmpty, decorators: [{
|
|
323
|
+
type: Directive,
|
|
324
|
+
args: [{
|
|
325
|
+
selector: '[skraComboboxEmpty]',
|
|
326
|
+
standalone: true,
|
|
327
|
+
}]
|
|
328
|
+
}] });
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Generated bundle index. Do not edit.
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
export { SkraCombobox, SkraComboboxEmpty };
|
|
335
|
+
//# sourceMappingURL=skra-ui-combobox.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skra-ui-combobox.mjs","sources":["../../../../libs/ui/combobox/src/lib/combobox/combobox.ts","../../../../libs/ui/combobox/src/lib/combobox/combobox-empty.ts","../../../../libs/ui/combobox/src/skra-ui-combobox.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n afterRenderEffect,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { _SkraListboxWidgetBase } from '@skra/ui/core';\nimport {\n SKRA_LISTBOX_PANEL,\n type SkraListboxKeyboardItem,\n} from '@skra/ui/core';\n\ndeclare const ngDevMode: boolean | undefined;\n\n/**\n * SkraCombobox — the WAI-ARIA APG **Editable Combobox** (list autocomplete, manual selection): an\n * editable `<input role=\"combobox\" aria-autocomplete=\"list\">` whose top-layer listbox (Popover API,\n * `popover=\"auto\"`, ADR 0044) of projected `skra-option` children NARROWS as the user types. The first\n * Tier-3 / higher-order component. Single-cardinality (ADR 0046); the multi sibling is future work.\n *\n * - **Filtering + option set are CONSUMER-OWNED (ADR 0049):** Forge emits the typed text via\n * `filterChange` (no debounce — timing is the consumer's); the consumer projects the matching\n * `skra-option` subset (sync computed OR async fetch). There is NO `[items]` input and NO internal\n * matcher — Forge renders exactly what is projected.\n * - **Value is OPTION-BOUND (ADR 0049):** `value = input<T>()` + `valueChange` (consumer-owned, ADR 037);\n * a committed value is always an existing option's `T`. On cancel/blur the input text reverts to the\n * selected option's label — typed text that matched nothing NEVER leaks as the value (free-text commit\n * is the separate `skra-autocomplete`).\n * - **Async (ADR 0049/028):** `loading` reflects `aria-busy` + drives a panel loading branch\n * (`[skraLoadingIndicator]` slot + fallback); an empty projected set drives the `[skraComboboxEmpty]`\n * branch; both are announced via `aria-live`.\n *\n * The trigger-shape-agnostic substrate (field cascade, panel toggle/positioner, keyboard wiring,\n * open/close, the `SkraListboxPanel` selection seam) lives in {@link _SkraListboxWidgetBase}, shared\n * verbatim with the select family (ADR 0046 amendment). `skra-option` is reused verbatim — combobox\n * provides the `SKRA_LISTBOX_PANEL` token so the option's selection/keyboard/indicator wiring works\n * unchanged.\n *\n * Full contract: adr/specs/component-combobox-spec.md.\n */\n@Component({\n selector: 'skra-combobox',\n standalone: true,\n exportAs: 'skraCombobox',\n template: `\n <input\n #trigger\n class=\"skra-combobox-input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n [attr.id]=\"triggerId()\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"hasError() ? 'true' : null\"\n [attr.aria-required]=\"isRequired() ? 'true' : null\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readOnly]=\"isReadonly()\"\n [value]=\"displayValue()\"\n (focus)=\"onFocus()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n class=\"skra-combobox-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggle()\"\n >\n <span class=\"skra-combobox-arrow\" aria-hidden=\"true\"></span>\n </button>\n\n <div\n #panel\n class=\"skra-combobox-listbox\"\n [id]=\"panelId\"\n role=\"listbox\"\n popover=\"auto\"\n (mousedown)=\"$event.preventDefault()\"\n (toggle)=\"onPanelToggle($event)\"\n >\n <!-- ONE persistent live region (text swapped via @if) so loading/empty announce reliably —\n a region injected on demand is unreliably spoken (ADR 0049 rule 4). Hidden when empty via\n :empty in SCSS. -->\n <div class=\"skra-combobox-status\" role=\"status\" aria-live=\"polite\">\n @if (loading()) {\n <ng-content select=\"[skraLoadingIndicator]\"></ng-content>\n <span class=\"skra-combobox-spinner\" aria-hidden=\"true\"></span>\n } @else if (options().length === 0) {\n <ng-content select=\"[skraComboboxEmpty]\"></ng-content>\n }\n </div>\n <ng-content></ng-content>\n </div>\n `,\n styleUrl: './combobox.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n '[attr.data-skra-loading]': 'loading() ? \"\" : null',\n '[attr.data-skra-empty]':\n '!loading() && options().length === 0 ? \"\" : null',\n },\n providers: [{ provide: SKRA_LISTBOX_PANEL, useExisting: SkraCombobox }],\n})\nexport class SkraCombobox<T = unknown> extends _SkraListboxWidgetBase<T> {\n /** Combobox panel token namespace — `--skra-comp-combobox-panel-*`. */\n protected readonly panelTokenPrefix = 'combobox';\n\n /** Consumer-owned value channel (ADR 037) — single, option-bound `T`. */\n readonly value = input<T>();\n /** Fires ONLY on user selection of an option — never on a programmatic `value()` change (ADR 037). */\n readonly valueChange = output<T>();\n /** The typed text, emitted on EVERY input change. Consumer-driven projection; NO Forge debounce (ADR 0049). */\n readonly filterChange = output<string>();\n /** Async hint (ADR 0049/028) — reflects `aria-busy` + drives the panel loading branch. */\n readonly loading = input(false, { transform: booleanAttribute });\n /** Cardinality hint (ADR 0046) — single-select. */\n readonly multiple = false;\n\n /** Internal editable-text state; emitted out via `filterChange`. NOT the value (ADR 0049). */\n protected readonly query = signal('');\n private readonly isFocused = signal(false);\n\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * Position the panel against — and match its width to — the HOST box, not the inner input. The input\n * is inset (host padding + chevron), so anchoring to it would make a narrower, misaligned panel; the\n * host gives full-width parity with `skra-select`.\n */\n protected override positioningAnchor(): HTMLElement {\n return this.hostEl.nativeElement;\n }\n\n /** The option whose value matches `value()` (ADR 037 selection model) — feeds the displayed text + active seed. */\n protected readonly selectedOption = computed(\n () => this.options().find((o) => this.isSelected(o.value())) ?? null,\n );\n\n /**\n * The text shown in the input: the live query while editing (focused), otherwise the selected option's\n * label. This is the controlled-input seam that enforces the option-bound value (ADR 0049) — on blur the\n * input snaps back to the selected label, so unmatched typed text never persists.\n */\n protected readonly displayValue = computed(() =>\n this.isFocused() ? this.query() : (this.selectedOption()?.getLabel() ?? ''),\n );\n\n constructor() {\n super();\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n // Dev-only consumer warning. Uses afterRenderEffect (NOT effect) so the projected\n // options' required `value` inputs are read only AFTER each render applies their\n // bindings — an eager read during the initial CD (SSR / first render) OR a runtime\n // re-projection (consumer filtering adds fresh <skra-option>s) would throw NG0950.\n // afterRender hooks never run on the server, so this stays SSR-safe. Covers both\n // F-13 (initial render) and F-16 (runtime re-projection, surfaced in dogfood).\n afterRenderEffect(() => {\n const v = this.value();\n if (v === undefined) return;\n const matched = this.options().some((o) =>\n this.compareWith()(o.value() as T, v),\n );\n if (!matched && this.options().length) {\n console.warn(\n '[skra-combobox] value has no matching <skra-option> in the projected set — the input ' +\n 'shows empty (likely a compareWith mismatch, or the selected option was filtered out).',\n );\n }\n });\n }\n }\n\n // ── SkraListboxPanel seam ──\n isSelected(value: unknown): boolean {\n const current = this.value();\n return current !== undefined && this.compareWith()(current, value as T);\n }\n\n selectValue(value: unknown): void {\n const opt =\n this.options().find((o) =>\n this.compareWith()(o.value() as T, value as T),\n ) ?? null;\n this.valueChange.emit(value as T);\n // Optimistic display: show the picked option's label immediately (focus is retained).\n this.query.set(opt?.getLabel() ?? '');\n this.close(); // single-select: pick + close\n }\n\n protected activeSeedItem(): SkraListboxKeyboardItem | null {\n return this.selectedOption();\n }\n\n // ── Editable-input trigger model (the divergence from the select-family button trigger) ──\n protected onFocus(): void {\n // Start editing from the current value's label (APG: the textbox shows the value, editable).\n this.query.set(this.selectedOption()?.getLabel() ?? '');\n this.isFocused.set(true);\n }\n\n protected onInput(event: Event): void {\n if (this.isReadonly()) return;\n const text = (event.target as HTMLInputElement).value;\n this.query.set(text);\n this.filterChange.emit(text); // ADR 0049 — consumer owns the matching + projection\n if (!this.isOpen()) this.open();\n }\n\n protected onBlur(): void {\n this.isFocused.set(false); // displayValue() snaps back to the selected label (option-bound, ADR 0049)\n if (this.isOpen()) this.close();\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.isDisabled() || this.isReadonly()) return;\n const key = event.key;\n\n if (key === 'ArrowDown' || key === 'ArrowUp') {\n event.preventDefault(); // do not move the text caret\n if (event.altKey) {\n // APG: Alt+Down opens (no active move); Alt+Up closes + reverts (keeps value).\n if (key === 'ArrowDown') {\n if (!this.isOpen()) this.open();\n } else if (this.isOpen()) {\n this.query.set(this.selectedOption()?.getLabel() ?? '');\n this.close();\n }\n return;\n }\n if (!this.isOpen()) this.open();\n else this.keyboard.onKeydown(event); // move the active descendant\n return;\n }\n if (key === 'Enter') {\n if (this.isOpen()) {\n event.preventDefault();\n this.selectActive(); // select the active option (no-op if none / soft-disabled)\n }\n return;\n }\n if (key === 'Escape') {\n if (this.isOpen()) {\n event.preventDefault();\n this.query.set(this.selectedOption()?.getLabel() ?? ''); // revert (keep value)\n this.close();\n }\n return;\n }\n // Home/End/printable/Backspace: native text editing — NOT routed to the listbox (no type-ahead).\n }\n\n protected toggle(): void {\n if (this.isDisabled() || this.isReadonly()) return; // pointer parity with the keydown/input guards (M1)\n const el = this.triggerRef()?.nativeElement;\n if (this.isOpen()) {\n this.close();\n } else {\n el?.focus(); // fires onFocus → seeds the editable text\n this.open();\n }\n }\n}\n","import { Directive } from '@angular/core';\n\n/**\n * SkraComboboxEmpty — the no-results marker (ADR 0049). Place on the element that should show inside the\n * combobox listbox when the consumer's filter yields zero `skra-option` children:\n *\n * <skra-combobox (filterChange)=\"query.set($event)\" [value]=\"city()\" (valueChange)=\"city.set($event)\">\n * @for (c of visible(); track c.id) { <skra-option [value]=\"c.id\">{{ c.name }}</skra-option> }\n * <span skraComboboxEmpty>No matches</span>\n * </skra-combobox>\n *\n * The text is consumer-supplied (i18n is the consumer's concern — Forge ships no hardcoded string).\n * Forge toggles its visibility via `@if (!loading() && options().length === 0)`; the marker only carries\n * the projection selector, so no logic lives here.\n */\n@Directive({\n selector: '[skraComboboxEmpty]',\n standalone: true,\n})\nexport class SkraComboboxEmpty {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|