@wildmason/aegis 1.13.0 → 1.14.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.
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { CdkTrapFocus } from '@angular/cdk/a11y';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { input, output, HostListener, ChangeDetectionStrategy, Component, model, signal, computed, inject, ElementRef, Renderer2, ChangeDetectorRef, viewChild, effect, ViewEncapsulation, Input, Directive, contentChildren, forwardRef, ViewChild, Injectable } from '@angular/core';
|
|
4
|
-
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
3
|
+
import { input, output, HostListener, ChangeDetectionStrategy, Component, model, signal, computed, inject, ElementRef, Renderer2, ChangeDetectorRef, viewChild, effect, ViewEncapsulation, Input, Directive, contentChildren, forwardRef, ViewChild, Injectable, Injector, DestroyRef } from '@angular/core';
|
|
4
|
+
import { CdkConnectedOverlay, CdkOverlayOrigin, createFlexibleConnectedPositionStrategy, createOverlayRef, createRepositionScrollStrategy } from '@angular/cdk/overlay';
|
|
5
5
|
import { NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
7
7
|
import { Subject, debounceTime, switchMap, of, catchError, finalize } from 'rxjs';
|
|
8
8
|
import * as i1 from '@angular/cdk/scrolling';
|
|
9
9
|
import { ScrollingModule, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
10
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
10
11
|
import { invoke } from '@tauri-apps/api/core';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -60,11 +61,11 @@ class WmModal {
|
|
|
60
61
|
this.dismiss.emit();
|
|
61
62
|
}
|
|
62
63
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
63
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: WmModal, isStandalone: true, selector: "wm-modal", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@if (visible()) {\
|
|
64
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: WmModal, isStandalone: true, selector: "wm-modal", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@if (visible()) {\n <div [class]=\"align() === 'top'\n ? 'fixed inset-0 flex items-start justify-center pt-20 z-50'\n : 'fixed inset-0 flex items-center justify-center z-50'\"\n style=\"background: var(--wm-modal-backdrop)\"\n (click)=\"dismiss.emit()\">\n <!-- The trap must sit on an element this template owns, inside this `@if`, so it\n has a parentNode by the time CdkTrapFocus runs ngAfterContentInit. A consumer\n cannot achieve that on projected content \u2014 see the class doc. -->\n <div (click)=\"$event.stopPropagation()\" cdkTrapFocus [cdkTrapFocusAutoCapture]=\"true\">\n <ng-content />\n </div>\n </div>\n}\n", dependencies: [{ kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
64
65
|
}
|
|
65
66
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmModal, decorators: [{
|
|
66
67
|
type: Component,
|
|
67
|
-
args: [{ selector: 'wm-modal', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CdkTrapFocus], template: "@if (visible()) {\
|
|
68
|
+
args: [{ selector: 'wm-modal', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CdkTrapFocus], template: "@if (visible()) {\n <div [class]=\"align() === 'top'\n ? 'fixed inset-0 flex items-start justify-center pt-20 z-50'\n : 'fixed inset-0 flex items-center justify-center z-50'\"\n style=\"background: var(--wm-modal-backdrop)\"\n (click)=\"dismiss.emit()\">\n <!-- The trap must sit on an element this template owns, inside this `@if`, so it\n has a parentNode by the time CdkTrapFocus runs ngAfterContentInit. A consumer\n cannot achieve that on projected content \u2014 see the class doc. -->\n <div (click)=\"$event.stopPropagation()\" cdkTrapFocus [cdkTrapFocusAutoCapture]=\"true\">\n <ng-content />\n </div>\n </div>\n}\n" }]
|
|
68
69
|
}], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: true }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], onEscape: [{
|
|
69
70
|
type: HostListener,
|
|
70
71
|
args: ['document:keydown.escape']
|
|
@@ -110,16 +111,110 @@ class WmDialog {
|
|
|
110
111
|
this.close();
|
|
111
112
|
}
|
|
112
113
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
113
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: WmDialog, isStandalone: true, selector: "wm-dialog", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@if (isOpen()) {\
|
|
114
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: WmDialog, isStandalone: true, selector: "wm-dialog", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@if (isOpen()) {\n <!-- Backdrop \u2014 click to close -->\n <div class=\"wm-dialog-backdrop\" (click)=\"close()\" aria-hidden=\"true\"></div>\n\n <!-- Panel -->\n <div\n class=\"wm-dialog\"\n [class]=\"'wm-dialog--' + size()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"title() ? 'wm-dialog-title' : null\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n <div class=\"wm-dialog-header\">\n <h2 class=\"wm-dialog-title\" id=\"wm-dialog-title\">{{ title() }}</h2>\n <button\n class=\"wm-dialog-close\"\n type=\"button\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <div class=\"wm-dialog-body\">\n <ng-content select=\"[body]\" />\n </div>\n </div>\n}\n", styles: [":host{display:contents}.wm-dialog-backdrop{position:fixed;inset:0;background:rgba(0,0,0,var(--wm-backdrop-opacity));z-index:100}.wm-dialog{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:101;background:var(--wm-bg-float);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:var(--wm-shadow-lg);width:var(--wm-dialog-width, 480px);max-width:calc(100vw - 2rem);max-height:calc(100dvh - 4rem);display:flex;flex-direction:column;overflow:hidden;outline:none}.wm-dialog--sm{width:var(--wm-dialog-width, 360px)}.wm-dialog--lg{width:var(--wm-dialog-width, 640px)}.wm-dialog-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:1.125rem 1.25rem;flex-shrink:0;background:var(--wm-bg-raised)}.wm-dialog-title{font-size:1rem;font-weight:600;color:var(--wm-text-primary);margin:0;line-height:1.3}.wm-dialog-close{display:flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;border-radius:var(--wm-radius-sm);border:none;background:transparent;color:var(--wm-text-muted);cursor:pointer;transition:background var(--wm-transition-fast),color var(--wm-transition-fast)}.wm-dialog-close:hover{background:var(--wm-bg-hover);color:var(--wm-text-primary)}.wm-dialog-close:focus-visible{outline:2px solid var(--wm-color-accent);outline-offset:1px}.wm-dialog-body{flex:1;overflow-y:auto;padding:1rem 1.25rem 1.25rem;font-size:.875rem;color:var(--wm-text-secondary);line-height:1.6}@media(prefers-reduced-motion:reduce){.wm-dialog-close{transition:none}}\n"], dependencies: [{ kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }] });
|
|
114
115
|
}
|
|
115
116
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmDialog, decorators: [{
|
|
116
117
|
type: Component,
|
|
117
|
-
args: [{ selector: 'wm-dialog', imports: [CdkTrapFocus], template: "@if (isOpen()) {\
|
|
118
|
+
args: [{ selector: 'wm-dialog', imports: [CdkTrapFocus], template: "@if (isOpen()) {\n <!-- Backdrop \u2014 click to close -->\n <div class=\"wm-dialog-backdrop\" (click)=\"close()\" aria-hidden=\"true\"></div>\n\n <!-- Panel -->\n <div\n class=\"wm-dialog\"\n [class]=\"'wm-dialog--' + size()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"title() ? 'wm-dialog-title' : null\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n <div class=\"wm-dialog-header\">\n <h2 class=\"wm-dialog-title\" id=\"wm-dialog-title\">{{ title() }}</h2>\n <button\n class=\"wm-dialog-close\"\n type=\"button\"\n (click)=\"close()\"\n aria-label=\"Close dialog\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <div class=\"wm-dialog-body\">\n <ng-content select=\"[body]\" />\n </div>\n </div>\n}\n", styles: [":host{display:contents}.wm-dialog-backdrop{position:fixed;inset:0;background:rgba(0,0,0,var(--wm-backdrop-opacity));z-index:100}.wm-dialog{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:101;background:var(--wm-bg-float);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:var(--wm-shadow-lg);width:var(--wm-dialog-width, 480px);max-width:calc(100vw - 2rem);max-height:calc(100dvh - 4rem);display:flex;flex-direction:column;overflow:hidden;outline:none}.wm-dialog--sm{width:var(--wm-dialog-width, 360px)}.wm-dialog--lg{width:var(--wm-dialog-width, 640px)}.wm-dialog-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:1.125rem 1.25rem;flex-shrink:0;background:var(--wm-bg-raised)}.wm-dialog-title{font-size:1rem;font-weight:600;color:var(--wm-text-primary);margin:0;line-height:1.3}.wm-dialog-close{display:flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;border-radius:var(--wm-radius-sm);border:none;background:transparent;color:var(--wm-text-muted);cursor:pointer;transition:background var(--wm-transition-fast),color var(--wm-transition-fast)}.wm-dialog-close:hover{background:var(--wm-bg-hover);color:var(--wm-text-primary)}.wm-dialog-close:focus-visible{outline:2px solid var(--wm-color-accent);outline-offset:1px}.wm-dialog-body{flex:1;overflow-y:auto;padding:1rem 1.25rem 1.25rem;font-size:.875rem;color:var(--wm-text-secondary);line-height:1.6}@media(prefers-reduced-motion:reduce){.wm-dialog-close{transition:none}}\n"] }]
|
|
118
119
|
}], propDecorators: { isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], onEscape: [{
|
|
119
120
|
type: HostListener,
|
|
120
121
|
args: ['document:keydown.escape']
|
|
121
122
|
}] } });
|
|
122
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Notify a handler whenever anything that can move an anchored overlay moves.
|
|
126
|
+
*
|
|
127
|
+
* This exists because of one property of the DOM that CDK's `ScrollDispatcher`
|
|
128
|
+
* does not account for: **`scroll` does not bubble.**
|
|
129
|
+
*
|
|
130
|
+
* `ScrollDispatcher` has exactly two sources. It listens for `scroll` on the
|
|
131
|
+
* document in the *bubbling* phase, which therefore only ever hears the
|
|
132
|
+
* document itself scrolling; and it hears containers that registered
|
|
133
|
+
* themselves through a `cdkScrollable` directive. In an application shell that
|
|
134
|
+
* is a fixed-height frame with an inner scrolling element — which is every
|
|
135
|
+
* Wildmason product — the document never scrolls and nothing is registered, so
|
|
136
|
+
* both sources are empty and any scroll strategy built on the dispatcher is a
|
|
137
|
+
* silent no-op. The overlay keeps the coordinates it was born with while its
|
|
138
|
+
* trigger scrolls out from under it.
|
|
139
|
+
*
|
|
140
|
+
* A **capture-phase** listener on the document sees the event from any
|
|
141
|
+
* descendant, so nothing has to be annotated. Requiring every consumer to mark
|
|
142
|
+
* their scroll containers — and failing invisibly when they forget — is not a
|
|
143
|
+
* contract a design system should hand out.
|
|
144
|
+
*
|
|
145
|
+
* @param handler called on any scroll or resize while the subscription is live
|
|
146
|
+
* @returns a function that removes exactly what was added
|
|
147
|
+
*/
|
|
148
|
+
function onViewportChange(handler) {
|
|
149
|
+
// The capture flag must match between add and remove. A listener added with
|
|
150
|
+
// capture and removed without it is never removed at all, and nothing
|
|
151
|
+
// observable goes wrong until every scroll on the page is paying for
|
|
152
|
+
// overlays that closed long ago.
|
|
153
|
+
document.addEventListener('scroll', handler, true);
|
|
154
|
+
window.addEventListener('resize', handler);
|
|
155
|
+
return () => {
|
|
156
|
+
document.removeEventListener('scroll', handler, true);
|
|
157
|
+
window.removeEventListener('resize', handler);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A scroll strategy that keeps a connected overlay anchored to its trigger,
|
|
163
|
+
* including when the thing that scrolled was an ordinary `overflow: auto`
|
|
164
|
+
* element rather than the document.
|
|
165
|
+
*
|
|
166
|
+
* CDK's own reposition strategy does the right thing and is simply never told:
|
|
167
|
+
* it listens through `ScrollDispatcher`, which cannot hear a scroll from a
|
|
168
|
+
* container that is not marked `cdkScrollable`. See `onViewportChange` for the
|
|
169
|
+
* full account. This strategy listens in the capture phase instead, so the
|
|
170
|
+
* consumer annotates nothing.
|
|
171
|
+
*/
|
|
172
|
+
class WmAnchoredScrollStrategy {
|
|
173
|
+
overlayRef = null;
|
|
174
|
+
release = null;
|
|
175
|
+
attach(overlayRef) {
|
|
176
|
+
this.overlayRef = overlayRef;
|
|
177
|
+
}
|
|
178
|
+
enable() {
|
|
179
|
+
// enable() is called on every attach and can arrive more than once for one
|
|
180
|
+
// overlay; a second subscription would reposition twice per scroll event.
|
|
181
|
+
if (this.release)
|
|
182
|
+
return;
|
|
183
|
+
this.release = onViewportChange(() => {
|
|
184
|
+
if (this.overlayRef?.hasAttached())
|
|
185
|
+
this.overlayRef.updatePosition();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
disable() {
|
|
189
|
+
this.release?.();
|
|
190
|
+
this.release = null;
|
|
191
|
+
}
|
|
192
|
+
detach() {
|
|
193
|
+
this.disable();
|
|
194
|
+
this.overlayRef = null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/*
|
|
198
|
+
* Why each component holds its own instance rather than an application-wide
|
|
199
|
+
* provider supplying one:
|
|
200
|
+
*
|
|
201
|
+
* A ScrollStrategy is inherently per-overlay. `attach()` stores a single
|
|
202
|
+
* `overlayRef`, and `enable()` / `disable()` take no argument, so one shared
|
|
203
|
+
* instance cannot tell two open overlays apart - a select inside a popover
|
|
204
|
+
* would leave one of them anchored to the other. CDK's own strategies are
|
|
205
|
+
* built by a factory for exactly this reason, and the only public token in
|
|
206
|
+
* CDK 21, `CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG`, carries an *instance*.
|
|
207
|
+
*
|
|
208
|
+
* So `wm-popover`, `wm-select` and `wm-combobox` each construct one. That also
|
|
209
|
+
* makes the fix arrive with no setup: a consumer upgrading gets anchored
|
|
210
|
+
* overlays without adding a provider, and an application that declares its own
|
|
211
|
+
* `cdkConnectedOverlay` can pass `new WmAnchoredScrollStrategy()` to
|
|
212
|
+
* `[cdkConnectedOverlayScrollStrategy]` the same way.
|
|
213
|
+
*
|
|
214
|
+
* `[wmTooltip]` does not use this - it owns its overlay directly and anchors
|
|
215
|
+
* itself, including closing when its trigger is scrolled out of view.
|
|
216
|
+
*/
|
|
217
|
+
|
|
123
218
|
/**
|
|
124
219
|
* WmSelect — Accessible dropdown select built on Angular CDK Overlay.
|
|
125
220
|
*
|
|
@@ -153,6 +248,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
153
248
|
* - Always pair with a visible label via <wm-label> or aria-label on the host
|
|
154
249
|
*/
|
|
155
250
|
class WmSelect {
|
|
251
|
+
/**
|
|
252
|
+
* Keeps the panel anchored to its trigger while an ancestor scrolls.
|
|
253
|
+
*
|
|
254
|
+
* One instance per component, because a ScrollStrategy holds a single
|
|
255
|
+
* overlayRef and could not tell two open overlays apart. Without it the
|
|
256
|
+
* panel takes CDK's default, which cannot hear a scroll from any container
|
|
257
|
+
* that is not marked `cdkScrollable` - so in a fixed-height shell the panel
|
|
258
|
+
* keeps the coordinates it was born with while the trigger scrolls away.
|
|
259
|
+
*/
|
|
260
|
+
scrollStrategy = new WmAnchoredScrollStrategy();
|
|
156
261
|
/** Array of options to display. */
|
|
157
262
|
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
158
263
|
/** The currently selected value. Two-way bindable. */
|
|
@@ -183,11 +288,11 @@ class WmSelect {
|
|
|
183
288
|
}
|
|
184
289
|
}
|
|
185
290
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
186
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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 -->\
|
|
291
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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"] }] });
|
|
187
292
|
}
|
|
188
293
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmSelect, decorators: [{
|
|
189
294
|
type: Component,
|
|
190
|
-
args: [{ selector: 'wm-select', imports: [CdkConnectedOverlay, CdkOverlayOrigin, CdkTrapFocus], template: "<!-- Trigger button \u2014 anchors the CDK overlay -->\
|
|
295
|
+
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"] }]
|
|
191
296
|
}], 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 }] }] } });
|
|
192
297
|
|
|
193
298
|
/**
|
|
@@ -230,6 +335,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
230
335
|
* Import WmAutoFocus from @wildmason/aegis/components and add it to your component's imports.
|
|
231
336
|
*/
|
|
232
337
|
class WmPopover {
|
|
338
|
+
/**
|
|
339
|
+
* Keeps the panel anchored to its trigger while an ancestor scrolls.
|
|
340
|
+
*
|
|
341
|
+
* One instance per component, because a ScrollStrategy holds a single
|
|
342
|
+
* overlayRef and could not tell two open overlays apart. Without it the
|
|
343
|
+
* panel takes CDK's default, which cannot hear a scroll from any container
|
|
344
|
+
* that is not marked `cdkScrollable` - so in a fixed-height shell the panel
|
|
345
|
+
* keeps the coordinates it was born with while the trigger scrolls away.
|
|
346
|
+
*/
|
|
347
|
+
scrollStrategy = new WmAnchoredScrollStrategy();
|
|
233
348
|
/** Whether the panel is currently open. */
|
|
234
349
|
isOpen = input(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
235
350
|
/** Panel width in pixels, or 'auto' to match content. */
|
|
@@ -318,11 +433,11 @@ class WmPopover {
|
|
|
318
433
|
this.closed.emit();
|
|
319
434
|
}
|
|
320
435
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmPopover, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
321
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.1", type: WmPopover, isStandalone: true, selector: "wm-popover", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hasPopup: { classPropertyName: "hasPopup", publicName: "hasPopup", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, trapFocusAutoCapture: { classPropertyName: "trapFocusAutoCapture", publicName: "trapFocusAutoCapture", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Trigger anchor -->\n<div cdkOverlayOrigin #origin=\"cdkOverlayOrigin\" class=\"wm-popover-trigger\">\n <ng-content select=\"[trigger]\"></ng-content>\n</div>\n\n<!-- Floating panel -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (backdropClick)=\"onClose()\"\n (detach)=\"onClose()\"\n (attach)=\"onAttach()\"\n>\n <div\n #panel\n [id]=\"panelId\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"wm-popover-panel\"\n [style.width]=\"width() === 'auto' ? 'auto' : width() + 'px'\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"trapFocusAutoCapture()\"\n (keydown.escape)=\"onClose()\"\n >\n <ng-content select=\"[content]\"></ng-content>\n </div>\n</ng-template>\n", styles: ["wm-popover{display:inline-flex;align-items:center}.wm-popover-trigger{display:block;width:100%}.wm-popover-panel{background:var(--wm-bg-raised);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #00000024;overflow:hidden;outline:none}\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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
436
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.1", type: WmPopover, isStandalone: true, selector: "wm-popover", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, hasPopup: { classPropertyName: "hasPopup", publicName: "hasPopup", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, trapFocusAutoCapture: { classPropertyName: "trapFocusAutoCapture", publicName: "trapFocusAutoCapture", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Trigger anchor -->\n<div cdkOverlayOrigin #origin=\"cdkOverlayOrigin\" class=\"wm-popover-trigger\">\n <ng-content select=\"[trigger]\"></ng-content>\n</div>\n\n<!-- Floating panel -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (backdropClick)=\"onClose()\"\n (detach)=\"onClose()\"\n (attach)=\"onAttach()\"\n>\n <div\n #panel\n [id]=\"panelId\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"wm-popover-panel\"\n [style.width]=\"width() === 'auto' ? 'auto' : width() + 'px'\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"trapFocusAutoCapture()\"\n (keydown.escape)=\"onClose()\"\n >\n <ng-content select=\"[content]\"></ng-content>\n </div>\n</ng-template>\n", styles: ["wm-popover{display:inline-flex;align-items:center}.wm-popover-trigger{display:block;width:100%}.wm-popover-panel{background:var(--wm-bg-raised);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #00000024;overflow:hidden;outline:none}\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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
322
437
|
}
|
|
323
438
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmPopover, decorators: [{
|
|
324
439
|
type: Component,
|
|
325
|
-
args: [{ selector: 'wm-popover', imports: [CdkConnectedOverlay, CdkOverlayOrigin, CdkTrapFocus], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<!-- Trigger anchor -->\n<div cdkOverlayOrigin #origin=\"cdkOverlayOrigin\" class=\"wm-popover-trigger\">\n <ng-content select=\"[trigger]\"></ng-content>\n</div>\n\n<!-- Floating panel -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (backdropClick)=\"onClose()\"\n (detach)=\"onClose()\"\n (attach)=\"onAttach()\"\n>\n <div\n #panel\n [id]=\"panelId\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"wm-popover-panel\"\n [style.width]=\"width() === 'auto' ? 'auto' : width() + 'px'\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"trapFocusAutoCapture()\"\n (keydown.escape)=\"onClose()\"\n >\n <ng-content select=\"[content]\"></ng-content>\n </div>\n</ng-template>\n", styles: ["wm-popover{display:inline-flex;align-items:center}.wm-popover-trigger{display:block;width:100%}.wm-popover-panel{background:var(--wm-bg-raised);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #00000024;overflow:hidden;outline:none}\n"] }]
|
|
440
|
+
args: [{ selector: 'wm-popover', imports: [CdkConnectedOverlay, CdkOverlayOrigin, CdkTrapFocus], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<!-- Trigger anchor -->\n<div cdkOverlayOrigin #origin=\"cdkOverlayOrigin\" class=\"wm-popover-trigger\">\n <ng-content select=\"[trigger]\"></ng-content>\n</div>\n\n<!-- Floating panel -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayScrollStrategy]=\"scrollStrategy\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (backdropClick)=\"onClose()\"\n (detach)=\"onClose()\"\n (attach)=\"onAttach()\"\n>\n <div\n #panel\n [id]=\"panelId\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"wm-popover-panel\"\n [style.width]=\"width() === 'auto' ? 'auto' : width() + 'px'\"\n cdkTrapFocus\n [cdkTrapFocusAutoCapture]=\"trapFocusAutoCapture()\"\n (keydown.escape)=\"onClose()\"\n >\n <ng-content select=\"[content]\"></ng-content>\n </div>\n</ng-template>\n", styles: ["wm-popover{display:inline-flex;align-items:center}.wm-popover-trigger{display:block;width:100%}.wm-popover-panel{background:var(--wm-bg-raised);border:1px solid var(--wm-border);border-radius:var(--wm-radius-lg);box-shadow:0 4px 6px -1px #00000014,0 10px 20px -4px #00000024;overflow:hidden;outline:none}\n"] }]
|
|
326
441
|
}], ctorParameters: () => [], propDecorators: { isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hasPopup: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasPopup", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], trapFocusAutoCapture: [{ type: i0.Input, args: [{ isSignal: true, alias: "trapFocusAutoCapture", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
|
|
327
442
|
|
|
328
443
|
/**
|
|
@@ -448,11 +563,11 @@ class WmTabs {
|
|
|
448
563
|
}
|
|
449
564
|
}
|
|
450
565
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
451
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: WmTabs, isStandalone: true, selector: "wm-tabs", inputs: { activeValue: { classPropertyName: "activeValue", publicName: "activeValue", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, queries: [{ propertyName: "tabs", predicate: WmTab, isSignal: true }], ngImport: i0, template: "<div\
|
|
566
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: WmTabs, isStandalone: true, selector: "wm-tabs", inputs: { activeValue: { classPropertyName: "activeValue", publicName: "activeValue", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, queries: [{ propertyName: "tabs", predicate: WmTab, isSignal: true }], ngImport: i0, template: "<div\n class=\"wm-tabs\"\n [class.wm-tabs--line]=\"variant() === 'line'\"\n [class.wm-tabs--pill]=\"variant() === 'pill'\"\n role=\"tablist\"\n #container\n (keydown)=\"onKeyDown($event, container)\"\n>\n @for (tab of tabs(); track tab.value(); let i = $index) {\n @if (tab.separatorBefore()) {\n <span\n class=\"wm-tab-separator\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n ></span>\n }\n <button\n type=\"button\"\n role=\"tab\"\n class=\"wm-tab\"\n [id]=\"'wm-tab-' + tab.value()\"\n [attr.aria-selected]=\"activeValue() === tab.value()\"\n [attr.aria-controls]=\"'wm-panel-' + tab.value()\"\n [attr.aria-disabled]=\"tab.unavailable() ? true : null\"\n [attr.aria-description]=\"tab.unavailable() ? tab.unavailableReason() : null\"\n [attr.title]=\"tab.unavailable() ? tab.unavailableReason() : null\"\n [tabIndex]=\"(focusedIndex() === null ? activeValue() === tab.value() : focusedIndex() === i) ? 0 : -1\"\n [class.is-active]=\"activeValue() === tab.value()\"\n [class.is-unavailable]=\"tab.unavailable()\"\n (click)=\"selectTab(tab, i)\"\n (focus)=\"focusedIndex.set(i)\"\n (blur)=\"onBlur()\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.tmpl()\" />\n </button>\n }\n</div>\n", styles: [".wm-tab{display:inline-flex;align-items:center;gap:6px;font-size:.8125rem;font-weight:600;background:transparent;border:none;cursor:pointer;white-space:nowrap;transition:color var(--wm-duration-fast) var(--wm-ease-standard),background var(--wm-duration-fast) var(--wm-ease-standard),border-color var(--wm-duration-fast) var(--wm-ease-standard)}.wm-tab:focus-visible{outline:2px solid var(--wm-color-accent);outline-offset:-2px;border-radius:var(--wm-radius-sm)}.wm-tab.is-unavailable{cursor:not-allowed;color:color-mix(in srgb,var(--wm-text-muted) 52%,transparent);box-shadow:none}.wm-tab.is-unavailable:hover{background:transparent;color:color-mix(in srgb,var(--wm-text-muted) 52%,transparent)}.wm-tab-separator{flex:0 0 1px;align-self:center;width:1px;height:1.25rem;margin:0 .375rem;background:var(--wm-border-subtle)}.wm-tabs--line{display:flex;gap:1.5rem;border-bottom:1px solid var(--wm-border-subtle)}.wm-tabs--line .wm-tab{padding:.625rem .25rem;color:var(--wm-text-muted);border-radius:0;border-bottom:2px solid transparent;margin-bottom:-1px}.wm-tabs--line .wm-tab:hover{color:var(--wm-text-secondary)}.wm-tabs--line .wm-tab.is-active{color:var(--wm-color-accent-readable);border-bottom-color:var(--wm-color-accent-readable)}.wm-tabs--pill{display:inline-flex;background:var(--wm-bg-sunken);padding:.25rem;border-radius:var(--wm-radius-md);gap:.125rem;border:1px solid var(--wm-border-subtle)}.wm-tabs--pill .wm-tab{padding:.375rem .75rem;border-radius:var(--wm-radius-sm);color:var(--wm-text-muted)}.wm-tabs--pill .wm-tab:hover{background:color-mix(in srgb,var(--wm-color-accent) 5%,transparent);color:var(--wm-text-secondary)}.wm-tabs--pill .wm-tab.is-active{background:var(--wm-bg-float);color:var(--wm-text-primary);box-shadow:var(--wm-shadow-sm)}.wm-tabs--line .wm-tab.is-unavailable,.wm-tabs--line .wm-tab.is-unavailable:hover,.wm-tabs--pill .wm-tab.is-unavailable,.wm-tabs--pill .wm-tab.is-unavailable:hover{background:transparent;color:color-mix(in srgb,var(--wm-text-muted) 52%,transparent);box-shadow:none}@media(prefers-reduced-motion:reduce){.wm-tab{transition:none}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
452
567
|
}
|
|
453
568
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmTabs, decorators: [{
|
|
454
569
|
type: Component,
|
|
455
|
-
args: [{ selector: 'wm-tabs', imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\
|
|
570
|
+
args: [{ selector: 'wm-tabs', imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"wm-tabs\"\n [class.wm-tabs--line]=\"variant() === 'line'\"\n [class.wm-tabs--pill]=\"variant() === 'pill'\"\n role=\"tablist\"\n #container\n (keydown)=\"onKeyDown($event, container)\"\n>\n @for (tab of tabs(); track tab.value(); let i = $index) {\n @if (tab.separatorBefore()) {\n <span\n class=\"wm-tab-separator\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n ></span>\n }\n <button\n type=\"button\"\n role=\"tab\"\n class=\"wm-tab\"\n [id]=\"'wm-tab-' + tab.value()\"\n [attr.aria-selected]=\"activeValue() === tab.value()\"\n [attr.aria-controls]=\"'wm-panel-' + tab.value()\"\n [attr.aria-disabled]=\"tab.unavailable() ? true : null\"\n [attr.aria-description]=\"tab.unavailable() ? tab.unavailableReason() : null\"\n [attr.title]=\"tab.unavailable() ? tab.unavailableReason() : null\"\n [tabIndex]=\"(focusedIndex() === null ? activeValue() === tab.value() : focusedIndex() === i) ? 0 : -1\"\n [class.is-active]=\"activeValue() === tab.value()\"\n [class.is-unavailable]=\"tab.unavailable()\"\n (click)=\"selectTab(tab, i)\"\n (focus)=\"focusedIndex.set(i)\"\n (blur)=\"onBlur()\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.tmpl()\" />\n </button>\n }\n</div>\n", styles: [".wm-tab{display:inline-flex;align-items:center;gap:6px;font-size:.8125rem;font-weight:600;background:transparent;border:none;cursor:pointer;white-space:nowrap;transition:color var(--wm-duration-fast) var(--wm-ease-standard),background var(--wm-duration-fast) var(--wm-ease-standard),border-color var(--wm-duration-fast) var(--wm-ease-standard)}.wm-tab:focus-visible{outline:2px solid var(--wm-color-accent);outline-offset:-2px;border-radius:var(--wm-radius-sm)}.wm-tab.is-unavailable{cursor:not-allowed;color:color-mix(in srgb,var(--wm-text-muted) 52%,transparent);box-shadow:none}.wm-tab.is-unavailable:hover{background:transparent;color:color-mix(in srgb,var(--wm-text-muted) 52%,transparent)}.wm-tab-separator{flex:0 0 1px;align-self:center;width:1px;height:1.25rem;margin:0 .375rem;background:var(--wm-border-subtle)}.wm-tabs--line{display:flex;gap:1.5rem;border-bottom:1px solid var(--wm-border-subtle)}.wm-tabs--line .wm-tab{padding:.625rem .25rem;color:var(--wm-text-muted);border-radius:0;border-bottom:2px solid transparent;margin-bottom:-1px}.wm-tabs--line .wm-tab:hover{color:var(--wm-text-secondary)}.wm-tabs--line .wm-tab.is-active{color:var(--wm-color-accent-readable);border-bottom-color:var(--wm-color-accent-readable)}.wm-tabs--pill{display:inline-flex;background:var(--wm-bg-sunken);padding:.25rem;border-radius:var(--wm-radius-md);gap:.125rem;border:1px solid var(--wm-border-subtle)}.wm-tabs--pill .wm-tab{padding:.375rem .75rem;border-radius:var(--wm-radius-sm);color:var(--wm-text-muted)}.wm-tabs--pill .wm-tab:hover{background:color-mix(in srgb,var(--wm-color-accent) 5%,transparent);color:var(--wm-text-secondary)}.wm-tabs--pill .wm-tab.is-active{background:var(--wm-bg-float);color:var(--wm-text-primary);box-shadow:var(--wm-shadow-sm)}.wm-tabs--line .wm-tab.is-unavailable,.wm-tabs--line .wm-tab.is-unavailable:hover,.wm-tabs--pill .wm-tab.is-unavailable,.wm-tabs--pill .wm-tab.is-unavailable:hover{background:transparent;color:color-mix(in srgb,var(--wm-text-muted) 52%,transparent);box-shadow:none}@media(prefers-reduced-motion:reduce){.wm-tab{transition:none}}\n"] }]
|
|
456
571
|
}], propDecorators: { activeValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeValue", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], select: [{ type: i0.Output, args: ["select"] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WmTab), { isSignal: true }] }] } });
|
|
457
572
|
|
|
458
573
|
class WmButton {
|
|
@@ -471,11 +586,11 @@ class WmButton {
|
|
|
471
586
|
return this.iconOnly() ? `${base} btn-icon-only` : base;
|
|
472
587
|
}, ...(ngDevMode ? [{ debugName: "btnClass" }] : []));
|
|
473
588
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
474
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.1", type: WmButton, isStandalone: true, selector: "wm-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, btnTitle: { classPropertyName: "btnTitle", publicName: "btnTitle", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<button\
|
|
589
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.1", type: WmButton, isStandalone: true, selector: "wm-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, btnTitle: { classPropertyName: "btnTitle", publicName: "btnTitle", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<button\n [class]=\"btnClass()\"\n [disabled]=\"disabled()\"\n [type]=\"type()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.title]=\"btnTitle() || null\"\n>\n <ng-content />\n</button>\n", styles: [":host{display:inline-flex}\n"] });
|
|
475
590
|
}
|
|
476
591
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmButton, decorators: [{
|
|
477
592
|
type: Component,
|
|
478
|
-
args: [{ selector: 'wm-button', template: "<button\
|
|
593
|
+
args: [{ selector: 'wm-button', template: "<button\n [class]=\"btnClass()\"\n [disabled]=\"disabled()\"\n [type]=\"type()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.title]=\"btnTitle() || null\"\n>\n <ng-content />\n</button>\n", styles: [":host{display:inline-flex}\n"] }]
|
|
479
594
|
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], btnTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "btnTitle", required: false }] }] } });
|
|
480
595
|
|
|
481
596
|
/**
|
|
@@ -522,11 +637,11 @@ class Checkbox {
|
|
|
522
637
|
}
|
|
523
638
|
}
|
|
524
639
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: Checkbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
525
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: Checkbox, isStandalone: true, selector: "wm-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, ngImport: i0, template: "<label class=\"wm-checkbox-wrap\" [class.is-disabled]=\"disabled()\">\
|
|
640
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: Checkbox, isStandalone: true, selector: "wm-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, ngImport: i0, template: "<label class=\"wm-checkbox-wrap\" [class.is-disabled]=\"disabled()\">\n <button\n type=\"button\"\n role=\"checkbox\"\n class=\"wm-checkbox-box\"\n [class.is-checked]=\"checked()\"\n [class.is-indeterminate]=\"indeterminate() && !checked()\"\n [attr.aria-checked]=\"ariaChecked()\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggle()\"\n (keydown.space)=\"$event.preventDefault(); toggle()\"\n >\n @if (indeterminate() && !checked()) {\n <!-- Dash for indeterminate (\"some selected\") state -->\n <svg class=\"wm-checkbox-icon\" viewBox=\"0 0 12 12\" aria-hidden=\"true\">\n <line x1=\"2\" y1=\"6\" x2=\"10\" y2=\"6\"\n stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\"/>\n </svg>\n } @else if (checked()) {\n <!-- Animated checkmark for selected state -->\n <svg class=\"wm-checkbox-icon\" viewBox=\"0 0 12 12\" aria-hidden=\"true\">\n <polyline class=\"check-mark\" points=\"2,6.5 4.5,9 10,3\"\n fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n\n @if (label()) {\n <span class=\"wm-checkbox-label\">{{ label() }}</span>\n }\n</label>\n", styles: [":host{display:inline-flex}.wm-checkbox-wrap{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer;-webkit-user-select:none;user-select:none}.wm-checkbox-wrap.is-disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.wm-checkbox-box{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;flex-shrink:0;border:2px solid var(--wm-border-input);border-radius:4px;background:var(--wm-bg-base);color:#fff;cursor:pointer;padding:0;outline:none;transition:background var(--wm-transition-fast),border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}.wm-checkbox-box:focus-visible{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-checkbox-box.is-checked,.wm-checkbox-box.is-indeterminate{background:var(--wm-color-accent);border-color:var(--wm-color-accent-readable)}.wm-checkbox-icon{width:12px;height:12px;display:block;flex-shrink:0}@keyframes check-draw{0%{stroke-dashoffset:16}to{stroke-dashoffset:0}}.check-mark{stroke-dasharray:16;stroke-dashoffset:0;animation:check-draw .18s ease-out forwards}.wm-checkbox-label{font-size:.875rem;color:var(--wm-text-primary);line-height:1.4}@media(prefers-reduced-motion:reduce){.wm-checkbox-box{transition:none}.check-mark{animation:none}}\n"] });
|
|
526
641
|
}
|
|
527
642
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: Checkbox, decorators: [{
|
|
528
643
|
type: Component,
|
|
529
|
-
args: [{ selector: 'wm-checkbox', template: "<label class=\"wm-checkbox-wrap\" [class.is-disabled]=\"disabled()\">\
|
|
644
|
+
args: [{ selector: 'wm-checkbox', template: "<label class=\"wm-checkbox-wrap\" [class.is-disabled]=\"disabled()\">\n <button\n type=\"button\"\n role=\"checkbox\"\n class=\"wm-checkbox-box\"\n [class.is-checked]=\"checked()\"\n [class.is-indeterminate]=\"indeterminate() && !checked()\"\n [attr.aria-checked]=\"ariaChecked()\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggle()\"\n (keydown.space)=\"$event.preventDefault(); toggle()\"\n >\n @if (indeterminate() && !checked()) {\n <!-- Dash for indeterminate (\"some selected\") state -->\n <svg class=\"wm-checkbox-icon\" viewBox=\"0 0 12 12\" aria-hidden=\"true\">\n <line x1=\"2\" y1=\"6\" x2=\"10\" y2=\"6\"\n stroke=\"currentColor\" stroke-width=\"2.25\" stroke-linecap=\"round\"/>\n </svg>\n } @else if (checked()) {\n <!-- Animated checkmark for selected state -->\n <svg class=\"wm-checkbox-icon\" viewBox=\"0 0 12 12\" aria-hidden=\"true\">\n <polyline class=\"check-mark\" points=\"2,6.5 4.5,9 10,3\"\n fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n\n @if (label()) {\n <span class=\"wm-checkbox-label\">{{ label() }}</span>\n }\n</label>\n", styles: [":host{display:inline-flex}.wm-checkbox-wrap{display:inline-flex;align-items:center;gap:.5rem;cursor:pointer;-webkit-user-select:none;user-select:none}.wm-checkbox-wrap.is-disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.wm-checkbox-box{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;flex-shrink:0;border:2px solid var(--wm-border-input);border-radius:4px;background:var(--wm-bg-base);color:#fff;cursor:pointer;padding:0;outline:none;transition:background var(--wm-transition-fast),border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}.wm-checkbox-box:focus-visible{box-shadow:0 0 0 2px var(--wm-bg-base),0 0 0 4px var(--wm-color-accent)}.wm-checkbox-box.is-checked,.wm-checkbox-box.is-indeterminate{background:var(--wm-color-accent);border-color:var(--wm-color-accent-readable)}.wm-checkbox-icon{width:12px;height:12px;display:block;flex-shrink:0}@keyframes check-draw{0%{stroke-dashoffset:16}to{stroke-dashoffset:0}}.check-mark{stroke-dasharray:16;stroke-dashoffset:0;animation:check-draw .18s ease-out forwards}.wm-checkbox-label{font-size:.875rem;color:var(--wm-text-primary);line-height:1.4}@media(prefers-reduced-motion:reduce){.wm-checkbox-box{transition:none}.check-mark{animation:none}}\n"] }]
|
|
530
645
|
}], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
531
646
|
|
|
532
647
|
/**
|
|
@@ -623,7 +738,7 @@ class NumberInput {
|
|
|
623
738
|
useExisting: forwardRef(() => NumberInput),
|
|
624
739
|
multi: true,
|
|
625
740
|
},
|
|
626
|
-
], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\
|
|
741
|
+
], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Uses .wm-input-group and .wm-number-* classes from inputs.css.\n The input fills the group; the controls column sits at the trailing edge.\n-->\n<div\n class=\"wm-input-group wm-number-field\"\n [class.is-disabled]=\"disabled()\"\n>\n <input\n #inputEl\n type=\"number\"\n class=\"wm-input-bare wm-number-input\"\n [value]=\"internalValue() ?? ''\"\n [placeholder]=\"placeholder()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [step]=\"step()\"\n [disabled]=\"disabled() || null\"\n (change)=\"onInputChange($event)\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onTouchedFn()\"\n />\n\n <div class=\"wm-number-controls\" aria-hidden=\"true\">\n <button\n type=\"button\"\n class=\"wm-number-btn\"\n tabindex=\"-1\"\n [disabled]=\"disabled() || null\"\n (mousedown)=\"preventFocusSteal($event)\"\n (click)=\"increment()\"\n aria-label=\"Increment\"\n >\n <!-- chevron-up (lucide) -->\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m18 15-6-6-6 6\"/>\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wm-number-btn\"\n tabindex=\"-1\"\n [disabled]=\"disabled() || null\"\n (mousedown)=\"preventFocusSteal($event)\"\n (click)=\"decrement()\"\n aria-label=\"Decrement\"\n >\n <!-- chevron-down (lucide) -->\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\"/>\n </svg>\n </button>\n </div>\n</div>\n", styles: [":host{display:block}.wm-number-field.is-disabled{opacity:.45;pointer-events:none}\n"] });
|
|
627
742
|
}
|
|
628
743
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: NumberInput, decorators: [{
|
|
629
744
|
type: Component,
|
|
@@ -633,7 +748,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
633
748
|
useExisting: forwardRef(() => NumberInput),
|
|
634
749
|
multi: true,
|
|
635
750
|
},
|
|
636
|
-
], template: "<!--\
|
|
751
|
+
], template: "<!--\n Uses .wm-input-group and .wm-number-* classes from inputs.css.\n The input fills the group; the controls column sits at the trailing edge.\n-->\n<div\n class=\"wm-input-group wm-number-field\"\n [class.is-disabled]=\"disabled()\"\n>\n <input\n #inputEl\n type=\"number\"\n class=\"wm-input-bare wm-number-input\"\n [value]=\"internalValue() ?? ''\"\n [placeholder]=\"placeholder()\"\n [attr.min]=\"min() ?? null\"\n [attr.max]=\"max() ?? null\"\n [step]=\"step()\"\n [disabled]=\"disabled() || null\"\n (change)=\"onInputChange($event)\"\n (input)=\"onInputChange($event)\"\n (blur)=\"onTouchedFn()\"\n />\n\n <div class=\"wm-number-controls\" aria-hidden=\"true\">\n <button\n type=\"button\"\n class=\"wm-number-btn\"\n tabindex=\"-1\"\n [disabled]=\"disabled() || null\"\n (mousedown)=\"preventFocusSteal($event)\"\n (click)=\"increment()\"\n aria-label=\"Increment\"\n >\n <!-- chevron-up (lucide) -->\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m18 15-6-6-6 6\"/>\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wm-number-btn\"\n tabindex=\"-1\"\n [disabled]=\"disabled() || null\"\n (mousedown)=\"preventFocusSteal($event)\"\n (click)=\"decrement()\"\n aria-label=\"Decrement\"\n >\n <!-- chevron-down (lucide) -->\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\"/>\n </svg>\n </button>\n </div>\n</div>\n", styles: [":host{display:block}.wm-number-field.is-disabled{opacity:.45;pointer-events:none}\n"] }]
|
|
637
752
|
}], propDecorators: { 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 }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }] } });
|
|
638
753
|
|
|
639
754
|
/**
|
|
@@ -662,11 +777,11 @@ class Toggle {
|
|
|
662
777
|
}
|
|
663
778
|
}
|
|
664
779
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: Toggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
665
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.1", type: Toggle, isStandalone: true, selector: "wm-toggle", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, ngImport: i0, template: "<button\
|
|
780
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.1", type: Toggle, isStandalone: true, selector: "wm-toggle", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, ngImport: i0, template: "<button\n type=\"button\"\n role=\"switch\"\n class=\"wm-toggle\"\n [class.is-on]=\"checked()\"\n [class.is-disabled]=\"disabled()\"\n [attr.aria-checked]=\"checked()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggle()\"\n>\n <span class=\"wm-toggle-track\" aria-hidden=\"true\">\n <span class=\"wm-toggle-thumb\"></span>\n </span>\n</button>\n", styles: [":host{display:inline-flex}.wm-toggle{display:inline-flex;align-items:center;background:none;border:none;padding:2px;margin:-2px;cursor:pointer;border-radius:var(--wm-radius-full);outline:none}.wm-toggle:focus-visible .wm-toggle-track{outline:2px solid var(--wm-color-accent);outline-offset:2px}.wm-toggle.is-disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.wm-toggle-track{position:relative;width:40px;height:22px;background:var(--wm-border-input);border-radius:var(--wm-radius-full);transition:background var(--wm-transition-fast);flex-shrink:0}.wm-toggle.is-on .wm-toggle-track{background:var(--wm-color-accent)}.wm-toggle-thumb{position:absolute;top:3px;left:3px;width:16px;height:16px;background:#fff;border-radius:var(--wm-radius-full);box-shadow:0 1px 3px #00000040;transition:transform var(--wm-transition-fast)}.wm-toggle.is-on .wm-toggle-thumb{transform:translate(18px)}@media(prefers-reduced-motion:reduce){.wm-toggle-track,.wm-toggle-thumb{transition:none}}\n"] });
|
|
666
781
|
}
|
|
667
782
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: Toggle, decorators: [{
|
|
668
783
|
type: Component,
|
|
669
|
-
args: [{ selector: 'wm-toggle', template: "<button\
|
|
784
|
+
args: [{ selector: 'wm-toggle', template: "<button\n type=\"button\"\n role=\"switch\"\n class=\"wm-toggle\"\n [class.is-on]=\"checked()\"\n [class.is-disabled]=\"disabled()\"\n [attr.aria-checked]=\"checked()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-disabled]=\"disabled() ? true : null\"\n [disabled]=\"disabled() || null\"\n (click)=\"toggle()\"\n>\n <span class=\"wm-toggle-track\" aria-hidden=\"true\">\n <span class=\"wm-toggle-thumb\"></span>\n </span>\n</button>\n", styles: [":host{display:inline-flex}.wm-toggle{display:inline-flex;align-items:center;background:none;border:none;padding:2px;margin:-2px;cursor:pointer;border-radius:var(--wm-radius-full);outline:none}.wm-toggle:focus-visible .wm-toggle-track{outline:2px solid var(--wm-color-accent);outline-offset:2px}.wm-toggle.is-disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.wm-toggle-track{position:relative;width:40px;height:22px;background:var(--wm-border-input);border-radius:var(--wm-radius-full);transition:background var(--wm-transition-fast);flex-shrink:0}.wm-toggle.is-on .wm-toggle-track{background:var(--wm-color-accent)}.wm-toggle-thumb{position:absolute;top:3px;left:3px;width:16px;height:16px;background:#fff;border-radius:var(--wm-radius-full);box-shadow:0 1px 3px #00000040;transition:transform var(--wm-transition-fast)}.wm-toggle.is-on .wm-toggle-thumb{transform:translate(18px)}@media(prefers-reduced-motion:reduce){.wm-toggle-track,.wm-toggle-thumb{transition:none}}\n"] }]
|
|
670
785
|
}], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
671
786
|
|
|
672
787
|
/**
|
|
@@ -717,11 +832,11 @@ class PageHeader {
|
|
|
717
832
|
subtitle = input(...(ngDevMode ? [undefined, { debugName: "subtitle" }] : []));
|
|
718
833
|
badgeLabel = input(...(ngDevMode ? [undefined, { debugName: "badgeLabel" }] : []));
|
|
719
834
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PageHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
720
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PageHeader, isStandalone: true, selector: "wm-page-header", inputs: { flavourText: { classPropertyName: "flavourText", publicName: "flavourText", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, badgeLabel: { classPropertyName: "badgeLabel", publicName: "badgeLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<header class=\"wm-page-header\">\
|
|
835
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PageHeader, isStandalone: true, selector: "wm-page-header", inputs: { flavourText: { classPropertyName: "flavourText", publicName: "flavourText", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, badgeLabel: { classPropertyName: "badgeLabel", publicName: "badgeLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<header class=\"wm-page-header\">\n <div class=\"wm-page-header__left\">\n <div class=\"wm-page-header__eyebrow\">\n <ng-content select=\"[icon]\" />\n <span>{{ flavourText() }}</span>\n </div>\n <h2 class=\"wm-page-header__title\">{{ title() }}</h2>\n @if (subtitle()) {\n <p class=\"wm-page-header__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n\n <div class=\"wm-page-header__right\">\n <div class=\"wm-page-header__status\">\n @if (badgeLabel()) {\n <span class=\"wm-page-header__badge\">{{ badgeLabel() }}</span>\n }\n <ng-content select=\"[back]\" />\n </div>\n <div class=\"wm-page-header__actions\">\n <ng-content />\n </div>\n </div>\n</header>\n", styles: ["wm-page-header,.wm-page-header{display:flex;align-items:flex-end;justify-content:space-between;border-bottom:1px solid var(--wm-border-subtle);padding-bottom:1.5rem}.wm-page-header__left{display:flex;flex-direction:column;gap:4px}.wm-page-header__eyebrow{display:flex;align-items:center;gap:8px;font-size:.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--wm-text-muted)}.wm-page-header__eyebrow svg,.wm-page-header__eyebrow img{width:12px;height:12px;flex-shrink:0}.wm-page-header__title{margin:0;font-size:1.5rem;font-weight:700;letter-spacing:-.025em;color:var(--wm-text-primary)}.wm-page-header__subtitle{margin:0;font-size:.875rem;color:var(--wm-text-muted)}.wm-page-header__right{display:flex;flex-direction:column;align-items:flex-end;gap:12px}.wm-page-header__status,.wm-page-header__actions{display:flex;align-items:center;gap:12px}.wm-page-header__actions:empty{display:none}.wm-page-header__badge{font-size:.6875rem;font-family:var(--wm-font-mono, monospace);font-weight:700;text-transform:uppercase;letter-spacing:-.05em;color:var(--wm-text-muted);background:var(--wm-bg-float);padding:4px 8px;border-radius:var(--wm-radius-sm);border:1px solid var(--wm-border-subtle);box-shadow:0 1px 2px #0000000d;white-space:nowrap}@media(max-width:767px){wm-page-header,.wm-page-header{flex-direction:column;align-items:stretch;gap:6px;padding-bottom:0;border-bottom:none}.wm-page-header__right{align-items:flex-start}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
721
836
|
}
|
|
722
837
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PageHeader, decorators: [{
|
|
723
838
|
type: Component,
|
|
724
|
-
args: [{ selector: 'wm-page-header', standalone: true, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"wm-page-header\">\
|
|
839
|
+
args: [{ selector: 'wm-page-header', standalone: true, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"wm-page-header\">\n <div class=\"wm-page-header__left\">\n <div class=\"wm-page-header__eyebrow\">\n <ng-content select=\"[icon]\" />\n <span>{{ flavourText() }}</span>\n </div>\n <h2 class=\"wm-page-header__title\">{{ title() }}</h2>\n @if (subtitle()) {\n <p class=\"wm-page-header__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n\n <div class=\"wm-page-header__right\">\n <div class=\"wm-page-header__status\">\n @if (badgeLabel()) {\n <span class=\"wm-page-header__badge\">{{ badgeLabel() }}</span>\n }\n <ng-content select=\"[back]\" />\n </div>\n <div class=\"wm-page-header__actions\">\n <ng-content />\n </div>\n </div>\n</header>\n", styles: ["wm-page-header,.wm-page-header{display:flex;align-items:flex-end;justify-content:space-between;border-bottom:1px solid var(--wm-border-subtle);padding-bottom:1.5rem}.wm-page-header__left{display:flex;flex-direction:column;gap:4px}.wm-page-header__eyebrow{display:flex;align-items:center;gap:8px;font-size:.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--wm-text-muted)}.wm-page-header__eyebrow svg,.wm-page-header__eyebrow img{width:12px;height:12px;flex-shrink:0}.wm-page-header__title{margin:0;font-size:1.5rem;font-weight:700;letter-spacing:-.025em;color:var(--wm-text-primary)}.wm-page-header__subtitle{margin:0;font-size:.875rem;color:var(--wm-text-muted)}.wm-page-header__right{display:flex;flex-direction:column;align-items:flex-end;gap:12px}.wm-page-header__status,.wm-page-header__actions{display:flex;align-items:center;gap:12px}.wm-page-header__actions:empty{display:none}.wm-page-header__badge{font-size:.6875rem;font-family:var(--wm-font-mono, monospace);font-weight:700;text-transform:uppercase;letter-spacing:-.05em;color:var(--wm-text-muted);background:var(--wm-bg-float);padding:4px 8px;border-radius:var(--wm-radius-sm);border:1px solid var(--wm-border-subtle);box-shadow:0 1px 2px #0000000d;white-space:nowrap}@media(max-width:767px){wm-page-header,.wm-page-header{flex-direction:column;align-items:stretch;gap:6px;padding-bottom:0;border-bottom:none}.wm-page-header__right{align-items:flex-start}}\n"] }]
|
|
725
840
|
}], propDecorators: { flavourText: [{ type: i0.Input, args: [{ isSignal: true, alias: "flavourText", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], badgeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeLabel", required: false }] }] } });
|
|
726
841
|
|
|
727
842
|
/** Module-level counter for unique panel instance IDs. */
|
|
@@ -887,6 +1002,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
887
1002
|
* <wm-combobox formControlName="tag" [options]="tags" />
|
|
888
1003
|
*/
|
|
889
1004
|
class WmCombobox {
|
|
1005
|
+
/**
|
|
1006
|
+
* Keeps the panel anchored to its trigger while an ancestor scrolls.
|
|
1007
|
+
*
|
|
1008
|
+
* One instance per component, because a ScrollStrategy holds a single
|
|
1009
|
+
* overlayRef and could not tell two open overlays apart. Without it the
|
|
1010
|
+
* panel takes CDK's default, which cannot hear a scroll from any container
|
|
1011
|
+
* that is not marked `cdkScrollable` - so in a fixed-height shell the panel
|
|
1012
|
+
* keeps the coordinates it was born with while the trigger scrolls away.
|
|
1013
|
+
*/
|
|
1014
|
+
scrollStrategy = new WmAnchoredScrollStrategy();
|
|
890
1015
|
// ── Public inputs ──────────────────────────────────────────────────────
|
|
891
1016
|
/** Full option list (before filtering). */
|
|
892
1017
|
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
@@ -1402,7 +1527,7 @@ class WmCombobox {
|
|
|
1402
1527
|
useExisting: forwardRef(() => WmCombobox),
|
|
1403
1528
|
multi: true,
|
|
1404
1529
|
},
|
|
1405
|
-
], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }, { propertyName: "panel", first: true, predicate: ComboboxPanel, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Trigger wrapper \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-cb-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n (click)=\"onTriggerClick()\"\n>\n <!-- Multi-select chips -->\n @if (multiple()) {\n @for (chip of chips(); track chip.value) {\n <span class=\"wm-cb-chip\">\n <span class=\"wm-cb-chip-label\">{{ chip.label }}</span>\n <button\n type=\"button\"\n class=\"wm-cb-chip-remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'Remove ' + chip.label\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); removeChip(chip.value)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </span>\n }\n }\n\n <!-- Text input -->\n <input\n #inputEl\n type=\"text\"\n class=\"wm-cb-input\"\n role=\"combobox\"\n autocomplete=\"off\"\n [attr.placeholder]=\"chips().length ? '' : placeholder()\"\n [attr.aria-label]=\"ariaLabelledby() ? null : ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledby()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-autocomplete]=\"allowFreeform() ? 'both' : 'list'\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-controls]=\"isOpen() ? panelListboxId() : null\"\n [disabled]=\"disabled() || null\"\n [value]=\"inputText()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n\n <!-- Chevron -->\n <svg\n class=\"wm-cb-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</div>\n\n<!-- Live region for screen readers -->\n<span class=\"wm-cb-sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ liveText() }}\n</span>\n\n<!-- Dropdown panel in CDK overlay -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"wm-cb-backdrop\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div class=\"wm-cb-panel\">\n <wm-combobox-panel\n [options]=\"filteredOptions()\"\n [highlightIndex]=\"highlightIndex()\"\n [selectedValues]=\"selectedValues()\"\n [multiple]=\"multiple()\"\n [itemHeight]=\"itemHeight()\"\n [maxHeight]=\"maxPanelHeight()\"\n [filterQuery]=\"inputText()\"\n [loading]=\"asyncLoading()\"\n [freeformHint]=\"freeformHint()\"\n (optionClicked)=\"onOptionClicked($event)\"\n />\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-cb-trigger{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;width:100%;min-height:36px;padding:.25rem .5rem;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-sm);color:var(--wm-text-primary);font-size:.875rem;cursor:text;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}.wm-cb-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-cb-trigger:focus-within:not(.is-disabled){border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-cb-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-cb-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-cb-input{flex:1 1 60px;min-width:60px;border:none;outline:none;background:transparent;color:inherit;font:inherit;padding:.125rem 0}.wm-cb-input::placeholder{color:var(--wm-text-placeholder)}.wm-cb-input:disabled{cursor:not-allowed}.wm-cb-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast);pointer-events:none}.wm-cb-chevron.is-open{transform:rotate(180deg)}.wm-cb-chip{display:inline-flex;align-items:center;gap:.25rem;max-width:100%;padding:.125rem .375rem;background:color-mix(in srgb,var(--wm-color-accent) 15%,transparent);border:1px solid color-mix(in srgb,var(--wm-color-accent) 30%,transparent);border-radius:var(--wm-radius-sm);font-size:.8125rem;line-height:1.4;color:var(--wm-text-primary);-webkit-user-select:none;user-select:none}.wm-cb-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-cb-chip-remove{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--wm-text-muted);cursor:pointer;border-radius:var(--wm-radius-sm);transition:color var(--wm-transition-fast)}.wm-cb-chip-remove:hover{color:var(--wm-text-primary)}.wm-cb-backdrop{position:fixed;inset:0}.wm-cb-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;padding:.25rem}.wm-cb-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\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: "component", type: ComboboxPanel, selector: "wm-combobox-panel", inputs: ["options", "highlightIndex", "selectedValues", "multiple", "itemHeight", "maxHeight", "filterQuery", "loading", "freeformHint"], outputs: ["optionClicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1530
|
+
], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }, { propertyName: "panel", first: true, predicate: ComboboxPanel, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Trigger wrapper \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-cb-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n (click)=\"onTriggerClick()\"\n>\n <!-- Multi-select chips -->\n @if (multiple()) {\n @for (chip of chips(); track chip.value) {\n <span class=\"wm-cb-chip\">\n <span class=\"wm-cb-chip-label\">{{ chip.label }}</span>\n <button\n type=\"button\"\n class=\"wm-cb-chip-remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'Remove ' + chip.label\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); removeChip(chip.value)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </span>\n }\n }\n\n <!-- Text input -->\n <input\n #inputEl\n type=\"text\"\n class=\"wm-cb-input\"\n role=\"combobox\"\n autocomplete=\"off\"\n [attr.placeholder]=\"chips().length ? '' : placeholder()\"\n [attr.aria-label]=\"ariaLabelledby() ? null : ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledby()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-autocomplete]=\"allowFreeform() ? 'both' : 'list'\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-controls]=\"isOpen() ? panelListboxId() : null\"\n [disabled]=\"disabled() || null\"\n [value]=\"inputText()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n\n <!-- Chevron -->\n <svg\n class=\"wm-cb-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</div>\n\n<!-- Live region for screen readers -->\n<span class=\"wm-cb-sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ liveText() }}\n</span>\n\n<!-- Dropdown panel in CDK overlay -->\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-cb-backdrop\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div class=\"wm-cb-panel\">\n <wm-combobox-panel\n [options]=\"filteredOptions()\"\n [highlightIndex]=\"highlightIndex()\"\n [selectedValues]=\"selectedValues()\"\n [multiple]=\"multiple()\"\n [itemHeight]=\"itemHeight()\"\n [maxHeight]=\"maxPanelHeight()\"\n [filterQuery]=\"inputText()\"\n [loading]=\"asyncLoading()\"\n [freeformHint]=\"freeformHint()\"\n (optionClicked)=\"onOptionClicked($event)\"\n />\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-cb-trigger{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;width:100%;min-height:36px;padding:.25rem .5rem;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-sm);color:var(--wm-text-primary);font-size:.875rem;cursor:text;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}.wm-cb-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-cb-trigger:focus-within:not(.is-disabled){border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-cb-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-cb-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-cb-input{flex:1 1 60px;min-width:60px;border:none;outline:none;background:transparent;color:inherit;font:inherit;padding:.125rem 0}.wm-cb-input::placeholder{color:var(--wm-text-placeholder)}.wm-cb-input:disabled{cursor:not-allowed}.wm-cb-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast);pointer-events:none}.wm-cb-chevron.is-open{transform:rotate(180deg)}.wm-cb-chip{display:inline-flex;align-items:center;gap:.25rem;max-width:100%;padding:.125rem .375rem;background:color-mix(in srgb,var(--wm-color-accent) 15%,transparent);border:1px solid color-mix(in srgb,var(--wm-color-accent) 30%,transparent);border-radius:var(--wm-radius-sm);font-size:.8125rem;line-height:1.4;color:var(--wm-text-primary);-webkit-user-select:none;user-select:none}.wm-cb-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-cb-chip-remove{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--wm-text-muted);cursor:pointer;border-radius:var(--wm-radius-sm);transition:color var(--wm-transition-fast)}.wm-cb-chip-remove:hover{color:var(--wm-text-primary)}.wm-cb-backdrop{position:fixed;inset:0}.wm-cb-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;padding:.25rem}.wm-cb-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\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: "component", type: ComboboxPanel, selector: "wm-combobox-panel", inputs: ["options", "highlightIndex", "selectedValues", "multiple", "itemHeight", "maxHeight", "filterQuery", "loading", "freeformHint"], outputs: ["optionClicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1406
1531
|
}
|
|
1407
1532
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmCombobox, decorators: [{
|
|
1408
1533
|
type: Component,
|
|
@@ -1412,10 +1537,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1412
1537
|
useExisting: forwardRef(() => WmCombobox),
|
|
1413
1538
|
multi: true,
|
|
1414
1539
|
},
|
|
1415
|
-
], template: "<!-- Trigger wrapper \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-cb-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n (click)=\"onTriggerClick()\"\n>\n <!-- Multi-select chips -->\n @if (multiple()) {\n @for (chip of chips(); track chip.value) {\n <span class=\"wm-cb-chip\">\n <span class=\"wm-cb-chip-label\">{{ chip.label }}</span>\n <button\n type=\"button\"\n class=\"wm-cb-chip-remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'Remove ' + chip.label\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); removeChip(chip.value)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </span>\n }\n }\n\n <!-- Text input -->\n <input\n #inputEl\n type=\"text\"\n class=\"wm-cb-input\"\n role=\"combobox\"\n autocomplete=\"off\"\n [attr.placeholder]=\"chips().length ? '' : placeholder()\"\n [attr.aria-label]=\"ariaLabelledby() ? null : ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledby()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-autocomplete]=\"allowFreeform() ? 'both' : 'list'\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-controls]=\"isOpen() ? panelListboxId() : null\"\n [disabled]=\"disabled() || null\"\n [value]=\"inputText()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n\n <!-- Chevron -->\n <svg\n class=\"wm-cb-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</div>\n\n<!-- Live region for screen readers -->\n<span class=\"wm-cb-sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ liveText() }}\n</span>\n\n<!-- Dropdown panel in CDK overlay -->\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"positions\"\n [cdkConnectedOverlayWidth]=\"overlayWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"wm-cb-backdrop\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div class=\"wm-cb-panel\">\n <wm-combobox-panel\n [options]=\"filteredOptions()\"\n [highlightIndex]=\"highlightIndex()\"\n [selectedValues]=\"selectedValues()\"\n [multiple]=\"multiple()\"\n [itemHeight]=\"itemHeight()\"\n [maxHeight]=\"maxPanelHeight()\"\n [filterQuery]=\"inputText()\"\n [loading]=\"asyncLoading()\"\n [freeformHint]=\"freeformHint()\"\n (optionClicked)=\"onOptionClicked($event)\"\n />\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-cb-trigger{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;width:100%;min-height:36px;padding:.25rem .5rem;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-sm);color:var(--wm-text-primary);font-size:.875rem;cursor:text;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}.wm-cb-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-cb-trigger:focus-within:not(.is-disabled){border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-cb-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-cb-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-cb-input{flex:1 1 60px;min-width:60px;border:none;outline:none;background:transparent;color:inherit;font:inherit;padding:.125rem 0}.wm-cb-input::placeholder{color:var(--wm-text-placeholder)}.wm-cb-input:disabled{cursor:not-allowed}.wm-cb-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast);pointer-events:none}.wm-cb-chevron.is-open{transform:rotate(180deg)}.wm-cb-chip{display:inline-flex;align-items:center;gap:.25rem;max-width:100%;padding:.125rem .375rem;background:color-mix(in srgb,var(--wm-color-accent) 15%,transparent);border:1px solid color-mix(in srgb,var(--wm-color-accent) 30%,transparent);border-radius:var(--wm-radius-sm);font-size:.8125rem;line-height:1.4;color:var(--wm-text-primary);-webkit-user-select:none;user-select:none}.wm-cb-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-cb-chip-remove{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--wm-text-muted);cursor:pointer;border-radius:var(--wm-radius-sm);transition:color var(--wm-transition-fast)}.wm-cb-chip-remove:hover{color:var(--wm-text-primary)}.wm-cb-backdrop{position:fixed;inset:0}.wm-cb-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;padding:.25rem}.wm-cb-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
1540
|
+
], template: "<!-- Trigger wrapper \u2014 anchors the CDK overlay -->\n<div\n class=\"wm-cb-trigger\"\n [class.is-open]=\"isOpen()\"\n [class.is-disabled]=\"disabled()\"\n cdkOverlayOrigin\n #origin=\"cdkOverlayOrigin\"\n (click)=\"onTriggerClick()\"\n>\n <!-- Multi-select chips -->\n @if (multiple()) {\n @for (chip of chips(); track chip.value) {\n <span class=\"wm-cb-chip\">\n <span class=\"wm-cb-chip-label\">{{ chip.label }}</span>\n <button\n type=\"button\"\n class=\"wm-cb-chip-remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'Remove ' + chip.label\"\n (mousedown)=\"$event.preventDefault(); $event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); removeChip(chip.value)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </span>\n }\n }\n\n <!-- Text input -->\n <input\n #inputEl\n type=\"text\"\n class=\"wm-cb-input\"\n role=\"combobox\"\n autocomplete=\"off\"\n [attr.placeholder]=\"chips().length ? '' : placeholder()\"\n [attr.aria-label]=\"ariaLabelledby() ? null : ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledby()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-haspopup=\"listbox\"\n [attr.aria-autocomplete]=\"allowFreeform() ? 'both' : 'list'\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-controls]=\"isOpen() ? panelListboxId() : null\"\n [disabled]=\"disabled() || null\"\n [value]=\"inputText()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n\n <!-- Chevron -->\n <svg\n class=\"wm-cb-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</div>\n\n<!-- Live region for screen readers -->\n<span class=\"wm-cb-sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ liveText() }}\n</span>\n\n<!-- Dropdown panel in CDK overlay -->\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-cb-backdrop\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n>\n <div class=\"wm-cb-panel\">\n <wm-combobox-panel\n [options]=\"filteredOptions()\"\n [highlightIndex]=\"highlightIndex()\"\n [selectedValues]=\"selectedValues()\"\n [multiple]=\"multiple()\"\n [itemHeight]=\"itemHeight()\"\n [maxHeight]=\"maxPanelHeight()\"\n [filterQuery]=\"inputText()\"\n [loading]=\"asyncLoading()\"\n [freeformHint]=\"freeformHint()\"\n (optionClicked)=\"onOptionClicked($event)\"\n />\n </div>\n</ng-template>\n", styles: [":host{display:block}.wm-cb-trigger{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;width:100%;min-height:36px;padding:.25rem .5rem;background:var(--wm-bg-base);border:1px solid var(--wm-border-input);border-radius:var(--wm-radius-sm);color:var(--wm-text-primary);font-size:.875rem;cursor:text;transition:border-color var(--wm-transition-fast),box-shadow var(--wm-transition-fast)}.wm-cb-trigger:hover:not(.is-disabled){border-color:var(--wm-color-accent-readable)}.wm-cb-trigger:focus-within:not(.is-disabled){border-color:var(--wm-color-accent-readable);box-shadow:0 0 0 3px color-mix(in srgb,var(--wm-color-accent) 25%,transparent)}.wm-cb-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-cb-trigger.is-disabled{opacity:.45;cursor:not-allowed}.wm-cb-input{flex:1 1 60px;min-width:60px;border:none;outline:none;background:transparent;color:inherit;font:inherit;padding:.125rem 0}.wm-cb-input::placeholder{color:var(--wm-text-placeholder)}.wm-cb-input:disabled{cursor:not-allowed}.wm-cb-chevron{flex-shrink:0;color:var(--wm-text-muted);transition:transform var(--wm-transition-fast);pointer-events:none}.wm-cb-chevron.is-open{transform:rotate(180deg)}.wm-cb-chip{display:inline-flex;align-items:center;gap:.25rem;max-width:100%;padding:.125rem .375rem;background:color-mix(in srgb,var(--wm-color-accent) 15%,transparent);border:1px solid color-mix(in srgb,var(--wm-color-accent) 30%,transparent);border-radius:var(--wm-radius-sm);font-size:.8125rem;line-height:1.4;color:var(--wm-text-primary);-webkit-user-select:none;user-select:none}.wm-cb-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wm-cb-chip-remove{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--wm-text-muted);cursor:pointer;border-radius:var(--wm-radius-sm);transition:color var(--wm-transition-fast)}.wm-cb-chip-remove:hover{color:var(--wm-text-primary)}.wm-cb-backdrop{position:fixed;inset:0}.wm-cb-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;padding:.25rem}.wm-cb-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
1416
1541
|
}], ctorParameters: () => [], 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"] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }, { type: i0.Output, args: ["valuesChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], allowFreeform: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFreeform", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterFn", required: false }] }], itemHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemHeight", required: false }] }], maxPanelHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxPanelHeight", required: false }] }], query: [{ type: i0.Output, args: ["query"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], panel: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ComboboxPanel), { isSignal: true }] }] } });
|
|
1417
1542
|
|
|
1418
|
-
let nextId = 0;
|
|
1543
|
+
let nextId$1 = 0;
|
|
1419
1544
|
/**
|
|
1420
1545
|
* WmToastService — Application-wide toast notification manager.
|
|
1421
1546
|
*
|
|
@@ -1429,7 +1554,7 @@ let nextId = 0;
|
|
|
1429
1554
|
class WmToastService {
|
|
1430
1555
|
toasts = signal([], ...(ngDevMode ? [{ debugName: "toasts" }] : []));
|
|
1431
1556
|
show(message, type = 'info', duration) {
|
|
1432
|
-
const id = ++nextId;
|
|
1557
|
+
const id = ++nextId$1;
|
|
1433
1558
|
this.toasts.update(list => [...list, { id, message, type }]);
|
|
1434
1559
|
const ms = duration ?? (type === 'error' ? 6000 : 4000);
|
|
1435
1560
|
setTimeout(() => this.dismiss(id), ms);
|
|
@@ -1447,7 +1572,7 @@ class WmToastService {
|
|
|
1447
1572
|
* The caller should abort the operation and then dismiss the toast.
|
|
1448
1573
|
*/
|
|
1449
1574
|
progress(message, onCancel) {
|
|
1450
|
-
const id = ++nextId;
|
|
1575
|
+
const id = ++nextId$1;
|
|
1451
1576
|
this.toasts.update(list => [
|
|
1452
1577
|
...list,
|
|
1453
1578
|
{ id, message, type: 'progress', persistent: true, createdAt: Date.now(), onCancel },
|
|
@@ -1516,6 +1641,503 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1516
1641
|
args: [{ selector: 'wm-toast-container', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"wm-toast-container\" role=\"log\" aria-live=\"polite\" aria-label=\"Notifications\">\n @for (toast of toastService.toasts(); track toast.id) {\n <div [class]=\"typeClass(toast)\" role=\"status\">\n <!-- Success icon (check-circle) -->\n @if (toast.type === 'success') {\n <svg class=\"wm-toast-icon\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/>\n <polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n }\n <!-- Error icon (x-circle) -->\n @if (toast.type === 'error') {\n <svg class=\"wm-toast-icon\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/>\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n }\n <!-- Info icon -->\n @if (toast.type === 'info') {\n <svg class=\"wm-toast-icon\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>\n </svg>\n }\n <!-- Progress spinner -->\n @if (toast.type === 'progress') {\n <span class=\"wm-toast-spinner\" aria-hidden=\"true\"></span>\n }\n\n <span class=\"wm-toast-message\">\n {{ toast.message }}\n @if (toast.type === 'progress' && toast.createdAt) {\n <span class=\"wm-toast-elapsed\"> ({{ elapsed(toast.createdAt) }})</span>\n }\n </span>\n\n <!-- Cancel button for progress toasts with an onCancel callback -->\n @if (toast.type === 'progress' && toast.onCancel) {\n <button class=\"wm-toast-cancel\" (click)=\"cancel(toast)\" aria-label=\"Cancel operation\">Cancel</button>\n }\n\n <!-- Dismiss button \u2014 always shown -->\n <button class=\"wm-toast-dismiss\" (click)=\"dismiss(toast.id)\" aria-label=\"Dismiss notification\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n }\n</div>\n", styles: [".wm-toast-container{position:fixed;bottom:1rem;right:1rem;display:flex;flex-direction:column;gap:.5rem;z-index:9999;pointer-events:none}.wm-toast{display:flex;align-items:flex-start;gap:.625rem;padding:.625rem .875rem;border-radius:var(--wm-radius-sm, 4px);box-shadow:var(--wm-shadow-md, 0 4px 12px rgba(0,0,0,.25));font-size:.8125rem;line-height:1.4;min-width:220px;max-width:360px;pointer-events:auto;animation:wm-toast-slide-in .15s ease-out both}.wm-toast--success{background:var(--wm-color-success-bg);color:var(--wm-color-success-readable);border:1px solid var(--wm-color-success-border)}.wm-toast--error{background:var(--wm-color-danger-bg);color:var(--wm-color-danger-readable);border:1px solid var(--wm-color-danger-border)}.wm-toast--info{background:var(--wm-bg-raised);color:var(--wm-text-primary);border:1px solid var(--wm-border)}.wm-toast--progress{background:var(--wm-bg-raised);color:var(--wm-text-primary);border:1px solid var(--wm-color-accent-border)}.wm-toast-icon{flex-shrink:0;margin-top:1px}.wm-toast-message{flex:1;line-height:1.4}.wm-toast-dismiss{flex-shrink:0;background:none;border:none;color:inherit;opacity:.6;cursor:pointer;padding:0;margin-left:.25rem;transition:opacity .15s ease}.wm-toast-dismiss:hover{opacity:1}.wm-toast-spinner{width:14px;height:14px;border:2px solid color-mix(in srgb,var(--wm-color-accent) 30%,transparent);border-top-color:var(--wm-color-accent-readable);border-radius:50%;flex-shrink:0;margin-top:1px;animation:wm-toast-spin .7s linear infinite}.wm-toast-elapsed{opacity:.6;font-size:.75rem;font-variant-numeric:tabular-nums}.wm-toast-cancel{flex-shrink:0;background:none;border:1px solid color-mix(in srgb,currentColor 30%,transparent);border-radius:var(--wm-radius-sm, 4px);color:inherit;opacity:.7;cursor:pointer;padding:.125rem .5rem;font-size:.75rem;transition:opacity .15s ease,border-color .15s ease}.wm-toast-cancel:hover{opacity:1;border-color:currentColor}@keyframes wm-toast-spin{to{transform:rotate(360deg)}}@keyframes wm-toast-slide-in{0%{opacity:0;transform:translate(1rem)}to{opacity:1;transform:translate(0)}}@media(prefers-reduced-motion:reduce){.wm-toast{animation:none}}\n"] }]
|
|
1517
1642
|
}] });
|
|
1518
1643
|
|
|
1644
|
+
/**
|
|
1645
|
+
* The floating surface a `[wmTooltip]` renders into.
|
|
1646
|
+
*
|
|
1647
|
+
* Internal to the tooltip — consumers never place this element themselves. It
|
|
1648
|
+
* exists as a component rather than a hand-built `<div>` so the panel's styles
|
|
1649
|
+
* ship with the component that needs them, the same arrangement `wm-popover`
|
|
1650
|
+
* uses for its panel.
|
|
1651
|
+
*
|
|
1652
|
+
* `ViewEncapsulation.None` is required, not stylistic: the panel is attached to
|
|
1653
|
+
* the CDK overlay container at the end of `<body>`, outside the host
|
|
1654
|
+
* component's DOM, so an emulated-encapsulation attribute selector would never
|
|
1655
|
+
* match it.
|
|
1656
|
+
*/
|
|
1657
|
+
class WmTooltipPanel {
|
|
1658
|
+
/** Tooltip body. Newlines are preserved — see `white-space` in tooltip.css. */
|
|
1659
|
+
text = input('', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
1660
|
+
/** Id the host points at with aria-describedby / aria-labelledby. */
|
|
1661
|
+
panelId = input('', ...(ngDevMode ? [{ debugName: "panelId" }] : []));
|
|
1662
|
+
/**
|
|
1663
|
+
* Drives the enter transition.
|
|
1664
|
+
*
|
|
1665
|
+
* The panel attaches with this false and is raised on the next frame, so the
|
|
1666
|
+
* fade has two states to move between. Attaching straight to the visible
|
|
1667
|
+
* state would give the browser nothing to interpolate from.
|
|
1668
|
+
*/
|
|
1669
|
+
visible = input(false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
1670
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmTooltipPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1671
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.1", type: WmTooltipPanel, isStandalone: true, selector: "wm-tooltip-panel", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, panelId: { classPropertyName: "panelId", publicName: "panelId", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<div
|
|
1672
|
+
class="wm-tooltip-panel"
|
|
1673
|
+
[class.wm-tooltip-panel--visible]="visible()"
|
|
1674
|
+
[id]="panelId()"
|
|
1675
|
+
role="tooltip"
|
|
1676
|
+
>{{ text() }}</div>`, isInline: true, styles: [".wm-tooltip-panel{max-width:var(--wm-tooltip-max-width, 260px);padding:6px 9px;background:var(--wm-text-primary);color:var(--wm-bg-base);border-radius:var(--wm-radius-md);box-shadow:var(--wm-shadow-md);font-family:var(--wm-font-sans);font-size:11.5px;font-weight:500;line-height:1.5;text-align:left;white-space:pre-line;overflow-wrap:break-word;opacity:0;transform:translateY(-2px);transition:opacity var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-tooltip-panel--visible{opacity:1;transform:translateY(0)}.wm-tooltip-above .wm-tooltip-panel{transform:translateY(2px)}.wm-tooltip-above .wm-tooltip-panel--visible{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.wm-tooltip-panel,.wm-tooltip-above .wm-tooltip-panel{transition:none;transform:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1677
|
+
}
|
|
1678
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmTooltipPanel, decorators: [{
|
|
1679
|
+
type: Component,
|
|
1680
|
+
args: [{ selector: 'wm-tooltip-panel', template: `<div
|
|
1681
|
+
class="wm-tooltip-panel"
|
|
1682
|
+
[class.wm-tooltip-panel--visible]="visible()"
|
|
1683
|
+
[id]="panelId()"
|
|
1684
|
+
role="tooltip"
|
|
1685
|
+
>{{ text() }}</div>`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: [".wm-tooltip-panel{max-width:var(--wm-tooltip-max-width, 260px);padding:6px 9px;background:var(--wm-text-primary);color:var(--wm-bg-base);border-radius:var(--wm-radius-md);box-shadow:var(--wm-shadow-md);font-family:var(--wm-font-sans);font-size:11.5px;font-weight:500;line-height:1.5;text-align:left;white-space:pre-line;overflow-wrap:break-word;opacity:0;transform:translateY(-2px);transition:opacity var(--wm-transition-fast),transform var(--wm-transition-fast)}.wm-tooltip-panel--visible{opacity:1;transform:translateY(0)}.wm-tooltip-above .wm-tooltip-panel{transform:translateY(2px)}.wm-tooltip-above .wm-tooltip-panel--visible{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.wm-tooltip-panel,.wm-tooltip-above .wm-tooltip-panel{transition:none;transform:none}}\n"] }]
|
|
1686
|
+
}], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], panelId: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelId", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }] } });
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* Add and remove one id from a space-separated ARIA id-reference attribute.
|
|
1690
|
+
*
|
|
1691
|
+
* A tooltip does not own `aria-describedby`. The host may already point at an
|
|
1692
|
+
* error message, a hint, or a description the application wrote, and the
|
|
1693
|
+
* tooltip has to sit alongside those rather than replace them. It also has to
|
|
1694
|
+
* leave the attribute exactly as it found it once the tooltip hides, because a
|
|
1695
|
+
* tooltip opens and closes many times over the life of one element and any
|
|
1696
|
+
* drift accumulates.
|
|
1697
|
+
*
|
|
1698
|
+
* Kept as pure string functions so the rule is testable without a DOM: this is
|
|
1699
|
+
* the part that has to be right every time, and the overlay around it is not.
|
|
1700
|
+
*/
|
|
1701
|
+
/** Split an id-reference list into its tokens, discarding empty runs. */
|
|
1702
|
+
function idTokens(value) {
|
|
1703
|
+
if (!value)
|
|
1704
|
+
return [];
|
|
1705
|
+
return value.split(/\s+/).filter((token) => token.length > 0);
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Return the attribute value with `id` present exactly once, appended last.
|
|
1709
|
+
*
|
|
1710
|
+
* Appending rather than prepending matters: assistive technology announces a
|
|
1711
|
+
* description list in order, and the application's own description is the more
|
|
1712
|
+
* specific one. The tooltip is supplementary, so it speaks last.
|
|
1713
|
+
*/
|
|
1714
|
+
function withIdToken(value, id) {
|
|
1715
|
+
const tokens = idTokens(value).filter((token) => token !== id);
|
|
1716
|
+
tokens.push(id);
|
|
1717
|
+
return tokens.join(' ');
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Return the attribute value with `id` removed, or `null` when nothing is left.
|
|
1721
|
+
*
|
|
1722
|
+
* `null` is the signal to remove the attribute outright. An empty
|
|
1723
|
+
* `aria-describedby=""` is not the same as no attribute: some assistive
|
|
1724
|
+
* technology treats the empty string as a broken reference, and it also makes
|
|
1725
|
+
* a host that never carried the attribute look, on inspection, as though it
|
|
1726
|
+
* did.
|
|
1727
|
+
*/
|
|
1728
|
+
function withoutIdToken(value, id) {
|
|
1729
|
+
const tokens = idTokens(value).filter((token) => token !== id);
|
|
1730
|
+
return tokens.length > 0 ? tokens.join(' ') : null;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
/** Grace period before a pointer leaving the host closes the tooltip. */
|
|
1734
|
+
const HIDE_DELAY_MS = 120;
|
|
1735
|
+
let nextId = 0;
|
|
1736
|
+
/**
|
|
1737
|
+
* WmTooltip — accessible floating explanation for any element.
|
|
1738
|
+
*
|
|
1739
|
+
* ```html
|
|
1740
|
+
* <button class="wm-btn btn-icon-only" aria-label="Undo" [wmTooltip]="'Undo the last commit'">…</button>
|
|
1741
|
+
* ```
|
|
1742
|
+
*
|
|
1743
|
+
* Why this exists next to the CSS `[data-tooltip]` chip: that chip is a
|
|
1744
|
+
* pseudo-element on the host, so it cannot escape an ancestor's
|
|
1745
|
+
* `overflow: hidden`, cannot wrap, and is invisible to assistive technology —
|
|
1746
|
+
* it is a visual hint that must be paired with an `aria-label` saying the same
|
|
1747
|
+
* thing twice. Reach for the chip for a one-to-five word label on a toolbar
|
|
1748
|
+
* button. Reach for this directive when the text explains something, when the
|
|
1749
|
+
* host sits inside a scrolling container, or when the explanation must be part
|
|
1750
|
+
* of the accessible description rather than a duplicate of it.
|
|
1751
|
+
*
|
|
1752
|
+
* Accessibility:
|
|
1753
|
+
* - The panel carries `role="tooltip"` and a generated id.
|
|
1754
|
+
* - While open, the host points at that id with `aria-describedby` (default)
|
|
1755
|
+
* or `aria-labelledby`. The id is appended to whatever the host already
|
|
1756
|
+
* referenced and removed again on hide, so an application's own description
|
|
1757
|
+
* survives untouched.
|
|
1758
|
+
* - Opens on hover and on `:focus-visible`, so a mouse click that moves focus
|
|
1759
|
+
* to a button does not leave a tooltip hanging behind the pointer.
|
|
1760
|
+
* - WCAG 1.4.13 (Content on Hover or Focus): Escape dismisses without moving
|
|
1761
|
+
* focus; the pointer can travel into the panel and read it without it
|
|
1762
|
+
* closing; nothing auto-dismisses on a timer.
|
|
1763
|
+
*
|
|
1764
|
+
* Keyboard reachability is the caller's responsibility. A tooltip on an element
|
|
1765
|
+
* that cannot take focus is unreachable by keyboard, so set
|
|
1766
|
+
* `wmTooltipFocusable` to make the host a tab stop — but NOT inside a
|
|
1767
|
+
* roving-tabindex container (tree, listbox, grid, tablist). There the container
|
|
1768
|
+
* is a single tab stop by design, and adding one stop per row is worse for
|
|
1769
|
+
* keyboard users than the gap it closes. Put the tooltip on the row itself, or
|
|
1770
|
+
* expose it from a control the roving focus already reaches.
|
|
1771
|
+
*/
|
|
1772
|
+
class WmTooltip {
|
|
1773
|
+
/** Tooltip body. Blank or absent means the tooltip never opens. */
|
|
1774
|
+
wmTooltip = input('', ...(ngDevMode ? [{ debugName: "wmTooltip" }] : []));
|
|
1775
|
+
/** Preferred side. Flips automatically when the viewport has no room. */
|
|
1776
|
+
wmTooltipPosition = input('bottom', ...(ngDevMode ? [{ debugName: "wmTooltipPosition" }] : []));
|
|
1777
|
+
/** Milliseconds a pointer must rest on the host before the panel opens. */
|
|
1778
|
+
wmTooltipDelay = input(250, ...(ngDevMode ? [{ debugName: "wmTooltipDelay" }] : []));
|
|
1779
|
+
/** Suppress the tooltip without unbinding it — for a control whose explanation only applies in some states. */
|
|
1780
|
+
wmTooltipDisabled = input(false, ...(ngDevMode ? [{ debugName: "wmTooltipDisabled" }] : []));
|
|
1781
|
+
/** Whether the panel describes the host or names it. See WmTooltipRelation. */
|
|
1782
|
+
wmTooltipRelation = input('description', ...(ngDevMode ? [{ debugName: "wmTooltipRelation" }] : []));
|
|
1783
|
+
/**
|
|
1784
|
+
* Make a non-focusable host a tab stop so a keyboard user can reach the
|
|
1785
|
+
* tooltip. Only applied when the host is not already focusable, and never
|
|
1786
|
+
* safe inside a roving-tabindex container — see the class comment.
|
|
1787
|
+
*/
|
|
1788
|
+
wmTooltipFocusable = input(false, ...(ngDevMode ? [{ debugName: "wmTooltipFocusable" }] : []));
|
|
1789
|
+
host = inject(ElementRef).nativeElement;
|
|
1790
|
+
injector = inject(Injector);
|
|
1791
|
+
renderer = inject(Renderer2);
|
|
1792
|
+
panelId = `wm-tooltip-${nextId++}`;
|
|
1793
|
+
overlayRef = null;
|
|
1794
|
+
panelRef = null;
|
|
1795
|
+
portal = null;
|
|
1796
|
+
showTimer = null;
|
|
1797
|
+
hideTimer = null;
|
|
1798
|
+
raiseFrame = null;
|
|
1799
|
+
pointerOnHost = false;
|
|
1800
|
+
pointerOnPanel = false;
|
|
1801
|
+
hostFocused = false;
|
|
1802
|
+
detachDocumentListeners = null;
|
|
1803
|
+
/**
|
|
1804
|
+
* Elements between the host and the document that can clip it, resolved
|
|
1805
|
+
* once per open. They cannot change while the panel is up, and walking the
|
|
1806
|
+
* ancestor chain on every scroll event would be work done per frame.
|
|
1807
|
+
*/
|
|
1808
|
+
clippingAncestors = [];
|
|
1809
|
+
constructor() {
|
|
1810
|
+
inject(DestroyRef).onDestroy(() => this.teardown());
|
|
1811
|
+
effect(() => {
|
|
1812
|
+
const focusable = this.wmTooltipFocusable();
|
|
1813
|
+
// Read so the effect re-runs when the host stops carrying a tooltip.
|
|
1814
|
+
const hasText = this.text().length > 0;
|
|
1815
|
+
if (focusable && hasText && !this.isNativelyFocusable()) {
|
|
1816
|
+
this.renderer.setAttribute(this.host, 'tabindex', '0');
|
|
1817
|
+
}
|
|
1818
|
+
});
|
|
1819
|
+
effect(() => {
|
|
1820
|
+
const text = this.text();
|
|
1821
|
+
const disabled = this.wmTooltipDisabled();
|
|
1822
|
+
if (disabled || text.length === 0) {
|
|
1823
|
+
this.hideNow();
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
this.panelRef?.setInput('text', text);
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
/** Open the tooltip immediately, bypassing the hover delay. */
|
|
1830
|
+
show() {
|
|
1831
|
+
this.clearTimer('show');
|
|
1832
|
+
this.clearTimer('hide');
|
|
1833
|
+
if (this.overlayRef?.hasAttached())
|
|
1834
|
+
return;
|
|
1835
|
+
if (this.wmTooltipDisabled() || this.text().length === 0)
|
|
1836
|
+
return;
|
|
1837
|
+
const overlayRef = this.ensureOverlay();
|
|
1838
|
+
this.panelRef = overlayRef.attach(this.portal);
|
|
1839
|
+
this.panelRef.setInput('text', this.text());
|
|
1840
|
+
this.panelRef.setInput('panelId', this.panelId);
|
|
1841
|
+
this.panelRef.changeDetectorRef.detectChanges();
|
|
1842
|
+
this.linkHostToPanel();
|
|
1843
|
+
this.clippingAncestors = this.resolveClippingAncestors();
|
|
1844
|
+
this.attachDocumentListeners();
|
|
1845
|
+
// Attach invisible, raise on the next frame: a transition needs a state to
|
|
1846
|
+
// move away from, and attaching straight to the visible state gives the
|
|
1847
|
+
// browser nothing to interpolate.
|
|
1848
|
+
this.raiseFrame = requestAnimationFrame(() => {
|
|
1849
|
+
this.raiseFrame = null;
|
|
1850
|
+
this.panelRef?.setInput('visible', true);
|
|
1851
|
+
this.panelRef?.changeDetectorRef.detectChanges();
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
/** Close the tooltip immediately, without the pointer grace period. */
|
|
1855
|
+
hideNow() {
|
|
1856
|
+
this.clearTimer('show');
|
|
1857
|
+
this.clearTimer('hide');
|
|
1858
|
+
if (this.raiseFrame !== null) {
|
|
1859
|
+
cancelAnimationFrame(this.raiseFrame);
|
|
1860
|
+
this.raiseFrame = null;
|
|
1861
|
+
}
|
|
1862
|
+
this.releaseDocumentListeners();
|
|
1863
|
+
this.unlinkHostFromPanel();
|
|
1864
|
+
this.clippingAncestors = [];
|
|
1865
|
+
if (this.overlayRef?.hasAttached()) {
|
|
1866
|
+
this.overlayRef.detach();
|
|
1867
|
+
}
|
|
1868
|
+
this.panelRef = null;
|
|
1869
|
+
}
|
|
1870
|
+
/** True while the panel is attached. Exposed for tests and for callers driving it manually. */
|
|
1871
|
+
get isOpen() {
|
|
1872
|
+
return this.overlayRef?.hasAttached() === true;
|
|
1873
|
+
}
|
|
1874
|
+
// ── Trigger handling ─────────────────────────────────────────────
|
|
1875
|
+
onPointerEnterHost() {
|
|
1876
|
+
this.pointerOnHost = true;
|
|
1877
|
+
this.scheduleShow();
|
|
1878
|
+
}
|
|
1879
|
+
onPointerLeaveHost() {
|
|
1880
|
+
this.pointerOnHost = false;
|
|
1881
|
+
this.scheduleHide();
|
|
1882
|
+
}
|
|
1883
|
+
onFocusHost() {
|
|
1884
|
+
// Only keyboard focus opens the tooltip. Clicking a button focuses it too,
|
|
1885
|
+
// and a tooltip that opens on click sits under the pointer that just left.
|
|
1886
|
+
if (!this.matchesFocusVisible())
|
|
1887
|
+
return;
|
|
1888
|
+
this.hostFocused = true;
|
|
1889
|
+
this.show();
|
|
1890
|
+
}
|
|
1891
|
+
onBlurHost() {
|
|
1892
|
+
this.hostFocused = false;
|
|
1893
|
+
this.scheduleHide();
|
|
1894
|
+
}
|
|
1895
|
+
// ── Internals ────────────────────────────────────────────────────
|
|
1896
|
+
text() {
|
|
1897
|
+
return (this.wmTooltip() ?? '').toString();
|
|
1898
|
+
}
|
|
1899
|
+
scheduleShow() {
|
|
1900
|
+
if (this.isOpen) {
|
|
1901
|
+
this.clearTimer('hide');
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
if (this.wmTooltipDisabled() || this.text().length === 0)
|
|
1905
|
+
return;
|
|
1906
|
+
this.clearTimer('hide');
|
|
1907
|
+
this.clearTimer('show');
|
|
1908
|
+
const delay = Math.max(0, this.wmTooltipDelay());
|
|
1909
|
+
this.showTimer = setTimeout(() => {
|
|
1910
|
+
this.showTimer = null;
|
|
1911
|
+
this.show();
|
|
1912
|
+
}, delay);
|
|
1913
|
+
}
|
|
1914
|
+
scheduleHide() {
|
|
1915
|
+
this.clearTimer('show');
|
|
1916
|
+
this.clearTimer('hide');
|
|
1917
|
+
this.hideTimer = setTimeout(() => {
|
|
1918
|
+
this.hideTimer = null;
|
|
1919
|
+
if (this.pointerOnHost || this.pointerOnPanel || this.hostFocused)
|
|
1920
|
+
return;
|
|
1921
|
+
this.hideNow();
|
|
1922
|
+
}, HIDE_DELAY_MS);
|
|
1923
|
+
}
|
|
1924
|
+
clearTimer(which) {
|
|
1925
|
+
const timer = which === 'show' ? this.showTimer : this.hideTimer;
|
|
1926
|
+
if (timer === null)
|
|
1927
|
+
return;
|
|
1928
|
+
clearTimeout(timer);
|
|
1929
|
+
if (which === 'show')
|
|
1930
|
+
this.showTimer = null;
|
|
1931
|
+
else
|
|
1932
|
+
this.hideTimer = null;
|
|
1933
|
+
}
|
|
1934
|
+
ensureOverlay() {
|
|
1935
|
+
if (this.overlayRef)
|
|
1936
|
+
return this.overlayRef;
|
|
1937
|
+
const positionStrategy = createFlexibleConnectedPositionStrategy(this.injector, this.host)
|
|
1938
|
+
.withFlexibleDimensions(false)
|
|
1939
|
+
.withPush(true)
|
|
1940
|
+
.withPositions(this.positions());
|
|
1941
|
+
this.overlayRef = createOverlayRef(this.injector, {
|
|
1942
|
+
positionStrategy,
|
|
1943
|
+
scrollStrategy: createRepositionScrollStrategy(this.injector),
|
|
1944
|
+
panelClass: 'wm-tooltip-overlay',
|
|
1945
|
+
hasBackdrop: false,
|
|
1946
|
+
});
|
|
1947
|
+
// The pointer may travel from the host into the panel. WCAG 1.4.13 requires
|
|
1948
|
+
// that reading the tooltip that way does not dismiss it, which matters most
|
|
1949
|
+
// for the long explanations this directive exists to carry.
|
|
1950
|
+
const overlayEl = this.overlayRef.overlayElement;
|
|
1951
|
+
overlayEl.addEventListener('mouseenter', this.onPointerEnterPanel);
|
|
1952
|
+
overlayEl.addEventListener('mouseleave', this.onPointerLeavePanel);
|
|
1953
|
+
positionStrategy.positionChanges.subscribe((change) => {
|
|
1954
|
+
const above = change.connectionPair.overlayY === 'bottom';
|
|
1955
|
+
overlayEl.classList.toggle('wm-tooltip-above', above);
|
|
1956
|
+
});
|
|
1957
|
+
this.portal = new ComponentPortal(WmTooltipPanel, null, this.injector);
|
|
1958
|
+
return this.overlayRef;
|
|
1959
|
+
}
|
|
1960
|
+
positions() {
|
|
1961
|
+
const offset = 6;
|
|
1962
|
+
const below = {
|
|
1963
|
+
originX: 'center', originY: 'bottom',
|
|
1964
|
+
overlayX: 'center', overlayY: 'top', offsetY: offset,
|
|
1965
|
+
};
|
|
1966
|
+
const above = {
|
|
1967
|
+
originX: 'center', originY: 'top',
|
|
1968
|
+
overlayX: 'center', overlayY: 'bottom', offsetY: -offset,
|
|
1969
|
+
};
|
|
1970
|
+
const after = {
|
|
1971
|
+
originX: 'end', originY: 'center',
|
|
1972
|
+
overlayX: 'start', overlayY: 'center', offsetX: offset,
|
|
1973
|
+
};
|
|
1974
|
+
const before = {
|
|
1975
|
+
originX: 'start', originY: 'center',
|
|
1976
|
+
overlayX: 'end', overlayY: 'center', offsetX: -offset,
|
|
1977
|
+
};
|
|
1978
|
+
// The preferred side first, then its opposite, then the perpendicular pair.
|
|
1979
|
+
// A tooltip that has run out of room on both preferred sides is better off
|
|
1980
|
+
// beside the host than pushed over the top of it.
|
|
1981
|
+
switch (this.wmTooltipPosition()) {
|
|
1982
|
+
case 'top': return [above, below, after, before];
|
|
1983
|
+
case 'left': return [before, after, above, below];
|
|
1984
|
+
case 'right': return [after, before, above, below];
|
|
1985
|
+
default: return [below, above, after, before];
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
onPointerEnterPanel = () => {
|
|
1989
|
+
this.pointerOnPanel = true;
|
|
1990
|
+
this.clearTimer('hide');
|
|
1991
|
+
};
|
|
1992
|
+
onPointerLeavePanel = () => {
|
|
1993
|
+
this.pointerOnPanel = false;
|
|
1994
|
+
this.scheduleHide();
|
|
1995
|
+
};
|
|
1996
|
+
attachDocumentListeners() {
|
|
1997
|
+
if (this.detachDocumentListeners)
|
|
1998
|
+
return;
|
|
1999
|
+
const onKeydown = (event) => {
|
|
2000
|
+
if (event.key !== 'Escape')
|
|
2001
|
+
return;
|
|
2002
|
+
// Dismiss without moving focus and without stopping the event: the host
|
|
2003
|
+
// may sit in a dialog or a menu whose own Escape handling still applies,
|
|
2004
|
+
// and swallowing it would trap the user one layer deeper than they are.
|
|
2005
|
+
this.hideNow();
|
|
2006
|
+
};
|
|
2007
|
+
// A pointer press anywhere settles the interaction. Without this, a panel
|
|
2008
|
+
// that overlaps the control below it can swallow the click that was meant
|
|
2009
|
+
// for that control.
|
|
2010
|
+
const onPointerDown = () => this.hideNow();
|
|
2011
|
+
// Re-anchor to the host whenever anything underneath it moves. The capture
|
|
2012
|
+
// phase is load-bearing and the reason this is not left to CDK's
|
|
2013
|
+
// reposition strategy - see `onViewportChange`.
|
|
2014
|
+
const stopFollowing = onViewportChange(() => this.reanchor());
|
|
2015
|
+
document.addEventListener('keydown', onKeydown, true);
|
|
2016
|
+
document.addEventListener('pointerdown', onPointerDown, true);
|
|
2017
|
+
this.detachDocumentListeners = () => {
|
|
2018
|
+
document.removeEventListener('keydown', onKeydown, true);
|
|
2019
|
+
document.removeEventListener('pointerdown', onPointerDown, true);
|
|
2020
|
+
stopFollowing();
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
releaseDocumentListeners() {
|
|
2024
|
+
this.detachDocumentListeners?.();
|
|
2025
|
+
this.detachDocumentListeners = null;
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Follow the host, or give up on it.
|
|
2029
|
+
*
|
|
2030
|
+
* Repositioning alone is not enough. Once the host has scrolled out of its
|
|
2031
|
+
* container the panel would be pushed against the container edge and left
|
|
2032
|
+
* explaining a row the reader can no longer see - worse than not being there,
|
|
2033
|
+
* because it now appears to describe whatever it happens to cover.
|
|
2034
|
+
*/
|
|
2035
|
+
reanchor() {
|
|
2036
|
+
if (!this.overlayRef?.hasAttached())
|
|
2037
|
+
return;
|
|
2038
|
+
if (this.isHostClipped()) {
|
|
2039
|
+
this.hideNow();
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
this.overlayRef.updatePosition();
|
|
2043
|
+
}
|
|
2044
|
+
/** Every ancestor that can clip the host, nearest first. */
|
|
2045
|
+
resolveClippingAncestors() {
|
|
2046
|
+
const clipping = [];
|
|
2047
|
+
let node = this.host.parentElement;
|
|
2048
|
+
while (node) {
|
|
2049
|
+
const style = getComputedStyle(node);
|
|
2050
|
+
const clips = [style.overflow, style.overflowX, style.overflowY].some((value) => value === 'auto' || value === 'scroll' || value === 'hidden');
|
|
2051
|
+
if (clips)
|
|
2052
|
+
clipping.push(node);
|
|
2053
|
+
node = node.parentElement;
|
|
2054
|
+
}
|
|
2055
|
+
return clipping;
|
|
2056
|
+
}
|
|
2057
|
+
/** True once no part of the host is inside every container that clips it. */
|
|
2058
|
+
isHostClipped() {
|
|
2059
|
+
const host = this.host.getBoundingClientRect();
|
|
2060
|
+
const outside = (bounds) => host.bottom < bounds.top ||
|
|
2061
|
+
host.top > bounds.bottom ||
|
|
2062
|
+
host.right < bounds.left ||
|
|
2063
|
+
host.left > bounds.right;
|
|
2064
|
+
const viewport = {
|
|
2065
|
+
top: 0,
|
|
2066
|
+
left: 0,
|
|
2067
|
+
bottom: window.innerHeight || document.documentElement.clientHeight,
|
|
2068
|
+
right: window.innerWidth || document.documentElement.clientWidth,
|
|
2069
|
+
};
|
|
2070
|
+
if (outside(viewport))
|
|
2071
|
+
return true;
|
|
2072
|
+
return this.clippingAncestors.some((ancestor) => outside(ancestor.getBoundingClientRect()));
|
|
2073
|
+
}
|
|
2074
|
+
relationAttribute() {
|
|
2075
|
+
return this.wmTooltipRelation() === 'label' ? 'aria-labelledby' : 'aria-describedby';
|
|
2076
|
+
}
|
|
2077
|
+
linkHostToPanel() {
|
|
2078
|
+
const attribute = this.relationAttribute();
|
|
2079
|
+
const next = withIdToken(this.host.getAttribute(attribute), this.panelId);
|
|
2080
|
+
this.renderer.setAttribute(this.host, attribute, next);
|
|
2081
|
+
}
|
|
2082
|
+
unlinkHostFromPanel() {
|
|
2083
|
+
// Both are cleared rather than only the current one: `wmTooltipRelation`
|
|
2084
|
+
// can change while the panel is open, and the attribute written on show
|
|
2085
|
+
// would otherwise be orphaned, leaving the host pointing at an id that no
|
|
2086
|
+
// longer exists in the document.
|
|
2087
|
+
for (const attribute of ['aria-describedby', 'aria-labelledby']) {
|
|
2088
|
+
const current = this.host.getAttribute(attribute);
|
|
2089
|
+
if (current === null)
|
|
2090
|
+
continue;
|
|
2091
|
+
const next = withoutIdToken(current, this.panelId);
|
|
2092
|
+
if (next === null)
|
|
2093
|
+
this.renderer.removeAttribute(this.host, attribute);
|
|
2094
|
+
else if (next !== current)
|
|
2095
|
+
this.renderer.setAttribute(this.host, attribute, next);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
isNativelyFocusable() {
|
|
2099
|
+
if (this.host.hasAttribute('tabindex'))
|
|
2100
|
+
return true;
|
|
2101
|
+
return ['a', 'button', 'input', 'select', 'textarea', 'summary'].includes(this.host.tagName.toLowerCase());
|
|
2102
|
+
}
|
|
2103
|
+
matchesFocusVisible() {
|
|
2104
|
+
// `:focus-visible` is unsupported in some test environments; treating an
|
|
2105
|
+
// unsupported selector as "not keyboard focus" would silently disable the
|
|
2106
|
+
// keyboard path, so fall back to opening on any focus.
|
|
2107
|
+
try {
|
|
2108
|
+
return this.host.matches(':focus-visible');
|
|
2109
|
+
}
|
|
2110
|
+
catch {
|
|
2111
|
+
return true;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
teardown() {
|
|
2115
|
+
this.hideNow();
|
|
2116
|
+
const overlayEl = this.overlayRef?.overlayElement;
|
|
2117
|
+
overlayEl?.removeEventListener('mouseenter', this.onPointerEnterPanel);
|
|
2118
|
+
overlayEl?.removeEventListener('mouseleave', this.onPointerLeavePanel);
|
|
2119
|
+
this.overlayRef?.dispose();
|
|
2120
|
+
this.overlayRef = null;
|
|
2121
|
+
this.portal = null;
|
|
2122
|
+
}
|
|
2123
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmTooltip, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2124
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.1", type: WmTooltip, isStandalone: true, selector: "[wmTooltip]", inputs: { wmTooltip: { classPropertyName: "wmTooltip", publicName: "wmTooltip", isSignal: true, isRequired: false, transformFunction: null }, wmTooltipPosition: { classPropertyName: "wmTooltipPosition", publicName: "wmTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, wmTooltipDelay: { classPropertyName: "wmTooltipDelay", publicName: "wmTooltipDelay", isSignal: true, isRequired: false, transformFunction: null }, wmTooltipDisabled: { classPropertyName: "wmTooltipDisabled", publicName: "wmTooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, wmTooltipRelation: { classPropertyName: "wmTooltipRelation", publicName: "wmTooltipRelation", isSignal: true, isRequired: false, transformFunction: null }, wmTooltipFocusable: { classPropertyName: "wmTooltipFocusable", publicName: "wmTooltipFocusable", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onPointerEnterHost()", "mouseleave": "onPointerLeaveHost()", "focusin": "onFocusHost()", "focusout": "onBlurHost()", "click": "hideNow()" } }, exportAs: ["wmTooltip"], ngImport: i0 });
|
|
2125
|
+
}
|
|
2126
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: WmTooltip, decorators: [{
|
|
2127
|
+
type: Directive,
|
|
2128
|
+
args: [{
|
|
2129
|
+
selector: '[wmTooltip]',
|
|
2130
|
+
exportAs: 'wmTooltip',
|
|
2131
|
+
host: {
|
|
2132
|
+
'(mouseenter)': 'onPointerEnterHost()',
|
|
2133
|
+
'(mouseleave)': 'onPointerLeaveHost()',
|
|
2134
|
+
'(focusin)': 'onFocusHost()',
|
|
2135
|
+
'(focusout)': 'onBlurHost()',
|
|
2136
|
+
'(click)': 'hideNow()',
|
|
2137
|
+
},
|
|
2138
|
+
}]
|
|
2139
|
+
}], ctorParameters: () => [], propDecorators: { wmTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "wmTooltip", required: false }] }], wmTooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "wmTooltipPosition", required: false }] }], wmTooltipDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "wmTooltipDelay", required: false }] }], wmTooltipDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "wmTooltipDisabled", required: false }] }], wmTooltipRelation: [{ type: i0.Input, args: [{ isSignal: true, alias: "wmTooltipRelation", required: false }] }], wmTooltipFocusable: [{ type: i0.Input, args: [{ isSignal: true, alias: "wmTooltipFocusable", required: false }] }] } });
|
|
2140
|
+
|
|
1519
2141
|
class WmLicenseService {
|
|
1520
2142
|
toast = inject(WmToastService);
|
|
1521
2143
|
license = signal(null, ...(ngDevMode ? [{ debugName: "license" }] : []));
|
|
@@ -1656,5 +2278,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1656
2278
|
* Generated bundle index. Do not edit.
|
|
1657
2279
|
*/
|
|
1658
2280
|
|
|
1659
|
-
export { Checkbox, GhostField, NumberInput, PageHeader, Toggle, WmAutoFocus, WmButton, WmCombobox, WmDialog, WmLicenseDialog, WmLicenseNudge, WmLicenseService, WmModal, WmPopover, WmSelect, WmTab, WmTabs, WmToastContainer, WmToastService };
|
|
2281
|
+
export { Checkbox, GhostField, NumberInput, PageHeader, Toggle, WmAnchoredScrollStrategy, WmAutoFocus, WmButton, WmCombobox, WmDialog, WmLicenseDialog, WmLicenseNudge, WmLicenseService, WmModal, WmPopover, WmSelect, WmTab, WmTabs, WmToastContainer, WmToastService, WmTooltip, onViewportChange };
|
|
1660
2282
|
//# sourceMappingURL=wildmason-aegis.mjs.map
|