@wildmason/aegis 2.2.1 → 2.3.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/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,51 @@ that 404s.
|
|
|
18
18
|
|
|
19
19
|
## [Unreleased]
|
|
20
20
|
|
|
21
|
+
## [2.3.0] - 2026-10-07
|
|
22
|
+
|
|
23
|
+
Minor: `WmSelect` gains a compact chip trigger, a suffix, option descriptions,
|
|
24
|
+
a danger tone and panel sizing. Every addition is opt-in. A select that sets
|
|
25
|
+
none of them renders the same trigger and option markup as 2.2.1, so a
|
|
26
|
+
consumer that styles `.wm-select-trigger`, `.wm-select-value` or
|
|
27
|
+
`.wm-select-option-label` sees no change. One behaviour changes for every
|
|
28
|
+
select: the panel takes the trigger's width when it opens, not on every check
|
|
29
|
+
(see Fixed).
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- `WmSelect` `appearance`: `'field'` (the default, as before) or `'chip'`, a
|
|
34
|
+
filled trigger with no border, as wide as its words, for a row of choices
|
|
35
|
+
with no label beside each one. The host takes `.wm-select--chip`.
|
|
36
|
+
- `WmSelect` `suffix`: a muted word after the chosen label, in the trigger
|
|
37
|
+
only, so `suffix="model"` reads "Opus model". It is left out while the
|
|
38
|
+
placeholder shows, so an empty chip never reads "Model model". On a chip the
|
|
39
|
+
suffix turns secondary while the trigger is hovered or open: muted text falls
|
|
40
|
+
under 3:1 on `--wm-bg-hover` in the `wildmason` theme.
|
|
41
|
+
- `SelectOption.description`: a second line under the label, in the panel
|
|
42
|
+
only. The option is named by its label (`aria-labelledby`) and described by
|
|
43
|
+
the line (`aria-describedby`); the ids are unique per select instance. The
|
|
44
|
+
line is secondary text, not muted: muted measures 3.2:1 on `--wm-bg-float`
|
|
45
|
+
in the `wildmason` theme, under the 4.5:1 a 12px line needs.
|
|
46
|
+
- `WmSelect` `tone`: `'neutral'` (the default) or `'danger'`, for a choice that
|
|
47
|
+
is risky to keep. It uses the guarded status recipe: `--wm-color-danger-readable`
|
|
48
|
+
on `--wm-color-danger-bg`, inside `--wm-color-danger-border`. The host takes
|
|
49
|
+
`.wm-select--danger`.
|
|
50
|
+
- `WmSelect` `panelWidth`: `'trigger'`, `'content'` (from the trigger's width
|
|
51
|
+
up to 24rem, where a description wraps) or a width in pixels. Unset, a field
|
|
52
|
+
keeps the trigger's width and a chip fits its content.
|
|
53
|
+
- Types `WmSelectAppearance`, `WmSelectTone` and `WmSelectPanelWidth`.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- `WmSelect` measures its trigger once, as the panel opens. It read
|
|
58
|
+
`offsetWidth` on every change-detection pass, so a trigger whose width moved
|
|
59
|
+
between Angular's update pass and its no-changes pass threw NG0100 in
|
|
60
|
+
development. A chip does that while its text renders.
|
|
61
|
+
- The docs app rendered the select page and the composition page from an old
|
|
62
|
+
copy of the component (`ui/src/app/components/select`), without the
|
|
63
|
+
scroll anchoring or the readable accent. Both pages now import the package
|
|
64
|
+
source, and the copy is deleted.
|
|
65
|
+
|
|
21
66
|
## [2.2.1] - 2026-10-07
|
|
22
67
|
|
|
23
68
|
Patch: one fix and one theme colour. Nothing is added, renamed or removed.
|
|
@@ -244,6 +244,9 @@ class WmAnchoredScrollStrategy {
|
|
|
244
244
|
* itself, including closing when its trigger is scrolled out of view.
|
|
245
245
|
*/
|
|
246
246
|
|
|
247
|
+
/** The widest a content-sized panel grows before its descriptions wrap. */
|
|
248
|
+
const CONTENT_MAX_WIDTH = '24rem';
|
|
249
|
+
let nextId$2 = 0;
|
|
247
250
|
/**
|
|
248
251
|
* WmSelect — Accessible dropdown select built on Angular CDK Overlay.
|
|
249
252
|
*
|
|
@@ -261,20 +264,26 @@ class WmAnchoredScrollStrategy {
|
|
|
261
264
|
* - Yes/No choices → use a Toggle or two radio buttons
|
|
262
265
|
*
|
|
263
266
|
* Options API:
|
|
264
|
-
* options: SelectOption[] — Array of { value, label, disabled? } objects.
|
|
267
|
+
* options: SelectOption[] — Array of { value, label, disabled?, description? } objects.
|
|
265
268
|
*
|
|
266
269
|
* Two-way binding:
|
|
267
270
|
* <wm-select [(value)]="selectedValue" [options]="myOptions" />
|
|
268
271
|
*
|
|
272
|
+
* A compact choice in a row of others, which reads "Opus model":
|
|
273
|
+
* <wm-select appearance="chip" suffix="model" aria-label="Model" [options]="models" [(value)]="model" />
|
|
274
|
+
*
|
|
269
275
|
* Accessibility:
|
|
270
276
|
* - Trigger has role="combobox", aria-expanded, aria-haspopup="listbox"
|
|
271
277
|
* - Panel has role="listbox"; each option has role="option" + aria-selected
|
|
272
278
|
* - Disabled options have aria-disabled="true"
|
|
279
|
+
* - An option with a description is named by its label (aria-labelledby) and
|
|
280
|
+
* described by the second line (aria-describedby)
|
|
273
281
|
* - Focus is trapped inside the open panel (cdkTrapFocus)
|
|
274
282
|
* - Clicking the backdrop closes the panel and returns focus to the trigger
|
|
275
283
|
* - Keyboard: Enter/Space on trigger opens; Escape closes; arrow keys
|
|
276
284
|
* navigate options (relies on natural tab order within the panel)
|
|
277
|
-
* - Always pair with a visible label via <wm-label> or aria-label on the host
|
|
285
|
+
* - Always pair with a visible label via <wm-label> or aria-label on the host.
|
|
286
|
+
* A chip has no visible label of its own, so it always needs the aria-label
|
|
278
287
|
*/
|
|
279
288
|
class WmSelect {
|
|
280
289
|
/**
|
|
@@ -287,6 +296,8 @@ class WmSelect {
|
|
|
287
296
|
* keeps the coordinates it was born with while the trigger scrolls away.
|
|
288
297
|
*/
|
|
289
298
|
scrollStrategy = new WmAnchoredScrollStrategy();
|
|
299
|
+
/** Prefix for the ids that tie an option to its label and description. */
|
|
300
|
+
uid = `wm-select-${++nextId$2}`;
|
|
290
301
|
/** Array of options to display. */
|
|
291
302
|
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
292
303
|
/** The currently selected value. Two-way bindable. */
|
|
@@ -295,18 +306,58 @@ class WmSelect {
|
|
|
295
306
|
placeholder = input('Select…', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
296
307
|
/** Disables the trigger and prevents the panel from opening. */
|
|
297
308
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
309
|
+
/**
|
|
310
|
+
* `field` (the default) is a full-width form control with a border.
|
|
311
|
+
* `chip` is a compact, filled trigger as wide as its words, for a row of
|
|
312
|
+
* choices such as a composer's model and permissions.
|
|
313
|
+
*/
|
|
314
|
+
appearance = input('field', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
|
|
315
|
+
/**
|
|
316
|
+
* A muted word after the chosen label, in the trigger only: `suffix="model"`
|
|
317
|
+
* reads "Opus model". It names the choice where no label sits beside it. It
|
|
318
|
+
* is left out while the placeholder shows.
|
|
319
|
+
*/
|
|
320
|
+
suffix = input(null, ...(ngDevMode ? [{ debugName: "suffix" }] : /* istanbul ignore next */ []));
|
|
321
|
+
/**
|
|
322
|
+
* `danger` draws the trigger in the danger status colours, for a choice that
|
|
323
|
+
* is risky to keep, such as running without permission prompts.
|
|
324
|
+
*/
|
|
325
|
+
tone = input('neutral', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
326
|
+
/**
|
|
327
|
+
* `trigger` holds the panel to the trigger's width. `content` lets it grow to
|
|
328
|
+
* its widest option, from the trigger's width up to 24rem, where a
|
|
329
|
+
* description wraps. A number is a fixed width in pixels. Left unset, a
|
|
330
|
+
* field's panel matches the trigger and a chip's panel fits its content.
|
|
331
|
+
*/
|
|
332
|
+
panelWidth = input(null, ...(ngDevMode ? [{ debugName: "panelWidth" }] : /* istanbul ignore next */ []));
|
|
298
333
|
isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
299
334
|
selectedOption = computed(() => this.options().find((o) => o.value === this.value()) ?? null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
|
|
335
|
+
sizing = computed(() => this.panelWidth() ?? (this.appearance() === 'chip' ? 'content' : 'trigger'), ...(ngDevMode ? [{ debugName: "sizing" }] : /* istanbul ignore next */ []));
|
|
300
336
|
hostEl = inject((ElementRef));
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
337
|
+
/**
|
|
338
|
+
* The trigger's width, measured as the panel opens. A width read on every
|
|
339
|
+
* check would differ between Angular's update pass and its no-changes pass
|
|
340
|
+
* whenever the trigger is resizing, which a chip does while its text renders.
|
|
341
|
+
*/
|
|
342
|
+
triggerWidth = signal(200, ...(ngDevMode ? [{ debugName: "triggerWidth" }] : /* istanbul ignore next */ []));
|
|
343
|
+
// The overlay leaves a size unset when it is given an empty string.
|
|
344
|
+
overlayWidth = computed(() => {
|
|
345
|
+
const sizing = this.sizing();
|
|
346
|
+
if (typeof sizing === 'number')
|
|
347
|
+
return sizing;
|
|
348
|
+
return sizing === 'trigger' ? this.triggerWidth() : '';
|
|
349
|
+
}, ...(ngDevMode ? [{ debugName: "overlayWidth" }] : /* istanbul ignore next */ []));
|
|
350
|
+
overlayMinWidth = computed(() => this.sizing() === 'content' ? this.triggerWidth() : '', ...(ngDevMode ? [{ debugName: "overlayMinWidth" }] : /* istanbul ignore next */ []));
|
|
351
|
+
/** The overlay has no max-width input, so the cap sits on the panel it holds. */
|
|
352
|
+
panelMaxWidth = computed(() => this.sizing() === 'content' ? CONTENT_MAX_WIDTH : '', ...(ngDevMode ? [{ debugName: "panelMaxWidth" }] : /* istanbul ignore next */ []));
|
|
304
353
|
positions = [
|
|
305
354
|
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
306
355
|
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
307
356
|
];
|
|
308
357
|
toggleOpen() {
|
|
309
358
|
if (!this.disabled()) {
|
|
359
|
+
if (!this.isOpen())
|
|
360
|
+
this.triggerWidth.set(this.hostEl.nativeElement.offsetWidth || 200);
|
|
310
361
|
this.isOpen.update((v) => !v);
|
|
311
362
|
}
|
|
312
363
|
}
|
|
@@ -317,12 +368,15 @@ class WmSelect {
|
|
|
317
368
|
}
|
|
318
369
|
}
|
|
319
370
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
320
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.23", type: WmSelect, isStandalone: true, selector: "wm-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0, template: "<!-- Trigger button \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-select-host\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n>\n <button\n type=\"button\"\n role=\"combobox\"\n class=\"wm-select-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggleOpen()\"\n >\n <span class=\"wm-select-value\" [class.is-placeholder]=\"!selectedOption()\">\n {{ selectedOption()?.label ?? placeholder() }}\n </span>\n <!-- Chevron icon \u2014 rotates when open -->\n <svg\n class=\"wm-select-chevron\"\n [class.is-open]=\"isOpen()\"\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6 6-6\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n</div>\n\n<!-- Dropdown panel \u2014 rendered in CDK overlay container -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"wm-select-backdrop\"\n (backdropClick)=\"isOpen.set(false)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n class=\"wm-select-panel\"\n role=\"listbox\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n @for (opt of options(); track opt.value) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"wm-select-option\"\n [class.is-selected]=\"opt.value === value()\"\n [class.is-disabled]=\"!!opt.disabled\"\n [attr.aria-selected]=\"opt.value === value()\"\n [attr.aria-disabled]=\"opt.disabled ? true : null\"\n [disabled]=\"opt.disabled || null\"\n (click)=\"selectOption(opt)\"\n >\n <span class=\"wm-select-option-label\">{{ opt.label }}</span>\n @if (opt.value === value()) {\n <!-- Checkmark for selected option -->\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"\n stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-select-host{display:block}.wm-select-trigger{display:flex;align-items:center;justify-content:space-between;gap:.5rem;width:100%;padding:0 .75rem;height:36px;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-md);color:var(--wm-text-primary);font-size:.875rem;font-family:inherit;cursor:pointer;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast);outline:none;text-align:left}.wm-select-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-select-trigger:focus-visible{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-select-trigger.is-open{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 20%,transparent)}.wm-select-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-select-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-select-value.is-placeholder{color:var(--wm-text-placeholder)}.wm-select-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast)}.wm-select-chevron.is-open{transform:rotate(180deg)}.wm-select-backdrop{position:fixed;inset:0}.wm-select-panel{width:100%;background:var(--wm-bg-float);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #0000001f;overflow:hidden;outline:none;padding:.25rem}.wm-select-option{display:flex;align-items:center;justify-content:space-between;gap:.5rem;width:100%;padding:.4375rem .625rem;border:none;border-radius:var(--wm-radius-sm);background:transparent;color:var(--wm-text-primary);font-size:.875rem;font-family:inherit;cursor:pointer;text-align:left;transition:background var(--wm-transition-fast)}.wm-select-option:hover:not(.is-disabled):not(.is-selected){background:var(--wm-bg-active)}.wm-select-option.is-selected{background:color-mix(in srgb,var(--wm-color-accent) 10%,transparent);color:var(--wm-color-accent-readable);font-weight:500}.wm-select-option.is-disabled{opacity:.4;cursor:not-allowed}.wm-select-option-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-select-option svg{flex-shrink:0;color:var(--wm-color-accent-readable)}\n"], dependencies: [{ kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }] });
|
|
371
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.23", type: WmSelect, isStandalone: true, selector: "wm-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.wm-select--chip": "appearance() === 'chip'", "class.wm-select--danger": "tone() === 'danger'" } }, ngImport: i0, template: "<!-- Trigger button \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-select-host\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n>\n <button\n type=\"button\"\n role=\"combobox\"\n class=\"wm-select-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggleOpen()\"\n >\n <span class=\"wm-select-value\" [class.is-placeholder]=\"!selectedOption()\">\n {{ selectedOption()?.label ?? placeholder() }}\n </span>\n @if (suffix() && selectedOption()) {\n <span class=\"wm-select-suffix\">{{ suffix() }}</span>\n }\n <!-- Chevron icon \u2014 rotates when open -->\n <svg\n class=\"wm-select-chevron\"\n [class.is-open]=\"isOpen()\"\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6 6-6\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n</div>\n\n<!-- Dropdown panel \u2014 rendered in CDK overlay container -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayWidth]=\"overlayWidth()\"\n [cdkConnectedOverlayMinWidth]=\"overlayMinWidth()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"wm-select-backdrop\"\n (backdropClick)=\"isOpen.set(false)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n class=\"wm-select-panel\"\n role=\"listbox\"\n [style.max-width]=\"panelMaxWidth() || null\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n @for (opt of options(); track opt.value; let i = $index) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"wm-select-option\"\n [class.is-selected]=\"opt.value === value()\"\n [class.is-disabled]=\"!!opt.disabled\"\n [attr.aria-selected]=\"opt.value === value()\"\n [attr.aria-disabled]=\"opt.disabled ? true : null\"\n [attr.aria-labelledby]=\"opt.description ? uid + '-label-' + i : null\"\n [attr.aria-describedby]=\"opt.description ? uid + '-description-' + i : null\"\n [disabled]=\"opt.disabled || null\"\n (click)=\"selectOption(opt)\"\n >\n @if (opt.description) {\n <span class=\"wm-select-option-text\">\n <span class=\"wm-select-option-label\" [id]=\"uid + '-label-' + i\">{{ opt.label }}</span>\n <span class=\"wm-select-option-description\" [id]=\"uid + '-description-' + i\">{{ opt.description }}</span>\n </span>\n } @else {\n <span class=\"wm-select-option-label\">{{ opt.label }}</span>\n }\n @if (opt.value === value()) {\n <!-- Checkmark for selected option -->\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"\n stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-select-host{display:block}.wm-select-trigger{display:flex;align-items:center;justify-content:space-between;gap:.5rem;width:100%;padding:0 .75rem;height:36px;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-md);color:var(--wm-text-primary);font-size:.875rem;font-family:inherit;cursor:pointer;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast);outline:none;text-align:left}.wm-select-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-select-trigger:focus-visible{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-select-trigger.is-open{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 20%,transparent)}.wm-select-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-select-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-select-value.is-placeholder{color:var(--wm-text-placeholder)}.wm-select-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast)}.wm-select-chevron.is-open{transform:rotate(180deg)}.wm-select-backdrop{position:fixed;inset:0}.wm-select-panel{width:100%;background:var(--wm-bg-float);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #0000001f;overflow:hidden;outline:none;padding:.25rem}.wm-select-option{display:flex;align-items:center;justify-content:space-between;gap:.5rem;width:100%;padding:.4375rem .625rem;border:none;border-radius:var(--wm-radius-sm);background:transparent;color:var(--wm-text-primary);font-size:.875rem;font-family:inherit;cursor:pointer;text-align:left;transition:background var(--wm-transition-fast)}.wm-select-option:hover:not(.is-disabled):not(.is-selected){background:var(--wm-bg-active)}.wm-select-option.is-selected{background:color-mix(in srgb,var(--wm-color-accent) 10%,transparent);color:var(--wm-color-accent-readable);font-weight:500}.wm-select-option.is-disabled{opacity:.4;cursor:not-allowed}.wm-select-option-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-select-option svg{flex-shrink:0;color:var(--wm-color-accent-readable)}.wm-select-suffix{flex-shrink:0;color:var(--wm-text-muted);font-size:.8125rem;font-weight:400}.wm-select-option-text{display:flex;flex:1;flex-direction:column;gap:.125rem;min-width:0}.wm-select-option:has(.wm-select-option-text){align-items:flex-start;padding-block:.5rem}.wm-select-option:has(.wm-select-option-text) svg{margin-top:.1875rem}.wm-select-option-description{color:var(--wm-text-secondary);font-size:.75rem;font-weight:400;line-height:1.4;white-space:normal}:host(.wm-select--chip){display:inline-block;max-width:100%}:host(.wm-select--chip) .wm-select-trigger{justify-content:flex-start;width:auto;max-width:100%;height:34px;padding:0 .625rem 0 .75rem;background:var(--wm-bg-base);border-color:transparent;font-weight:500;transition:background var(--wm-transition-fast),border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}:host(.wm-select--chip) .wm-select-value{flex:0 1 auto}:host(.wm-select--chip) .wm-select-chevron{width:14px;height:14px}:host(.wm-select--chip) .wm-select-trigger:hover:not(.is-disabled),:host(.wm-select--chip) .wm-select-trigger.is-open{background:var(--wm-bg-hover);border-color:transparent;box-shadow:none;animation:none}:host(.wm-select--chip) .wm-select-trigger:hover:not(.is-disabled) .wm-select-suffix,:host(.wm-select--chip) .wm-select-trigger.is-open .wm-select-suffix{color:var(--wm-text-secondary)}:host(.wm-select--chip) .wm-select-trigger:focus-visible{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}:host(.wm-select--danger) .wm-select-trigger,:host(.wm-select--danger) .wm-select-trigger:hover:not(.is-disabled),:host(.wm-select--danger) .wm-select-trigger.is-open{border-color:var(--wm-color-danger-border);color:var(--wm-color-danger-readable)}:host(.wm-select--danger) .wm-select-suffix,:host(.wm-select--danger) .wm-select-chevron{color:var(--wm-color-danger-readable)}:host(.wm-select--danger.wm-select--chip) .wm-select-trigger{background:linear-gradient(var(--wm-color-danger-bg),var(--wm-color-danger-bg)),var(--wm-bg-base)}:host(.wm-select--danger.wm-select--chip) .wm-select-trigger:hover:not(.is-disabled),:host(.wm-select--danger.wm-select--chip) .wm-select-trigger.is-open{background:linear-gradient(var(--wm-color-danger-bg),var(--wm-color-danger-bg)),var(--wm-bg-hover)}\n"], dependencies: [{ kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }] });
|
|
321
372
|
}
|
|
322
373
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmSelect, decorators: [{
|
|
323
374
|
type: Component,
|
|
324
|
-
args: [{ selector: 'wm-select', imports: [CdkConnectedOverlay, CdkOverlayOrigin, CdkTrapFocus],
|
|
325
|
-
|
|
375
|
+
args: [{ selector: 'wm-select', imports: [CdkConnectedOverlay, CdkOverlayOrigin, CdkTrapFocus], host: {
|
|
376
|
+
'[class.wm-select--chip]': "appearance() === 'chip'",
|
|
377
|
+
'[class.wm-select--danger]': "tone() === 'danger'",
|
|
378
|
+
}, template: "<!-- Trigger button \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-select-host\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n>\n <button\n type=\"button\"\n role=\"combobox\"\n class=\"wm-select-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggleOpen()\"\n >\n <span class=\"wm-select-value\" [class.is-placeholder]=\"!selectedOption()\">\n {{ selectedOption()?.label ?? placeholder() }}\n </span>\n @if (suffix() && selectedOption()) {\n <span class=\"wm-select-suffix\">{{ suffix() }}</span>\n }\n <!-- Chevron icon \u2014 rotates when open -->\n <svg\n class=\"wm-select-chevron\"\n [class.is-open]=\"isOpen()\"\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\"\n fill=\"none\" aria-hidden=\"true\"\n >\n <path d=\"M6 9l6 6 6-6\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n</div>\n\n<!-- Dropdown panel \u2014 rendered in CDK overlay container -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayWidth]=\"overlayWidth()\"\n [cdkConnectedOverlayMinWidth]=\"overlayMinWidth()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"wm-select-backdrop\"\n (backdropClick)=\"isOpen.set(false)\"\n (detach)=\"isOpen.set(false)\"\n>\n <div\n class=\"wm-select-panel\"\n role=\"listbox\"\n [style.max-width]=\"panelMaxWidth() || null\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n @for (opt of options(); track opt.value; let i = $index) {\n <button\n type=\"button\"\n role=\"option\"\n class=\"wm-select-option\"\n [class.is-selected]=\"opt.value === value()\"\n [class.is-disabled]=\"!!opt.disabled\"\n [attr.aria-selected]=\"opt.value === value()\"\n [attr.aria-disabled]=\"opt.disabled ? true : null\"\n [attr.aria-labelledby]=\"opt.description ? uid + '-label-' + i : null\"\n [attr.aria-describedby]=\"opt.description ? uid + '-description-' + i : null\"\n [disabled]=\"opt.disabled || null\"\n (click)=\"selectOption(opt)\"\n >\n @if (opt.description) {\n <span class=\"wm-select-option-text\">\n <span class=\"wm-select-option-label\" [id]=\"uid + '-label-' + i\">{{ opt.label }}</span>\n <span class=\"wm-select-option-description\" [id]=\"uid + '-description-' + i\">{{ opt.description }}</span>\n </span>\n } @else {\n <span class=\"wm-select-option-label\">{{ opt.label }}</span>\n }\n @if (opt.value === value()) {\n <!-- Checkmark for selected option -->\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"\n stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-select-host{display:block}.wm-select-trigger{display:flex;align-items:center;justify-content:space-between;gap:.5rem;width:100%;padding:0 .75rem;height:36px;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-md);color:var(--wm-text-primary);font-size:.875rem;font-family:inherit;cursor:pointer;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast);outline:none;text-align:left}.wm-select-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-select-trigger:focus-visible{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-select-trigger.is-open{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 20%,transparent)}.wm-select-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-select-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-select-value.is-placeholder{color:var(--wm-text-placeholder)}.wm-select-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast)}.wm-select-chevron.is-open{transform:rotate(180deg)}.wm-select-backdrop{position:fixed;inset:0}.wm-select-panel{width:100%;background:var(--wm-bg-float);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #0000001f;overflow:hidden;outline:none;padding:.25rem}.wm-select-option{display:flex;align-items:center;justify-content:space-between;gap:.5rem;width:100%;padding:.4375rem .625rem;border:none;border-radius:var(--wm-radius-sm);background:transparent;color:var(--wm-text-primary);font-size:.875rem;font-family:inherit;cursor:pointer;text-align:left;transition:background var(--wm-transition-fast)}.wm-select-option:hover:not(.is-disabled):not(.is-selected){background:var(--wm-bg-active)}.wm-select-option.is-selected{background:color-mix(in srgb,var(--wm-color-accent) 10%,transparent);color:var(--wm-color-accent-readable);font-weight:500}.wm-select-option.is-disabled{opacity:.4;cursor:not-allowed}.wm-select-option-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-select-option svg{flex-shrink:0;color:var(--wm-color-accent-readable)}.wm-select-suffix{flex-shrink:0;color:var(--wm-text-muted);font-size:.8125rem;font-weight:400}.wm-select-option-text{display:flex;flex:1;flex-direction:column;gap:.125rem;min-width:0}.wm-select-option:has(.wm-select-option-text){align-items:flex-start;padding-block:.5rem}.wm-select-option:has(.wm-select-option-text) svg{margin-top:.1875rem}.wm-select-option-description{color:var(--wm-text-secondary);font-size:.75rem;font-weight:400;line-height:1.4;white-space:normal}:host(.wm-select--chip){display:inline-block;max-width:100%}:host(.wm-select--chip) .wm-select-trigger{justify-content:flex-start;width:auto;max-width:100%;height:34px;padding:0 .625rem 0 .75rem;background:var(--wm-bg-base);border-color:transparent;font-weight:500;transition:background var(--wm-transition-fast),border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}:host(.wm-select--chip) .wm-select-value{flex:0 1 auto}:host(.wm-select--chip) .wm-select-chevron{width:14px;height:14px}:host(.wm-select--chip) .wm-select-trigger:hover:not(.is-disabled),:host(.wm-select--chip) .wm-select-trigger.is-open{background:var(--wm-bg-hover);border-color:transparent;box-shadow:none;animation:none}:host(.wm-select--chip) .wm-select-trigger:hover:not(.is-disabled) .wm-select-suffix,:host(.wm-select--chip) .wm-select-trigger.is-open .wm-select-suffix{color:var(--wm-text-secondary)}:host(.wm-select--chip) .wm-select-trigger:focus-visible{border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}:host(.wm-select--danger) .wm-select-trigger,:host(.wm-select--danger) .wm-select-trigger:hover:not(.is-disabled),:host(.wm-select--danger) .wm-select-trigger.is-open{border-color:var(--wm-color-danger-border);color:var(--wm-color-danger-readable)}:host(.wm-select--danger) .wm-select-suffix,:host(.wm-select--danger) .wm-select-chevron{color:var(--wm-color-danger-readable)}:host(.wm-select--danger.wm-select--chip) .wm-select-trigger{background:linear-gradient(var(--wm-color-danger-bg),var(--wm-color-danger-bg)),var(--wm-bg-base)}:host(.wm-select--danger.wm-select--chip) .wm-select-trigger:hover:not(.is-disabled),:host(.wm-select--danger.wm-select--chip) .wm-select-trigger.is-open{background:linear-gradient(var(--wm-color-danger-bg),var(--wm-color-danger-bg)),var(--wm-bg-hover)}\n"] }]
|
|
379
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }] } });
|
|
326
380
|
|
|
327
381
|
/**
|
|
328
382
|
* WmPopover — general-purpose CDK-overlay floating panel.
|