@wildmason/aegis 2.2.0 → 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,74 @@ 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
+
66
+ ## [2.2.1] - 2026-10-07
67
+
68
+ Patch: one fix and one theme colour. Nothing is added, renamed or removed.
69
+
70
+ ### Fixed
71
+
72
+ - `WmSlider`: the thumb sits on the value after the value and the range change
73
+ in one render. The native input bound its `value` before its `min`, `max`
74
+ and `step`, and the browser sanitizes a written value against the bounds the
75
+ input holds at that moment. A value that rose with `max` was clamped to the
76
+ old `max`, and the binding was not written again because the value did not
77
+ change. The thumb and the input's own value, which a screen reader reads as
78
+ the current value, sat a stop short, while the fill, the readout and
79
+ `aria-valuetext` named the new value. Scaffold hit it when an effort scale
80
+ gained a stop: the readout said "ultra" and the thumb sat on "max". The
81
+ bounds and the step are now bound first.
82
+
83
+ ### Changed
84
+
85
+ - `wildmason` theme: `--wm-text-muted` is `#949d94`, down from `#c0cac0`. The
86
+ old value was within 2/255 of `--wm-text-secondary` (`#c2ccc2`), so muted text
87
+ such as a timestamp read as body text. Every muted label on this theme dims.
88
+
21
89
  ## [2.2.0] - 2026-10-02
22
90
 
23
91
  Minor: new components, variants and inputs, and accessibility fixes. Nothing is
@@ -760,7 +828,8 @@ a `^1.x` range does not pick this release up by accident.
760
828
  Releases before 1.8.0 are in `git log`. See also `wiki/products/Aegis - Health.md`
761
829
  for the decisions behind these changes, including the ones decided against.
762
830
 
763
- [Unreleased]: https://github.com/wildmason/aegis/compare/v2.2.0...HEAD
831
+ [Unreleased]: https://github.com/wildmason/aegis/compare/v2.2.1...HEAD
832
+ [2.2.1]: https://github.com/wildmason/aegis/releases/tag/v2.2.1
764
833
  [2.2.0]: https://github.com/wildmason/aegis/releases/tag/v2.2.0
765
834
  [2.1.0]: https://github.com/wildmason/aegis/releases/tag/v2.1.0
766
835
  [2.0.0]: https://github.com/wildmason/aegis/releases/tag/v2.0.0
@@ -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
- get overlayWidth() {
302
- return this.hostEl.nativeElement.offsetWidth || 200;
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], 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"] }]
325
- }], 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 }] }] } });
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.
@@ -1310,7 +1364,7 @@ class WmSlider {
1310
1364
  useExisting: forwardRef(() => WmSlider),
1311
1365
  multi: true,
1312
1366
  },
1313
- ], ngImport: i0, template: "<!--\n The native <input type=\"range\"> is the control, not a decoration over one:\n its track and thumb are the styled surfaces. --wm-slider-p carries the thumb\n position to the CSS so the filled part of the track, every tick and every\n block are placed from the same number the thumb is.\n-->\n<div\n class=\"wm-slider\"\n [attr.data-track]=\"track()\"\n [class.is-disabled]=\"isDisabled()\"\n [class.has-ticks]=\"resolvedTicks().length > 0\"\n [style.--wm-slider-p]=\"fraction()\"\n>\n <div class=\"wm-slider-rail\">\n @if (segments().length > 0) {\n <!--\n Blocks sit under the transparent native track. They are bounded by the\n same resolved grid the marks come from, so a block and the mark on its\n edge can never disagree about where a value falls.\n -->\n <div class=\"wm-slider-segments\" aria-hidden=\"true\">\n @for (segment of segments(); track segment.from) {\n <span\n class=\"wm-slider-seg\"\n [class.is-passed]=\"segment.passed\"\n [class.is-current]=\"segment.current\"\n [style.flexGrow]=\"segment.span\"\n [style.--wm-slider-seg-p]=\"segment.fill\"\n ></span>\n }\n </div>\n }\n\n @if (resolvedTicks().length > 0) {\n <div class=\"wm-slider-ticks\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n <span\n class=\"wm-slider-tick\"\n [class.wm-slider-tick--minor]=\"tick.level === 'minor'\"\n [class.is-passed]=\"tick.passed\"\n [style.left]=\"tick.left\"\n ></span>\n }\n </div>\n }\n\n <input\n class=\"wm-slider-input\"\n type=\"range\"\n [value]=\"value()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [step]=\"step() > 0 ? step() : 'any'\"\n [disabled]=\"isDisabled() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-valuetext]=\"valueText()\"\n (input)=\"onRangeInput($event)\"\n (blur)=\"onTouchedFn()\"\n />\n </div>\n\n @if (showValue()) {\n <!--\n The sizers stack on the output in one grid cell, so the column reserves\n the widest string the readout can hold and stops stealing width from the\n rail every time the value changes.\n -->\n <div class=\"wm-slider-value-cell\">\n <output class=\"wm-slider-value\">{{ displayValue() }}</output>\n @for (candidate of valueCandidates(); track candidate) {\n <span class=\"wm-slider-value-sizer\" aria-hidden=\"true\">{{ candidate }}</span>\n }\n </div>\n }\n\n @if (hasTickLabels()) {\n <div class=\"wm-slider-tick-labels\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n @if (tick.label !== undefined) {\n <span class=\"wm-slider-tick-label\" [style.left]=\"tick.left\">{{ tick.label }}</span>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:block}.wm-slider{--wm-slider-thumb: 16px;--wm-slider-track: 4px;--wm-slider-gap: 3px;--wm-slider-p: 0;--wm-slider-stop: calc( var(--wm-slider-thumb) / 2 + var(--wm-slider-p) * (100% - var(--wm-slider-thumb)) );--wm-slider-fill: linear-gradient( to right, var(--wm-color-accent) 0 var(--wm-slider-stop), var(--wm-border-input) var(--wm-slider-stop) 100% );display:grid;grid-template-columns:1fr auto;align-items:center;column-gap:.75rem;row-gap:.375rem}.wm-slider-rail{position:relative;min-width:0}.wm-slider.has-ticks .wm-slider-rail{margin-bottom:10px}.wm-slider-input{-webkit-appearance:none;appearance:none;display:block;width:100%;height:var(--wm-slider-thumb);margin:0;padding:0;background:transparent;cursor:pointer;position:relative;z-index:1}.wm-slider-input:focus{outline:none}.wm-slider-input::-webkit-slider-runnable-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-webkit-slider-thumb{-webkit-appearance:none;width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);margin-top:calc((var(--wm-slider-track) - var(--wm-slider-thumb)) / 2);border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-webkit-slider-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-input::-moz-range-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-moz-range-thumb{width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);box-sizing:border-box;border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-moz-range-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-moz-range-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-ticks{position:absolute;inset-inline:0;top:100%;margin-top:2px;height:6px;pointer-events:none}.wm-slider-tick{position:absolute;top:0;width:2px;height:6px;margin-left:-1px;border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-tick.is-passed{background:var(--wm-color-accent)}.wm-slider-tick--minor{height:3px;background:var(--wm-border-subtle)}.wm-slider-tick--minor.is-passed{background:color-mix(in srgb,var(--wm-color-accent) 55%,transparent)}.wm-slider[data-track=segmented]{--wm-slider-fill: none}.wm-slider-segments{position:absolute;inset:0 calc(var(--wm-slider-thumb) / 2);display:flex;align-items:center;gap:var(--wm-slider-gap);pointer-events:none}.wm-slider-seg{flex-basis:0;min-width:0;height:var(--wm-slider-track);border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-seg.is-passed{background:var(--wm-color-accent)}.wm-slider-seg.is-current{background-image:linear-gradient(var(--wm-color-accent) 0 0);background-repeat:no-repeat;background-size:calc(var(--wm-slider-seg-p, 0) * 100%) 100%}.wm-slider-tick-labels{position:relative;grid-column:1;height:1rem}.wm-slider-tick-label{position:absolute;top:0;transform:translate(-50%);font-size:.75rem;color:var(--wm-text-muted);white-space:nowrap}.wm-slider-value-cell{grid-area:1 / 2;display:grid;justify-items:end}.wm-slider-value,.wm-slider-value-sizer{grid-area:1 / 1;font-family:var(--wm-font-mono);font-size:.8125rem;font-variant-numeric:tabular-nums;white-space:nowrap}.wm-slider-value{color:var(--wm-text-secondary);min-width:4ch;text-align:right}.wm-slider-value-sizer{visibility:hidden;pointer-events:none}.wm-slider.is-disabled{opacity:.5}.wm-slider-input:disabled{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.wm-slider-input::-webkit-slider-thumb,.wm-slider-input::-moz-range-thumb,.wm-slider-tick{transition:none}.wm-slider-input:hover::-webkit-slider-thumb,.wm-slider-input:hover::-moz-range-thumb{transform:none}}\n"] });
1367
+ ], ngImport: i0, template: "<!--\n The native <input type=\"range\"> is the control, not a decoration over one:\n its track and thumb are the styled surfaces. --wm-slider-p carries the thumb\n position to the CSS so the filled part of the track, every tick and every\n block are placed from the same number the thumb is.\n-->\n<div\n class=\"wm-slider\"\n [attr.data-track]=\"track()\"\n [class.is-disabled]=\"isDisabled()\"\n [class.has-ticks]=\"resolvedTicks().length > 0\"\n [style.--wm-slider-p]=\"fraction()\"\n>\n <div class=\"wm-slider-rail\">\n @if (segments().length > 0) {\n <!--\n Blocks sit under the transparent native track. They are bounded by the\n same resolved grid the marks come from, so a block and the mark on its\n edge can never disagree about where a value falls.\n -->\n <div class=\"wm-slider-segments\" aria-hidden=\"true\">\n @for (segment of segments(); track segment.from) {\n <span\n class=\"wm-slider-seg\"\n [class.is-passed]=\"segment.passed\"\n [class.is-current]=\"segment.current\"\n [style.flexGrow]=\"segment.span\"\n [style.--wm-slider-seg-p]=\"segment.fill\"\n ></span>\n }\n </div>\n }\n\n @if (resolvedTicks().length > 0) {\n <div class=\"wm-slider-ticks\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n <span\n class=\"wm-slider-tick\"\n [class.wm-slider-tick--minor]=\"tick.level === 'minor'\"\n [class.is-passed]=\"tick.passed\"\n [style.left]=\"tick.left\"\n ></span>\n }\n </div>\n }\n\n <!--\n The bounds and the step are bound before the value. The browser sanitizes a\n written value against the bounds the input holds at that moment, so a value\n bound first is clamped or snapped to the old range when both change in one\n render, and it is not written again because its signal did not change.\n -->\n <input\n class=\"wm-slider-input\"\n type=\"range\"\n [min]=\"min()\"\n [max]=\"max()\"\n [step]=\"step() > 0 ? step() : 'any'\"\n [value]=\"value()\"\n [disabled]=\"isDisabled() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-valuetext]=\"valueText()\"\n (input)=\"onRangeInput($event)\"\n (blur)=\"onTouchedFn()\"\n />\n </div>\n\n @if (showValue()) {\n <!--\n The sizers stack on the output in one grid cell, so the column reserves\n the widest string the readout can hold and stops stealing width from the\n rail every time the value changes.\n -->\n <div class=\"wm-slider-value-cell\">\n <output class=\"wm-slider-value\">{{ displayValue() }}</output>\n @for (candidate of valueCandidates(); track candidate) {\n <span class=\"wm-slider-value-sizer\" aria-hidden=\"true\">{{ candidate }}</span>\n }\n </div>\n }\n\n @if (hasTickLabels()) {\n <div class=\"wm-slider-tick-labels\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n @if (tick.label !== undefined) {\n <span class=\"wm-slider-tick-label\" [style.left]=\"tick.left\">{{ tick.label }}</span>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:block}.wm-slider{--wm-slider-thumb: 16px;--wm-slider-track: 4px;--wm-slider-gap: 3px;--wm-slider-p: 0;--wm-slider-stop: calc( var(--wm-slider-thumb) / 2 + var(--wm-slider-p) * (100% - var(--wm-slider-thumb)) );--wm-slider-fill: linear-gradient( to right, var(--wm-color-accent) 0 var(--wm-slider-stop), var(--wm-border-input) var(--wm-slider-stop) 100% );display:grid;grid-template-columns:1fr auto;align-items:center;column-gap:.75rem;row-gap:.375rem}.wm-slider-rail{position:relative;min-width:0}.wm-slider.has-ticks .wm-slider-rail{margin-bottom:10px}.wm-slider-input{-webkit-appearance:none;appearance:none;display:block;width:100%;height:var(--wm-slider-thumb);margin:0;padding:0;background:transparent;cursor:pointer;position:relative;z-index:1}.wm-slider-input:focus{outline:none}.wm-slider-input::-webkit-slider-runnable-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-webkit-slider-thumb{-webkit-appearance:none;width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);margin-top:calc((var(--wm-slider-track) - var(--wm-slider-thumb)) / 2);border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-webkit-slider-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-input::-moz-range-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-moz-range-thumb{width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);box-sizing:border-box;border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-moz-range-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-moz-range-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-ticks{position:absolute;inset-inline:0;top:100%;margin-top:2px;height:6px;pointer-events:none}.wm-slider-tick{position:absolute;top:0;width:2px;height:6px;margin-left:-1px;border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-tick.is-passed{background:var(--wm-color-accent)}.wm-slider-tick--minor{height:3px;background:var(--wm-border-subtle)}.wm-slider-tick--minor.is-passed{background:color-mix(in srgb,var(--wm-color-accent) 55%,transparent)}.wm-slider[data-track=segmented]{--wm-slider-fill: none}.wm-slider-segments{position:absolute;inset:0 calc(var(--wm-slider-thumb) / 2);display:flex;align-items:center;gap:var(--wm-slider-gap);pointer-events:none}.wm-slider-seg{flex-basis:0;min-width:0;height:var(--wm-slider-track);border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-seg.is-passed{background:var(--wm-color-accent)}.wm-slider-seg.is-current{background-image:linear-gradient(var(--wm-color-accent) 0 0);background-repeat:no-repeat;background-size:calc(var(--wm-slider-seg-p, 0) * 100%) 100%}.wm-slider-tick-labels{position:relative;grid-column:1;height:1rem}.wm-slider-tick-label{position:absolute;top:0;transform:translate(-50%);font-size:.75rem;color:var(--wm-text-muted);white-space:nowrap}.wm-slider-value-cell{grid-area:1 / 2;display:grid;justify-items:end}.wm-slider-value,.wm-slider-value-sizer{grid-area:1 / 1;font-family:var(--wm-font-mono);font-size:.8125rem;font-variant-numeric:tabular-nums;white-space:nowrap}.wm-slider-value{color:var(--wm-text-secondary);min-width:4ch;text-align:right}.wm-slider-value-sizer{visibility:hidden;pointer-events:none}.wm-slider.is-disabled{opacity:.5}.wm-slider-input:disabled{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.wm-slider-input::-webkit-slider-thumb,.wm-slider-input::-moz-range-thumb,.wm-slider-tick{transition:none}.wm-slider-input:hover::-webkit-slider-thumb,.wm-slider-input:hover::-moz-range-thumb{transform:none}}\n"] });
1314
1368
  }
1315
1369
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmSlider, decorators: [{
1316
1370
  type: Component,
@@ -1320,7 +1374,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImpo
1320
1374
  useExisting: forwardRef(() => WmSlider),
1321
1375
  multi: true,
1322
1376
  },
1323
- ], template: "<!--\n The native <input type=\"range\"> is the control, not a decoration over one:\n its track and thumb are the styled surfaces. --wm-slider-p carries the thumb\n position to the CSS so the filled part of the track, every tick and every\n block are placed from the same number the thumb is.\n-->\n<div\n class=\"wm-slider\"\n [attr.data-track]=\"track()\"\n [class.is-disabled]=\"isDisabled()\"\n [class.has-ticks]=\"resolvedTicks().length > 0\"\n [style.--wm-slider-p]=\"fraction()\"\n>\n <div class=\"wm-slider-rail\">\n @if (segments().length > 0) {\n <!--\n Blocks sit under the transparent native track. They are bounded by the\n same resolved grid the marks come from, so a block and the mark on its\n edge can never disagree about where a value falls.\n -->\n <div class=\"wm-slider-segments\" aria-hidden=\"true\">\n @for (segment of segments(); track segment.from) {\n <span\n class=\"wm-slider-seg\"\n [class.is-passed]=\"segment.passed\"\n [class.is-current]=\"segment.current\"\n [style.flexGrow]=\"segment.span\"\n [style.--wm-slider-seg-p]=\"segment.fill\"\n ></span>\n }\n </div>\n }\n\n @if (resolvedTicks().length > 0) {\n <div class=\"wm-slider-ticks\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n <span\n class=\"wm-slider-tick\"\n [class.wm-slider-tick--minor]=\"tick.level === 'minor'\"\n [class.is-passed]=\"tick.passed\"\n [style.left]=\"tick.left\"\n ></span>\n }\n </div>\n }\n\n <input\n class=\"wm-slider-input\"\n type=\"range\"\n [value]=\"value()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [step]=\"step() > 0 ? step() : 'any'\"\n [disabled]=\"isDisabled() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-valuetext]=\"valueText()\"\n (input)=\"onRangeInput($event)\"\n (blur)=\"onTouchedFn()\"\n />\n </div>\n\n @if (showValue()) {\n <!--\n The sizers stack on the output in one grid cell, so the column reserves\n the widest string the readout can hold and stops stealing width from the\n rail every time the value changes.\n -->\n <div class=\"wm-slider-value-cell\">\n <output class=\"wm-slider-value\">{{ displayValue() }}</output>\n @for (candidate of valueCandidates(); track candidate) {\n <span class=\"wm-slider-value-sizer\" aria-hidden=\"true\">{{ candidate }}</span>\n }\n </div>\n }\n\n @if (hasTickLabels()) {\n <div class=\"wm-slider-tick-labels\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n @if (tick.label !== undefined) {\n <span class=\"wm-slider-tick-label\" [style.left]=\"tick.left\">{{ tick.label }}</span>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:block}.wm-slider{--wm-slider-thumb: 16px;--wm-slider-track: 4px;--wm-slider-gap: 3px;--wm-slider-p: 0;--wm-slider-stop: calc( var(--wm-slider-thumb) / 2 + var(--wm-slider-p) * (100% - var(--wm-slider-thumb)) );--wm-slider-fill: linear-gradient( to right, var(--wm-color-accent) 0 var(--wm-slider-stop), var(--wm-border-input) var(--wm-slider-stop) 100% );display:grid;grid-template-columns:1fr auto;align-items:center;column-gap:.75rem;row-gap:.375rem}.wm-slider-rail{position:relative;min-width:0}.wm-slider.has-ticks .wm-slider-rail{margin-bottom:10px}.wm-slider-input{-webkit-appearance:none;appearance:none;display:block;width:100%;height:var(--wm-slider-thumb);margin:0;padding:0;background:transparent;cursor:pointer;position:relative;z-index:1}.wm-slider-input:focus{outline:none}.wm-slider-input::-webkit-slider-runnable-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-webkit-slider-thumb{-webkit-appearance:none;width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);margin-top:calc((var(--wm-slider-track) - var(--wm-slider-thumb)) / 2);border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-webkit-slider-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-input::-moz-range-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-moz-range-thumb{width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);box-sizing:border-box;border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-moz-range-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-moz-range-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-ticks{position:absolute;inset-inline:0;top:100%;margin-top:2px;height:6px;pointer-events:none}.wm-slider-tick{position:absolute;top:0;width:2px;height:6px;margin-left:-1px;border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-tick.is-passed{background:var(--wm-color-accent)}.wm-slider-tick--minor{height:3px;background:var(--wm-border-subtle)}.wm-slider-tick--minor.is-passed{background:color-mix(in srgb,var(--wm-color-accent) 55%,transparent)}.wm-slider[data-track=segmented]{--wm-slider-fill: none}.wm-slider-segments{position:absolute;inset:0 calc(var(--wm-slider-thumb) / 2);display:flex;align-items:center;gap:var(--wm-slider-gap);pointer-events:none}.wm-slider-seg{flex-basis:0;min-width:0;height:var(--wm-slider-track);border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-seg.is-passed{background:var(--wm-color-accent)}.wm-slider-seg.is-current{background-image:linear-gradient(var(--wm-color-accent) 0 0);background-repeat:no-repeat;background-size:calc(var(--wm-slider-seg-p, 0) * 100%) 100%}.wm-slider-tick-labels{position:relative;grid-column:1;height:1rem}.wm-slider-tick-label{position:absolute;top:0;transform:translate(-50%);font-size:.75rem;color:var(--wm-text-muted);white-space:nowrap}.wm-slider-value-cell{grid-area:1 / 2;display:grid;justify-items:end}.wm-slider-value,.wm-slider-value-sizer{grid-area:1 / 1;font-family:var(--wm-font-mono);font-size:.8125rem;font-variant-numeric:tabular-nums;white-space:nowrap}.wm-slider-value{color:var(--wm-text-secondary);min-width:4ch;text-align:right}.wm-slider-value-sizer{visibility:hidden;pointer-events:none}.wm-slider.is-disabled{opacity:.5}.wm-slider-input:disabled{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.wm-slider-input::-webkit-slider-thumb,.wm-slider-input::-moz-range-thumb,.wm-slider-tick{transition:none}.wm-slider-input:hover::-webkit-slider-thumb,.wm-slider-input:hover::-moz-range-thumb{transform:none}}\n"] }]
1377
+ ], template: "<!--\n The native <input type=\"range\"> is the control, not a decoration over one:\n its track and thumb are the styled surfaces. --wm-slider-p carries the thumb\n position to the CSS so the filled part of the track, every tick and every\n block are placed from the same number the thumb is.\n-->\n<div\n class=\"wm-slider\"\n [attr.data-track]=\"track()\"\n [class.is-disabled]=\"isDisabled()\"\n [class.has-ticks]=\"resolvedTicks().length > 0\"\n [style.--wm-slider-p]=\"fraction()\"\n>\n <div class=\"wm-slider-rail\">\n @if (segments().length > 0) {\n <!--\n Blocks sit under the transparent native track. They are bounded by the\n same resolved grid the marks come from, so a block and the mark on its\n edge can never disagree about where a value falls.\n -->\n <div class=\"wm-slider-segments\" aria-hidden=\"true\">\n @for (segment of segments(); track segment.from) {\n <span\n class=\"wm-slider-seg\"\n [class.is-passed]=\"segment.passed\"\n [class.is-current]=\"segment.current\"\n [style.flexGrow]=\"segment.span\"\n [style.--wm-slider-seg-p]=\"segment.fill\"\n ></span>\n }\n </div>\n }\n\n @if (resolvedTicks().length > 0) {\n <div class=\"wm-slider-ticks\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n <span\n class=\"wm-slider-tick\"\n [class.wm-slider-tick--minor]=\"tick.level === 'minor'\"\n [class.is-passed]=\"tick.passed\"\n [style.left]=\"tick.left\"\n ></span>\n }\n </div>\n }\n\n <!--\n The bounds and the step are bound before the value. The browser sanitizes a\n written value against the bounds the input holds at that moment, so a value\n bound first is clamped or snapped to the old range when both change in one\n render, and it is not written again because its signal did not change.\n -->\n <input\n class=\"wm-slider-input\"\n type=\"range\"\n [min]=\"min()\"\n [max]=\"max()\"\n [step]=\"step() > 0 ? step() : 'any'\"\n [value]=\"value()\"\n [disabled]=\"isDisabled() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-valuetext]=\"valueText()\"\n (input)=\"onRangeInput($event)\"\n (blur)=\"onTouchedFn()\"\n />\n </div>\n\n @if (showValue()) {\n <!--\n The sizers stack on the output in one grid cell, so the column reserves\n the widest string the readout can hold and stops stealing width from the\n rail every time the value changes.\n -->\n <div class=\"wm-slider-value-cell\">\n <output class=\"wm-slider-value\">{{ displayValue() }}</output>\n @for (candidate of valueCandidates(); track candidate) {\n <span class=\"wm-slider-value-sizer\" aria-hidden=\"true\">{{ candidate }}</span>\n }\n </div>\n }\n\n @if (hasTickLabels()) {\n <div class=\"wm-slider-tick-labels\" aria-hidden=\"true\">\n @for (tick of resolvedTicks(); track tick.value) {\n @if (tick.label !== undefined) {\n <span class=\"wm-slider-tick-label\" [style.left]=\"tick.left\">{{ tick.label }}</span>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:block}.wm-slider{--wm-slider-thumb: 16px;--wm-slider-track: 4px;--wm-slider-gap: 3px;--wm-slider-p: 0;--wm-slider-stop: calc( var(--wm-slider-thumb) / 2 + var(--wm-slider-p) * (100% - var(--wm-slider-thumb)) );--wm-slider-fill: linear-gradient( to right, var(--wm-color-accent) 0 var(--wm-slider-stop), var(--wm-border-input) var(--wm-slider-stop) 100% );display:grid;grid-template-columns:1fr auto;align-items:center;column-gap:.75rem;row-gap:.375rem}.wm-slider-rail{position:relative;min-width:0}.wm-slider.has-ticks .wm-slider-rail{margin-bottom:10px}.wm-slider-input{-webkit-appearance:none;appearance:none;display:block;width:100%;height:var(--wm-slider-thumb);margin:0;padding:0;background:transparent;cursor:pointer;position:relative;z-index:1}.wm-slider-input:focus{outline:none}.wm-slider-input::-webkit-slider-runnable-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-webkit-slider-thumb{-webkit-appearance:none;width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);margin-top:calc((var(--wm-slider-track) - var(--wm-slider-thumb)) / 2);border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-webkit-slider-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-input::-moz-range-track{height:var(--wm-slider-track);border-radius:var(--wm-radius-full);background:var(--wm-slider-fill)}.wm-slider-input::-moz-range-thumb{width:var(--wm-slider-thumb);height:var(--wm-slider-thumb);box-sizing:border-box;border-radius:var(--wm-radius-full);background:var(--wm-color-accent);border:2px solid var(--wm-bg-base);box-shadow:var(--wm-shadow-sm);transition:background var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-slider-input:hover::-moz-range-thumb{background:var(--wm-color-accent-hover);transform:scale(1.12)}.wm-slider-input:focus-visible::-moz-range-thumb{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-slider-ticks{position:absolute;inset-inline:0;top:100%;margin-top:2px;height:6px;pointer-events:none}.wm-slider-tick{position:absolute;top:0;width:2px;height:6px;margin-left:-1px;border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-tick.is-passed{background:var(--wm-color-accent)}.wm-slider-tick--minor{height:3px;background:var(--wm-border-subtle)}.wm-slider-tick--minor.is-passed{background:color-mix(in srgb,var(--wm-color-accent) 55%,transparent)}.wm-slider[data-track=segmented]{--wm-slider-fill: none}.wm-slider-segments{position:absolute;inset:0 calc(var(--wm-slider-thumb) / 2);display:flex;align-items:center;gap:var(--wm-slider-gap);pointer-events:none}.wm-slider-seg{flex-basis:0;min-width:0;height:var(--wm-slider-track);border-radius:1px;background:var(--wm-border-input);transition:background var(--wm-transition-fast)}.wm-slider-seg.is-passed{background:var(--wm-color-accent)}.wm-slider-seg.is-current{background-image:linear-gradient(var(--wm-color-accent) 0 0);background-repeat:no-repeat;background-size:calc(var(--wm-slider-seg-p, 0) * 100%) 100%}.wm-slider-tick-labels{position:relative;grid-column:1;height:1rem}.wm-slider-tick-label{position:absolute;top:0;transform:translate(-50%);font-size:.75rem;color:var(--wm-text-muted);white-space:nowrap}.wm-slider-value-cell{grid-area:1 / 2;display:grid;justify-items:end}.wm-slider-value,.wm-slider-value-sizer{grid-area:1 / 1;font-family:var(--wm-font-mono);font-size:.8125rem;font-variant-numeric:tabular-nums;white-space:nowrap}.wm-slider-value{color:var(--wm-text-secondary);min-width:4ch;text-align:right}.wm-slider-value-sizer{visibility:hidden;pointer-events:none}.wm-slider.is-disabled{opacity:.5}.wm-slider-input:disabled{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.wm-slider-input::-webkit-slider-thumb,.wm-slider-input::-moz-range-thumb,.wm-slider-tick{transition:none}.wm-slider-input:hover::-webkit-slider-thumb,.wm-slider-input:hover::-moz-range-thumb{transform:none}}\n"] }]
1324
1378
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], formatValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatValue", required: false }] }], track: [{ type: i0.Input, args: [{ isSignal: true, alias: "track", required: false }] }], ticks: [{ type: i0.Input, args: [{ isSignal: true, alias: "ticks", required: false }] }] } });
1325
1379
  /**
1326
1380
  * A stop every `interval`, starting at `min` and never passing `max`. Returns