@lumston/ds-angular 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/lumston-ds-angular-src-data-display.mjs +215 -0
- package/fesm2022/lumston-ds-angular-src-data-display.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-dropdown.mjs +94 -0
- package/fesm2022/lumston-ds-angular-src-dropdown.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-feedback.mjs +400 -0
- package/fesm2022/lumston-ds-angular-src-feedback.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-icon.mjs +148 -0
- package/fesm2022/lumston-ds-angular-src-icon.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-inputs.mjs +1132 -0
- package/fesm2022/lumston-ds-angular-src-inputs.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-navigation.mjs +473 -0
- package/fesm2022/lumston-ds-angular-src-navigation.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-overlay.mjs +1038 -0
- package/fesm2022/lumston-ds-angular-src-overlay.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular-src-typography.mjs +303 -0
- package/fesm2022/lumston-ds-angular-src-typography.mjs.map +1 -0
- package/fesm2022/lumston-ds-angular.mjs +90 -65
- package/fesm2022/lumston-ds-angular.mjs.map +1 -1
- package/package.json +58 -32
- package/styles/index.css +4 -0
- package/styles/ls-icons.css +482 -0
- package/types/lumston-ds-angular-src-data-display.d.ts +50 -0
- package/types/lumston-ds-angular-src-data-display.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-dropdown.d.ts +28 -0
- package/types/lumston-ds-angular-src-dropdown.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-feedback.d.ts +75 -0
- package/types/lumston-ds-angular-src-feedback.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-icon.d.ts +27 -0
- package/types/lumston-ds-angular-src-icon.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-inputs.d.ts +201 -0
- package/types/lumston-ds-angular-src-inputs.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-navigation.d.ts +186 -0
- package/types/lumston-ds-angular-src-navigation.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-overlay.d.ts +357 -0
- package/types/lumston-ds-angular-src-overlay.d.ts.map +1 -0
- package/types/lumston-ds-angular-src-typography.d.ts +51 -0
- package/types/lumston-ds-angular-src-typography.d.ts.map +1 -0
- package/types/lumston-ds-angular.d.ts.map +1 -1
- package/styles/data-display/badge/badge.styles.css +0 -244
- package/styles/data-display/chip/chip.styles.css +0 -245
- package/styles/dropdown/dropdown.styles.css +0 -14
- package/styles/feedback/alert/alert.styles.css +0 -304
- package/styles/feedback/loader/loader.styles.css +0 -132
- package/styles/feedback/progress-bar/progress-bar.styles.css +0 -193
- package/styles/icon/icon.styles.css +0 -11
- package/styles/inputs/button/button.styles.css +0 -377
- package/styles/inputs/checkbox/checkbox.styles.css +0 -157
- package/styles/inputs/icon-button/icon-button.styles.css +0 -157
- package/styles/inputs/radio-button/radio-button.styles.css +0 -274
- package/styles/inputs/slider/slider.styles.css +0 -228
- package/styles/inputs/switch/switch.styles.css +0 -483
- package/styles/media/avatar/avatar.styles.css +0 -112
- package/styles/media/avatar-group/avatar-group.styles.css +0 -37
- package/styles/media/logo/logo.styles.css +0 -40
- package/styles/navigation/breadcrumb/breadcrumb.styles.css +0 -144
- package/styles/navigation/pagination/pagination.styles.css +0 -336
- package/styles/overlay/menu/menu.styles.css +0 -138
- package/styles/overlay/modal/modal.styles.css +0 -178
- package/styles/overlay/popover/popover.styles.css +0 -112
- package/styles/overlay/tooltip/tooltip.styles.css +0 -172
- package/styles/styles/index.css +0 -28
- package/styles/typography/link/link.styles.css +0 -93
- package/styles/typography/tag/tag.styles.css +0 -274
- package/styles/typography/text/text.styles.css +0 -176
- /package/styles/{styles/_base.css → _base.css} +0 -0
- /package/styles/{styles/_tokens.css → _tokens.css} +0 -0
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, DestroyRef, model, input, output, viewChild, signal, effect, untracked, HostListener, ChangeDetectionStrategy, Component, computed, ElementRef, Directive, Injectable, contentChild, Renderer2 } from '@angular/core';
|
|
3
|
+
import { DOCUMENT } from '@angular/common';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Renders an anchored floating menu panel attached to a trigger element.
|
|
7
|
+
*
|
|
8
|
+
* Supports both **controlled** and **directive-driven** open modes:
|
|
9
|
+
* - **Controlled**: bind `[(open)]` and respond to `opened`/`closed` outputs.
|
|
10
|
+
* - **Directive-driven**: apply `lsMenuTriggerFor` to any host element;
|
|
11
|
+
* the directive calls `setAnchorEl()` and `toggle()` automatically.
|
|
12
|
+
*
|
|
13
|
+
* Panel position is derived from the anchor element's bounding rect combined
|
|
14
|
+
* with `anchorOrigin` (attachment point on the trigger) and `transformOrigin`
|
|
15
|
+
* (attachment point on the panel). Position is recomputed each time the
|
|
16
|
+
* panel opens via a `setTimeout(0)` deferral to ensure the panel is measured.
|
|
17
|
+
*
|
|
18
|
+
* When `disablePortal` is `false` (default), the panel uses `position: fixed`
|
|
19
|
+
* for viewport-relative placement. When `true`, it uses `position: absolute`.
|
|
20
|
+
*
|
|
21
|
+
* Keyboard: `Escape` closes the panel; `ArrowDown`/`ArrowUp` move focus
|
|
22
|
+
* between enabled `[role="menuitem"]` elements inside the panel.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```html
|
|
26
|
+
* <!-- Controlled -->
|
|
27
|
+
* <ls-menu [(open)]="isOpen" [anchorEl]="triggerEl">
|
|
28
|
+
* <ls-menu-item (itemClick)="onEdit()">Edit</ls-menu-item>
|
|
29
|
+
* </ls-menu>
|
|
30
|
+
*
|
|
31
|
+
* <!-- Directive-driven -->
|
|
32
|
+
* <button [lsMenuTriggerFor]="myMenu">Open</button>
|
|
33
|
+
* <ls-menu #myMenu>
|
|
34
|
+
* <ls-menu-item>Delete</ls-menu-item>
|
|
35
|
+
* </ls-menu>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
class MenuComponent {
|
|
39
|
+
constructor() {
|
|
40
|
+
this._document = inject(DOCUMENT);
|
|
41
|
+
this._destroyRef = inject(DestroyRef);
|
|
42
|
+
/** Controls the visibility of the menu panel. Supports two-way binding. */
|
|
43
|
+
this.open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
44
|
+
/**
|
|
45
|
+
* Element used to position the menu panel.
|
|
46
|
+
*
|
|
47
|
+
* Accepts a native DOM element, an `ElementRef`, or any Angular component
|
|
48
|
+
* that exposes a `nativeElement: HTMLElement` property (e.g. `ls-button`).
|
|
49
|
+
* In templates, pass a template reference variable directly:
|
|
50
|
+
* `[anchorEl]="btn"` where `#btn` is on any element or component.
|
|
51
|
+
*
|
|
52
|
+
* When using `lsMenuTriggerFor`, this is set automatically by the directive
|
|
53
|
+
* via `setAnchorEl()`. When using controlled mode, bind directly.
|
|
54
|
+
*
|
|
55
|
+
* @default null
|
|
56
|
+
*/
|
|
57
|
+
this.anchorEl = input(null, ...(ngDevMode ? [{ debugName: "anchorEl" }] : []));
|
|
58
|
+
/**
|
|
59
|
+
* Defines the attachment point on the anchor element.
|
|
60
|
+
*
|
|
61
|
+
* Combined with `transformOrigin` to compute the panel's `top`/`left`
|
|
62
|
+
* position via `getBoundingClientRect()`.
|
|
63
|
+
*
|
|
64
|
+
* @default { vertical: 'bottom', horizontal: 'left' }
|
|
65
|
+
*/
|
|
66
|
+
this.anchorOrigin = input({
|
|
67
|
+
vertical: 'bottom',
|
|
68
|
+
horizontal: 'left',
|
|
69
|
+
}, ...(ngDevMode ? [{ debugName: "anchorOrigin" }] : []));
|
|
70
|
+
/**
|
|
71
|
+
* Defines the attachment point on the menu panel surface.
|
|
72
|
+
*
|
|
73
|
+
* The panel is translated so this point aligns with `anchorOrigin`.
|
|
74
|
+
*
|
|
75
|
+
* @default { vertical: 'top', horizontal: 'left' }
|
|
76
|
+
*/
|
|
77
|
+
this.transformOrigin = input({
|
|
78
|
+
vertical: 'top',
|
|
79
|
+
horizontal: 'left',
|
|
80
|
+
}, ...(ngDevMode ? [{ debugName: "transformOrigin" }] : []));
|
|
81
|
+
/**
|
|
82
|
+
* When `false` (default), the panel uses `position: fixed` for
|
|
83
|
+
* viewport-relative placement. When `true`, uses `position: absolute`
|
|
84
|
+
* and renders relative to the nearest positioned ancestor.
|
|
85
|
+
*
|
|
86
|
+
* @default false
|
|
87
|
+
*/
|
|
88
|
+
this.disablePortal = input(false, ...(ngDevMode ? [{ debugName: "disablePortal" }] : []));
|
|
89
|
+
/**
|
|
90
|
+
* When `true`, the panel closes automatically after the user activates
|
|
91
|
+
* a `ls-menu-item` (i.e., a `[role="menuitem"]` element) inside it.
|
|
92
|
+
*
|
|
93
|
+
* @default true
|
|
94
|
+
*/
|
|
95
|
+
this.closeOnClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnClick" }] : []));
|
|
96
|
+
/**
|
|
97
|
+
* Emits when the panel's opening animation completes.
|
|
98
|
+
* Does NOT emit when `open` is set programmatically without animation.
|
|
99
|
+
*/
|
|
100
|
+
this.opened = output();
|
|
101
|
+
/**
|
|
102
|
+
* Emits when the panel's closing animation completes and the panel
|
|
103
|
+
* is fully hidden. In controlled mode, the host can use this to update
|
|
104
|
+
* state after the transition finishes.
|
|
105
|
+
*/
|
|
106
|
+
this.closed = output();
|
|
107
|
+
/**
|
|
108
|
+
* Emits when the user clicks the transparent backdrop behind the panel.
|
|
109
|
+
* The menu closes automatically after this output emits.
|
|
110
|
+
*/
|
|
111
|
+
this.backdropClick = output();
|
|
112
|
+
this._panelRef = viewChild('panelRef', ...(ngDevMode ? [{ debugName: "_panelRef" }] : []));
|
|
113
|
+
this._isAnimating = signal(false, ...(ngDevMode ? [{ debugName: "_isAnimating" }] : []));
|
|
114
|
+
this._wasOpen = false;
|
|
115
|
+
this._previouslyFocused = null;
|
|
116
|
+
this._savedBodyOverflow = '';
|
|
117
|
+
this._savedBodyPaddingRight = '';
|
|
118
|
+
this._resolvedAnchorEl = signal(null, ...(ngDevMode ? [{ debugName: "_resolvedAnchorEl" }] : []));
|
|
119
|
+
this._repositionHandler = () => this._computeAndApplyPosition();
|
|
120
|
+
// Manages animation state, scroll lock, focus, and repositioning listeners
|
|
121
|
+
// when the panel opens or closes.
|
|
122
|
+
effect(() => {
|
|
123
|
+
const isOpen = this.open();
|
|
124
|
+
untracked(() => {
|
|
125
|
+
if (isOpen) {
|
|
126
|
+
this._isAnimating.set(true);
|
|
127
|
+
this._previouslyFocused = this._document.activeElement;
|
|
128
|
+
this._savedBodyOverflow = this._document.body.style.overflow;
|
|
129
|
+
this._savedBodyPaddingRight = this._document.body.style.paddingRight;
|
|
130
|
+
const scrollbarWidth = this._getScrollbarWidth();
|
|
131
|
+
if (scrollbarWidth > 0) {
|
|
132
|
+
this._document.body.style.paddingRight = `${scrollbarWidth}px`;
|
|
133
|
+
}
|
|
134
|
+
this._document.body.style.overflow = 'hidden';
|
|
135
|
+
setTimeout(() => this._panelRef()?.nativeElement.focus(), 0);
|
|
136
|
+
window.addEventListener('resize', this._repositionHandler);
|
|
137
|
+
window.addEventListener('scroll', this._repositionHandler, true);
|
|
138
|
+
this._wasOpen = true;
|
|
139
|
+
}
|
|
140
|
+
else if (this._wasOpen) {
|
|
141
|
+
this._isAnimating.set(true);
|
|
142
|
+
window.removeEventListener('resize', this._repositionHandler);
|
|
143
|
+
window.removeEventListener('scroll', this._repositionHandler, true);
|
|
144
|
+
this._wasOpen = false;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// Computes and applies panel position whenever the panel enters the DOM
|
|
149
|
+
// or any positioning input changes. Using a separate effect (rather than
|
|
150
|
+
// setTimeout) guarantees that Angular has already updated the viewChild
|
|
151
|
+
// signal before this runs, so _panelRef() is always non-null here.
|
|
152
|
+
effect(() => {
|
|
153
|
+
const panel = this._panelRef()?.nativeElement;
|
|
154
|
+
if (!panel)
|
|
155
|
+
return;
|
|
156
|
+
this._computeAndApplyPosition();
|
|
157
|
+
});
|
|
158
|
+
this._destroyRef.onDestroy(() => {
|
|
159
|
+
window.removeEventListener('resize', this._repositionHandler);
|
|
160
|
+
window.removeEventListener('scroll', this._repositionHandler, true);
|
|
161
|
+
if (this._wasOpen) {
|
|
162
|
+
this._document.body.style.overflow = this._savedBodyOverflow;
|
|
163
|
+
this._document.body.style.paddingRight = this._savedBodyPaddingRight;
|
|
164
|
+
this._previouslyFocused?.focus?.();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/** Toggles the panel between open and closed. */
|
|
169
|
+
toggle() {
|
|
170
|
+
this.open.set(!this.open());
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Sets the anchor element used for panel positioning.
|
|
174
|
+
* Called automatically by `MenuTriggerDirective`.
|
|
175
|
+
*/
|
|
176
|
+
setAnchorEl(el) {
|
|
177
|
+
this._resolvedAnchorEl.set(el);
|
|
178
|
+
}
|
|
179
|
+
/** Closes the panel without waiting for a user interaction. */
|
|
180
|
+
requestClose() {
|
|
181
|
+
this.open.set(false);
|
|
182
|
+
}
|
|
183
|
+
onBackdropClick(event) {
|
|
184
|
+
this.backdropClick.emit(event);
|
|
185
|
+
this.open.set(false);
|
|
186
|
+
}
|
|
187
|
+
onPanelClick(event) {
|
|
188
|
+
if (!this.closeOnClick())
|
|
189
|
+
return;
|
|
190
|
+
const target = event.target;
|
|
191
|
+
if (target.closest('[role="menuitem"]') !== null) {
|
|
192
|
+
this.open.set(false);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
onAnimationEnd() {
|
|
196
|
+
this._isAnimating.set(false);
|
|
197
|
+
if (this.open()) {
|
|
198
|
+
this.opened.emit();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
this._document.body.style.overflow = this._savedBodyOverflow;
|
|
202
|
+
this._document.body.style.paddingRight = this._savedBodyPaddingRight;
|
|
203
|
+
this.closed.emit();
|
|
204
|
+
this._previouslyFocused?.focus?.();
|
|
205
|
+
this._previouslyFocused = null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
onEscape() {
|
|
209
|
+
if (this.open()) {
|
|
210
|
+
this.open.set(false);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
onArrowDown(event) {
|
|
214
|
+
if (!this.open())
|
|
215
|
+
return;
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
this._moveFocus(1);
|
|
218
|
+
}
|
|
219
|
+
onArrowUp(event) {
|
|
220
|
+
if (!this.open())
|
|
221
|
+
return;
|
|
222
|
+
event.preventDefault();
|
|
223
|
+
this._moveFocus(-1);
|
|
224
|
+
}
|
|
225
|
+
_computeAndApplyPosition() {
|
|
226
|
+
const rawAnchor = this.anchorEl() ?? this._resolvedAnchorEl();
|
|
227
|
+
const panel = this._panelRef()?.nativeElement;
|
|
228
|
+
if (!rawAnchor || !panel)
|
|
229
|
+
return;
|
|
230
|
+
const anchorEl = this._toHTMLElement(rawAnchor);
|
|
231
|
+
if (!anchorEl)
|
|
232
|
+
return;
|
|
233
|
+
const anchorRect = anchorEl.getBoundingClientRect();
|
|
234
|
+
const panelRect = panel.getBoundingClientRect();
|
|
235
|
+
const ao = this.anchorOrigin();
|
|
236
|
+
const to = this.transformOrigin();
|
|
237
|
+
const anchorX = anchorRect.left +
|
|
238
|
+
(ao.horizontal === 'center'
|
|
239
|
+
? anchorRect.width / 2
|
|
240
|
+
: ao.horizontal === 'right'
|
|
241
|
+
? anchorRect.width
|
|
242
|
+
: 0);
|
|
243
|
+
const anchorY = anchorRect.top +
|
|
244
|
+
(ao.vertical === 'center'
|
|
245
|
+
? anchorRect.height / 2
|
|
246
|
+
: ao.vertical === 'bottom'
|
|
247
|
+
? anchorRect.height
|
|
248
|
+
: 0);
|
|
249
|
+
const transformX = to.horizontal === 'center'
|
|
250
|
+
? panelRect.width / 2
|
|
251
|
+
: to.horizontal === 'right'
|
|
252
|
+
? panelRect.width
|
|
253
|
+
: 0;
|
|
254
|
+
const transformY = to.vertical === 'center'
|
|
255
|
+
? panelRect.height / 2
|
|
256
|
+
: to.vertical === 'bottom'
|
|
257
|
+
? panelRect.height
|
|
258
|
+
: 0;
|
|
259
|
+
panel.style.setProperty('--menu-top', `${anchorY - transformY}px`);
|
|
260
|
+
panel.style.setProperty('--menu-left', `${anchorX - transformX}px`);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Resolves any supported anchor value to a plain `HTMLElement`.
|
|
264
|
+
*
|
|
265
|
+
* Handles: `HTMLElement`, `ElementRef<HTMLElement>`, and objects that expose
|
|
266
|
+
* a `nativeElement: HTMLElement` property (e.g. `ls-button` component refs).
|
|
267
|
+
*/
|
|
268
|
+
_toHTMLElement(anchor) {
|
|
269
|
+
if (!anchor)
|
|
270
|
+
return null;
|
|
271
|
+
if (anchor instanceof HTMLElement)
|
|
272
|
+
return anchor;
|
|
273
|
+
if (typeof anchor === 'object' &&
|
|
274
|
+
anchor !== null &&
|
|
275
|
+
'nativeElement' in anchor) {
|
|
276
|
+
const el = anchor.nativeElement;
|
|
277
|
+
return el instanceof HTMLElement ? el : null;
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
_moveFocus(direction) {
|
|
282
|
+
const panel = this._panelRef()?.nativeElement;
|
|
283
|
+
if (!panel)
|
|
284
|
+
return;
|
|
285
|
+
const items = Array.from(panel.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])'));
|
|
286
|
+
if (items.length === 0)
|
|
287
|
+
return;
|
|
288
|
+
const currentIndex = items.indexOf(this._document.activeElement);
|
|
289
|
+
const nextIndex = currentIndex === -1
|
|
290
|
+
? direction === 1
|
|
291
|
+
? 0
|
|
292
|
+
: items.length - 1
|
|
293
|
+
: (currentIndex + direction + items.length) % items.length;
|
|
294
|
+
items[nextIndex]?.focus();
|
|
295
|
+
}
|
|
296
|
+
_getScrollbarWidth() {
|
|
297
|
+
return window.innerWidth - this._document.documentElement.clientWidth;
|
|
298
|
+
}
|
|
299
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
300
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: MenuComponent, isStandalone: true, selector: "ls-menu", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: false, transformFunction: null }, anchorOrigin: { classPropertyName: "anchorOrigin", publicName: "anchorOrigin", isSignal: true, isRequired: false, transformFunction: null }, transformOrigin: { classPropertyName: "transformOrigin", publicName: "transformOrigin", isSignal: true, isRequired: false, transformFunction: null }, disablePortal: { classPropertyName: "disablePortal", publicName: "disablePortal", isSignal: true, isRequired: false, transformFunction: null }, closeOnClick: { classPropertyName: "closeOnClick", publicName: "closeOnClick", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", opened: "opened", closed: "closed", backdropClick: "backdropClick" }, host: { listeners: { "document:keydown.escape": "onEscape()", "document:keydown.arrowdown": "onArrowDown($event)", "document:keydown.arrowup": "onArrowUp($event)" } }, viewQueries: [{ propertyName: "_panelRef", first: true, predicate: ["panelRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
301
|
+
@if (open() || _isAnimating()) {
|
|
302
|
+
<div class="menu-backdrop" (click)="onBackdropClick($event)"></div>
|
|
303
|
+
<div
|
|
304
|
+
#panelRef
|
|
305
|
+
class="menu-panel"
|
|
306
|
+
[class.menu-panel-inline]="disablePortal()"
|
|
307
|
+
[class.menu-enter]="open() && _isAnimating()"
|
|
308
|
+
[class.menu-leave]="!open() && _isAnimating()"
|
|
309
|
+
role="menu"
|
|
310
|
+
tabindex="-1"
|
|
311
|
+
(click)="onPanelClick($event)"
|
|
312
|
+
(animationend)="onAnimationEnd()">
|
|
313
|
+
<ng-content />
|
|
314
|
+
</div>
|
|
315
|
+
}
|
|
316
|
+
`, isInline: true, styles: [":host{display:contents}.menu-backdrop{position:fixed;inset:0;z-index:var(--ls-menu-z-index);background-color:transparent}.menu-panel{position:fixed;top:var(--menu-top, 0px);left:var(--menu-left, 0px);z-index:calc(var(--ls-menu-z-index) + 1);min-width:10rem;padding:.25rem 0;background-color:#fff;border-radius:var(--ls-border-radius-md);box-shadow:0 4px 24px #0000001f;outline:none}.menu-panel.menu-panel-inline{position:absolute}@keyframes menu-enter-kf{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes menu-leave-kf{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.95) translateY(-4px)}}.menu-panel.menu-enter{animation:menu-enter-kf .15s ease-out forwards}.menu-panel.menu-leave{animation:menu-leave-kf .1s ease-in forwards}.menu-item{display:flex;align-items:center;width:100%;padding:.5rem 1rem;background:none;border:none;text-align:left;font-family:var(--ls-font-family);font-size:.875rem;color:#3b3f5c;cursor:pointer;transition:background-color var(--ls-transition-duration) ease}.menu-item:hover:not(:disabled){background-color:#f1f2f3}.menu-item:focus-visible{outline:2px solid var(--ls-color-primary);outline-offset:-2px;background-color:#f1f2f3}.menu-item-dense{padding-top:.25rem;padding-bottom:.25rem}.menu-item-no-gutters{padding-inline:0}.menu-item-divider{border-bottom:1px solid #e0e6ed}.menu-item-disabled{color:#888ea8;pointer-events:none;cursor:not-allowed}body.dark .menu-panel{background-color:#1b2e4b;box-shadow:0 4px 24px #0006}body.dark .menu-item{color:#e0e6ed}body.dark .menu-item:hover:not(:disabled){background-color:#253b5e}body.dark .menu-item:focus-visible{background-color:#253b5e}body.dark .menu-item-divider{border-bottom-color:#253b5e}body.dark .menu-item-disabled{color:#506690}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
317
|
+
}
|
|
318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MenuComponent, decorators: [{
|
|
319
|
+
type: Component,
|
|
320
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [], selector: 'ls-menu', standalone: true, template: `
|
|
321
|
+
@if (open() || _isAnimating()) {
|
|
322
|
+
<div class="menu-backdrop" (click)="onBackdropClick($event)"></div>
|
|
323
|
+
<div
|
|
324
|
+
#panelRef
|
|
325
|
+
class="menu-panel"
|
|
326
|
+
[class.menu-panel-inline]="disablePortal()"
|
|
327
|
+
[class.menu-enter]="open() && _isAnimating()"
|
|
328
|
+
[class.menu-leave]="!open() && _isAnimating()"
|
|
329
|
+
role="menu"
|
|
330
|
+
tabindex="-1"
|
|
331
|
+
(click)="onPanelClick($event)"
|
|
332
|
+
(animationend)="onAnimationEnd()">
|
|
333
|
+
<ng-content />
|
|
334
|
+
</div>
|
|
335
|
+
}
|
|
336
|
+
`, styles: [":host{display:contents}.menu-backdrop{position:fixed;inset:0;z-index:var(--ls-menu-z-index);background-color:transparent}.menu-panel{position:fixed;top:var(--menu-top, 0px);left:var(--menu-left, 0px);z-index:calc(var(--ls-menu-z-index) + 1);min-width:10rem;padding:.25rem 0;background-color:#fff;border-radius:var(--ls-border-radius-md);box-shadow:0 4px 24px #0000001f;outline:none}.menu-panel.menu-panel-inline{position:absolute}@keyframes menu-enter-kf{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes menu-leave-kf{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.95) translateY(-4px)}}.menu-panel.menu-enter{animation:menu-enter-kf .15s ease-out forwards}.menu-panel.menu-leave{animation:menu-leave-kf .1s ease-in forwards}.menu-item{display:flex;align-items:center;width:100%;padding:.5rem 1rem;background:none;border:none;text-align:left;font-family:var(--ls-font-family);font-size:.875rem;color:#3b3f5c;cursor:pointer;transition:background-color var(--ls-transition-duration) ease}.menu-item:hover:not(:disabled){background-color:#f1f2f3}.menu-item:focus-visible{outline:2px solid var(--ls-color-primary);outline-offset:-2px;background-color:#f1f2f3}.menu-item-dense{padding-top:.25rem;padding-bottom:.25rem}.menu-item-no-gutters{padding-inline:0}.menu-item-divider{border-bottom:1px solid #e0e6ed}.menu-item-disabled{color:#888ea8;pointer-events:none;cursor:not-allowed}body.dark .menu-panel{background-color:#1b2e4b;box-shadow:0 4px 24px #0006}body.dark .menu-item{color:#e0e6ed}body.dark .menu-item:hover:not(:disabled){background-color:#253b5e}body.dark .menu-item:focus-visible{background-color:#253b5e}body.dark .menu-item-divider{border-bottom-color:#253b5e}body.dark .menu-item-disabled{color:#506690}\n"] }]
|
|
337
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], anchorEl: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorEl", required: false }] }], anchorOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorOrigin", required: false }] }], transformOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformOrigin", required: false }] }], disablePortal: [{ type: i0.Input, args: [{ isSignal: true, alias: "disablePortal", required: false }] }], closeOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClick", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], backdropClick: [{ type: i0.Output, args: ["backdropClick"] }], _panelRef: [{ type: i0.ViewChild, args: ['panelRef', { isSignal: true }] }], onEscape: [{
|
|
338
|
+
type: HostListener,
|
|
339
|
+
args: ['document:keydown.escape']
|
|
340
|
+
}], onArrowDown: [{
|
|
341
|
+
type: HostListener,
|
|
342
|
+
args: ['document:keydown.arrowdown', ['$event']]
|
|
343
|
+
}], onArrowUp: [{
|
|
344
|
+
type: HostListener,
|
|
345
|
+
args: ['document:keydown.arrowup', ['$event']]
|
|
346
|
+
}] } });
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Interactive item rendered inside an `ls-menu` panel.
|
|
350
|
+
*
|
|
351
|
+
* Renders a `<button role="menuitem">` that projects its label via
|
|
352
|
+
* `<ng-content>`. Visual density, gutters, and a bottom divider are
|
|
353
|
+
* configurable via inputs. When `disabled` is `true`, the item is not
|
|
354
|
+
* interactive and the `itemClick` output will not emit.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```html
|
|
358
|
+
* <ls-menu-item [dense]="true" (itemClick)="onEdit($event)">
|
|
359
|
+
* Edit
|
|
360
|
+
* </ls-menu-item>
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
class MenuItemComponent {
|
|
364
|
+
constructor() {
|
|
365
|
+
/**
|
|
366
|
+
* Reduces the vertical padding of the item for compact layouts.
|
|
367
|
+
*
|
|
368
|
+
* @default false
|
|
369
|
+
*/
|
|
370
|
+
this.dense = input(false, ...(ngDevMode ? [{ debugName: "dense" }] : []));
|
|
371
|
+
/**
|
|
372
|
+
* Removes left and right padding when `true`.
|
|
373
|
+
*
|
|
374
|
+
* @default false
|
|
375
|
+
*/
|
|
376
|
+
this.disableGutters = input(false, ...(ngDevMode ? [{ debugName: "disableGutters" }] : []));
|
|
377
|
+
/**
|
|
378
|
+
* Adds a 1px border at the bottom of the item to act as a visual divider.
|
|
379
|
+
*
|
|
380
|
+
* @default false
|
|
381
|
+
*/
|
|
382
|
+
this.divider = input(false, ...(ngDevMode ? [{ debugName: "divider" }] : []));
|
|
383
|
+
/**
|
|
384
|
+
* Prevents interaction. Applies disabled styling, sets `aria-disabled="true"`,
|
|
385
|
+
* and suppresses the `itemClick` output.
|
|
386
|
+
*
|
|
387
|
+
* @default false
|
|
388
|
+
*/
|
|
389
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
390
|
+
/**
|
|
391
|
+
* Emits the originating `MouseEvent` when the user activates the item.
|
|
392
|
+
* Does NOT emit when `disabled` is `true`.
|
|
393
|
+
*/
|
|
394
|
+
this.itemClick = output();
|
|
395
|
+
this._itemClasses = computed(() => {
|
|
396
|
+
const classes = ['menu-item'];
|
|
397
|
+
if (this.dense())
|
|
398
|
+
classes.push('menu-item-dense');
|
|
399
|
+
if (this.disableGutters())
|
|
400
|
+
classes.push('menu-item-no-gutters');
|
|
401
|
+
if (this.divider())
|
|
402
|
+
classes.push('menu-item-divider');
|
|
403
|
+
if (this.disabled())
|
|
404
|
+
classes.push('menu-item-disabled');
|
|
405
|
+
return classes.join(' ');
|
|
406
|
+
}, ...(ngDevMode ? [{ debugName: "_itemClasses" }] : []));
|
|
407
|
+
}
|
|
408
|
+
handleClick(event) {
|
|
409
|
+
if (this.disabled())
|
|
410
|
+
return;
|
|
411
|
+
this.itemClick.emit(event);
|
|
412
|
+
}
|
|
413
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
414
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.2", type: MenuItemComponent, isStandalone: true, selector: "ls-menu-item", inputs: { dense: { classPropertyName: "dense", publicName: "dense", isSignal: true, isRequired: false, transformFunction: null }, disableGutters: { classPropertyName: "disableGutters", publicName: "disableGutters", isSignal: true, isRequired: false, transformFunction: null }, divider: { classPropertyName: "divider", publicName: "divider", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, ngImport: i0, template: `
|
|
415
|
+
<button
|
|
416
|
+
type="button"
|
|
417
|
+
[class]="_itemClasses()"
|
|
418
|
+
[disabled]="disabled()"
|
|
419
|
+
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
420
|
+
role="menuitem"
|
|
421
|
+
(click)="handleClick($event)">
|
|
422
|
+
<ng-content />
|
|
423
|
+
</button>
|
|
424
|
+
`, isInline: true, styles: [":host{display:contents}.menu-backdrop{position:fixed;inset:0;z-index:var(--ls-menu-z-index);background-color:transparent}.menu-panel{position:fixed;top:var(--menu-top, 0px);left:var(--menu-left, 0px);z-index:calc(var(--ls-menu-z-index) + 1);min-width:10rem;padding:.25rem 0;background-color:#fff;border-radius:var(--ls-border-radius-md);box-shadow:0 4px 24px #0000001f;outline:none}.menu-panel.menu-panel-inline{position:absolute}@keyframes menu-enter-kf{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes menu-leave-kf{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.95) translateY(-4px)}}.menu-panel.menu-enter{animation:menu-enter-kf .15s ease-out forwards}.menu-panel.menu-leave{animation:menu-leave-kf .1s ease-in forwards}.menu-item{display:flex;align-items:center;width:100%;padding:.5rem 1rem;background:none;border:none;text-align:left;font-family:var(--ls-font-family);font-size:.875rem;color:#3b3f5c;cursor:pointer;transition:background-color var(--ls-transition-duration) ease}.menu-item:hover:not(:disabled){background-color:#f1f2f3}.menu-item:focus-visible{outline:2px solid var(--ls-color-primary);outline-offset:-2px;background-color:#f1f2f3}.menu-item-dense{padding-top:.25rem;padding-bottom:.25rem}.menu-item-no-gutters{padding-inline:0}.menu-item-divider{border-bottom:1px solid #e0e6ed}.menu-item-disabled{color:#888ea8;pointer-events:none;cursor:not-allowed}body.dark .menu-panel{background-color:#1b2e4b;box-shadow:0 4px 24px #0006}body.dark .menu-item{color:#e0e6ed}body.dark .menu-item:hover:not(:disabled){background-color:#253b5e}body.dark .menu-item:focus-visible{background-color:#253b5e}body.dark .menu-item-divider{border-bottom-color:#253b5e}body.dark .menu-item-disabled{color:#506690}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
425
|
+
}
|
|
426
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MenuItemComponent, decorators: [{
|
|
427
|
+
type: Component,
|
|
428
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [], selector: 'ls-menu-item', standalone: true, template: `
|
|
429
|
+
<button
|
|
430
|
+
type="button"
|
|
431
|
+
[class]="_itemClasses()"
|
|
432
|
+
[disabled]="disabled()"
|
|
433
|
+
[attr.aria-disabled]="disabled() ? 'true' : null"
|
|
434
|
+
role="menuitem"
|
|
435
|
+
(click)="handleClick($event)">
|
|
436
|
+
<ng-content />
|
|
437
|
+
</button>
|
|
438
|
+
`, styles: [":host{display:contents}.menu-backdrop{position:fixed;inset:0;z-index:var(--ls-menu-z-index);background-color:transparent}.menu-panel{position:fixed;top:var(--menu-top, 0px);left:var(--menu-left, 0px);z-index:calc(var(--ls-menu-z-index) + 1);min-width:10rem;padding:.25rem 0;background-color:#fff;border-radius:var(--ls-border-radius-md);box-shadow:0 4px 24px #0000001f;outline:none}.menu-panel.menu-panel-inline{position:absolute}@keyframes menu-enter-kf{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes menu-leave-kf{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.95) translateY(-4px)}}.menu-panel.menu-enter{animation:menu-enter-kf .15s ease-out forwards}.menu-panel.menu-leave{animation:menu-leave-kf .1s ease-in forwards}.menu-item{display:flex;align-items:center;width:100%;padding:.5rem 1rem;background:none;border:none;text-align:left;font-family:var(--ls-font-family);font-size:.875rem;color:#3b3f5c;cursor:pointer;transition:background-color var(--ls-transition-duration) ease}.menu-item:hover:not(:disabled){background-color:#f1f2f3}.menu-item:focus-visible{outline:2px solid var(--ls-color-primary);outline-offset:-2px;background-color:#f1f2f3}.menu-item-dense{padding-top:.25rem;padding-bottom:.25rem}.menu-item-no-gutters{padding-inline:0}.menu-item-divider{border-bottom:1px solid #e0e6ed}.menu-item-disabled{color:#888ea8;pointer-events:none;cursor:not-allowed}body.dark .menu-panel{background-color:#1b2e4b;box-shadow:0 4px 24px #0006}body.dark .menu-item{color:#e0e6ed}body.dark .menu-item:hover:not(:disabled){background-color:#253b5e}body.dark .menu-item:focus-visible{background-color:#253b5e}body.dark .menu-item-divider{border-bottom-color:#253b5e}body.dark .menu-item-disabled{color:#506690}\n"] }]
|
|
439
|
+
}], propDecorators: { dense: [{ type: i0.Input, args: [{ isSignal: true, alias: "dense", required: false }] }], disableGutters: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableGutters", required: false }] }], divider: [{ type: i0.Input, args: [{ isSignal: true, alias: "divider", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Connects any host element to a `MenuComponent` instance, enabling
|
|
443
|
+
* click-to-toggle behavior without manual open-state management.
|
|
444
|
+
*
|
|
445
|
+
* Sets `anchorEl` on the linked `MenuComponent` to the host element
|
|
446
|
+
* before toggling visibility, so the panel is correctly positioned
|
|
447
|
+
* relative to the trigger. Also applies `aria-haspopup="menu"` to the
|
|
448
|
+
* host for accessibility.
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```html
|
|
452
|
+
* <button type="button" [lsMenuTriggerFor]="myMenu">Options</button>
|
|
453
|
+
* <ls-menu #myMenu>
|
|
454
|
+
* <ls-menu-item>Delete</ls-menu-item>
|
|
455
|
+
* </ls-menu>
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
class MenuTriggerDirective {
|
|
459
|
+
constructor() {
|
|
460
|
+
/**
|
|
461
|
+
* The `MenuComponent` instance this directive controls.
|
|
462
|
+
* Assign a template reference variable of `ls-menu`.
|
|
463
|
+
*/
|
|
464
|
+
this.lsMenuTriggerFor = input.required(...(ngDevMode ? [{ debugName: "lsMenuTriggerFor" }] : []));
|
|
465
|
+
this._elementRef = inject((ElementRef));
|
|
466
|
+
}
|
|
467
|
+
onClick() {
|
|
468
|
+
const menu = this.lsMenuTriggerFor();
|
|
469
|
+
menu.setAnchorEl(this._resolveAnchorElement());
|
|
470
|
+
menu.toggle();
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Resolves the first ancestor element that has a real layout box.
|
|
474
|
+
*
|
|
475
|
+
* Angular component hosts commonly use `:host { display: contents }`,
|
|
476
|
+
* which means the element itself has no bounding box and
|
|
477
|
+
* `getBoundingClientRect()` returns all zeros. This method walks down
|
|
478
|
+
* through `display: contents` wrappers until it reaches the first child
|
|
479
|
+
* that participates in layout, so the menu panel is positioned relative
|
|
480
|
+
* to the visible trigger element rather than a phantom box.
|
|
481
|
+
*/
|
|
482
|
+
_resolveAnchorElement() {
|
|
483
|
+
let el = this._elementRef.nativeElement;
|
|
484
|
+
while (el.firstElementChild && getComputedStyle(el).display === 'contents') {
|
|
485
|
+
el = el.firstElementChild;
|
|
486
|
+
}
|
|
487
|
+
return el;
|
|
488
|
+
}
|
|
489
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
490
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.2", type: MenuTriggerDirective, isStandalone: true, selector: "[lsMenuTriggerFor]", inputs: { lsMenuTriggerFor: { classPropertyName: "lsMenuTriggerFor", publicName: "lsMenuTriggerFor", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "aria-haspopup": "menu" }, listeners: { "click": "onClick()" } }, ngImport: i0 }); }
|
|
491
|
+
}
|
|
492
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: MenuTriggerDirective, decorators: [{
|
|
493
|
+
type: Directive,
|
|
494
|
+
args: [{
|
|
495
|
+
host: { 'aria-haspopup': 'menu' },
|
|
496
|
+
selector: '[lsMenuTriggerFor]',
|
|
497
|
+
standalone: true,
|
|
498
|
+
}]
|
|
499
|
+
}], propDecorators: { lsMenuTriggerFor: [{ type: i0.Input, args: [{ isSignal: true, alias: "lsMenuTriggerFor", required: true }] }], onClick: [{
|
|
500
|
+
type: HostListener,
|
|
501
|
+
args: ['click']
|
|
502
|
+
}] } });
|
|
503
|
+
|
|
504
|
+
class ModalActionsComponent {
|
|
505
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
506
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ModalActionsComponent, isStandalone: true, selector: "ls-modal-actions", host: { classAttribute: "modal-actions" }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
507
|
+
}
|
|
508
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalActionsComponent, decorators: [{
|
|
509
|
+
type: Component,
|
|
510
|
+
args: [{
|
|
511
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
512
|
+
host: { class: 'modal-actions' },
|
|
513
|
+
imports: [],
|
|
514
|
+
selector: 'ls-modal-actions',
|
|
515
|
+
standalone: true,
|
|
516
|
+
template: `<ng-content />`,
|
|
517
|
+
}]
|
|
518
|
+
}] });
|
|
519
|
+
|
|
520
|
+
let _titleIdCounter = 0;
|
|
521
|
+
class ModalTitleComponent {
|
|
522
|
+
constructor() {
|
|
523
|
+
this.id = `ls-modal-title-${++_titleIdCounter}`;
|
|
524
|
+
}
|
|
525
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
526
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ModalTitleComponent, isStandalone: true, selector: "ls-modal-title", host: { properties: { "id": "id" }, classAttribute: "modal-title" }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
527
|
+
}
|
|
528
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalTitleComponent, decorators: [{
|
|
529
|
+
type: Component,
|
|
530
|
+
args: [{
|
|
531
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
532
|
+
host: { class: 'modal-title', '[id]': 'id' },
|
|
533
|
+
imports: [],
|
|
534
|
+
selector: 'ls-modal-title',
|
|
535
|
+
standalone: true,
|
|
536
|
+
template: `<ng-content />`,
|
|
537
|
+
}]
|
|
538
|
+
}] });
|
|
539
|
+
|
|
540
|
+
class ModalStackService {
|
|
541
|
+
constructor() {
|
|
542
|
+
this._stack = signal([], ...(ngDevMode ? [{ debugName: "_stack" }] : []));
|
|
543
|
+
}
|
|
544
|
+
push(id) {
|
|
545
|
+
this._stack.update(stack => [...stack, id]);
|
|
546
|
+
}
|
|
547
|
+
pop(id) {
|
|
548
|
+
this._stack.update(stack => stack.filter(s => s !== id));
|
|
549
|
+
}
|
|
550
|
+
isTop(id) {
|
|
551
|
+
const stack = this._stack();
|
|
552
|
+
return stack.length > 0 && stack[stack.length - 1] === id;
|
|
553
|
+
}
|
|
554
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalStackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
555
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalStackService, providedIn: 'root' }); }
|
|
556
|
+
}
|
|
557
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalStackService, decorators: [{
|
|
558
|
+
type: Injectable,
|
|
559
|
+
args: [{ providedIn: 'root' }]
|
|
560
|
+
}] });
|
|
561
|
+
|
|
562
|
+
let _modalIdCounter = 0;
|
|
563
|
+
class ModalComponent {
|
|
564
|
+
constructor() {
|
|
565
|
+
this.open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
|
|
566
|
+
this.disableBackdropClose = input(false, ...(ngDevMode ? [{ debugName: "disableBackdropClose" }] : []));
|
|
567
|
+
this.fullScreen = input(false, ...(ngDevMode ? [{ debugName: "fullScreen" }] : []));
|
|
568
|
+
this.fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : []));
|
|
569
|
+
this.maxWidth = input('md', ...(ngDevMode ? [{ debugName: "maxWidth" }] : []));
|
|
570
|
+
this.scroll = input('dialog', ...(ngDevMode ? [{ debugName: "scroll" }] : []));
|
|
571
|
+
this.showCloseButton = input(true, ...(ngDevMode ? [{ debugName: "showCloseButton" }] : []));
|
|
572
|
+
this.closed = output();
|
|
573
|
+
this._modalStackService = inject(ModalStackService);
|
|
574
|
+
this._titleComponent = contentChild(ModalTitleComponent, ...(ngDevMode ? [{ debugName: "_titleComponent" }] : []));
|
|
575
|
+
this._panel = viewChild('panel', ...(ngDevMode ? [{ debugName: "_panel" }] : []));
|
|
576
|
+
this._id = `ls-modal-${++_modalIdCounter}`;
|
|
577
|
+
this._previouslyFocused = null;
|
|
578
|
+
this.labelledById = computed(() => {
|
|
579
|
+
const title = this._titleComponent();
|
|
580
|
+
return title ? title.id : null;
|
|
581
|
+
}, ...(ngDevMode ? [{ debugName: "labelledById" }] : []));
|
|
582
|
+
this.overlayClasses = computed(() => {
|
|
583
|
+
const classes = ['modal-overlay'];
|
|
584
|
+
if (this.scroll() === 'body')
|
|
585
|
+
classes.push('modal-overlay-body');
|
|
586
|
+
return classes.join(' ');
|
|
587
|
+
}, ...(ngDevMode ? [{ debugName: "overlayClasses" }] : []));
|
|
588
|
+
this.panelClasses = computed(() => {
|
|
589
|
+
const classes = [
|
|
590
|
+
'modal-panel',
|
|
591
|
+
`modal-max-${this.maxWidth()}`,
|
|
592
|
+
`modal-scroll-${this.scroll()}`,
|
|
593
|
+
];
|
|
594
|
+
if (this.fullScreen())
|
|
595
|
+
classes.push('modal-full-screen');
|
|
596
|
+
if (this.fullWidth())
|
|
597
|
+
classes.push('modal-full-width');
|
|
598
|
+
if (this.showCloseButton())
|
|
599
|
+
classes.push('modal-has-close-btn');
|
|
600
|
+
return classes.join(' ');
|
|
601
|
+
}, ...(ngDevMode ? [{ debugName: "panelClasses" }] : []));
|
|
602
|
+
effect(() => {
|
|
603
|
+
if (this.open()) {
|
|
604
|
+
this._previouslyFocused = document.activeElement;
|
|
605
|
+
this._modalStackService.push(this._id);
|
|
606
|
+
setTimeout(() => {
|
|
607
|
+
untracked(() => this._panel()?.nativeElement.focus());
|
|
608
|
+
}, 0);
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
this._modalStackService.pop(this._id);
|
|
612
|
+
this._previouslyFocused?.focus?.();
|
|
613
|
+
this._previouslyFocused = null;
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
ngOnDestroy() {
|
|
618
|
+
if (this.open()) {
|
|
619
|
+
this._modalStackService.pop(this._id);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
onEscape() {
|
|
623
|
+
if (this.open() && this._modalStackService.isTop(this._id)) {
|
|
624
|
+
this.closed.emit();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
onBackdropClick() {
|
|
628
|
+
if (!this.disableBackdropClose()) {
|
|
629
|
+
this.closed.emit();
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
onCloseButtonClick() {
|
|
633
|
+
this.closed.emit();
|
|
634
|
+
}
|
|
635
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
636
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: ModalComponent, isStandalone: true, selector: "ls-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, disableBackdropClose: { classPropertyName: "disableBackdropClose", publicName: "disableBackdropClose", isSignal: true, isRequired: false, transformFunction: null }, fullScreen: { classPropertyName: "fullScreen", publicName: "fullScreen", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, scroll: { classPropertyName: "scroll", publicName: "scroll", isSignal: true, isRequired: false, transformFunction: null }, showCloseButton: { classPropertyName: "showCloseButton", publicName: "showCloseButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, queries: [{ propertyName: "_titleComponent", first: true, predicate: ModalTitleComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "_panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
637
|
+
@if (open()) {
|
|
638
|
+
<div [class]="overlayClasses()" (click)="onBackdropClick()">
|
|
639
|
+
<div
|
|
640
|
+
#panel
|
|
641
|
+
[attr.aria-labelledby]="labelledById()"
|
|
642
|
+
[attr.aria-modal]="true"
|
|
643
|
+
[class]="panelClasses()"
|
|
644
|
+
role="dialog"
|
|
645
|
+
tabindex="-1"
|
|
646
|
+
(click)="$event.stopPropagation()">
|
|
647
|
+
@if (showCloseButton()) {
|
|
648
|
+
<button
|
|
649
|
+
aria-label="Close"
|
|
650
|
+
class="modal-close-btn"
|
|
651
|
+
type="button"
|
|
652
|
+
(click)="onCloseButtonClick()">
|
|
653
|
+
<i class="ls-icon ls-x ls-icon-sm"></i>
|
|
654
|
+
</button>
|
|
655
|
+
}
|
|
656
|
+
<ng-content />
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
}
|
|
660
|
+
`, isInline: true, styles: [":host{display:contents}.modal-overlay{align-items:center;background-color:var(--ls-modal-backdrop-bg);display:flex;inset:0;justify-content:center;padding:1rem;position:fixed;z-index:var(--ls-modal-z-index)}.modal-overlay-body{align-items:flex-start;overflow-y:auto}.modal-panel{background-color:var(--ls-color-white);border-radius:var(--ls-border-radius-lg);box-shadow:0 20px 60px #0000004d;display:flex;flex-direction:column;max-height:calc(100vh - 2rem);outline:none;position:relative;width:100%}.modal-max-sm{max-width:24rem}.modal-max-md{max-width:32rem}.modal-max-lg{max-width:48rem}.modal-max-xl{max-width:64rem}.modal-max-full{max-width:100%}.modal-full-screen{border-radius:0;height:100vh;max-height:100vh;max-width:100vw;width:100vw}.modal-full-width{max-width:100%}.modal-scroll-dialog{max-height:calc(100vh - 2rem);overflow:hidden}.modal-scroll-body{max-height:none}.modal-close-btn{align-items:center;background:transparent;border:none;border-radius:.25rem;color:inherit;cursor:pointer;display:flex;justify-content:center;padding:.25rem;position:absolute;right:.75rem;top:.75rem;transition:background-color .15s}.modal-close-btn:hover{background-color:var(--ls-color-dark-light)}.modal-has-close-btn .modal-title{padding-right:3rem}.modal-title{border-bottom:1px solid var(--ls-color-dark-light);font-family:var(--ls-font-family);font-size:1.125rem;font-weight:700;padding:1.25rem 1.5rem}.modal-content{flex:1;overflow-y:auto;padding:1.5rem}.modal-actions{border-top:1px solid var(--ls-color-dark-light);display:flex;gap:.75rem;justify-content:flex-end;padding:1rem 1.5rem}body.dark .modal-panel{background-color:#1b2e4b;box-shadow:0 20px 60px #0009}body.dark .modal-title{border-bottom-color:#253958}body.dark .modal-actions{border-top-color:#253958}body.dark .modal-close-btn:hover{background-color:#253958}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
661
|
+
}
|
|
662
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalComponent, decorators: [{
|
|
663
|
+
type: Component,
|
|
664
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [], selector: 'ls-modal', standalone: true, template: `
|
|
665
|
+
@if (open()) {
|
|
666
|
+
<div [class]="overlayClasses()" (click)="onBackdropClick()">
|
|
667
|
+
<div
|
|
668
|
+
#panel
|
|
669
|
+
[attr.aria-labelledby]="labelledById()"
|
|
670
|
+
[attr.aria-modal]="true"
|
|
671
|
+
[class]="panelClasses()"
|
|
672
|
+
role="dialog"
|
|
673
|
+
tabindex="-1"
|
|
674
|
+
(click)="$event.stopPropagation()">
|
|
675
|
+
@if (showCloseButton()) {
|
|
676
|
+
<button
|
|
677
|
+
aria-label="Close"
|
|
678
|
+
class="modal-close-btn"
|
|
679
|
+
type="button"
|
|
680
|
+
(click)="onCloseButtonClick()">
|
|
681
|
+
<i class="ls-icon ls-x ls-icon-sm"></i>
|
|
682
|
+
</button>
|
|
683
|
+
}
|
|
684
|
+
<ng-content />
|
|
685
|
+
</div>
|
|
686
|
+
</div>
|
|
687
|
+
}
|
|
688
|
+
`, styles: [":host{display:contents}.modal-overlay{align-items:center;background-color:var(--ls-modal-backdrop-bg);display:flex;inset:0;justify-content:center;padding:1rem;position:fixed;z-index:var(--ls-modal-z-index)}.modal-overlay-body{align-items:flex-start;overflow-y:auto}.modal-panel{background-color:var(--ls-color-white);border-radius:var(--ls-border-radius-lg);box-shadow:0 20px 60px #0000004d;display:flex;flex-direction:column;max-height:calc(100vh - 2rem);outline:none;position:relative;width:100%}.modal-max-sm{max-width:24rem}.modal-max-md{max-width:32rem}.modal-max-lg{max-width:48rem}.modal-max-xl{max-width:64rem}.modal-max-full{max-width:100%}.modal-full-screen{border-radius:0;height:100vh;max-height:100vh;max-width:100vw;width:100vw}.modal-full-width{max-width:100%}.modal-scroll-dialog{max-height:calc(100vh - 2rem);overflow:hidden}.modal-scroll-body{max-height:none}.modal-close-btn{align-items:center;background:transparent;border:none;border-radius:.25rem;color:inherit;cursor:pointer;display:flex;justify-content:center;padding:.25rem;position:absolute;right:.75rem;top:.75rem;transition:background-color .15s}.modal-close-btn:hover{background-color:var(--ls-color-dark-light)}.modal-has-close-btn .modal-title{padding-right:3rem}.modal-title{border-bottom:1px solid var(--ls-color-dark-light);font-family:var(--ls-font-family);font-size:1.125rem;font-weight:700;padding:1.25rem 1.5rem}.modal-content{flex:1;overflow-y:auto;padding:1.5rem}.modal-actions{border-top:1px solid var(--ls-color-dark-light);display:flex;gap:.75rem;justify-content:flex-end;padding:1rem 1.5rem}body.dark .modal-panel{background-color:#1b2e4b;box-shadow:0 20px 60px #0009}body.dark .modal-title{border-bottom-color:#253958}body.dark .modal-actions{border-top-color:#253958}body.dark .modal-close-btn:hover{background-color:#253958}\n"] }]
|
|
689
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], disableBackdropClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableBackdropClose", required: false }] }], fullScreen: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullScreen", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], scroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "scroll", required: false }] }], showCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCloseButton", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], _titleComponent: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ModalTitleComponent), { isSignal: true }] }], _panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], onEscape: [{
|
|
690
|
+
type: HostListener,
|
|
691
|
+
args: ['document:keydown.escape']
|
|
692
|
+
}] } });
|
|
693
|
+
|
|
694
|
+
class ModalContentComponent {
|
|
695
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
696
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ModalContentComponent, isStandalone: true, selector: "ls-modal-content", host: { classAttribute: "modal-content" }, ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
697
|
+
}
|
|
698
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ModalContentComponent, decorators: [{
|
|
699
|
+
type: Component,
|
|
700
|
+
args: [{
|
|
701
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
702
|
+
host: { class: 'modal-content' },
|
|
703
|
+
imports: [],
|
|
704
|
+
selector: 'ls-modal-content',
|
|
705
|
+
standalone: true,
|
|
706
|
+
template: `<ng-content />`,
|
|
707
|
+
}]
|
|
708
|
+
}] });
|
|
709
|
+
|
|
710
|
+
class PopoverComponent {
|
|
711
|
+
constructor() {
|
|
712
|
+
this._renderer = inject(Renderer2);
|
|
713
|
+
this._destroyRef = inject(DestroyRef);
|
|
714
|
+
this._document = inject(DOCUMENT);
|
|
715
|
+
this._elementRef = inject(ElementRef);
|
|
716
|
+
this._cleanupScroll = null;
|
|
717
|
+
this.placement = input('bottom', ...(ngDevMode ? [{ debugName: "placement" }] : []));
|
|
718
|
+
this.showArrow = input(true, ...(ngDevMode ? [{ debugName: "showArrow" }] : []));
|
|
719
|
+
this.offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : []));
|
|
720
|
+
this.autoFlip = input(true, ...(ngDevMode ? [{ debugName: "autoFlip" }] : []));
|
|
721
|
+
this.trigger = input(undefined, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
722
|
+
this.isOpen = model(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
723
|
+
this.onOpened = output();
|
|
724
|
+
this.onHidden = output();
|
|
725
|
+
this._panelRef = viewChild('panelRef', ...(ngDevMode ? [{ debugName: "_panelRef" }] : []));
|
|
726
|
+
this._currentPlacement = signal('bottom', ...(ngDevMode ? [{ debugName: "_currentPlacement" }] : []));
|
|
727
|
+
this._isAnimating = signal(false, ...(ngDevMode ? [{ debugName: "_isAnimating" }] : []));
|
|
728
|
+
this._cleanupClickOutside = null;
|
|
729
|
+
this._cleanupEscape = null;
|
|
730
|
+
this._hoverBridgeTimeout = null;
|
|
731
|
+
this._wasOpen = false;
|
|
732
|
+
this._panelClasses = computed(() => ['popover-panel', `popover-${this._currentPlacement()}`].join(' '), ...(ngDevMode ? [{ debugName: "_panelClasses" }] : []));
|
|
733
|
+
this._arrowClasses = computed(() => `popover-arrow popover-arrow-${this._currentPlacement()}`, ...(ngDevMode ? [{ debugName: "_arrowClasses" }] : []));
|
|
734
|
+
effect(() => {
|
|
735
|
+
const isOpen = this.isOpen();
|
|
736
|
+
untracked(() => {
|
|
737
|
+
if (isOpen) {
|
|
738
|
+
this._currentPlacement.set(this.placement());
|
|
739
|
+
this._isAnimating.set(true);
|
|
740
|
+
this._setupListeners();
|
|
741
|
+
setTimeout(() => {
|
|
742
|
+
if (this.isOpen()) {
|
|
743
|
+
this._computePosition();
|
|
744
|
+
}
|
|
745
|
+
}, 0);
|
|
746
|
+
this._wasOpen = true;
|
|
747
|
+
}
|
|
748
|
+
else if (this._wasOpen) {
|
|
749
|
+
this._isAnimating.set(true);
|
|
750
|
+
this._wasOpen = false;
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
this._removeListeners();
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
this._destroyRef.onDestroy(() => {
|
|
758
|
+
this._removeListeners();
|
|
759
|
+
if (this._hoverBridgeTimeout) {
|
|
760
|
+
clearTimeout(this._hoverBridgeTimeout);
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
open() {
|
|
765
|
+
this.isOpen.set(true);
|
|
766
|
+
}
|
|
767
|
+
close() {
|
|
768
|
+
this.isOpen.set(false);
|
|
769
|
+
}
|
|
770
|
+
toggle() {
|
|
771
|
+
if (this.isOpen()) {
|
|
772
|
+
this.close();
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.open();
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
_onAnimationEnd() {
|
|
779
|
+
this._isAnimating.set(false);
|
|
780
|
+
if (this.isOpen()) {
|
|
781
|
+
this.onOpened.emit();
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
this._removeListeners();
|
|
785
|
+
this.onHidden.emit();
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
_onPanelMouseEnter() {
|
|
789
|
+
if (this._hoverBridgeTimeout) {
|
|
790
|
+
clearTimeout(this._hoverBridgeTimeout);
|
|
791
|
+
this._hoverBridgeTimeout = null;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
_onPanelMouseLeave() {
|
|
795
|
+
this._hoverBridgeTimeout = setTimeout(() => this.close(), 100);
|
|
796
|
+
}
|
|
797
|
+
_computePosition() {
|
|
798
|
+
const anchor = this.trigger() ??
|
|
799
|
+
this._elementRef.nativeElement.parentElement;
|
|
800
|
+
const panel = this._panelRef()?.nativeElement;
|
|
801
|
+
if (!anchor || !panel)
|
|
802
|
+
return;
|
|
803
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
804
|
+
const panelRect = panel.getBoundingClientRect();
|
|
805
|
+
const offset = this.offset();
|
|
806
|
+
let placement = this._currentPlacement();
|
|
807
|
+
if (this.autoFlip()) {
|
|
808
|
+
placement = this._getFlippedPlacement(placement, anchorRect, panelRect, offset);
|
|
809
|
+
this._currentPlacement.set(placement);
|
|
810
|
+
}
|
|
811
|
+
let top = 0;
|
|
812
|
+
let left = 0;
|
|
813
|
+
switch (placement) {
|
|
814
|
+
case 'top':
|
|
815
|
+
top = anchorRect.top - panelRect.height - offset;
|
|
816
|
+
left =
|
|
817
|
+
anchorRect.left +
|
|
818
|
+
(anchorRect.width - panelRect.width) / 2;
|
|
819
|
+
break;
|
|
820
|
+
case 'bottom':
|
|
821
|
+
top = anchorRect.bottom + offset;
|
|
822
|
+
left =
|
|
823
|
+
anchorRect.left +
|
|
824
|
+
(anchorRect.width - panelRect.width) / 2;
|
|
825
|
+
break;
|
|
826
|
+
case 'left':
|
|
827
|
+
top =
|
|
828
|
+
anchorRect.top +
|
|
829
|
+
(anchorRect.height - panelRect.height) / 2;
|
|
830
|
+
left = anchorRect.left - panelRect.width - offset;
|
|
831
|
+
break;
|
|
832
|
+
case 'right':
|
|
833
|
+
top =
|
|
834
|
+
anchorRect.top +
|
|
835
|
+
(anchorRect.height - panelRect.height) / 2;
|
|
836
|
+
left = anchorRect.right + offset;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
panel.style.setProperty('--popover-top', `${top}px`);
|
|
840
|
+
panel.style.setProperty('--popover-left', `${left}px`);
|
|
841
|
+
}
|
|
842
|
+
_getFlippedPlacement(placement, anchorRect, panelRect, offset) {
|
|
843
|
+
const viewportWidth = this._document.defaultView?.innerWidth ?? 0;
|
|
844
|
+
const viewportHeight = this._document.defaultView?.innerHeight ?? 0;
|
|
845
|
+
const opposites = {
|
|
846
|
+
top: 'bottom',
|
|
847
|
+
bottom: 'top',
|
|
848
|
+
left: 'right',
|
|
849
|
+
right: 'left',
|
|
850
|
+
};
|
|
851
|
+
switch (placement) {
|
|
852
|
+
case 'top':
|
|
853
|
+
if (anchorRect.top - panelRect.height - offset < 0) {
|
|
854
|
+
return opposites[placement];
|
|
855
|
+
}
|
|
856
|
+
break;
|
|
857
|
+
case 'bottom':
|
|
858
|
+
if (anchorRect.bottom + panelRect.height + offset >
|
|
859
|
+
viewportHeight) {
|
|
860
|
+
return opposites[placement];
|
|
861
|
+
}
|
|
862
|
+
break;
|
|
863
|
+
case 'left':
|
|
864
|
+
if (anchorRect.left - panelRect.width - offset < 0) {
|
|
865
|
+
return opposites[placement];
|
|
866
|
+
}
|
|
867
|
+
break;
|
|
868
|
+
case 'right':
|
|
869
|
+
if (anchorRect.right + panelRect.width + offset >
|
|
870
|
+
viewportWidth) {
|
|
871
|
+
return opposites[placement];
|
|
872
|
+
}
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
return placement;
|
|
876
|
+
}
|
|
877
|
+
_setupListeners() {
|
|
878
|
+
this._removeListeners();
|
|
879
|
+
this._cleanupClickOutside = this._renderer.listen(this._document, 'click', (event) => {
|
|
880
|
+
const anchor = this.trigger() ??
|
|
881
|
+
this._elementRef.nativeElement.parentElement;
|
|
882
|
+
const panel = this._panelRef()?.nativeElement;
|
|
883
|
+
const target = event.target;
|
|
884
|
+
if (panel &&
|
|
885
|
+
!panel.contains(target) &&
|
|
886
|
+
anchor &&
|
|
887
|
+
!anchor.contains(target)) {
|
|
888
|
+
this.close();
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
this._cleanupEscape = this._renderer.listen(this._document, 'keydown', (event) => {
|
|
892
|
+
if (event.key === 'Escape') {
|
|
893
|
+
this.close();
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
this._cleanupScroll = this._renderer.listen('window', 'scroll', () => {
|
|
897
|
+
if (this.isOpen()) {
|
|
898
|
+
this.close();
|
|
899
|
+
}
|
|
900
|
+
}, { capture: true });
|
|
901
|
+
}
|
|
902
|
+
_removeListeners() {
|
|
903
|
+
if (this._cleanupClickOutside) {
|
|
904
|
+
this._cleanupClickOutside();
|
|
905
|
+
this._cleanupClickOutside = null;
|
|
906
|
+
}
|
|
907
|
+
if (this._cleanupEscape) {
|
|
908
|
+
this._cleanupEscape();
|
|
909
|
+
this._cleanupEscape = null;
|
|
910
|
+
}
|
|
911
|
+
if (this._cleanupScroll) {
|
|
912
|
+
this._cleanupScroll();
|
|
913
|
+
this._cleanupScroll = null;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
917
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: PopoverComponent, isStandalone: true, selector: "ls-popover", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, showArrow: { classPropertyName: "showArrow", publicName: "showArrow", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, autoFlip: { classPropertyName: "autoFlip", publicName: "autoFlip", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange", onOpened: "onOpened", onHidden: "onHidden" }, viewQueries: [{ propertyName: "_panelRef", first: true, predicate: ["panelRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
918
|
+
@if (isOpen() || _isAnimating()) {
|
|
919
|
+
<div
|
|
920
|
+
[class]="_panelClasses()"
|
|
921
|
+
[class.popover-enter]="isOpen() && _isAnimating()"
|
|
922
|
+
[class.popover-exit]="!isOpen() && _isAnimating()"
|
|
923
|
+
(animationend)="_onAnimationEnd()"
|
|
924
|
+
(mouseenter)="_onPanelMouseEnter()"
|
|
925
|
+
(mouseleave)="_onPanelMouseLeave()"
|
|
926
|
+
role="dialog"
|
|
927
|
+
aria-modal="false"
|
|
928
|
+
#panelRef>
|
|
929
|
+
@if (showArrow()) {
|
|
930
|
+
<span [class]="_arrowClasses()"></span>
|
|
931
|
+
}
|
|
932
|
+
<ng-content />
|
|
933
|
+
</div>
|
|
934
|
+
}
|
|
935
|
+
`, isInline: true, styles: [":host{display:contents}.popover-panel{position:fixed;top:var(--popover-top, 0);left:var(--popover-left, 0);z-index:1050;background:var(--ls-color-white, #fff);border:1px solid var(--ls-color-border, #e0e6ed);border-radius:8px;box-shadow:0 4px 16px #0000001f;padding:12px 16px;animation-duration:.15s;animation-fill-mode:both;animation-timing-function:ease-out}.popover-enter{animation-name:popoverFadeIn}.popover-exit{animation-name:popoverFadeOut}@keyframes popoverFadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes popoverFadeOut{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(4px)}}.popover-arrow{position:absolute;width:10px;height:10px;background:inherit;border:inherit;transform:rotate(45deg);z-index:-1}.popover-arrow-top{bottom:-6px;left:50%;margin-left:-5px;border-top:0;border-left:0}.popover-arrow-bottom{top:-6px;left:50%;margin-left:-5px;border-bottom:0;border-right:0}.popover-arrow-left{right:-6px;top:50%;margin-top:-5px;border-bottom:0;border-left:0}.popover-arrow-right{left:-6px;top:50%;margin-top:-5px;border-top:0;border-right:0}:host-context(body.dark) .popover-panel{background:#1b2e4b!important;border-color:#253b5c!important;color:#e0e6ed!important;box-shadow:0 4px 16px #00000080!important}:host-context(.dark) .popover-panel{background:#1b2e4b!important;border-color:#253b5c!important;color:#e0e6ed!important}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
936
|
+
}
|
|
937
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: PopoverComponent, decorators: [{
|
|
938
|
+
type: Component,
|
|
939
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ls-popover', standalone: true, template: `
|
|
940
|
+
@if (isOpen() || _isAnimating()) {
|
|
941
|
+
<div
|
|
942
|
+
[class]="_panelClasses()"
|
|
943
|
+
[class.popover-enter]="isOpen() && _isAnimating()"
|
|
944
|
+
[class.popover-exit]="!isOpen() && _isAnimating()"
|
|
945
|
+
(animationend)="_onAnimationEnd()"
|
|
946
|
+
(mouseenter)="_onPanelMouseEnter()"
|
|
947
|
+
(mouseleave)="_onPanelMouseLeave()"
|
|
948
|
+
role="dialog"
|
|
949
|
+
aria-modal="false"
|
|
950
|
+
#panelRef>
|
|
951
|
+
@if (showArrow()) {
|
|
952
|
+
<span [class]="_arrowClasses()"></span>
|
|
953
|
+
}
|
|
954
|
+
<ng-content />
|
|
955
|
+
</div>
|
|
956
|
+
}
|
|
957
|
+
`, styles: [":host{display:contents}.popover-panel{position:fixed;top:var(--popover-top, 0);left:var(--popover-left, 0);z-index:1050;background:var(--ls-color-white, #fff);border:1px solid var(--ls-color-border, #e0e6ed);border-radius:8px;box-shadow:0 4px 16px #0000001f;padding:12px 16px;animation-duration:.15s;animation-fill-mode:both;animation-timing-function:ease-out}.popover-enter{animation-name:popoverFadeIn}.popover-exit{animation-name:popoverFadeOut}@keyframes popoverFadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes popoverFadeOut{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(4px)}}.popover-arrow{position:absolute;width:10px;height:10px;background:inherit;border:inherit;transform:rotate(45deg);z-index:-1}.popover-arrow-top{bottom:-6px;left:50%;margin-left:-5px;border-top:0;border-left:0}.popover-arrow-bottom{top:-6px;left:50%;margin-left:-5px;border-bottom:0;border-right:0}.popover-arrow-left{right:-6px;top:50%;margin-top:-5px;border-bottom:0;border-left:0}.popover-arrow-right{left:-6px;top:50%;margin-top:-5px;border-top:0;border-right:0}:host-context(body.dark) .popover-panel{background:#1b2e4b!important;border-color:#253b5c!important;color:#e0e6ed!important;box-shadow:0 4px 16px #00000080!important}:host-context(.dark) .popover-panel{background:#1b2e4b!important;border-color:#253b5c!important;color:#e0e6ed!important}\n"] }]
|
|
958
|
+
}], ctorParameters: () => [], propDecorators: { placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], showArrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArrow", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], autoFlip: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFlip", required: false }] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], onOpened: [{ type: i0.Output, args: ["onOpened"] }], onHidden: [{ type: i0.Output, args: ["onHidden"] }], _panelRef: [{ type: i0.ViewChild, args: ['panelRef', { isSignal: true }] }] } });
|
|
959
|
+
|
|
960
|
+
let nextId = 0;
|
|
961
|
+
class TooltipComponent {
|
|
962
|
+
constructor() {
|
|
963
|
+
this.content = input.required(...(ngDevMode ? [{ debugName: "content" }] : []));
|
|
964
|
+
this.placement = input('top', ...(ngDevMode ? [{ debugName: "placement" }] : []));
|
|
965
|
+
this.backgroundClass = input('dark', ...(ngDevMode ? [{ debugName: "backgroundClass" }] : []));
|
|
966
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
967
|
+
this._isVisible = signal(false, ...(ngDevMode ? [{ debugName: "_isVisible" }] : []));
|
|
968
|
+
this._tooltipId = `ls-tooltip-${nextId++}`;
|
|
969
|
+
}
|
|
970
|
+
get tooltipClasses() {
|
|
971
|
+
return [
|
|
972
|
+
'tooltip-box',
|
|
973
|
+
`tooltip-${this.placement()}`,
|
|
974
|
+
`tooltip-bg-${this.backgroundClass()}`,
|
|
975
|
+
].join(' ');
|
|
976
|
+
}
|
|
977
|
+
show() {
|
|
978
|
+
this._isVisible.set(true);
|
|
979
|
+
}
|
|
980
|
+
hide() {
|
|
981
|
+
this._isVisible.set(false);
|
|
982
|
+
}
|
|
983
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
984
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: TooltipComponent, isStandalone: true, selector: "ls-tooltip", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: true, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, backgroundClass: { classPropertyName: "backgroundClass", publicName: "backgroundClass", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
985
|
+
<span
|
|
986
|
+
class="ls-tooltip-trigger"
|
|
987
|
+
[class.ls-tooltip-disabled]="disabled()"
|
|
988
|
+
[attr.aria-describedby]="disabled() ? null : _tooltipId"
|
|
989
|
+
(mouseenter)="show()"
|
|
990
|
+
(mouseleave)="hide()"
|
|
991
|
+
(focus)="show()"
|
|
992
|
+
(blur)="hide()">
|
|
993
|
+
<ng-content />
|
|
994
|
+
@if (_isVisible() && !disabled()) {
|
|
995
|
+
<span
|
|
996
|
+
[id]="_tooltipId"
|
|
997
|
+
role="tooltip"
|
|
998
|
+
[attr.aria-hidden]="!_isVisible()"
|
|
999
|
+
[class]="tooltipClasses">
|
|
1000
|
+
{{ content() }}
|
|
1001
|
+
<span class="tooltip-arrow"></span>
|
|
1002
|
+
</span>
|
|
1003
|
+
}
|
|
1004
|
+
</span>
|
|
1005
|
+
`, isInline: true, styles: [":host{display:contents}.ls-tooltip-trigger{position:relative;display:inline-flex}.ls-tooltip-disabled{pointer-events:none}.tooltip-box{position:absolute;z-index:10;padding:2px 8px;border-radius:6px;font-family:var(--ls-font-family);font-size:.8rem;font-weight:500;white-space:nowrap;box-shadow:0 2px 8px #00000026;pointer-events:none;animation:tooltip-fade-in var(--ls-transition-duration) ease forwards}@keyframes tooltip-fade-in{0%{opacity:0}to{opacity:1}}.tooltip-top{bottom:calc(100% + 8px);left:50%;transform:translate(-50%)}.tooltip-bottom{top:calc(100% + 8px);left:50%;transform:translate(-50%)}.tooltip-left{right:calc(100% + 8px);top:50%;transform:translateY(-50%)}.tooltip-right{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}.tooltip-arrow{position:absolute;width:0;height:0;border:6px solid transparent}.tooltip-top .tooltip-arrow{bottom:-6px;left:50%;transform:translate(-50%);border-bottom:none;border-top-color:inherit}.tooltip-bottom .tooltip-arrow{top:-6px;left:50%;transform:translate(-50%);border-top:none;border-bottom-color:inherit}.tooltip-left .tooltip-arrow{right:-6px;top:50%;transform:translateY(-50%);border-right:none;border-left-color:inherit}.tooltip-right .tooltip-arrow{left:-6px;top:50%;transform:translateY(-50%);border-left:none;border-right-color:inherit}.tooltip-bg-primary{background-color:var(--ls-color-primary);color:#fff;border-color:var(--ls-color-primary)}.tooltip-bg-success{background-color:var(--ls-color-success);color:#fff;border-color:var(--ls-color-success)}.tooltip-bg-info{background-color:var(--ls-color-info);color:#fff;border-color:var(--ls-color-info)}.tooltip-bg-danger{background-color:var(--ls-color-danger);color:#fff;border-color:var(--ls-color-danger)}.tooltip-bg-warning{background-color:var(--ls-color-warning);color:#fff;border-color:var(--ls-color-warning)}.tooltip-bg-secondary{background-color:var(--ls-color-secondary);color:#fff;border-color:var(--ls-color-secondary)}.tooltip-bg-dark{background-color:var(--ls-color-dark);color:#fff;border-color:var(--ls-color-dark)}body.dark .tooltip-bg-dark{background-color:#e0e6ed;color:var(--ls-color-dark);border-color:#e0e6ed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1006
|
+
}
|
|
1007
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: TooltipComponent, decorators: [{
|
|
1008
|
+
type: Component,
|
|
1009
|
+
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ls-tooltip', standalone: true, template: `
|
|
1010
|
+
<span
|
|
1011
|
+
class="ls-tooltip-trigger"
|
|
1012
|
+
[class.ls-tooltip-disabled]="disabled()"
|
|
1013
|
+
[attr.aria-describedby]="disabled() ? null : _tooltipId"
|
|
1014
|
+
(mouseenter)="show()"
|
|
1015
|
+
(mouseleave)="hide()"
|
|
1016
|
+
(focus)="show()"
|
|
1017
|
+
(blur)="hide()">
|
|
1018
|
+
<ng-content />
|
|
1019
|
+
@if (_isVisible() && !disabled()) {
|
|
1020
|
+
<span
|
|
1021
|
+
[id]="_tooltipId"
|
|
1022
|
+
role="tooltip"
|
|
1023
|
+
[attr.aria-hidden]="!_isVisible()"
|
|
1024
|
+
[class]="tooltipClasses">
|
|
1025
|
+
{{ content() }}
|
|
1026
|
+
<span class="tooltip-arrow"></span>
|
|
1027
|
+
</span>
|
|
1028
|
+
}
|
|
1029
|
+
</span>
|
|
1030
|
+
`, styles: [":host{display:contents}.ls-tooltip-trigger{position:relative;display:inline-flex}.ls-tooltip-disabled{pointer-events:none}.tooltip-box{position:absolute;z-index:10;padding:2px 8px;border-radius:6px;font-family:var(--ls-font-family);font-size:.8rem;font-weight:500;white-space:nowrap;box-shadow:0 2px 8px #00000026;pointer-events:none;animation:tooltip-fade-in var(--ls-transition-duration) ease forwards}@keyframes tooltip-fade-in{0%{opacity:0}to{opacity:1}}.tooltip-top{bottom:calc(100% + 8px);left:50%;transform:translate(-50%)}.tooltip-bottom{top:calc(100% + 8px);left:50%;transform:translate(-50%)}.tooltip-left{right:calc(100% + 8px);top:50%;transform:translateY(-50%)}.tooltip-right{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}.tooltip-arrow{position:absolute;width:0;height:0;border:6px solid transparent}.tooltip-top .tooltip-arrow{bottom:-6px;left:50%;transform:translate(-50%);border-bottom:none;border-top-color:inherit}.tooltip-bottom .tooltip-arrow{top:-6px;left:50%;transform:translate(-50%);border-top:none;border-bottom-color:inherit}.tooltip-left .tooltip-arrow{right:-6px;top:50%;transform:translateY(-50%);border-right:none;border-left-color:inherit}.tooltip-right .tooltip-arrow{left:-6px;top:50%;transform:translateY(-50%);border-left:none;border-right-color:inherit}.tooltip-bg-primary{background-color:var(--ls-color-primary);color:#fff;border-color:var(--ls-color-primary)}.tooltip-bg-success{background-color:var(--ls-color-success);color:#fff;border-color:var(--ls-color-success)}.tooltip-bg-info{background-color:var(--ls-color-info);color:#fff;border-color:var(--ls-color-info)}.tooltip-bg-danger{background-color:var(--ls-color-danger);color:#fff;border-color:var(--ls-color-danger)}.tooltip-bg-warning{background-color:var(--ls-color-warning);color:#fff;border-color:var(--ls-color-warning)}.tooltip-bg-secondary{background-color:var(--ls-color-secondary);color:#fff;border-color:var(--ls-color-secondary)}.tooltip-bg-dark{background-color:var(--ls-color-dark);color:#fff;border-color:var(--ls-color-dark)}body.dark .tooltip-bg-dark{background-color:#e0e6ed;color:var(--ls-color-dark);border-color:#e0e6ed}\n"] }]
|
|
1031
|
+
}], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: true }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], backgroundClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundClass", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Generated bundle index. Do not edit.
|
|
1035
|
+
*/
|
|
1036
|
+
|
|
1037
|
+
export { MenuComponent, MenuItemComponent, MenuTriggerDirective, ModalActionsComponent, ModalComponent, ModalContentComponent, ModalStackService, ModalTitleComponent, PopoverComponent, TooltipComponent };
|
|
1038
|
+
//# sourceMappingURL=lumston-ds-angular-src-overlay.mjs.map
|