ngwr 9.0.1 → 10.0.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/README.md +14 -0
- package/badge/styles/_index.scss +16 -6
- package/badge/styles/_tag.scss +10 -1
- package/button/styles/_index.scss +48 -1
- package/date-picker/styles/_index.scss +62 -0
- package/dialog/styles/_index.scss +65 -0
- package/drawer/styles/_index.scss +83 -0
- package/fesm2022/ngwr-action-sheet.mjs +1 -1
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +2 -2
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +6 -5
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +6 -5
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +7 -7
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +521 -8
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +10 -2
- package/fesm2022/ngwr-density.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +95 -13
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +2 -2
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +377 -22
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +4 -3
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +21 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +22 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +74 -1
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +74 -35
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +2 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +2 -2
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +166 -10
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +173 -3
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +17 -4
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +4 -3
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +10 -3
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +233 -43
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +1 -1
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +13 -3
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +4 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +25 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/i18n/en.json +165 -0
- package/i18n/ru.json +165 -0
- package/icon/styles/_index.scss +10 -1
- package/input/styles/_index.scss +16 -0
- package/input-number/styles/_index.scss +8 -1
- package/keyboard/styles/_index.scss +10 -3
- package/llms-full.txt +14 -14
- package/llms.txt +12 -6
- package/mention/styles/_index.scss +23 -0
- package/package.json +23 -5
- package/pagination/styles/_index.scss +12 -0
- package/popover/styles/_index.scss +129 -45
- package/schematics/ng-add/index.js +18 -4
- package/schematics/package.json +3 -0
- package/schematics/use/symbol-map.json +7 -0
- package/select/styles/_index.scss +13 -0
- package/splitter/styles/_index.scss +7 -1
- package/table/styles/_index.scss +28 -10
- package/theme/styles/_box-model.scss +29 -0
- package/theme/styles/_colors.scss +74 -15
- package/theme/styles/_dark.scss +52 -6
- package/theme/styles/_index.scss +1 -0
- package/types/ngwr-cascader.d.ts +1 -0
- package/types/ngwr-color-picker.d.ts +1 -0
- package/types/ngwr-context-menu.d.ts +1 -0
- package/types/ngwr-date-picker.d.ts +172 -4
- package/types/ngwr-density.d.ts +8 -1
- package/types/ngwr-dialog.d.ts +80 -8
- package/types/ngwr-drawer.d.ts +245 -10
- package/types/ngwr-dropdown.d.ts +1 -0
- package/types/ngwr-i18n.d.ts +65 -2
- package/types/ngwr-icon.d.ts +11 -19
- package/types/ngwr-mention.d.ts +45 -1
- package/types/ngwr-overlay.d.ts +80 -2
- package/types/ngwr-pagination.d.ts +14 -1
- package/types/ngwr-popconfirm.d.ts +1 -0
- package/types/ngwr-popover.d.ts +1 -0
- package/types/ngwr-select.d.ts +121 -9
- package/types/ngwr-theme.d.ts +8 -1
- package/types/ngwr-tree.d.ts +1 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves.d.ts +10 -0
- package/waves/styles/_index.scss +21 -0
package/fesm2022/ngwr-drawer.mjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
|
2
2
|
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
3
|
-
import { TemplatePortal } from '@angular/cdk/portal';
|
|
3
|
+
import { TemplatePortal, ComponentPortal } from '@angular/cdk/portal';
|
|
4
4
|
import { isPlatformBrowser } from '@angular/common';
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
6
|
-
import { model, input, viewChild, TemplateRef, inject, ViewContainerRef, DestroyRef, PLATFORM_ID, effect, ViewEncapsulation, Component, Directive, computed } from '@angular/core';
|
|
6
|
+
import { model, input, viewChild, TemplateRef, inject, ViewContainerRef, DestroyRef, PLATFORM_ID, effect, ViewEncapsulation, Component, InjectionToken, EnvironmentInjector, Injector, afterNextRender, Service, Directive, computed } from '@angular/core';
|
|
7
7
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
8
|
-
import {
|
|
8
|
+
import { useI18nText, WrI18n } from 'ngwr/i18n';
|
|
9
|
+
import { WR_OVERLAY, wrAppendOverlayClose } from 'ngwr/overlay';
|
|
10
|
+
import { ScrollStrategyOptions } from '@angular/cdk/overlay';
|
|
11
|
+
import { ReplaySubject } from 'rxjs';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* @license
|
|
@@ -75,6 +78,15 @@ class WrDrawer {
|
|
|
75
78
|
closeOnBackdropClick = input(true, { ...(ngDevMode ? { debugName: "closeOnBackdropClick" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
76
79
|
/** Close on Escape. @default true */
|
|
77
80
|
closeOnEscape = input(true, { ...(ngDevMode ? { debugName: "closeOnEscape" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
81
|
+
/**
|
|
82
|
+
* Show a dismiss (×) button in the panel's top-right corner. Set `false` when
|
|
83
|
+
* the projected content supplies its own close affordance. @default true
|
|
84
|
+
*/
|
|
85
|
+
closable = input(true, { ...(ngDevMode ? { debugName: "closable" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
86
|
+
/** Accessible name for the dismiss button. Falls back to `drawer.close`. */
|
|
87
|
+
closeLabel = input(null, /* @ts-ignore */
|
|
88
|
+
...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
|
|
89
|
+
resolvedCloseLabel = useI18nText(this.closeLabel, 'drawer.close', 'Close drawer');
|
|
78
90
|
panelTpl = viewChild.required(TemplateRef, /* @ts-ignore */
|
|
79
91
|
...(ngDevMode ? [{ debugName: "panelTpl" }] : /* istanbul ignore next */ []));
|
|
80
92
|
panelClass() {
|
|
@@ -83,6 +95,8 @@ class WrDrawer {
|
|
|
83
95
|
parts.push('wr-drawer__panel--rounded');
|
|
84
96
|
if (this.safeArea())
|
|
85
97
|
parts.push('wr-drawer__panel--safe-area');
|
|
98
|
+
if (this.closable())
|
|
99
|
+
parts.push('wr-drawer__panel--closable');
|
|
86
100
|
return parts.join(' ');
|
|
87
101
|
}
|
|
88
102
|
// Swipe-to-dismiss — gated to the grab handle, so it never fights the
|
|
@@ -91,28 +105,31 @@ class WrDrawer {
|
|
|
91
105
|
swipeStartX = 0;
|
|
92
106
|
swipeStartY = 0;
|
|
93
107
|
swiping = false;
|
|
108
|
+
// Pointer, not Touch. The handle has always advertised itself to a mouse with
|
|
109
|
+
// `cursor: grab`, but the gesture was bound to touch events only, so on a
|
|
110
|
+
// desktop it could not be dragged at all. Pointer Events cover touch, mouse and
|
|
111
|
+
// pen through one path — the same idiom `wr-splitter` already uses.
|
|
94
112
|
onSwipeStart(event) {
|
|
95
|
-
|
|
96
|
-
if (!
|
|
113
|
+
// Ignore secondary contacts of a multi-touch gesture.
|
|
114
|
+
if (!event.isPrimary)
|
|
97
115
|
return;
|
|
98
|
-
this.swipeStartX =
|
|
99
|
-
this.swipeStartY =
|
|
116
|
+
this.swipeStartX = event.clientX;
|
|
117
|
+
this.swipeStartY = event.clientY;
|
|
100
118
|
this.swiping = true;
|
|
119
|
+
// Capture, so the drag keeps reporting once the cursor leaves the 2.5rem
|
|
120
|
+
// grabber — without it a mouse drag dies the moment it slips off the handle.
|
|
121
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
101
122
|
}
|
|
102
123
|
onSwipeMove(event, panel) {
|
|
103
124
|
if (!this.swiping)
|
|
104
125
|
return;
|
|
105
|
-
const touch = event.touches[0];
|
|
106
|
-
if (!touch)
|
|
107
|
-
return;
|
|
108
126
|
const { axis, sign } = this.closeAxis();
|
|
109
|
-
const delta = axis === 'y' ?
|
|
110
|
-
// Only follow the
|
|
127
|
+
const delta = axis === 'y' ? event.clientY - this.swipeStartY : event.clientX - this.swipeStartX;
|
|
128
|
+
// Only follow the pointer when dragging toward the closing edge.
|
|
111
129
|
if (delta * sign <= 0) {
|
|
112
130
|
panel.style.transform = '';
|
|
113
131
|
return;
|
|
114
132
|
}
|
|
115
|
-
event.preventDefault();
|
|
116
133
|
panel.style.transition = 'none';
|
|
117
134
|
panel.style.transform = axis === 'y' ? `translateY(${delta}px)` : `translateX(${delta}px)`;
|
|
118
135
|
}
|
|
@@ -120,9 +137,8 @@ class WrDrawer {
|
|
|
120
137
|
if (!this.swiping)
|
|
121
138
|
return;
|
|
122
139
|
this.swiping = false;
|
|
123
|
-
const touch = event.changedTouches[0];
|
|
124
140
|
const { axis, sign } = this.closeAxis();
|
|
125
|
-
const delta =
|
|
141
|
+
const delta = axis === 'y' ? event.clientY - this.swipeStartY : event.clientX - this.swipeStartX;
|
|
126
142
|
const panelSize = axis === 'y' ? panel.offsetHeight : panel.offsetWidth;
|
|
127
143
|
if (delta * sign > panelSize * 0.3) {
|
|
128
144
|
this.open.set(false); // keep the dragged offset as the overlay disposes
|
|
@@ -238,12 +254,329 @@ class WrDrawer {
|
|
|
238
254
|
}
|
|
239
255
|
}
|
|
240
256
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
241
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: WrDrawer, isStandalone: true, selector: "wr-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, showHandle: { classPropertyName: "showHandle", publicName: "showHandle", isSignal: true, isRequired: false, transformFunction: null }, safeArea: { classPropertyName: "safeArea", publicName: "safeArea", isSignal: true, isRequired: false, transformFunction: null }, hasBackdrop: { classPropertyName: "hasBackdrop", publicName: "hasBackdrop", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { styleAttribute: "display:none" }, viewQueries: [{ propertyName: "panelTpl", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <div #panel [class]=\"panelClass()\">\n @if (showHandle()) {\n <div\n class=\"wr-drawer__handle\"\n aria-hidden=\"true\"\n (
|
|
257
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: WrDrawer, isStandalone: true, selector: "wr-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, showHandle: { classPropertyName: "showHandle", publicName: "showHandle", isSignal: true, isRequired: false, transformFunction: null }, safeArea: { classPropertyName: "safeArea", publicName: "safeArea", isSignal: true, isRequired: false, transformFunction: null }, hasBackdrop: { classPropertyName: "hasBackdrop", publicName: "hasBackdrop", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { styleAttribute: "display:none" }, viewQueries: [{ propertyName: "panelTpl", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <div #panel [class]=\"panelClass()\">\n @if (showHandle()) {\n <div\n class=\"wr-drawer__handle\"\n aria-hidden=\"true\"\n (pointerdown)=\"onSwipeStart($event)\"\n (pointermove)=\"onSwipeMove($event, panel)\"\n (pointerup)=\"onSwipeEnd($event, panel)\"\n (pointercancel)=\"onSwipeEnd($event, panel)\"\n >\n <span class=\"wr-drawer__grabber\"></span>\n </div>\n }\n @if (closable()) {\n <button type=\"button\" class=\"wr-drawer__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"open.set(false)\">\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n <ng-content />\n </div>\n</ng-template>\n", encapsulation: i0.ViewEncapsulation.None });
|
|
242
258
|
}
|
|
243
259
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawer, decorators: [{
|
|
244
260
|
type: Component,
|
|
245
|
-
args: [{ selector: 'wr-drawer', encapsulation: ViewEncapsulation.None, host: { style: 'display:none' }, template: "<ng-template>\n <div #panel [class]=\"panelClass()\">\n @if (showHandle()) {\n <div\n class=\"wr-drawer__handle\"\n aria-hidden=\"true\"\n (
|
|
246
|
-
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], showHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHandle", required: false }] }], safeArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "safeArea", required: false }] }], hasBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], panelTpl: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
|
|
261
|
+
args: [{ selector: 'wr-drawer', encapsulation: ViewEncapsulation.None, host: { style: 'display:none' }, template: "<ng-template>\n <div #panel [class]=\"panelClass()\">\n @if (showHandle()) {\n <div\n class=\"wr-drawer__handle\"\n aria-hidden=\"true\"\n (pointerdown)=\"onSwipeStart($event)\"\n (pointermove)=\"onSwipeMove($event, panel)\"\n (pointerup)=\"onSwipeEnd($event, panel)\"\n (pointercancel)=\"onSwipeEnd($event, panel)\"\n >\n <span class=\"wr-drawer__grabber\"></span>\n </div>\n }\n @if (closable()) {\n <button type=\"button\" class=\"wr-drawer__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"open.set(false)\">\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n <ng-content />\n </div>\n</ng-template>\n" }]
|
|
262
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], showHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHandle", required: false }] }], safeArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "safeArea", required: false }] }], hasBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], panelTpl: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @license
|
|
266
|
+
*
|
|
267
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
268
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
269
|
+
*/
|
|
270
|
+
/**
|
|
271
|
+
* Handle returned by `WrDrawerManager.open()`.
|
|
272
|
+
*
|
|
273
|
+
* Wraps the underlying `OverlayRef` and tracks the close result. It is also
|
|
274
|
+
* provided inside the drawer's own injector, so the projected content can
|
|
275
|
+
* inject it and close itself — the counterpart to `[wrDrawerClose]` when the
|
|
276
|
+
* close is programmatic (after a save succeeds, say) rather than a click.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* // Caller side.
|
|
281
|
+
* const ref = drawers.open<ChatComponent, boolean>(ChatComponent, { position: 'right' });
|
|
282
|
+
* const result = await ref.awaitClose(); // boolean | undefined
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```ts
|
|
287
|
+
* // Content side — the drawer closes itself.
|
|
288
|
+
* @Component({...})
|
|
289
|
+
* export class ChatComponent {
|
|
290
|
+
* private readonly ref = inject(WrDrawerRef);
|
|
291
|
+
*
|
|
292
|
+
* send(): void {
|
|
293
|
+
* this.store.send(this.draft());
|
|
294
|
+
* this.ref.close(true);
|
|
295
|
+
* }
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
class WrDrawerRef {
|
|
300
|
+
_overlayRef;
|
|
301
|
+
/**
|
|
302
|
+
* Emits the close result once and completes.
|
|
303
|
+
*
|
|
304
|
+
* A `ReplaySubject`, not a plain `Subject`, so the result survives a late
|
|
305
|
+
* subscriber: content can close itself before `open()` has even returned, and
|
|
306
|
+
* `awaitClose()` may be called long after the dismissal — both would otherwise
|
|
307
|
+
* see only `complete` and resolve `undefined`.
|
|
308
|
+
*/
|
|
309
|
+
closed = new ReplaySubject(1);
|
|
310
|
+
/** @internal */
|
|
311
|
+
componentRef = null;
|
|
312
|
+
/** @internal */
|
|
313
|
+
focusTrap = null;
|
|
314
|
+
/** @internal */
|
|
315
|
+
previouslyFocused = null;
|
|
316
|
+
constructor(_overlayRef) {
|
|
317
|
+
this._overlayRef = _overlayRef;
|
|
318
|
+
}
|
|
319
|
+
/** The instantiated drawer component. */
|
|
320
|
+
get componentInstance() {
|
|
321
|
+
if (!this.componentRef) {
|
|
322
|
+
throw new Error('WrDrawerRef: component not yet attached');
|
|
323
|
+
}
|
|
324
|
+
return this.componentRef.instance;
|
|
325
|
+
}
|
|
326
|
+
isClosed = false;
|
|
327
|
+
/** Close the drawer, optionally returning a result. */
|
|
328
|
+
close(result) {
|
|
329
|
+
if (this.isClosed)
|
|
330
|
+
return;
|
|
331
|
+
this.isClosed = true;
|
|
332
|
+
this.closed.next(result);
|
|
333
|
+
this.closed.complete();
|
|
334
|
+
// Content that closes itself while constructing gets here with
|
|
335
|
+
// `OverlayRef.attach()` still on the stack — `componentRef` is assigned
|
|
336
|
+
// only once attach returns. Disposing mid-attach leaves the CDK writing to
|
|
337
|
+
// a detached host, so let the current task finish first.
|
|
338
|
+
if (!this.componentRef) {
|
|
339
|
+
queueMicrotask(() => this.teardown());
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
this.teardown();
|
|
343
|
+
}
|
|
344
|
+
teardown() {
|
|
345
|
+
this.focusTrap?.destroy();
|
|
346
|
+
this.focusTrap = null;
|
|
347
|
+
const restore = this.previouslyFocused;
|
|
348
|
+
this.previouslyFocused = null;
|
|
349
|
+
this._overlayRef.dispose();
|
|
350
|
+
// Drop the destroyed component so a retained ref doesn't pin its instance,
|
|
351
|
+
// host element and view in memory.
|
|
352
|
+
this.componentRef = null;
|
|
353
|
+
// Restore focus after disposal so the trigger is reachable again.
|
|
354
|
+
if (restore && typeof restore.focus === 'function') {
|
|
355
|
+
restore.focus();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/** Resolves with the close result when the drawer is dismissed. */
|
|
359
|
+
awaitClose() {
|
|
360
|
+
return new Promise(resolve => {
|
|
361
|
+
this.closed.subscribe({
|
|
362
|
+
next: value => resolve(value),
|
|
363
|
+
complete: () => resolve(undefined),
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/** Underlying overlay ref — escape hatch for advanced cases. */
|
|
368
|
+
get overlayRef() {
|
|
369
|
+
return this._overlayRef;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @license
|
|
375
|
+
*
|
|
376
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
377
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
378
|
+
*/
|
|
379
|
+
/**
|
|
380
|
+
* Token that exposes the `data` payload passed to `WrDrawerManager.open()`.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```ts
|
|
384
|
+
* @Component({...})
|
|
385
|
+
* export class ChatComponent {
|
|
386
|
+
* readonly data = inject<ChatData>(WR_DRAWER_DATA);
|
|
387
|
+
* }
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
const WR_DRAWER_DATA = new InjectionToken('WR_DRAWER_DATA');
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @license
|
|
394
|
+
*
|
|
395
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
396
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
397
|
+
*/
|
|
398
|
+
/**
|
|
399
|
+
* Token exposing the open drawer's `WrDrawerRef` to components and
|
|
400
|
+
* directives rendered inside it (e.g. `[wrDrawerClose]`).
|
|
401
|
+
*
|
|
402
|
+
* **Prefer `inject(WrDrawerRef)`** — the ref is provided under the class token
|
|
403
|
+
* too, and the class path is the documented one. It already covers both cases
|
|
404
|
+
* you might reach for a token for: `inject(WrDrawerRef, { optional: true })`
|
|
405
|
+
* works for content that also renders outside a drawer, and
|
|
406
|
+
* `inject<WrDrawerRef<ChatComponent, boolean>>(WrDrawerRef)` types the generics
|
|
407
|
+
* without a cast.
|
|
408
|
+
*
|
|
409
|
+
* This token is the same object under a second key. It exists for the library's
|
|
410
|
+
* own directives (`[wrDrawerClose]`) and for swapping the provider out — a test
|
|
411
|
+
* stub, say — without touching the class.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```ts
|
|
415
|
+
* @Component({...})
|
|
416
|
+
* export class ChatComponent {
|
|
417
|
+
* private readonly ref = inject<WrDrawerRef<ChatComponent, boolean>>(WrDrawerRef);
|
|
418
|
+
*
|
|
419
|
+
* dismiss(): void {
|
|
420
|
+
* this.ref.close(true);
|
|
421
|
+
* }
|
|
422
|
+
* }
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
const WR_DRAWER_REF = new InjectionToken('WR_DRAWER_REF');
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* @license
|
|
429
|
+
*
|
|
430
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
431
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
432
|
+
*/
|
|
433
|
+
const DEFAULT_POSITION = 'right';
|
|
434
|
+
const DEFAULT_WIDTH = '20rem';
|
|
435
|
+
const DEFAULT_HEIGHT = '16rem';
|
|
436
|
+
const BACKDROP_CLASS = 'wr-drawer-backdrop';
|
|
437
|
+
/**
|
|
438
|
+
* Opens drawer components in an isolated NGWR overlay — the programmatic
|
|
439
|
+
* counterpart to `<wr-drawer [(open)]="…">`.
|
|
440
|
+
*
|
|
441
|
+
* Use the component when the drawer is part of a template and its open state
|
|
442
|
+
* is a signal you already own; use this service when the caller doesn't own
|
|
443
|
+
* the markup (opening a chat panel from a toolbar action, from an effect, from
|
|
444
|
+
* a route guard) or when you need the close result.
|
|
445
|
+
*
|
|
446
|
+
* Uses `WR_OVERLAY` so it composes cleanly with `provideWrOverlay()`
|
|
447
|
+
* — drawers render into NGWR's own overlay container and never collide
|
|
448
|
+
* with other CDK consumers (Material, NG-ZORRO, etc.).
|
|
449
|
+
*
|
|
450
|
+
* `showHandle` / swipe-to-dismiss is component-only: it needs the drawer's own
|
|
451
|
+
* wrapper markup, which the service path replaces with your component.
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```ts
|
|
455
|
+
* const drawers = inject(WrDrawerManager);
|
|
456
|
+
*
|
|
457
|
+
* const ref = drawers.open(ChatComponent, {
|
|
458
|
+
* data: { threadId },
|
|
459
|
+
* position: 'right',
|
|
460
|
+
* width: '28rem',
|
|
461
|
+
* });
|
|
462
|
+
*
|
|
463
|
+
* await ref.awaitClose();
|
|
464
|
+
* ```
|
|
465
|
+
*
|
|
466
|
+
* @see https://ngwr.dev/reference/components/drawer
|
|
467
|
+
*/
|
|
468
|
+
class WrDrawerManager {
|
|
469
|
+
overlay = inject(WR_OVERLAY);
|
|
470
|
+
scrollStrategies = inject(ScrollStrategyOptions);
|
|
471
|
+
parentInjector = inject(EnvironmentInjector);
|
|
472
|
+
focusTrapFactory = inject(ConfigurableFocusTrapFactory);
|
|
473
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
474
|
+
i18n = inject(WrI18n);
|
|
475
|
+
open(component, options = {}) {
|
|
476
|
+
const position = options.position ?? DEFAULT_POSITION;
|
|
477
|
+
const isHorizontal = position === 'left' || position === 'right';
|
|
478
|
+
// The content component is attached straight into the overlay pane, so the
|
|
479
|
+
// panel styles land on the pane rather than on an inner wrapper.
|
|
480
|
+
const panelClasses = [
|
|
481
|
+
'wr-drawer-overlay',
|
|
482
|
+
`wr-drawer-overlay--${position}`,
|
|
483
|
+
'wr-drawer__panel',
|
|
484
|
+
`wr-drawer__panel--${position}`,
|
|
485
|
+
];
|
|
486
|
+
if (options.rounded)
|
|
487
|
+
panelClasses.push('wr-drawer__panel--rounded');
|
|
488
|
+
if (options.safeArea)
|
|
489
|
+
panelClasses.push('wr-drawer__panel--safe-area');
|
|
490
|
+
const extra = options.panelClass;
|
|
491
|
+
if (typeof extra === 'string') {
|
|
492
|
+
panelClasses.push(extra);
|
|
493
|
+
}
|
|
494
|
+
else if (extra) {
|
|
495
|
+
for (const cls of extra)
|
|
496
|
+
panelClasses.push(cls);
|
|
497
|
+
}
|
|
498
|
+
const positionStrategy = this.overlay
|
|
499
|
+
.position()
|
|
500
|
+
.global()[position === 'left' ? 'left' : position === 'right' ? 'right' : 'centerHorizontally']();
|
|
501
|
+
if (position === 'top')
|
|
502
|
+
positionStrategy.top();
|
|
503
|
+
else if (position === 'bottom')
|
|
504
|
+
positionStrategy.bottom();
|
|
505
|
+
else
|
|
506
|
+
positionStrategy.centerVertically();
|
|
507
|
+
const cap = options.maxHeight ?? null;
|
|
508
|
+
const overlayRef = this.overlay.create({
|
|
509
|
+
positionStrategy,
|
|
510
|
+
scrollStrategy: this.scrollStrategies.block(),
|
|
511
|
+
hasBackdrop: options.hasBackdrop !== false,
|
|
512
|
+
backdropClass: BACKDROP_CLASS,
|
|
513
|
+
panelClass: panelClasses,
|
|
514
|
+
width: isHorizontal ? (options.width ?? DEFAULT_WIDTH) : '100vw',
|
|
515
|
+
height: isHorizontal ? undefined : (options.height ?? DEFAULT_HEIGHT),
|
|
516
|
+
maxHeight: isHorizontal ? undefined : (cap ?? undefined),
|
|
517
|
+
});
|
|
518
|
+
const drawerRef = new WrDrawerRef(overlayRef);
|
|
519
|
+
if (this.isBrowser) {
|
|
520
|
+
const active = document.activeElement;
|
|
521
|
+
drawerRef.previouslyFocused = active instanceof HTMLElement ? active : null;
|
|
522
|
+
}
|
|
523
|
+
const injector = Injector.create({
|
|
524
|
+
parent: this.parentInjector,
|
|
525
|
+
providers: [
|
|
526
|
+
{ provide: WR_DRAWER_DATA, useValue: options.data },
|
|
527
|
+
{ provide: WR_DRAWER_REF, useValue: drawerRef },
|
|
528
|
+
// Provided as its own token as well, so drawer content can reach for
|
|
529
|
+
// the familiar `inject(WrDrawerRef)` and close itself.
|
|
530
|
+
{ provide: WrDrawerRef, useValue: drawerRef },
|
|
531
|
+
],
|
|
532
|
+
});
|
|
533
|
+
const portal = new ComponentPortal(component, null, injector);
|
|
534
|
+
drawerRef.componentRef = overlayRef.attach(portal);
|
|
535
|
+
if (this.isBrowser) {
|
|
536
|
+
const host = overlayRef.overlayElement;
|
|
537
|
+
host.setAttribute('role', 'dialog');
|
|
538
|
+
host.setAttribute('aria-modal', 'true');
|
|
539
|
+
// Built-in dismiss, same contract as WrDialog's `closable`.
|
|
540
|
+
if (options.closable !== false) {
|
|
541
|
+
host.classList.add('wr-drawer__panel--closable');
|
|
542
|
+
const label = options.closeLabel ?? this.i18n.t('drawer.close');
|
|
543
|
+
wrAppendOverlayClose(host, 'wr-drawer__close', label, () => drawerRef.close());
|
|
544
|
+
}
|
|
545
|
+
// Wire aria-labelledby to wrDrawerTitle's auto-id once content is in DOM.
|
|
546
|
+
// Has to wait for a render, not a microtask: attaching the portal only
|
|
547
|
+
// schedules change detection, so under zoneless CD a microtask runs while
|
|
548
|
+
// the content is still an empty view and the title id doesn't exist yet.
|
|
549
|
+
afterNextRender(() => {
|
|
550
|
+
const titleEl = host.querySelector('[wrDrawerTitle], [wr-drawer-title]');
|
|
551
|
+
if (titleEl?.id)
|
|
552
|
+
host.setAttribute('aria-labelledby', titleEl.id);
|
|
553
|
+
}, { injector: this.parentInjector });
|
|
554
|
+
// Trap focus inside the drawer and move initial focus in.
|
|
555
|
+
const trap = this.focusTrapFactory.create(host);
|
|
556
|
+
drawerRef.focusTrap = trap;
|
|
557
|
+
void trap.focusInitialElementWhenReady();
|
|
558
|
+
}
|
|
559
|
+
// Overlay subscriptions complete when the overlay is disposed, so no
|
|
560
|
+
// explicit teardown is required.
|
|
561
|
+
if (options.closeOnBackdropClick !== false) {
|
|
562
|
+
overlayRef.backdropClick().subscribe(() => drawerRef.close());
|
|
563
|
+
}
|
|
564
|
+
if (options.closeOnEscape !== false) {
|
|
565
|
+
overlayRef.keydownEvents().subscribe(event => {
|
|
566
|
+
if (event.key === 'Escape') {
|
|
567
|
+
event.preventDefault();
|
|
568
|
+
drawerRef.close();
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
return drawerRef;
|
|
573
|
+
}
|
|
574
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerManager, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
575
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerManager });
|
|
576
|
+
}
|
|
577
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerManager, decorators: [{
|
|
578
|
+
type: Service
|
|
579
|
+
}] });
|
|
247
580
|
|
|
248
581
|
/**
|
|
249
582
|
* @license
|
|
@@ -348,18 +681,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
348
681
|
/**
|
|
349
682
|
* Closes the parent drawer on click. Attach to any clickable element.
|
|
350
683
|
*
|
|
684
|
+
* Works for both drawer flavours: inside `<wr-drawer>` it clears the
|
|
685
|
+
* component's `open` model, and inside a `WrDrawerManager.open()` drawer it
|
|
686
|
+
* closes the ref — optionally passing a `[wrDrawerClose]` value as the close
|
|
687
|
+
* result (the component flavour has no result to carry, so the value is
|
|
688
|
+
* ignored there).
|
|
689
|
+
*
|
|
351
690
|
* @example
|
|
352
691
|
* ```html
|
|
353
692
|
* <wr-btn wrDrawerClose>Close</wr-btn>
|
|
693
|
+
* <wr-btn color="primary" [wrDrawerClose]="true">Save</wr-btn>
|
|
354
694
|
* ```
|
|
355
695
|
*/
|
|
356
696
|
class WrDrawerClose {
|
|
697
|
+
/**
|
|
698
|
+
* Value to pass to `close()` for service-opened drawers. When unset — or
|
|
699
|
+
* used as a bare attribute — the drawer closes with `undefined`.
|
|
700
|
+
*/
|
|
701
|
+
result = input(undefined, { ...(ngDevMode ? { debugName: "result" } : /* istanbul ignore next */ {}), alias: 'wrDrawerClose',
|
|
702
|
+
// A bare `wrDrawerClose` attribute arrives as the empty string; that means
|
|
703
|
+
// "no result", not a result of `''`.
|
|
704
|
+
transform: value => (value === '' ? undefined : value) });
|
|
357
705
|
drawer = inject(WrDrawer, { optional: true });
|
|
706
|
+
ref = inject(WR_DRAWER_REF, { optional: true });
|
|
358
707
|
close() {
|
|
359
|
-
|
|
708
|
+
// Element injectors win, so a surrounding `<wr-drawer>` is the nearer
|
|
709
|
+
// owner even when a service-opened drawer sits further up the tree.
|
|
710
|
+
if (this.drawer) {
|
|
711
|
+
this.drawer.open.set(false);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
this.ref?.close(this.result());
|
|
360
715
|
}
|
|
361
716
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerClose, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
362
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
717
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.0", type: WrDrawerClose, isStandalone: true, selector: "[wrDrawerClose]", inputs: { result: { classPropertyName: "result", publicName: "wrDrawerClose", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "close()" } }, ngImport: i0 });
|
|
363
718
|
}
|
|
364
719
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerClose, decorators: [{
|
|
365
720
|
type: Directive,
|
|
@@ -367,11 +722,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
367
722
|
selector: '[wrDrawerClose]',
|
|
368
723
|
host: { '(click)': 'close()' },
|
|
369
724
|
}]
|
|
370
|
-
}] });
|
|
725
|
+
}], propDecorators: { result: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrDrawerClose", required: false }] }] } });
|
|
371
726
|
|
|
372
727
|
/**
|
|
373
728
|
* Generated bundle index. Do not edit.
|
|
374
729
|
*/
|
|
375
730
|
|
|
376
|
-
export { WrDrawer, WrDrawerClose, WrDrawerContent, WrDrawerFooter, WrDrawerTitle };
|
|
731
|
+
export { WR_DRAWER_DATA, WR_DRAWER_REF, WrDrawer, WrDrawerClose, WrDrawerContent, WrDrawerFooter, WrDrawerManager, WrDrawerRef, WrDrawerTitle };
|
|
377
732
|
//# sourceMappingURL=ngwr-drawer.mjs.map
|