@memberjunction/ng-ui-components 5.51.0 → 6.1.0-edge.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +9 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.d.ts +96 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.d.ts.map +1 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.js +300 -0
- package/dist/lib/bottom-sheet/bottom-sheet.component.js.map +1 -0
- package/dist/lib/slide-panel/slide-panel.component.js +6 -6
- package/dist/lib/slide-panel/slide-panel.component.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +12 -10
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MemberJunction
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -225,6 +225,15 @@ Floating, **non-modal** panel (no backdrop): `Draggable`, `Resizable` (8 handles
|
|
|
225
225
|
### `mj-filter-popover` — MJFilterPopoverComponent
|
|
226
226
|
Filter trigger button + CDK popover on desktop, docked bottom-sheet on mobile. Inputs: `Label`, `Icon`, `ActiveCount` (badge), `ShowClearAll`. Output: `ClearAllRequested`. Project your filter UI (dropdowns, chips) as content.
|
|
227
227
|
|
|
228
|
+
### `mj-bottom-sheet` — MJBottomSheetComponent
|
|
229
|
+
Generic mobile bottom sheet: scrim + panel sliding up from the bottom edge with a grab handle. Pure chrome, no breakpoint logic — the caller decides when a sheet is the right presentation. Fully modal: `role="dialog"` + `aria-modal`, focus moves in on open and is **trapped** (Tab/Shift+Tab cycle within), body scroll locks while open, focus returns to the opener on close. Owns enter/exit transitions (class-driven, dismissal animates), Escape, scrim-click close, `prefers-reduced-motion`. Inputs: `[(Visible)]` (two-way), `Title` (optional header), `AriaLabel`. Output: `Closed` (after the exit transition). Sits in the mobile-chrome z band (9998/9999, below `mj-window`); settled open state is `transform: none` so `position: fixed` descendants (dropdowns) aren't displaced.
|
|
230
|
+
|
|
231
|
+
```html
|
|
232
|
+
<mj-bottom-sheet [(Visible)]="sheetOpen" Title="Open Records">
|
|
233
|
+
<div class="rows">…</div>
|
|
234
|
+
</mj-bottom-sheet>
|
|
235
|
+
```
|
|
236
|
+
|
|
228
237
|
## Patterns
|
|
229
238
|
|
|
230
239
|
### `mj-empty-state` — MJEmptyStateComponent
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EventEmitter, OnDestroy } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* Generic bottom sheet — the canonical mobile overlay surface (scrim + panel
|
|
5
|
+
* sliding up from the bottom edge with a grab handle).
|
|
6
|
+
*
|
|
7
|
+
* Pure chrome, no breakpoint logic: the CALLER decides when a sheet is the
|
|
8
|
+
* right presentation (typically below its mobile breakpoint). Content is
|
|
9
|
+
* projected; the sheet owns the scrim, enter/exit transitions, Escape,
|
|
10
|
+
* focus capture/restore, and the open/close lifecycle.
|
|
11
|
+
*
|
|
12
|
+
* Modality contract (this is a shared primitive — the a11y baseline is the
|
|
13
|
+
* full one, not the historic house minimum): `role="dialog"` +
|
|
14
|
+
* `aria-modal="true"`, focus moves INTO the sheet on open and is trapped
|
|
15
|
+
* there (Tab/Shift+Tab cycle within), body scroll is locked while open, and
|
|
16
|
+
* focus returns to the opener on close.
|
|
17
|
+
*
|
|
18
|
+
* Mechanics notes (learned from the earlier hand-rolled sheets):
|
|
19
|
+
* - Enter AND exit are class-driven `transition`s (the filter-popover sheet's
|
|
20
|
+
* mount-only `animation` cannot animate dismissal).
|
|
21
|
+
* - The settled open state is `transform: none`, NOT `translateY(0)` — any
|
|
22
|
+
* non-none transform makes the sheet a containing block and breaks
|
|
23
|
+
* `position: fixed` descendants (dropdowns/popovers projected into it).
|
|
24
|
+
* - z-index 9998/9999: the established mobile-chrome band (same as the
|
|
25
|
+
* left-nav drawer and filter sheet; below mj-window at 10000, and
|
|
26
|
+
* MJConfirmService dialogs at 20000 still land above).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```html
|
|
30
|
+
* <mj-bottom-sheet [(Visible)]="sheetOpen" Title="Open Records">
|
|
31
|
+
* <div>...rows...</div>
|
|
32
|
+
* </mj-bottom-sheet>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class MJBottomSheetComponent implements OnDestroy {
|
|
36
|
+
private cdr;
|
|
37
|
+
private sheetEl?;
|
|
38
|
+
/** Optional header row text; also the default accessible name */
|
|
39
|
+
Title: string;
|
|
40
|
+
/** Accessible name override (falls back to Title) */
|
|
41
|
+
AriaLabel: string;
|
|
42
|
+
set Visible(value: boolean);
|
|
43
|
+
get Visible(): boolean;
|
|
44
|
+
private _visible;
|
|
45
|
+
VisibleChange: EventEmitter<boolean>;
|
|
46
|
+
/** Emitted after the exit transition completes */
|
|
47
|
+
Closed: EventEmitter<void>;
|
|
48
|
+
/** @if gate — outlives Visible through the exit transition */
|
|
49
|
+
IsRendered: boolean;
|
|
50
|
+
/** Drives the enter/exit transition classes */
|
|
51
|
+
IsOpen: boolean;
|
|
52
|
+
/** Open transition finished — transform settles to `none` */
|
|
53
|
+
IsSettled: boolean;
|
|
54
|
+
/** Focus restore target captured at open */
|
|
55
|
+
private previousFocus;
|
|
56
|
+
/** body overflow value before the scroll lock; null = not locked by us */
|
|
57
|
+
private previousBodyOverflow;
|
|
58
|
+
private closeFallbackTimer;
|
|
59
|
+
/** Pending open rAFs — canceled on close so a rapid open→close can't
|
|
60
|
+
* resurrect the open class after the close started */
|
|
61
|
+
private openRafIds;
|
|
62
|
+
/** Settle fallback: under prefers-reduced-motion no transitionend fires,
|
|
63
|
+
* and the sheet must still reach transform:none (containing-block fix) */
|
|
64
|
+
private settleFallbackTimer;
|
|
65
|
+
OnEscape(): void;
|
|
66
|
+
/** Scrim click / programmatic dismiss */
|
|
67
|
+
Close(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Focus trap: aria-modal promises assistive tech the background is inert —
|
|
70
|
+
* Tab must cycle within the sheet, never walk out behind the scrim.
|
|
71
|
+
*/
|
|
72
|
+
OnSheetKeydown(event: KeyboardEvent): void;
|
|
73
|
+
OnTransitionEnd(event: TransitionEvent): void;
|
|
74
|
+
private open;
|
|
75
|
+
/**
|
|
76
|
+
* @param emit true when the sheet itself initiated the dismissal (Escape,
|
|
77
|
+
* scrim) — the Visible setter path passes false because the caller's
|
|
78
|
+
* binding is already up to date.
|
|
79
|
+
*/
|
|
80
|
+
private close;
|
|
81
|
+
private finishClose;
|
|
82
|
+
/**
|
|
83
|
+
* Body scroll lock while open: aria-modal content must not let the page
|
|
84
|
+
* scroll behind the scrim. Restores the EXACT prior inline value (which is
|
|
85
|
+
* usually '') so a host page's own overflow styling survives.
|
|
86
|
+
*/
|
|
87
|
+
private lockBodyScroll;
|
|
88
|
+
private unlockBodyScroll;
|
|
89
|
+
private clearCloseFallback;
|
|
90
|
+
private clearSettleFallback;
|
|
91
|
+
private cancelOpenRafs;
|
|
92
|
+
ngOnDestroy(): void;
|
|
93
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MJBottomSheetComponent, never>;
|
|
94
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MJBottomSheetComponent, "mj-bottom-sheet", never, { "Title": { "alias": "Title"; "required": false; }; "AriaLabel": { "alias": "AriaLabel"; "required": false; }; "Visible": { "alias": "Visible"; "required": false; }; }, { "VisibleChange": "VisibleChange"; "Closed": "Closed"; }, never, ["*"], true, never>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=bottom-sheet.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bottom-sheet.component.d.ts","sourceRoot":"","sources":["../../../src/lib/bottom-sheet/bottom-sheet.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACqB,YAAY,EACZ,SAAS,EACpC,MAAM,eAAe,CAAC;;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAOa,sBAAuB,YAAW,SAAS;IACtD,OAAO,CAAC,GAAG,CAA6B;IAEpB,OAAO,CAAC,OAAO,CAAC,CAA0B;IAE9D,iEAAiE;IACxD,KAAK,SAAM;IACpB,qDAAqD;IAC5C,SAAS,SAAM;IAExB,IACI,OAAO,CAAC,KAAK,EAAE,OAAO,EAUzB;IACD,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,OAAO,CAAC,QAAQ,CAAS;IAEf,aAAa,wBAA+B;IACtD,kDAAkD;IACxC,MAAM,qBAA4B;IAE5C,8DAA8D;IACvD,UAAU,UAAS;IAC1B,+CAA+C;IACxC,MAAM,UAAS;IACtB,6DAA6D;IACtD,SAAS,UAAS;IAEzB,4CAA4C;IAC5C,OAAO,CAAC,aAAa,CAA4B;IACjD,0EAA0E;IAC1E,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,kBAAkB,CAA8C;IACxE;2DACuD;IACvD,OAAO,CAAC,UAAU,CAAgB;IAClC;+EAC2E;IAC3E,OAAO,CAAC,mBAAmB,CAA8C;IAGlE,QAAQ,IAAI,IAAI;IAMvB,yCAAyC;IAClC,KAAK,IAAI,IAAI;IAMpB;;;OAGG;IACI,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IA0B1C,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAapD,OAAO,CAAC,IAAI;IA8BZ;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAgBb,OAAO,CAAC,WAAW;IAanB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAKtB,WAAW,IAAI,IAAI;yCAjNR,sBAAsB;2CAAtB,sBAAsB;CAyNlC"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, ChangeDetectionStrategy, HostListener, ViewChild, inject } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
const _c0 = ["sheet"];
|
|
4
|
+
const _c1 = ["*"];
|
|
5
|
+
function MJBottomSheetComponent_Conditional_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
6
|
+
i0.ɵɵdomElementStart(0, "div", 4);
|
|
7
|
+
i0.ɵɵtext(1);
|
|
8
|
+
i0.ɵɵdomElementEnd();
|
|
9
|
+
} if (rf & 2) {
|
|
10
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
11
|
+
i0.ɵɵadvance();
|
|
12
|
+
i0.ɵɵtextInterpolate(ctx_r1.Title);
|
|
13
|
+
} }
|
|
14
|
+
function MJBottomSheetComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
15
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
16
|
+
i0.ɵɵdomElementStart(0, "div", 1);
|
|
17
|
+
i0.ɵɵdomListener("click", function MJBottomSheetComponent_Conditional_0_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Close()); });
|
|
18
|
+
i0.ɵɵdomElementEnd();
|
|
19
|
+
i0.ɵɵdomElementStart(1, "div", 2, 0);
|
|
20
|
+
i0.ɵɵdomListener("keydown", function MJBottomSheetComponent_Conditional_0_Template_div_keydown_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnSheetKeydown($event)); })("transitionend", function MJBottomSheetComponent_Conditional_0_Template_div_transitionend_1_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnTransitionEnd($event)); });
|
|
21
|
+
i0.ɵɵdomElement(3, "div", 3);
|
|
22
|
+
i0.ɵɵconditionalCreate(4, MJBottomSheetComponent_Conditional_0_Conditional_4_Template, 2, 1, "div", 4);
|
|
23
|
+
i0.ɵɵdomElementStart(5, "div", 5);
|
|
24
|
+
i0.ɵɵprojection(6);
|
|
25
|
+
i0.ɵɵdomElementEnd()();
|
|
26
|
+
} if (rf & 2) {
|
|
27
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
28
|
+
i0.ɵɵclassProp("mj-bottom-sheet-scrim--open", ctx_r1.IsOpen);
|
|
29
|
+
i0.ɵɵadvance();
|
|
30
|
+
i0.ɵɵclassProp("mj-bottom-sheet--open", ctx_r1.IsOpen)("mj-bottom-sheet--settled", ctx_r1.IsSettled);
|
|
31
|
+
i0.ɵɵattribute("aria-label", ctx_r1.AriaLabel || ctx_r1.Title || null);
|
|
32
|
+
i0.ɵɵadvance(3);
|
|
33
|
+
i0.ɵɵconditional(ctx_r1.Title ? 4 : -1);
|
|
34
|
+
} }
|
|
35
|
+
/**
|
|
36
|
+
* Generic bottom sheet — the canonical mobile overlay surface (scrim + panel
|
|
37
|
+
* sliding up from the bottom edge with a grab handle).
|
|
38
|
+
*
|
|
39
|
+
* Pure chrome, no breakpoint logic: the CALLER decides when a sheet is the
|
|
40
|
+
* right presentation (typically below its mobile breakpoint). Content is
|
|
41
|
+
* projected; the sheet owns the scrim, enter/exit transitions, Escape,
|
|
42
|
+
* focus capture/restore, and the open/close lifecycle.
|
|
43
|
+
*
|
|
44
|
+
* Modality contract (this is a shared primitive — the a11y baseline is the
|
|
45
|
+
* full one, not the historic house minimum): `role="dialog"` +
|
|
46
|
+
* `aria-modal="true"`, focus moves INTO the sheet on open and is trapped
|
|
47
|
+
* there (Tab/Shift+Tab cycle within), body scroll is locked while open, and
|
|
48
|
+
* focus returns to the opener on close.
|
|
49
|
+
*
|
|
50
|
+
* Mechanics notes (learned from the earlier hand-rolled sheets):
|
|
51
|
+
* - Enter AND exit are class-driven `transition`s (the filter-popover sheet's
|
|
52
|
+
* mount-only `animation` cannot animate dismissal).
|
|
53
|
+
* - The settled open state is `transform: none`, NOT `translateY(0)` — any
|
|
54
|
+
* non-none transform makes the sheet a containing block and breaks
|
|
55
|
+
* `position: fixed` descendants (dropdowns/popovers projected into it).
|
|
56
|
+
* - z-index 9998/9999: the established mobile-chrome band (same as the
|
|
57
|
+
* left-nav drawer and filter sheet; below mj-window at 10000, and
|
|
58
|
+
* MJConfirmService dialogs at 20000 still land above).
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```html
|
|
62
|
+
* <mj-bottom-sheet [(Visible)]="sheetOpen" Title="Open Records">
|
|
63
|
+
* <div>...rows...</div>
|
|
64
|
+
* </mj-bottom-sheet>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export class MJBottomSheetComponent {
|
|
68
|
+
cdr = inject(ChangeDetectorRef);
|
|
69
|
+
sheetEl;
|
|
70
|
+
/** Optional header row text; also the default accessible name */
|
|
71
|
+
Title = '';
|
|
72
|
+
/** Accessible name override (falls back to Title) */
|
|
73
|
+
AriaLabel = '';
|
|
74
|
+
set Visible(value) {
|
|
75
|
+
if (value === this._visible) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this._visible = value;
|
|
79
|
+
if (value) {
|
|
80
|
+
this.open();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.close(false);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
get Visible() {
|
|
87
|
+
return this._visible;
|
|
88
|
+
}
|
|
89
|
+
_visible = false;
|
|
90
|
+
VisibleChange = new EventEmitter();
|
|
91
|
+
/** Emitted after the exit transition completes */
|
|
92
|
+
Closed = new EventEmitter();
|
|
93
|
+
/** @if gate — outlives Visible through the exit transition */
|
|
94
|
+
IsRendered = false;
|
|
95
|
+
/** Drives the enter/exit transition classes */
|
|
96
|
+
IsOpen = false;
|
|
97
|
+
/** Open transition finished — transform settles to `none` */
|
|
98
|
+
IsSettled = false;
|
|
99
|
+
/** Focus restore target captured at open */
|
|
100
|
+
previousFocus = null;
|
|
101
|
+
/** body overflow value before the scroll lock; null = not locked by us */
|
|
102
|
+
previousBodyOverflow = null;
|
|
103
|
+
closeFallbackTimer = null;
|
|
104
|
+
/** Pending open rAFs — canceled on close so a rapid open→close can't
|
|
105
|
+
* resurrect the open class after the close started */
|
|
106
|
+
openRafIds = [];
|
|
107
|
+
/** Settle fallback: under prefers-reduced-motion no transitionend fires,
|
|
108
|
+
* and the sheet must still reach transform:none (containing-block fix) */
|
|
109
|
+
settleFallbackTimer = null;
|
|
110
|
+
OnEscape() {
|
|
111
|
+
if (this.IsOpen) {
|
|
112
|
+
this.close(true);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Scrim click / programmatic dismiss */
|
|
116
|
+
Close() {
|
|
117
|
+
if (this.IsOpen) {
|
|
118
|
+
this.close(true);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Focus trap: aria-modal promises assistive tech the background is inert —
|
|
123
|
+
* Tab must cycle within the sheet, never walk out behind the scrim.
|
|
124
|
+
*/
|
|
125
|
+
OnSheetKeydown(event) {
|
|
126
|
+
if (event.key !== 'Tab' || !this.sheetEl) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const sheet = this.sheetEl.nativeElement;
|
|
130
|
+
const focusables = Array.from(sheet.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
|
|
131
|
+
if (focusables.length === 0) {
|
|
132
|
+
event.preventDefault(); // nothing to move to — keep focus on the sheet
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const first = focusables[0];
|
|
136
|
+
const last = focusables[focusables.length - 1];
|
|
137
|
+
const active = document.activeElement;
|
|
138
|
+
if (event.shiftKey) {
|
|
139
|
+
if (active === first || active === sheet) {
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
last.focus();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (active === last || active === sheet) {
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
first.focus();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
OnTransitionEnd(event) {
|
|
150
|
+
if (event.target !== this.sheetEl?.nativeElement || event.propertyName !== 'transform') {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (this.IsOpen) {
|
|
154
|
+
// Settled: transform → none so fixed-position descendants aren't trapped
|
|
155
|
+
this.IsSettled = true;
|
|
156
|
+
this.cdr.markForCheck();
|
|
157
|
+
}
|
|
158
|
+
else if (this.IsRendered) {
|
|
159
|
+
this.finishClose();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
open() {
|
|
163
|
+
this.clearCloseFallback();
|
|
164
|
+
this.previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
165
|
+
this.lockBodyScroll();
|
|
166
|
+
this.IsRendered = true;
|
|
167
|
+
this.cdr.markForCheck();
|
|
168
|
+
// Two frames: one for the @if to render at translateY(100%), one so the
|
|
169
|
+
// class flip actually transitions (same-frame flips snap instead)
|
|
170
|
+
this.openRafIds.push(requestAnimationFrame(() => {
|
|
171
|
+
this.openRafIds.push(requestAnimationFrame(() => {
|
|
172
|
+
this.openRafIds = [];
|
|
173
|
+
if (!this._visible) {
|
|
174
|
+
return; // closed again before the frames ran — stay closed
|
|
175
|
+
}
|
|
176
|
+
this.IsOpen = true;
|
|
177
|
+
this.cdr.markForCheck();
|
|
178
|
+
this.sheetEl?.nativeElement?.focus();
|
|
179
|
+
// Reduced-motion (transition:none) never fires transitionend — the
|
|
180
|
+
// fallback still settles the transform to none
|
|
181
|
+
this.clearSettleFallback();
|
|
182
|
+
this.settleFallbackTimer = setTimeout(() => {
|
|
183
|
+
if (this.IsOpen && !this.IsSettled) {
|
|
184
|
+
this.IsSettled = true;
|
|
185
|
+
this.cdr.markForCheck();
|
|
186
|
+
}
|
|
187
|
+
}, 300);
|
|
188
|
+
}));
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* @param emit true when the sheet itself initiated the dismissal (Escape,
|
|
193
|
+
* scrim) — the Visible setter path passes false because the caller's
|
|
194
|
+
* binding is already up to date.
|
|
195
|
+
*/
|
|
196
|
+
close(emit) {
|
|
197
|
+
this._visible = false;
|
|
198
|
+
this.cancelOpenRafs();
|
|
199
|
+
this.clearSettleFallback();
|
|
200
|
+
this.IsOpen = false;
|
|
201
|
+
this.IsSettled = false;
|
|
202
|
+
this.cdr.markForCheck();
|
|
203
|
+
if (emit) {
|
|
204
|
+
this.VisibleChange.emit(false);
|
|
205
|
+
}
|
|
206
|
+
// transitionend normally finishes the close; the timer covers
|
|
207
|
+
// prefers-reduced-motion (no transition event) and detached nodes
|
|
208
|
+
this.clearCloseFallback();
|
|
209
|
+
this.closeFallbackTimer = setTimeout(() => this.finishClose(), 300);
|
|
210
|
+
}
|
|
211
|
+
finishClose() {
|
|
212
|
+
this.clearCloseFallback();
|
|
213
|
+
if (!this.IsRendered) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
this.IsRendered = false;
|
|
217
|
+
this.unlockBodyScroll();
|
|
218
|
+
this.cdr.markForCheck();
|
|
219
|
+
this.Closed.emit();
|
|
220
|
+
this.previousFocus?.focus();
|
|
221
|
+
this.previousFocus = null;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Body scroll lock while open: aria-modal content must not let the page
|
|
225
|
+
* scroll behind the scrim. Restores the EXACT prior inline value (which is
|
|
226
|
+
* usually '') so a host page's own overflow styling survives.
|
|
227
|
+
*/
|
|
228
|
+
lockBodyScroll() {
|
|
229
|
+
if (this.previousBodyOverflow === null) {
|
|
230
|
+
this.previousBodyOverflow = document.body.style.overflow;
|
|
231
|
+
document.body.style.overflow = 'hidden';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
unlockBodyScroll() {
|
|
235
|
+
if (this.previousBodyOverflow !== null) {
|
|
236
|
+
document.body.style.overflow = this.previousBodyOverflow;
|
|
237
|
+
this.previousBodyOverflow = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
clearCloseFallback() {
|
|
241
|
+
if (this.closeFallbackTimer !== null) {
|
|
242
|
+
clearTimeout(this.closeFallbackTimer);
|
|
243
|
+
this.closeFallbackTimer = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
clearSettleFallback() {
|
|
247
|
+
if (this.settleFallbackTimer !== null) {
|
|
248
|
+
clearTimeout(this.settleFallbackTimer);
|
|
249
|
+
this.settleFallbackTimer = null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
cancelOpenRafs() {
|
|
253
|
+
this.openRafIds.forEach(id => cancelAnimationFrame(id));
|
|
254
|
+
this.openRafIds = [];
|
|
255
|
+
}
|
|
256
|
+
ngOnDestroy() {
|
|
257
|
+
this.clearCloseFallback();
|
|
258
|
+
this.clearSettleFallback();
|
|
259
|
+
this.cancelOpenRafs();
|
|
260
|
+
// Destroyed while open (route change, parent teardown): never leave the
|
|
261
|
+
// page unscrollable
|
|
262
|
+
this.unlockBodyScroll();
|
|
263
|
+
}
|
|
264
|
+
static ɵfac = function MJBottomSheetComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJBottomSheetComponent)(); };
|
|
265
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJBottomSheetComponent, selectors: [["mj-bottom-sheet"]], viewQuery: function MJBottomSheetComponent_Query(rf, ctx) { if (rf & 1) {
|
|
266
|
+
i0.ɵɵviewQuery(_c0, 5);
|
|
267
|
+
} if (rf & 2) {
|
|
268
|
+
let _t;
|
|
269
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.sheetEl = _t.first);
|
|
270
|
+
} }, hostBindings: function MJBottomSheetComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
271
|
+
i0.ɵɵlistener("keydown.escape", function MJBottomSheetComponent_keydown_escape_HostBindingHandler() { return ctx.OnEscape(); }, i0.ɵɵresolveDocument);
|
|
272
|
+
} }, inputs: { Title: "Title", AriaLabel: "AriaLabel", Visible: "Visible" }, outputs: { VisibleChange: "VisibleChange", Closed: "Closed" }, ngContentSelectors: _c1, decls: 1, vars: 1, consts: [["sheet", ""], ["aria-hidden", "true", 1, "mj-bottom-sheet-scrim", 3, "click"], ["role", "dialog", "aria-modal", "true", "tabindex", "-1", 1, "mj-bottom-sheet", 3, "keydown", "transitionend"], ["aria-hidden", "true", 1, "mj-bottom-sheet-grab"], [1, "mj-bottom-sheet-header"], [1, "mj-bottom-sheet-content"]], template: function MJBottomSheetComponent_Template(rf, ctx) { if (rf & 1) {
|
|
273
|
+
i0.ɵɵprojectionDef();
|
|
274
|
+
i0.ɵɵconditionalCreate(0, MJBottomSheetComponent_Conditional_0_Template, 7, 8);
|
|
275
|
+
} if (rf & 2) {
|
|
276
|
+
i0.ɵɵconditional(ctx.IsRendered ? 0 : -1);
|
|
277
|
+
} }, styles: ["\n\n\n\n\n.mj-bottom-sheet-scrim[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n z-index: 9998;\n background: var(--mj-bg-overlay);\n opacity: 0;\n transition: opacity 0.24s ease;\n}\n\n.mj-bottom-sheet-scrim--open[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.mj-bottom-sheet[_ngcontent-%COMP%] {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n max-height: 85vh;\n background: var(--mj-bg-surface);\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: var(--mj-shadow-lg);\n transform: translateY(100%);\n transition: transform 0.24s ease;\n outline: none;\n}\n\n.mj-bottom-sheet--open[_ngcontent-%COMP%] {\n transform: translateY(0);\n}\n\n\n\n\n\n.mj-bottom-sheet--settled[_ngcontent-%COMP%] {\n transform: none;\n}\n\n.mj-bottom-sheet-grab[_ngcontent-%COMP%] {\n width: 36px;\n height: 4px;\n border-radius: 2px;\n background: var(--mj-border-strong);\n margin: 10px auto 2px;\n flex-shrink: 0;\n}\n\n.mj-bottom-sheet-header[_ngcontent-%COMP%] {\n flex-shrink: 0;\n padding: 8px 16px 10px;\n font-size: var(--mj-text-base, 15px);\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-bottom-sheet-content[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n padding-bottom: var(--mj-space-6);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .mj-bottom-sheet-scrim[_ngcontent-%COMP%], \n .mj-bottom-sheet[_ngcontent-%COMP%] {\n transition: none;\n }\n}"], changeDetection: 0 });
|
|
278
|
+
}
|
|
279
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJBottomSheetComponent, [{
|
|
280
|
+
type: Component,
|
|
281
|
+
args: [{ standalone: true, selector: 'mj-bottom-sheet', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (IsRendered) {\n <div\n class=\"mj-bottom-sheet-scrim\"\n [class.mj-bottom-sheet-scrim--open]=\"IsOpen\"\n (click)=\"Close()\"\n aria-hidden=\"true\"></div>\n <div\n #sheet\n class=\"mj-bottom-sheet\"\n [class.mj-bottom-sheet--open]=\"IsOpen\"\n [class.mj-bottom-sheet--settled]=\"IsSettled\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"AriaLabel || Title || null\"\n tabindex=\"-1\"\n (keydown)=\"OnSheetKeydown($event)\"\n (transitionend)=\"OnTransitionEnd($event)\">\n <div class=\"mj-bottom-sheet-grab\" aria-hidden=\"true\"></div>\n @if (Title) {\n <div class=\"mj-bottom-sheet-header\">{{ Title }}</div>\n }\n <div class=\"mj-bottom-sheet-content\">\n <ng-content></ng-content>\n </div>\n </div>\n}\n", styles: ["/* z-index 9998/9999: the mobile-chrome overlay band (left-nav drawer,\n filter sheet). Below mj-window (10000); MJConfirmService (20000) and\n toasts (100000) still land above a sheet. */\n\n.mj-bottom-sheet-scrim {\n position: fixed;\n inset: 0;\n z-index: 9998;\n background: var(--mj-bg-overlay);\n opacity: 0;\n transition: opacity 0.24s ease;\n}\n\n.mj-bottom-sheet-scrim--open {\n opacity: 1;\n}\n\n.mj-bottom-sheet {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n max-height: 85vh;\n background: var(--mj-bg-surface);\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: var(--mj-shadow-lg);\n transform: translateY(100%);\n transition: transform 0.24s ease;\n outline: none;\n}\n\n.mj-bottom-sheet--open {\n transform: translateY(0);\n}\n\n/* Settled open state: transform must be NONE, not translateY(0) \u2014 a non-none\n transform creates a containing block that displaces position:fixed\n descendants (dropdowns/popovers projected into the sheet). */\n.mj-bottom-sheet--settled {\n transform: none;\n}\n\n.mj-bottom-sheet-grab {\n width: 36px;\n height: 4px;\n border-radius: 2px;\n background: var(--mj-border-strong);\n margin: 10px auto 2px;\n flex-shrink: 0;\n}\n\n.mj-bottom-sheet-header {\n flex-shrink: 0;\n padding: 8px 16px 10px;\n font-size: var(--mj-text-base, 15px);\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-bottom-sheet-content {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n padding-bottom: var(--mj-space-6);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .mj-bottom-sheet-scrim,\n .mj-bottom-sheet {\n transition: none;\n }\n}\n"] }]
|
|
282
|
+
}], null, { sheetEl: [{
|
|
283
|
+
type: ViewChild,
|
|
284
|
+
args: ['sheet']
|
|
285
|
+
}], Title: [{
|
|
286
|
+
type: Input
|
|
287
|
+
}], AriaLabel: [{
|
|
288
|
+
type: Input
|
|
289
|
+
}], Visible: [{
|
|
290
|
+
type: Input
|
|
291
|
+
}], VisibleChange: [{
|
|
292
|
+
type: Output
|
|
293
|
+
}], Closed: [{
|
|
294
|
+
type: Output
|
|
295
|
+
}], OnEscape: [{
|
|
296
|
+
type: HostListener,
|
|
297
|
+
args: ['document:keydown.escape']
|
|
298
|
+
}] }); })();
|
|
299
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJBottomSheetComponent, { className: "MJBottomSheetComponent", filePath: "lib/bottom-sheet/bottom-sheet.component.ts", lineNumber: 45 }); })();
|
|
300
|
+
//# sourceMappingURL=bottom-sheet.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bottom-sheet.component.js","sourceRoot":"","sources":["../../../src/lib/bottom-sheet/bottom-sheet.component.ts","../../../src/lib/bottom-sheet/bottom-sheet.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter, ChangeDetectorRef, ChangeDetectionStrategy,\n ElementRef, HostListener, OnDestroy, ViewChild, inject\n} from '@angular/core';\n\n/**\n * Generic bottom sheet — the canonical mobile overlay surface (scrim + panel\n * sliding up from the bottom edge with a grab handle).\n *\n * Pure chrome, no breakpoint logic: the CALLER decides when a sheet is the\n * right presentation (typically below its mobile breakpoint). Content is\n * projected; the sheet owns the scrim, enter/exit transitions, Escape,\n * focus capture/restore, and the open/close lifecycle.\n *\n * Modality contract (this is a shared primitive — the a11y baseline is the\n * full one, not the historic house minimum): `role=\"dialog\"` +\n * `aria-modal=\"true\"`, focus moves INTO the sheet on open and is trapped\n * there (Tab/Shift+Tab cycle within), body scroll is locked while open, and\n * focus returns to the opener on close.\n *\n * Mechanics notes (learned from the earlier hand-rolled sheets):\n * - Enter AND exit are class-driven `transition`s (the filter-popover sheet's\n * mount-only `animation` cannot animate dismissal).\n * - The settled open state is `transform: none`, NOT `translateY(0)` — any\n * non-none transform makes the sheet a containing block and breaks\n * `position: fixed` descendants (dropdowns/popovers projected into it).\n * - z-index 9998/9999: the established mobile-chrome band (same as the\n * left-nav drawer and filter sheet; below mj-window at 10000, and\n * MJConfirmService dialogs at 20000 still land above).\n *\n * @example\n * ```html\n * <mj-bottom-sheet [(Visible)]=\"sheetOpen\" Title=\"Open Records\">\n * <div>...rows...</div>\n * </mj-bottom-sheet>\n * ```\n */\n@Component({\n standalone: true,\n selector: 'mj-bottom-sheet',\n templateUrl: './bottom-sheet.component.html',\n styleUrls: ['./bottom-sheet.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class MJBottomSheetComponent implements OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n @ViewChild('sheet') private sheetEl?: ElementRef<HTMLElement>;\n\n /** Optional header row text; also the default accessible name */\n @Input() Title = '';\n /** Accessible name override (falls back to Title) */\n @Input() AriaLabel = '';\n\n @Input()\n set Visible(value: boolean) {\n if (value === this._visible) {\n return;\n }\n this._visible = value;\n if (value) {\n this.open();\n } else {\n this.close(false);\n }\n }\n get Visible(): boolean {\n return this._visible;\n }\n private _visible = false;\n\n @Output() VisibleChange = new EventEmitter<boolean>();\n /** Emitted after the exit transition completes */\n @Output() Closed = new EventEmitter<void>();\n\n /** @if gate — outlives Visible through the exit transition */\n public IsRendered = false;\n /** Drives the enter/exit transition classes */\n public IsOpen = false;\n /** Open transition finished — transform settles to `none` */\n public IsSettled = false;\n\n /** Focus restore target captured at open */\n private previousFocus: HTMLElement | null = null;\n /** body overflow value before the scroll lock; null = not locked by us */\n private previousBodyOverflow: string | null = null;\n private closeFallbackTimer: ReturnType<typeof setTimeout> | null = null;\n /** Pending open rAFs — canceled on close so a rapid open→close can't\n * resurrect the open class after the close started */\n private openRafIds: number[] = [];\n /** Settle fallback: under prefers-reduced-motion no transitionend fires,\n * and the sheet must still reach transform:none (containing-block fix) */\n private settleFallbackTimer: ReturnType<typeof setTimeout> | null = null;\n\n @HostListener('document:keydown.escape')\n public OnEscape(): void {\n if (this.IsOpen) {\n this.close(true);\n }\n }\n\n /** Scrim click / programmatic dismiss */\n public Close(): void {\n if (this.IsOpen) {\n this.close(true);\n }\n }\n\n /**\n * Focus trap: aria-modal promises assistive tech the background is inert —\n * Tab must cycle within the sheet, never walk out behind the scrim.\n */\n public OnSheetKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Tab' || !this.sheetEl) {\n return;\n }\n const sheet = this.sheetEl.nativeElement;\n const focusables = Array.from(sheet.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n ));\n if (focusables.length === 0) {\n event.preventDefault(); // nothing to move to — keep focus on the sheet\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = document.activeElement;\n if (event.shiftKey) {\n if (active === first || active === sheet) {\n event.preventDefault();\n last.focus();\n }\n } else if (active === last || active === sheet) {\n event.preventDefault();\n first.focus();\n }\n }\n\n public OnTransitionEnd(event: TransitionEvent): void {\n if (event.target !== this.sheetEl?.nativeElement || event.propertyName !== 'transform') {\n return;\n }\n if (this.IsOpen) {\n // Settled: transform → none so fixed-position descendants aren't trapped\n this.IsSettled = true;\n this.cdr.markForCheck();\n } else if (this.IsRendered) {\n this.finishClose();\n }\n }\n\n private open(): void {\n this.clearCloseFallback();\n this.previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.lockBodyScroll();\n this.IsRendered = true;\n this.cdr.markForCheck();\n // Two frames: one for the @if to render at translateY(100%), one so the\n // class flip actually transitions (same-frame flips snap instead)\n this.openRafIds.push(requestAnimationFrame(() => {\n this.openRafIds.push(requestAnimationFrame(() => {\n this.openRafIds = [];\n if (!this._visible) {\n return; // closed again before the frames ran — stay closed\n }\n this.IsOpen = true;\n this.cdr.markForCheck();\n this.sheetEl?.nativeElement?.focus();\n // Reduced-motion (transition:none) never fires transitionend — the\n // fallback still settles the transform to none\n this.clearSettleFallback();\n this.settleFallbackTimer = setTimeout(() => {\n if (this.IsOpen && !this.IsSettled) {\n this.IsSettled = true;\n this.cdr.markForCheck();\n }\n }, 300);\n }));\n }));\n }\n\n /**\n * @param emit true when the sheet itself initiated the dismissal (Escape,\n * scrim) — the Visible setter path passes false because the caller's\n * binding is already up to date.\n */\n private close(emit: boolean): void {\n this._visible = false;\n this.cancelOpenRafs();\n this.clearSettleFallback();\n this.IsOpen = false;\n this.IsSettled = false;\n this.cdr.markForCheck();\n if (emit) {\n this.VisibleChange.emit(false);\n }\n // transitionend normally finishes the close; the timer covers\n // prefers-reduced-motion (no transition event) and detached nodes\n this.clearCloseFallback();\n this.closeFallbackTimer = setTimeout(() => this.finishClose(), 300);\n }\n\n private finishClose(): void {\n this.clearCloseFallback();\n if (!this.IsRendered) {\n return;\n }\n this.IsRendered = false;\n this.unlockBodyScroll();\n this.cdr.markForCheck();\n this.Closed.emit();\n this.previousFocus?.focus();\n this.previousFocus = null;\n }\n\n /**\n * Body scroll lock while open: aria-modal content must not let the page\n * scroll behind the scrim. Restores the EXACT prior inline value (which is\n * usually '') so a host page's own overflow styling survives.\n */\n private lockBodyScroll(): void {\n if (this.previousBodyOverflow === null) {\n this.previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n }\n }\n\n private unlockBodyScroll(): void {\n if (this.previousBodyOverflow !== null) {\n document.body.style.overflow = this.previousBodyOverflow;\n this.previousBodyOverflow = null;\n }\n }\n\n private clearCloseFallback(): void {\n if (this.closeFallbackTimer !== null) {\n clearTimeout(this.closeFallbackTimer);\n this.closeFallbackTimer = null;\n }\n }\n\n private clearSettleFallback(): void {\n if (this.settleFallbackTimer !== null) {\n clearTimeout(this.settleFallbackTimer);\n this.settleFallbackTimer = null;\n }\n }\n\n private cancelOpenRafs(): void {\n this.openRafIds.forEach(id => cancelAnimationFrame(id));\n this.openRafIds = [];\n }\n\n ngOnDestroy(): void {\n this.clearCloseFallback();\n this.clearSettleFallback();\n this.cancelOpenRafs();\n // Destroyed while open (route change, parent teardown): never leave the\n // page unscrollable\n this.unlockBodyScroll();\n }\n}\n","@if (IsRendered) {\n <div\n class=\"mj-bottom-sheet-scrim\"\n [class.mj-bottom-sheet-scrim--open]=\"IsOpen\"\n (click)=\"Close()\"\n aria-hidden=\"true\"></div>\n <div\n #sheet\n class=\"mj-bottom-sheet\"\n [class.mj-bottom-sheet--open]=\"IsOpen\"\n [class.mj-bottom-sheet--settled]=\"IsSettled\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"AriaLabel || Title || null\"\n tabindex=\"-1\"\n (keydown)=\"OnSheetKeydown($event)\"\n (transitionend)=\"OnTransitionEnd($event)\">\n <div class=\"mj-bottom-sheet-grab\" aria-hidden=\"true\"></div>\n @if (Title) {\n <div class=\"mj-bottom-sheet-header\">{{ Title }}</div>\n }\n <div class=\"mj-bottom-sheet-content\">\n <ng-content></ng-content>\n </div>\n </div>\n}\n"]}
|
|
@@ -33,7 +33,7 @@ function MjSlidePanelComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
|
33
33
|
} if (rf & 2) {
|
|
34
34
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
35
35
|
i0.ɵɵstyleProp("width", ctx_r1.WidthPx, "px")("max-width", ctx_r1.MaxWidthRatio * 100 + "vw");
|
|
36
|
-
i0.ɵɵclassProp("
|
|
36
|
+
i0.ɵɵclassProp("sp-open", ctx_r1.IsVisible);
|
|
37
37
|
i0.ɵɵadvance();
|
|
38
38
|
i0.ɵɵconditional(ctx_r1.Resizable ? 1 : -1);
|
|
39
39
|
i0.ɵɵadvance();
|
|
@@ -62,7 +62,7 @@ function MjSlidePanelComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
|
62
62
|
} if (rf & 2) {
|
|
63
63
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
64
64
|
i0.ɵɵstyleProp("width", ctx_r1.WidthPx, "px");
|
|
65
|
-
i0.ɵɵclassProp("
|
|
65
|
+
i0.ɵɵclassProp("sp-open", ctx_r1.IsVisible);
|
|
66
66
|
i0.ɵɵadvance();
|
|
67
67
|
i0.ɵɵconditional(ctx_r1.Title ? 1 : -1);
|
|
68
68
|
} }
|
|
@@ -210,7 +210,7 @@ export class MjSlidePanelComponent {
|
|
|
210
210
|
static ɵfac = function MjSlidePanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MjSlidePanelComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(i0.ElementRef)); };
|
|
211
211
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MjSlidePanelComponent, selectors: [["mj-slide-panel"]], hostBindings: function MjSlidePanelComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
212
212
|
i0.ɵɵlistener("keydown.escape", function MjSlidePanelComponent_keydown_escape_HostBindingHandler() { return ctx.OnEscapeKey(); }, i0.ɵɵresolveDocument);
|
|
213
|
-
} }, inputs: { Mode: "Mode", Title: "Title", Visible: "Visible", Resizable: "Resizable", MinWidthPx: "MinWidthPx", MaxWidthRatio: "MaxWidthRatio", WidthPx: "WidthPx", CanClose: "CanClose" }, outputs: { Closed: "Closed", WidthChanged: "WidthChanged" }, ngContentSelectors: _c1, decls: 3, vars: 6, consts: [[1, "sp-backdrop", 3, "click"], [1, "sp-panel", 3, "
|
|
213
|
+
} }, inputs: { Mode: "Mode", Title: "Title", Visible: "Visible", Resizable: "Resizable", MinWidthPx: "MinWidthPx", MaxWidthRatio: "MaxWidthRatio", WidthPx: "WidthPx", CanClose: "CanClose" }, outputs: { Closed: "Closed", WidthChanged: "WidthChanged" }, ngContentSelectors: _c1, decls: 3, vars: 6, consts: [[1, "sp-backdrop", 3, "click"], [1, "sp-panel", 3, "sp-open", "width", "max-width"], [1, "sp-dialog", 3, "sp-open", "width"], [1, "sp-panel"], [1, "sp-resize-handle"], [1, "sp-header"], [1, "sp-body"], [1, "sp-resize-handle", 3, "mousedown"], [1, "sp-resize-grip"], [1, "sp-title"], [1, "sp-close-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "sp-dialog"], [1, "sp-body", "sp-dialog-body"]], template: function MjSlidePanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
214
214
|
i0.ɵɵprojectionDef(_c0);
|
|
215
215
|
i0.ɵɵdomElementStart(0, "div", 0);
|
|
216
216
|
i0.ɵɵdomListener("click", function MjSlidePanelComponent_Template_div_click_0_listener() { return ctx.OnBackdropClick(); });
|
|
@@ -218,16 +218,16 @@ export class MjSlidePanelComponent {
|
|
|
218
218
|
i0.ɵɵconditionalCreate(1, MjSlidePanelComponent_Conditional_1_Template, 5, 8, "div", 1);
|
|
219
219
|
i0.ɵɵconditionalCreate(2, MjSlidePanelComponent_Conditional_2_Template, 4, 5, "div", 2);
|
|
220
220
|
} if (rf & 2) {
|
|
221
|
-
i0.ɵɵclassProp("
|
|
221
|
+
i0.ɵɵclassProp("sp-open", ctx.IsVisible)("sp-dialog-backdrop", ctx.Mode === "dialog");
|
|
222
222
|
i0.ɵɵadvance();
|
|
223
223
|
i0.ɵɵconditional(ctx.Mode === "slide" ? 1 : -1);
|
|
224
224
|
i0.ɵɵadvance();
|
|
225
225
|
i0.ɵɵconditional(ctx.Mode === "dialog" ? 2 : -1);
|
|
226
|
-
} }, styles: ["\n\n\n\n\n\n\n.sp-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: transparent;\n z-index: 1000;\n transition: background 0.3s ease;\n pointer-events: none;\n}\n\n.sp-backdrop.
|
|
226
|
+
} }, styles: ["\n\n\n\n\n\n\n.sp-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: transparent;\n z-index: 1000;\n transition: background 0.3s ease;\n pointer-events: none;\n}\n\n\n\n\n\n\n\n.sp-backdrop.sp-open[_ngcontent-%COMP%] {\n background: var(--mj-bg-overlay);\n pointer-events: auto;\n}\n\n.sp-dialog-backdrop.sp-open[_ngcontent-%COMP%] {\n background: var(--mj-bg-overlay);\n}\n\n\n\n\n\n\n\n\n.sp-panel[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n right: 0;\n height: 100vh;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-lg);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n min-width: 400px;\n max-width: 92vw;\n}\n\n.sp-panel.sp-open[_ngcontent-%COMP%] {\n \n\n\n\n\n\n transform: none;\n}\n\n\n\n\n\n\n\n\n.sp-dialog[_ngcontent-%COMP%] {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.95);\n max-height: 85vh;\n background: var(--mj-bg-surface);\n border-radius: 16px;\n box-shadow: var(--mj-shadow-lg);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n opacity: 0;\n transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n min-width: 360px;\n max-width: 95vw;\n}\n\n.sp-dialog.sp-open[_ngcontent-%COMP%] {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n}\n\n\n\n\n\n\n\n\n.sp-resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n left: -4px;\n top: 0;\n width: 8px;\n height: 100%;\n cursor: col-resize;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sp-resize-handle[_ngcontent-%COMP%]:hover .sp-resize-grip[_ngcontent-%COMP%], \n.sp-resize-handle[_ngcontent-%COMP%]:active .sp-resize-grip[_ngcontent-%COMP%] {\n opacity: 1;\n background: var(--mj-brand-primary);\n}\n\n.sp-resize-grip[_ngcontent-%COMP%] {\n width: 3px;\n height: 40px;\n background: var(--mj-border-strong);\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease, background 0.2s ease;\n}\n\n.sp-resize-handle[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n\n\n\n\n\n\n\n.sp-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.sp-title[_ngcontent-%COMP%] {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n}\n\n.sp-close-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.sp-close-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n}\n\n\n\n\n\n\n\n\n.sp-body[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.sp-dialog-body[_ngcontent-%COMP%] {\n padding: 0;\n}\n\n\n\n\n\n\n\n\n@media (max-width: 768px) {\n .sp-panel[_ngcontent-%COMP%] {\n width: 100% !important;\n min-width: unset;\n }\n\n .sp-resize-handle[_ngcontent-%COMP%] {\n display: none;\n }\n\n .sp-dialog[_ngcontent-%COMP%] {\n width: 95vw !important;\n max-height: 90vh;\n }\n}"], changeDetection: 0 });
|
|
227
227
|
}
|
|
228
228
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjSlidePanelComponent, [{
|
|
229
229
|
type: Component,
|
|
230
|
-
args: [{ standalone: true, selector: 'mj-slide-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Backdrop overlay -->\n<div class=\"sp-backdrop\"\n [class.
|
|
230
|
+
args: [{ standalone: true, selector: 'mj-slide-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Backdrop overlay -->\n<div class=\"sp-backdrop\"\n [class.sp-open]=\"IsVisible\"\n [class.sp-dialog-backdrop]=\"Mode === 'dialog'\"\n (click)=\"OnBackdropClick()\">\n</div>\n\n<!-- Panel (slide-in from right) -->\n@if (Mode === 'slide') {\n <div\n class=\"sp-panel\"\n [class.sp-open]=\"IsVisible\"\n [style.width.px]=\"WidthPx\"\n [style.max-width]=\"(MaxWidthRatio * 100) + 'vw'\">\n <!-- Resize handle -->\n @if (Resizable) {\n <div class=\"sp-resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"sp-resize-grip\"></div>\n </div>\n }\n <!-- Header (if Title provided) -->\n @if (Title) {\n <div class=\"sp-header\">\n <h2 class=\"sp-title\">{{Title}}</h2>\n <button class=\"sp-close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n <!-- Content projection -->\n <div class=\"sp-body\">\n <ng-content></ng-content>\n </div>\n </div>\n}\n\n<!-- Dialog (centered modal) -->\n@if (Mode === 'dialog') {\n <div\n class=\"sp-dialog\"\n [class.sp-open]=\"IsVisible\"\n [style.width.px]=\"WidthPx\">\n <!-- Header (if Title provided) -->\n @if (Title) {\n <div class=\"sp-header\">\n <h2 class=\"sp-title\">{{Title}}</h2>\n <button class=\"sp-close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n <!-- Content projection -->\n <div class=\"sp-body sp-dialog-body\">\n <ng-content select=\"[dialog-content]\"></ng-content>\n </div>\n </div>\n}\n", styles: ["/* ================================================================= */\n/* BACKDROP */\n/* ================================================================= */\n\n.sp-backdrop {\n position: fixed;\n inset: 0;\n background: transparent;\n z-index: 1000;\n transition: background 0.3s ease;\n pointer-events: none;\n}\n\n/* State class is sp-open, NEVER `visible`: explorer-app ships a global\n `.visible { visibility: visible !important }` utility, and a state class\n named `visible` accidentally matches it \u2014 forcing the panel to punch\n through ancestors hidden with visibility:hidden (e.g. the shell's\n region-hidden main area). */\n.sp-backdrop.sp-open {\n background: var(--mj-bg-overlay);\n pointer-events: auto;\n}\n\n.sp-dialog-backdrop.sp-open {\n background: var(--mj-bg-overlay);\n}\n\n/* ================================================================= */\n/* SLIDE PANEL */\n/* ================================================================= */\n\n.sp-panel {\n position: fixed;\n top: 0;\n right: 0;\n height: 100vh;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-lg);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n min-width: 400px;\n max-width: 92vw;\n}\n\n.sp-panel.sp-open {\n /* `none` (not translateX(0)) on purpose: a non-none transform makes this panel\n the containing block for position:fixed DESCENDANTS (dropdown panels, popovers\n that position via getBoundingClientRect viewport math), displacing them\n offscreen. `none` interpolates as the identity matrix, so the slide-in\n animation is unaffected while open-state descendants get the real viewport. */\n transform: none;\n}\n\n/* ================================================================= */\n/* DIALOG (centered modal) */\n/* ================================================================= */\n\n.sp-dialog {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.95);\n max-height: 85vh;\n background: var(--mj-bg-surface);\n border-radius: 16px;\n box-shadow: var(--mj-shadow-lg);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n opacity: 0;\n transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n min-width: 360px;\n max-width: 95vw;\n}\n\n.sp-dialog.sp-open {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* ================================================================= */\n/* RESIZE HANDLE */\n/* ================================================================= */\n\n.sp-resize-handle {\n position: absolute;\n left: -4px;\n top: 0;\n width: 8px;\n height: 100%;\n cursor: col-resize;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sp-resize-handle:hover .sp-resize-grip,\n.sp-resize-handle:active .sp-resize-grip {\n opacity: 1;\n background: var(--mj-brand-primary);\n}\n\n.sp-resize-grip {\n width: 3px;\n height: 40px;\n background: var(--mj-border-strong);\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease, background 0.2s ease;\n}\n\n.sp-resize-handle:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, transparent);\n}\n\n/* ================================================================= */\n/* HEADER */\n/* ================================================================= */\n\n.sp-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.sp-title {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n}\n\n.sp-close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.sp-close-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n}\n\n/* ================================================================= */\n/* BODY */\n/* ================================================================= */\n\n.sp-body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.sp-dialog-body {\n padding: 0;\n}\n\n/* ================================================================= */\n/* RESPONSIVE */\n/* ================================================================= */\n\n@media (max-width: 768px) {\n .sp-panel {\n width: 100% !important;\n min-width: unset;\n }\n\n .sp-resize-handle {\n display: none;\n }\n\n .sp-dialog {\n width: 95vw !important;\n max-height: 90vh;\n }\n}\n"] }]
|
|
231
231
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i0.ElementRef }], { Mode: [{
|
|
232
232
|
type: Input
|
|
233
233
|
}], Title: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slide-panel.component.js","sourceRoot":"","sources":["../../../src/lib/slide-panel/slide-panel.component.ts","../../../src/lib/slide-panel/slide-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter, OnInit, OnDestroy,\n ChangeDetectorRef, ChangeDetectionStrategy, HostListener, ElementRef, NgZone\n} from '@angular/core';\n\n/** Display mode for the slide panel container. */\nexport type SlidePanelMode = 'slide' | 'dialog';\n\n/**\n * Generic slide-in / centered-dialog panel primitive.\n *\n * - `Mode='slide'` → slides in from the right edge, full viewport height, optionally resizable.\n * - `Mode='dialog'` → centered modal card.\n *\n * Pure chrome: it owns the backdrop, open/close animation, resize handle, optional\n * title bar, and content projection. It performs no data work and has no routing.\n * Consumers project their content and handle Save/close via the projected UI and the\n * `Closed` output.\n *\n * @example\n * ```html\n * <mj-slide-panel [Visible]=\"open\" Title=\"Details\" (Closed)=\"open = false\">\n * <my-content></my-content>\n * </mj-slide-panel>\n * ```\n */\n@Component({\n standalone: true,\n selector: 'mj-slide-panel',\n templateUrl: './slide-panel.component.html',\n styleUrls: ['./slide-panel.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class MjSlidePanelComponent implements OnInit, OnDestroy {\n @Input() Mode: SlidePanelMode = 'slide';\n @Input() Title = '';\n @Input()\n set Visible(value: boolean) {\n const changed = this._visible !== value;\n this._visible = value;\n if (changed && this.initialized) {\n if (value) {\n // Opening: animate in on next microtask\n Promise.resolve().then(() => {\n this.IsVisible = true;\n this.cdr.markForCheck();\n });\n } else {\n this.IsVisible = false;\n this.cdr.markForCheck();\n }\n }\n }\n get Visible(): boolean {\n return this._visible;\n }\n private _visible = true;\n private initialized = false;\n\n @Input() Resizable = true;\n @Input() MinWidthPx = 400;\n @Input() MaxWidthRatio = 0.92;\n\n /** Initial width in pixels. Defaults to 65% of viewport for slide, 800px for dialog. */\n @Input()\n set WidthPx(value: number) {\n this._widthPx = value;\n }\n get WidthPx(): number {\n return this._widthPx;\n }\n\n @Output() Closed = new EventEmitter<void>();\n @Output() WidthChanged = new EventEmitter<number>();\n\n /**\n * Optional guard called before any close gesture (X button, backdrop click, Escape).\n * Return `false` to cancel the close — e.g., when an in-progress operation is running.\n * The panel itself never shows a dialog; the consumer is responsible for any confirmation UI.\n */\n @Input() CanClose: (() => boolean) | null = null;\n\n public IsVisible = false;\n private _widthPx = 0;\n private isResizing = false;\n\n private boundOnResizeMove = this.onResizeMove.bind(this);\n private boundOnResizeEnd = this.onResizeEnd.bind(this);\n\n constructor(\n private cdr: ChangeDetectorRef,\n private ngZone: NgZone,\n private elRef: ElementRef\n ) {}\n\n ngOnInit(): void {\n if (this._widthPx === 0) {\n this._widthPx = this.Mode === 'dialog'\n ? 800\n : Math.max(this.MinWidthPx, Math.min(window.innerWidth * 0.65, 1000));\n }\n\n this.initialized = true;\n\n // Animate in on next microtask if initially visible\n if (this._visible) {\n Promise.resolve().then(() => {\n this.IsVisible = true;\n this.cdr.markForCheck();\n });\n }\n }\n\n ngOnDestroy(): void {\n document.removeEventListener('mousemove', this.boundOnResizeMove);\n document.removeEventListener('mouseup', this.boundOnResizeEnd);\n }\n\n @HostListener('document:keydown.escape')\n public OnEscapeKey(): void {\n this.OnClose();\n }\n\n public OnClose(): void {\n if (this.CanClose && !this.CanClose()) return;\n this.IsVisible = false;\n this._visible = false;\n this.cdr.markForCheck();\n // Wait for CSS transition to complete\n setTimeout(() => this.Closed.emit(), 300);\n }\n\n public OnBackdropClick(): void {\n this.OnClose();\n }\n\n // =========================================================================\n // Resize\n // =========================================================================\n\n public OnResizeStart(event: MouseEvent): void {\n if (!this.Resizable || this.Mode === 'dialog') return;\n event.preventDefault();\n this.isResizing = true;\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n\n this.ngZone.runOutsideAngular(() => {\n document.addEventListener('mousemove', this.boundOnResizeMove);\n document.addEventListener('mouseup', this.boundOnResizeEnd);\n });\n }\n\n private onResizeMove(event: MouseEvent): void {\n if (!this.isResizing) return;\n const viewportWidth = window.innerWidth;\n const maxWidth = viewportWidth * this.MaxWidthRatio;\n this._widthPx = Math.max(this.MinWidthPx, Math.min(maxWidth, viewportWidth - event.clientX));\n this.ngZone.run(() => this.cdr.markForCheck());\n }\n\n private onResizeEnd(): void {\n if (!this.isResizing) return;\n this.isResizing = false;\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n document.removeEventListener('mousemove', this.boundOnResizeMove);\n document.removeEventListener('mouseup', this.boundOnResizeEnd);\n this.WidthChanged.emit(this._widthPx);\n }\n}\n","<!-- Backdrop overlay -->\n<div class=\"sp-backdrop\"\n [class.visible]=\"IsVisible\"\n [class.sp-dialog-backdrop]=\"Mode === 'dialog'\"\n (click)=\"OnBackdropClick()\">\n</div>\n\n<!-- Panel (slide-in from right) -->\n@if (Mode === 'slide') {\n <div\n class=\"sp-panel\"\n [class.visible]=\"IsVisible\"\n [style.width.px]=\"WidthPx\"\n [style.max-width]=\"(MaxWidthRatio * 100) + 'vw'\">\n <!-- Resize handle -->\n @if (Resizable) {\n <div class=\"sp-resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"sp-resize-grip\"></div>\n </div>\n }\n <!-- Header (if Title provided) -->\n @if (Title) {\n <div class=\"sp-header\">\n <h2 class=\"sp-title\">{{Title}}</h2>\n <button class=\"sp-close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n <!-- Content projection -->\n <div class=\"sp-body\">\n <ng-content></ng-content>\n </div>\n </div>\n}\n\n<!-- Dialog (centered modal) -->\n@if (Mode === 'dialog') {\n <div\n class=\"sp-dialog\"\n [class.visible]=\"IsVisible\"\n [style.width.px]=\"WidthPx\">\n <!-- Header (if Title provided) -->\n @if (Title) {\n <div class=\"sp-header\">\n <h2 class=\"sp-title\">{{Title}}</h2>\n <button class=\"sp-close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n <!-- Content projection -->\n <div class=\"sp-body sp-dialog-body\">\n <ng-content select=\"[dialog-content]\"></ng-content>\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"slide-panel.component.js","sourceRoot":"","sources":["../../../src/lib/slide-panel/slide-panel.component.ts","../../../src/lib/slide-panel/slide-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter, OnInit, OnDestroy,\n ChangeDetectorRef, ChangeDetectionStrategy, HostListener, ElementRef, NgZone\n} from '@angular/core';\n\n/** Display mode for the slide panel container. */\nexport type SlidePanelMode = 'slide' | 'dialog';\n\n/**\n * Generic slide-in / centered-dialog panel primitive.\n *\n * - `Mode='slide'` → slides in from the right edge, full viewport height, optionally resizable.\n * - `Mode='dialog'` → centered modal card.\n *\n * Pure chrome: it owns the backdrop, open/close animation, resize handle, optional\n * title bar, and content projection. It performs no data work and has no routing.\n * Consumers project their content and handle Save/close via the projected UI and the\n * `Closed` output.\n *\n * @example\n * ```html\n * <mj-slide-panel [Visible]=\"open\" Title=\"Details\" (Closed)=\"open = false\">\n * <my-content></my-content>\n * </mj-slide-panel>\n * ```\n */\n@Component({\n standalone: true,\n selector: 'mj-slide-panel',\n templateUrl: './slide-panel.component.html',\n styleUrls: ['./slide-panel.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class MjSlidePanelComponent implements OnInit, OnDestroy {\n @Input() Mode: SlidePanelMode = 'slide';\n @Input() Title = '';\n @Input()\n set Visible(value: boolean) {\n const changed = this._visible !== value;\n this._visible = value;\n if (changed && this.initialized) {\n if (value) {\n // Opening: animate in on next microtask\n Promise.resolve().then(() => {\n this.IsVisible = true;\n this.cdr.markForCheck();\n });\n } else {\n this.IsVisible = false;\n this.cdr.markForCheck();\n }\n }\n }\n get Visible(): boolean {\n return this._visible;\n }\n private _visible = true;\n private initialized = false;\n\n @Input() Resizable = true;\n @Input() MinWidthPx = 400;\n @Input() MaxWidthRatio = 0.92;\n\n /** Initial width in pixels. Defaults to 65% of viewport for slide, 800px for dialog. */\n @Input()\n set WidthPx(value: number) {\n this._widthPx = value;\n }\n get WidthPx(): number {\n return this._widthPx;\n }\n\n @Output() Closed = new EventEmitter<void>();\n @Output() WidthChanged = new EventEmitter<number>();\n\n /**\n * Optional guard called before any close gesture (X button, backdrop click, Escape).\n * Return `false` to cancel the close — e.g., when an in-progress operation is running.\n * The panel itself never shows a dialog; the consumer is responsible for any confirmation UI.\n */\n @Input() CanClose: (() => boolean) | null = null;\n\n public IsVisible = false;\n private _widthPx = 0;\n private isResizing = false;\n\n private boundOnResizeMove = this.onResizeMove.bind(this);\n private boundOnResizeEnd = this.onResizeEnd.bind(this);\n\n constructor(\n private cdr: ChangeDetectorRef,\n private ngZone: NgZone,\n private elRef: ElementRef\n ) {}\n\n ngOnInit(): void {\n if (this._widthPx === 0) {\n this._widthPx = this.Mode === 'dialog'\n ? 800\n : Math.max(this.MinWidthPx, Math.min(window.innerWidth * 0.65, 1000));\n }\n\n this.initialized = true;\n\n // Animate in on next microtask if initially visible\n if (this._visible) {\n Promise.resolve().then(() => {\n this.IsVisible = true;\n this.cdr.markForCheck();\n });\n }\n }\n\n ngOnDestroy(): void {\n document.removeEventListener('mousemove', this.boundOnResizeMove);\n document.removeEventListener('mouseup', this.boundOnResizeEnd);\n }\n\n @HostListener('document:keydown.escape')\n public OnEscapeKey(): void {\n this.OnClose();\n }\n\n public OnClose(): void {\n if (this.CanClose && !this.CanClose()) return;\n this.IsVisible = false;\n this._visible = false;\n this.cdr.markForCheck();\n // Wait for CSS transition to complete\n setTimeout(() => this.Closed.emit(), 300);\n }\n\n public OnBackdropClick(): void {\n this.OnClose();\n }\n\n // =========================================================================\n // Resize\n // =========================================================================\n\n public OnResizeStart(event: MouseEvent): void {\n if (!this.Resizable || this.Mode === 'dialog') return;\n event.preventDefault();\n this.isResizing = true;\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n\n this.ngZone.runOutsideAngular(() => {\n document.addEventListener('mousemove', this.boundOnResizeMove);\n document.addEventListener('mouseup', this.boundOnResizeEnd);\n });\n }\n\n private onResizeMove(event: MouseEvent): void {\n if (!this.isResizing) return;\n const viewportWidth = window.innerWidth;\n const maxWidth = viewportWidth * this.MaxWidthRatio;\n this._widthPx = Math.max(this.MinWidthPx, Math.min(maxWidth, viewportWidth - event.clientX));\n this.ngZone.run(() => this.cdr.markForCheck());\n }\n\n private onResizeEnd(): void {\n if (!this.isResizing) return;\n this.isResizing = false;\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n document.removeEventListener('mousemove', this.boundOnResizeMove);\n document.removeEventListener('mouseup', this.boundOnResizeEnd);\n this.WidthChanged.emit(this._widthPx);\n }\n}\n","<!-- Backdrop overlay -->\n<div class=\"sp-backdrop\"\n [class.sp-open]=\"IsVisible\"\n [class.sp-dialog-backdrop]=\"Mode === 'dialog'\"\n (click)=\"OnBackdropClick()\">\n</div>\n\n<!-- Panel (slide-in from right) -->\n@if (Mode === 'slide') {\n <div\n class=\"sp-panel\"\n [class.sp-open]=\"IsVisible\"\n [style.width.px]=\"WidthPx\"\n [style.max-width]=\"(MaxWidthRatio * 100) + 'vw'\">\n <!-- Resize handle -->\n @if (Resizable) {\n <div class=\"sp-resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"sp-resize-grip\"></div>\n </div>\n }\n <!-- Header (if Title provided) -->\n @if (Title) {\n <div class=\"sp-header\">\n <h2 class=\"sp-title\">{{Title}}</h2>\n <button class=\"sp-close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n <!-- Content projection -->\n <div class=\"sp-body\">\n <ng-content></ng-content>\n </div>\n </div>\n}\n\n<!-- Dialog (centered modal) -->\n@if (Mode === 'dialog') {\n <div\n class=\"sp-dialog\"\n [class.sp-open]=\"IsVisible\"\n [style.width.px]=\"WidthPx\">\n <!-- Header (if Title provided) -->\n @if (Title) {\n <div class=\"sp-header\">\n <h2 class=\"sp-title\">{{Title}}</h2>\n <button class=\"sp-close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n <!-- Content projection -->\n <div class=\"sp-body sp-dialog-body\">\n <ng-content select=\"[dialog-content]\"></ng-content>\n </div>\n </div>\n}\n"]}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from './lib/accordion/accordion.component';
|
|
|
15
15
|
export * from './lib/alert/alert.component';
|
|
16
16
|
export * from './lib/window/window.component';
|
|
17
17
|
export * from './lib/slide-panel/slide-panel.component';
|
|
18
|
+
export * from './lib/bottom-sheet/bottom-sheet.component';
|
|
18
19
|
export * from './lib/combobox/combobox.component';
|
|
19
20
|
export * from './lib/page-header/page-header.component';
|
|
20
21
|
export * from './lib/page-layout/page-layout.component';
|
package/dist/public-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAGA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2DAA2D,CAAC;AAC1E,cAAc,uDAAuD,CAAC;AACtE,cAAc,yCAAyC,CAAC"}
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAGA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2DAA2D,CAAC;AAC1E,cAAc,uDAAuD,CAAC;AACtE,cAAc,yCAAyC,CAAC"}
|
package/dist/public-api.js
CHANGED
|
@@ -17,6 +17,7 @@ export * from './lib/accordion/accordion.component';
|
|
|
17
17
|
export * from './lib/alert/alert.component';
|
|
18
18
|
export * from './lib/window/window.component';
|
|
19
19
|
export * from './lib/slide-panel/slide-panel.component';
|
|
20
|
+
export * from './lib/bottom-sheet/bottom-sheet.component';
|
|
20
21
|
export * from './lib/combobox/combobox.component';
|
|
21
22
|
export * from './lib/page-header/page-header.component';
|
|
22
23
|
export * from './lib/page-layout/page-layout.component';
|
package/dist/public-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,+DAA+D;AAE/D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2DAA2D,CAAC;AAC1E,cAAc,uDAAuD,CAAC;AACtE,cAAc,yCAAyC,CAAC","sourcesContent":["// @memberjunction/ng-ui-components\n// Reusable standalone Angular UI components for MemberJunction\n\nexport * from './lib/button/button.directive';\nexport * from './lib/clickable/clickable.directive';\nexport * from './lib/dialog/dialog.component';\nexport * from './lib/dialog/dialog.service';\nexport * from './lib/confirm-dialog/confirm-dialog.component';\nexport * from './lib/confirm-dialog/confirm-dialog.service';\nexport * from './lib/dropdown/dropdown.component';\nexport * from './lib/switch/switch.component';\nexport * from './lib/numeric-input/numeric-input.component';\nexport * from './lib/progress-bar/progress-bar.component';\nexport * from './lib/calendar/calendar-utils';\nexport * from './lib/datepicker/datepicker.component';\nexport * from './lib/splitter/splitter.component';\nexport * from './lib/accordion/accordion.component';\nexport * from './lib/alert/alert.component';\nexport * from './lib/window/window.component';\nexport * from './lib/slide-panel/slide-panel.component';\nexport * from './lib/combobox/combobox.component';\nexport * from './lib/page-header/page-header.component';\nexport * from './lib/page-layout/page-layout.component';\nexport * from './lib/page-body/page-body.component';\nexport * from './lib/stat-badge/stat-badge.component';\nexport * from './lib/refresh-button/refresh-button.component';\nexport * from './lib/filter-popover/filter-popover.component';\nexport * from './lib/page-search/page-search.component';\nexport * from './lib/filter-chip/filter-chip.component';\nexport * from './lib/filter-field/filter-field.component';\nexport * from './lib/filter-panel/filter-panel.component';\nexport * from './lib/tab-nav/tab-nav.component';\nexport * from './lib/view-toggle/view-toggle.component';\nexport * from './lib/left-nav/left-nav.component';\nexport * from './lib/left-nav/left-nav-content.component';\nexport * from './lib/page-header-interior/page-header-interior.component';\nexport * from './lib/page-body-interior/page-body-interior.component';\nexport * from './lib/empty-state/empty-state.component';\n"]}
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,+DAA+D;AAE/D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,yCAAyC,CAAC;AACxD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2DAA2D,CAAC;AAC1E,cAAc,uDAAuD,CAAC;AACtE,cAAc,yCAAyC,CAAC","sourcesContent":["// @memberjunction/ng-ui-components\n// Reusable standalone Angular UI components for MemberJunction\n\nexport * from './lib/button/button.directive';\nexport * from './lib/clickable/clickable.directive';\nexport * from './lib/dialog/dialog.component';\nexport * from './lib/dialog/dialog.service';\nexport * from './lib/confirm-dialog/confirm-dialog.component';\nexport * from './lib/confirm-dialog/confirm-dialog.service';\nexport * from './lib/dropdown/dropdown.component';\nexport * from './lib/switch/switch.component';\nexport * from './lib/numeric-input/numeric-input.component';\nexport * from './lib/progress-bar/progress-bar.component';\nexport * from './lib/calendar/calendar-utils';\nexport * from './lib/datepicker/datepicker.component';\nexport * from './lib/splitter/splitter.component';\nexport * from './lib/accordion/accordion.component';\nexport * from './lib/alert/alert.component';\nexport * from './lib/window/window.component';\nexport * from './lib/slide-panel/slide-panel.component';\nexport * from './lib/bottom-sheet/bottom-sheet.component';\nexport * from './lib/combobox/combobox.component';\nexport * from './lib/page-header/page-header.component';\nexport * from './lib/page-layout/page-layout.component';\nexport * from './lib/page-body/page-body.component';\nexport * from './lib/stat-badge/stat-badge.component';\nexport * from './lib/refresh-button/refresh-button.component';\nexport * from './lib/filter-popover/filter-popover.component';\nexport * from './lib/page-search/page-search.component';\nexport * from './lib/filter-chip/filter-chip.component';\nexport * from './lib/filter-field/filter-field.component';\nexport * from './lib/filter-panel/filter-panel.component';\nexport * from './lib/tab-nav/tab-nav.component';\nexport * from './lib/view-toggle/view-toggle.component';\nexport * from './lib/left-nav/left-nav.component';\nexport * from './lib/left-nav/left-nav-content.component';\nexport * from './lib/page-header-interior/page-header-interior.component';\nexport * from './lib/page-body-interior/page-body-interior.component';\nexport * from './lib/empty-state/empty-state.component';\n"]}
|
package/package.json
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-ui-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.0-edge.0",
|
|
4
4
|
"description": "MemberJunction: Angular UI Components - reusable standalone components replacing Kendo UI dependencies.",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"/dist"
|
|
9
9
|
],
|
|
10
|
-
"scripts": {
|
|
11
|
-
"test": "vitest run",
|
|
12
|
-
"build": "npm run clean && ngc && npm run copy-assets",
|
|
13
|
-
"copy-assets": "cpy src/lib/button/button.scss dist/lib/button --flat && cpy src/lib/dialog/dialog.scss dist/lib/dialog --flat && cpy src/lib/dropdown/dropdown.scss dist/lib/dropdown --flat && cpy src/lib/combobox/combobox.scss dist/lib/combobox --flat && cpy src/lib/input/input.scss dist/lib/input --flat && cpy src/lib/input/input-switch-progress.scss dist/lib/input --flat && cpy src/lib/datepicker/datepicker.scss dist/lib/datepicker --flat && cpy src/lib/splitter/splitter.scss dist/lib/splitter --flat && cpy src/lib/accordion/accordion.scss dist/lib/accordion --flat && cpy src/lib/window/window.scss dist/lib/window --flat && cpy src/lib/input/chip.scss dist/lib/input --flat",
|
|
14
|
-
"clean": "rimraf dist",
|
|
15
|
-
"test:watch": "vitest"
|
|
16
|
-
},
|
|
17
10
|
"keywords": [
|
|
18
11
|
"MemberJunction",
|
|
19
12
|
"Angular"
|
|
@@ -23,7 +16,7 @@
|
|
|
23
16
|
"devDependencies": {
|
|
24
17
|
"@angular/compiler": "21.1.3",
|
|
25
18
|
"@angular/compiler-cli": "21.1.3",
|
|
26
|
-
"@memberjunction/ng-test-utils": "
|
|
19
|
+
"@memberjunction/ng-test-utils": "6.1.0-edge.0",
|
|
27
20
|
"vitest": "^4.0.18"
|
|
28
21
|
},
|
|
29
22
|
"peerDependencies": {
|
|
@@ -41,5 +34,14 @@
|
|
|
41
34
|
"repository": {
|
|
42
35
|
"type": "git",
|
|
43
36
|
"url": "https://github.com/MemberJunction/MJ"
|
|
37
|
+
},
|
|
38
|
+
"mjUILayer": "widgets",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:types": "tsc --noEmit -p tsconfig.spec.json",
|
|
42
|
+
"build": "npm run clean && ngc && npm run copy-assets",
|
|
43
|
+
"copy-assets": "cpy src/lib/button/button.scss dist/lib/button --flat && cpy src/lib/dialog/dialog.scss dist/lib/dialog --flat && cpy src/lib/dropdown/dropdown.scss dist/lib/dropdown --flat && cpy src/lib/combobox/combobox.scss dist/lib/combobox --flat && cpy src/lib/input/input.scss dist/lib/input --flat && cpy src/lib/input/input-switch-progress.scss dist/lib/input --flat && cpy src/lib/datepicker/datepicker.scss dist/lib/datepicker --flat && cpy src/lib/splitter/splitter.scss dist/lib/splitter --flat && cpy src/lib/accordion/accordion.scss dist/lib/accordion --flat && cpy src/lib/window/window.scss dist/lib/window --flat && cpy src/lib/input/chip.scss dist/lib/input --flat",
|
|
44
|
+
"clean": "rimraf dist",
|
|
45
|
+
"test:watch": "vitest"
|
|
44
46
|
}
|
|
45
|
-
}
|
|
47
|
+
}
|