ng-primitives 0.59.0 → 0.61.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/fesm2022/ng-primitives-a11y.mjs +3 -3
- package/fesm2022/ng-primitives-a11y.mjs.map +1 -1
- package/fesm2022/ng-primitives-internal.mjs +1 -1
- package/fesm2022/ng-primitives-internal.mjs.map +1 -1
- package/fesm2022/ng-primitives-popover.mjs +18 -6
- package/fesm2022/ng-primitives-popover.mjs.map +1 -1
- package/fesm2022/ng-primitives-toast.mjs +405 -160
- package/fesm2022/ng-primitives-toast.mjs.map +1 -1
- package/package.json +27 -27
- package/popover/popover-trigger/popover-trigger.d.ts +9 -3
- package/schematics/ng-generate/templates/toast/toast.__fileSuffix@dasherize__.ts.template +104 -36
- package/toast/config/toast-config.d.ts +36 -7
- package/toast/index.d.ts +2 -1
- package/toast/toast/toast-context.d.ts +3 -0
- package/toast/toast/toast-manager.d.ts +50 -0
- package/toast/toast/toast-options.d.ts +34 -0
- package/toast/toast/toast-timer.d.ts +14 -0
- package/toast/toast/toast.d.ts +60 -37
- package/toast/toast/toast-ref.d.ts +0 -49
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { InjectionToken, inject, ApplicationRef, RendererFactory2, Injector, signal, ViewContainerRef, computed, Injectable, afterNextRender, HostListener, Directive } from '@angular/core';
|
|
3
|
+
import { InteractivityChecker } from '@angular/cdk/a11y';
|
|
4
|
+
import { explicitEffect } from 'ng-primitives/internal';
|
|
5
|
+
import { injectDimensions } from 'ng-primitives/utils';
|
|
6
|
+
import { createPortal } from 'ng-primitives/portal';
|
|
5
7
|
|
|
6
8
|
const defaultToastConfig = {
|
|
7
|
-
gap:
|
|
9
|
+
gap: 14,
|
|
8
10
|
duration: 3000,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
width: 360,
|
|
12
|
+
offsetTop: 24,
|
|
13
|
+
offsetBottom: 24,
|
|
14
|
+
offsetLeft: 24,
|
|
15
|
+
offsetRight: 24,
|
|
16
|
+
swipeThreshold: 45,
|
|
17
|
+
swipeDirections: ['left', 'right', 'top', 'bottom'],
|
|
18
|
+
dismissible: true,
|
|
19
|
+
maxToasts: 3,
|
|
20
|
+
zIndex: 9999999,
|
|
12
21
|
ariaLive: 'polite',
|
|
13
22
|
};
|
|
14
23
|
const NgpToastConfigToken = new InjectionToken('NgpToastConfigToken');
|
|
@@ -33,209 +42,445 @@ function injectToastConfig() {
|
|
|
33
42
|
return inject(NgpToastConfigToken, { optional: true }) ?? defaultToastConfig;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
this.
|
|
59
|
-
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
this.setPosition(position);
|
|
65
|
-
this.setGravity(gravity);
|
|
66
|
-
this.setAriaLive(ariaLive);
|
|
67
|
-
this.setupTimeouts();
|
|
68
|
-
this.setupListeners();
|
|
45
|
+
const NgpToastContext = new InjectionToken('NgpToastContext');
|
|
46
|
+
function provideToastContext(context) {
|
|
47
|
+
return { provide: NgpToastContext, useValue: context };
|
|
48
|
+
}
|
|
49
|
+
function injectToastContext() {
|
|
50
|
+
return inject(NgpToastContext);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const NgpToastOptions = new InjectionToken('NgpToastOptions');
|
|
54
|
+
function provideToastOptions(context) {
|
|
55
|
+
return { provide: NgpToastOptions, useValue: context };
|
|
56
|
+
}
|
|
57
|
+
function injectToastOptions() {
|
|
58
|
+
return inject(NgpToastOptions);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class NgpToastManager {
|
|
62
|
+
constructor() {
|
|
63
|
+
this.config = injectToastConfig();
|
|
64
|
+
this.applicationRef = inject(ApplicationRef);
|
|
65
|
+
this.rendererFactory = inject(RendererFactory2);
|
|
66
|
+
this.renderer = this.rendererFactory.createRenderer(null, null);
|
|
67
|
+
this.injector = inject(Injector);
|
|
68
|
+
// Map to store containers by placement
|
|
69
|
+
this.containers = new Map();
|
|
70
|
+
this.toasts = signal([]);
|
|
71
|
+
/** Signal that tracks which placements are expanded */
|
|
72
|
+
this.expanded = signal([]);
|
|
69
73
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
/** Show a toast notification */
|
|
75
|
+
show(toast, options = {}) {
|
|
76
|
+
// services can't access the view container directly, so this is a workaround
|
|
77
|
+
const viewContainerRef = this.applicationRef.components[0].injector.get(ViewContainerRef);
|
|
78
|
+
let instance = null;
|
|
79
|
+
const placement = options.placement ?? 'top-end';
|
|
80
|
+
const container = this.getOrCreateContainer(placement);
|
|
81
|
+
const portal = createPortal(toast, viewContainerRef, Injector.create({
|
|
82
|
+
parent: this.injector,
|
|
83
|
+
providers: [
|
|
84
|
+
provideToastContext(options.context),
|
|
85
|
+
provideToastOptions({
|
|
86
|
+
placement,
|
|
87
|
+
duration: options.duration ?? 5000,
|
|
88
|
+
register: (toast) => (instance = toast),
|
|
89
|
+
expanded: computed(() => this.expanded().includes(placement)),
|
|
90
|
+
dismissible: options.dismissible ?? this.config.dismissible,
|
|
91
|
+
swipeDirections: options.swipeDirections ?? this.config.swipeDirections,
|
|
92
|
+
}),
|
|
93
|
+
],
|
|
94
|
+
}), {
|
|
95
|
+
// Hide the toast when the dismiss method is called
|
|
96
|
+
dismiss: () => this.dismiss(instance),
|
|
97
|
+
context: options.context,
|
|
98
|
+
});
|
|
99
|
+
portal.attach(container);
|
|
100
|
+
// Add the toast to the list of toasts
|
|
101
|
+
if (!instance) {
|
|
102
|
+
throw new Error('A toast must have the NgpToast directive applied.');
|
|
77
103
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
removeElement() {
|
|
83
|
-
this.toastElement.parentNode?.removeChild(this.toastElement);
|
|
84
|
-
this.onDismiss();
|
|
104
|
+
this.toasts.update(toasts => [{ instance: instance, portal }, ...toasts]);
|
|
105
|
+
return {
|
|
106
|
+
dismiss: () => this.dismiss(instance),
|
|
107
|
+
};
|
|
85
108
|
}
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
109
|
+
/** Hide a toast notification */
|
|
110
|
+
async dismiss(toast) {
|
|
111
|
+
const ref = this.toasts().find(t => t.instance === toast);
|
|
112
|
+
if (ref) {
|
|
113
|
+
// Detach the portal from the container
|
|
114
|
+
await ref.portal.detach();
|
|
115
|
+
// Remove the toast from the list of toasts
|
|
116
|
+
this.toasts.update(toasts => toasts.filter(t => t !== ref));
|
|
117
|
+
// if there are no more toasts, ensure the container is no longer considered expanded
|
|
118
|
+
if (this.toasts().length === 0) {
|
|
119
|
+
this.expanded.update(expanded => expanded.filter(p => p !== toast.context.placement));
|
|
120
|
+
}
|
|
91
121
|
}
|
|
92
|
-
this.timeoutId = window.setTimeout(() => this.dismiss(), this.duration);
|
|
93
122
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Lazily create or get a container for a given placement.
|
|
125
|
+
*/
|
|
126
|
+
getOrCreateContainer(placement) {
|
|
127
|
+
if (this.containers.has(placement)) {
|
|
128
|
+
return this.containers.get(placement);
|
|
97
129
|
}
|
|
98
|
-
|
|
99
|
-
this.
|
|
100
|
-
|
|
101
|
-
|
|
130
|
+
const container = this.createContainer(placement);
|
|
131
|
+
this.containers.set(placement, container);
|
|
132
|
+
return container;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a section in which toasts will be rendered for a specific placement.
|
|
136
|
+
*/
|
|
137
|
+
createContainer(placement) {
|
|
138
|
+
const container = this.renderer.createElement('section');
|
|
139
|
+
this.renderer.setAttribute(container, 'aria-live', this.config.ariaLive);
|
|
140
|
+
this.renderer.setAttribute(container, 'aria-atomic', 'false');
|
|
141
|
+
this.renderer.setAttribute(container, 'tabindex', '-1');
|
|
142
|
+
this.renderer.setAttribute(container, 'data-ngp-toast-container', placement);
|
|
143
|
+
container.style.setProperty('position', 'fixed');
|
|
144
|
+
container.style.setProperty('z-index', `${this.config.zIndex}`);
|
|
145
|
+
container.style.setProperty('width', `${this.config.width}px`);
|
|
146
|
+
container.style.setProperty('--ngp-toast-offset-top', `${this.config.offsetTop}px`);
|
|
147
|
+
container.style.setProperty('--ngp-toast-offset-bottom', `${this.config.offsetBottom}px`);
|
|
148
|
+
container.style.setProperty('--ngp-toast-offset-left', `${this.config.offsetLeft}px`);
|
|
149
|
+
container.style.setProperty('--ngp-toast-offset-right', `${this.config.offsetRight}px`);
|
|
150
|
+
container.style.setProperty('--ngp-toast-gap', `${this.config.gap}px`);
|
|
151
|
+
container.style.setProperty('--ngp-toast-width', `${this.config.width}px`);
|
|
152
|
+
// mark the container as expanded
|
|
153
|
+
this.renderer.listen(container, 'mouseenter', () => this.expanded.update(expanded => [...expanded, placement]));
|
|
154
|
+
this.renderer.listen(container, 'mouseleave', () => {
|
|
155
|
+
this.expanded.update(expanded => expanded.filter(p => p !== placement));
|
|
102
156
|
});
|
|
103
|
-
|
|
157
|
+
// Set placement styles
|
|
158
|
+
switch (placement) {
|
|
159
|
+
case 'top-start':
|
|
160
|
+
container.style.setProperty('top', `${this.config.offsetTop}px`);
|
|
161
|
+
container.style.setProperty('left', `${this.config.offsetLeft}px`);
|
|
162
|
+
break;
|
|
163
|
+
case 'top-center':
|
|
164
|
+
container.style.setProperty('top', '0');
|
|
165
|
+
container.style.setProperty('left', '50%');
|
|
166
|
+
container.style.setProperty('transform', 'translateX(-50%)');
|
|
167
|
+
break;
|
|
168
|
+
case 'top-end':
|
|
169
|
+
container.style.setProperty('top', `${this.config.offsetTop}px`);
|
|
170
|
+
container.style.setProperty('right', `${this.config.offsetRight}px`);
|
|
171
|
+
break;
|
|
172
|
+
case 'bottom-start':
|
|
173
|
+
container.style.setProperty('bottom', `${this.config.offsetBottom}px`);
|
|
174
|
+
container.style.setProperty('left', `${this.config.offsetLeft}px`);
|
|
175
|
+
break;
|
|
176
|
+
case 'bottom-center':
|
|
177
|
+
container.style.setProperty('bottom', '0');
|
|
178
|
+
container.style.setProperty('left', '50%');
|
|
179
|
+
container.style.setProperty('transform', 'translateX(-50%)');
|
|
180
|
+
break;
|
|
181
|
+
case 'bottom-end':
|
|
182
|
+
container.style.setProperty('bottom', `${this.config.offsetBottom}px`);
|
|
183
|
+
container.style.setProperty('right', `${this.config.offsetRight}px`);
|
|
184
|
+
break;
|
|
185
|
+
default:
|
|
186
|
+
throw new Error(`Unknown toast placement: ${placement}`);
|
|
187
|
+
}
|
|
188
|
+
this.renderer.appendChild(document.body, container);
|
|
189
|
+
return container;
|
|
104
190
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
191
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpToastManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
192
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpToastManager, providedIn: 'root' }); }
|
|
193
|
+
}
|
|
194
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpToastManager, decorators: [{
|
|
195
|
+
type: Injectable,
|
|
196
|
+
args: [{
|
|
197
|
+
providedIn: 'root',
|
|
198
|
+
}]
|
|
199
|
+
}] });
|
|
200
|
+
|
|
201
|
+
class NgpToastTimer {
|
|
202
|
+
constructor(duration, callback) {
|
|
203
|
+
this.duration = duration;
|
|
204
|
+
this.callback = callback;
|
|
205
|
+
this.startTime = null;
|
|
206
|
+
this.timeoutId = null;
|
|
207
|
+
this.isRunning = false;
|
|
208
|
+
this.remaining = duration;
|
|
108
209
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
210
|
+
start() {
|
|
211
|
+
if (this.isRunning)
|
|
212
|
+
return;
|
|
213
|
+
this.isRunning = true;
|
|
214
|
+
this.startTime = Date.now();
|
|
215
|
+
this.timeoutId = setTimeout(() => {
|
|
216
|
+
this.isRunning = false;
|
|
217
|
+
this.callback();
|
|
218
|
+
}, this.remaining);
|
|
112
219
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
220
|
+
pause() {
|
|
221
|
+
if (!this.isRunning || this.startTime === null)
|
|
222
|
+
return;
|
|
223
|
+
this.isRunning = false;
|
|
224
|
+
clearTimeout(this.timeoutId);
|
|
225
|
+
const elapsed = Date.now() - this.startTime;
|
|
226
|
+
this.remaining -= elapsed;
|
|
227
|
+
this.startTime = null;
|
|
228
|
+
this.timeoutId = null;
|
|
116
229
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.
|
|
230
|
+
stop() {
|
|
231
|
+
this.isRunning = false;
|
|
232
|
+
clearTimeout(this.timeoutId);
|
|
233
|
+
this.timeoutId = null;
|
|
234
|
+
this.startTime = null;
|
|
235
|
+
this.remaining = this.duration;
|
|
122
236
|
}
|
|
123
237
|
}
|
|
238
|
+
function toastTimer(duration, callback) {
|
|
239
|
+
return new NgpToastTimer(duration, callback);
|
|
240
|
+
}
|
|
124
241
|
|
|
125
242
|
class NgpToast {
|
|
126
243
|
constructor() {
|
|
127
|
-
this.
|
|
128
|
-
/** Access the ng-template */
|
|
129
|
-
this.template = inject(TemplateRef);
|
|
130
|
-
/** Access the view container */
|
|
131
|
-
this.viewContainer = inject(ViewContainerRef);
|
|
132
|
-
/** Access the injector */
|
|
244
|
+
this.manager = inject(NgpToastManager);
|
|
133
245
|
this.injector = inject(Injector);
|
|
134
|
-
|
|
135
|
-
|
|
246
|
+
this.config = injectToastConfig();
|
|
247
|
+
/** @internal */
|
|
248
|
+
this.context = injectToastOptions();
|
|
249
|
+
this.interactivityChecker = inject(InteractivityChecker);
|
|
250
|
+
this.isInteracting = signal(false);
|
|
251
|
+
this.pointerStartRef = null;
|
|
252
|
+
this.dragStartTime = null;
|
|
253
|
+
this.swiping = signal(false);
|
|
254
|
+
this.swipeDirection = signal(null);
|
|
255
|
+
this.swipeAmount = signal({ x: 0, y: 0 });
|
|
256
|
+
this.swipeOutDirection = computed(() => {
|
|
257
|
+
const direction = this.swipeDirection();
|
|
258
|
+
if (direction === 'x') {
|
|
259
|
+
return this.swipeAmount().x > 0 ? 'right' : 'left';
|
|
260
|
+
}
|
|
261
|
+
else if (direction === 'y') {
|
|
262
|
+
return this.swipeAmount().y > 0 ? 'bottom' : 'top';
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
});
|
|
266
|
+
/**
|
|
267
|
+
* Get all toasts that are currently being displayed in the same position.
|
|
268
|
+
*/
|
|
269
|
+
this.toasts = computed(() => this.manager
|
|
270
|
+
.toasts()
|
|
271
|
+
.filter(toast => toast.instance.context.placement === this.context.placement));
|
|
136
272
|
/**
|
|
137
|
-
* The
|
|
138
|
-
* @default 3000
|
|
273
|
+
* The number of toasts that are currently being displayed before this toast.
|
|
139
274
|
*/
|
|
140
|
-
this.
|
|
141
|
-
|
|
142
|
-
transform: numberAttribute,
|
|
275
|
+
this.index = computed(() => {
|
|
276
|
+
return this.toasts().findIndex(toast => toast.instance === this);
|
|
143
277
|
});
|
|
144
278
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
279
|
+
* Determine the position of the toast in the list of toasts.
|
|
280
|
+
* This is the combination of the heights of all the toasts before this one, plus the gap between them.
|
|
147
281
|
*/
|
|
148
|
-
this.
|
|
149
|
-
|
|
282
|
+
this.offset = computed(() => {
|
|
283
|
+
const gap = this.config.gap;
|
|
284
|
+
return this.toasts()
|
|
285
|
+
.slice(0, this.index())
|
|
286
|
+
.reduce((acc, toast) => acc + toast.instance.dimensions().height + gap, 0);
|
|
150
287
|
});
|
|
151
288
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
289
|
+
* Determine if this toast is visible.
|
|
290
|
+
* Visible considers the maximum number of toasts that can be displayed at once, and the last x toasts that are currently being displayed.
|
|
154
291
|
*/
|
|
155
|
-
this.
|
|
156
|
-
|
|
292
|
+
this.visible = computed(() => {
|
|
293
|
+
const maxToasts = this.config.maxToasts;
|
|
294
|
+
// determine if this toast is within the maximum number of toasts that can be displayed
|
|
295
|
+
return this.index() < maxToasts || this.toasts().length <= maxToasts;
|
|
157
296
|
});
|
|
158
297
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
298
|
+
* Determine the height of the front toast.
|
|
299
|
+
* This is used to determine the height of the toast when it is not expanded.
|
|
161
300
|
*/
|
|
162
|
-
this.
|
|
163
|
-
|
|
164
|
-
|
|
301
|
+
this.frontToastHeight = computed(() => {
|
|
302
|
+
// get the first toast in the list with height - as when a new toast is added, it may not initially have dimensions
|
|
303
|
+
return (this.toasts()
|
|
304
|
+
.find(toast => toast.instance.dimensions().height)
|
|
305
|
+
?.instance.dimensions().height || 0);
|
|
165
306
|
});
|
|
166
307
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
308
|
+
* Determine the z-index of the toast. This is the inverse of the index.
|
|
309
|
+
* The first toast will have the highest z-index, and the last toast will have the lowest z-index.
|
|
310
|
+
* This is used to ensure that the first toast is always on top of the others.
|
|
311
|
+
*/
|
|
312
|
+
this.zIndex = computed(() => this.toasts().length - this.index());
|
|
313
|
+
/**
|
|
314
|
+
* The height of the toast in pixels.
|
|
315
|
+
*/
|
|
316
|
+
this.dimensions = injectDimensions();
|
|
317
|
+
/**
|
|
318
|
+
* The x position of the toast.
|
|
169
319
|
*/
|
|
170
|
-
this.
|
|
171
|
-
|
|
320
|
+
this.x = this.context.placement.split('-')[1] || 'end';
|
|
321
|
+
/**
|
|
322
|
+
* The y position of the toast.
|
|
323
|
+
*/
|
|
324
|
+
this.y = this.context.placement.split('-')[0] || 'top';
|
|
325
|
+
/**
|
|
326
|
+
* The toast timer instance.
|
|
327
|
+
*/
|
|
328
|
+
this.timer = toastTimer(this.config.duration, () => this.manager.dismiss(this));
|
|
329
|
+
this.context.register(this);
|
|
330
|
+
// Start the timer when the toast is created
|
|
331
|
+
this.timer.start();
|
|
332
|
+
// Pause the timer when the toast is expanded or when the user is interacting with it
|
|
333
|
+
explicitEffect([this.context.expanded, this.isInteracting], ([expanded, interacting]) => {
|
|
334
|
+
// If the toast is expanded, or if the user is interacting with it, reset the timer
|
|
335
|
+
if (expanded || interacting) {
|
|
336
|
+
this.timer.pause();
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
this.timer.start();
|
|
340
|
+
}
|
|
172
341
|
});
|
|
173
342
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}));
|
|
190
|
-
const viewRef = domOutlet.attach(portal);
|
|
191
|
-
viewRef.detectChanges();
|
|
192
|
-
const toastElement = viewRef.rootNodes[0];
|
|
193
|
-
const toastRef = new NgpToastRef(toastElement, this.duration(), this.position(), this.gravity(), this.stopOnHover(), this.ariaLive(), () => {
|
|
194
|
-
NgpToast.toasts = NgpToast.toasts.filter(t => t !== toastRef);
|
|
195
|
-
this.reposition();
|
|
196
|
-
});
|
|
197
|
-
NgpToast.toasts = [...NgpToast.toasts, toastRef];
|
|
343
|
+
onPointerDown(event) {
|
|
344
|
+
// right click should not trigger swipe and we check if the toast is dismissible
|
|
345
|
+
if (event.button === 2 || !this.context.dismissible) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
this.isInteracting.set(true);
|
|
349
|
+
// we need to check if the pointer is on an interactive element, if so, we should not start swiping
|
|
350
|
+
if (this.interactivityChecker.isFocusable(event.target)) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
this.dragStartTime = new Date();
|
|
354
|
+
// Ensure we maintain correct pointer capture even when going outside of the toast (e.g. when swiping)
|
|
355
|
+
event.target.setPointerCapture(event.pointerId);
|
|
356
|
+
this.swiping.set(true);
|
|
357
|
+
this.pointerStartRef = { x: event.clientX, y: event.clientY };
|
|
198
358
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
359
|
+
onPointerMove(event) {
|
|
360
|
+
if (!this.pointerStartRef || !this.context.dismissible) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const isHighlighted = window.getSelection()?.toString().length ?? 0 > 0;
|
|
364
|
+
if (isHighlighted) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const yDelta = event.clientY - this.pointerStartRef.y;
|
|
368
|
+
const xDelta = event.clientX - this.pointerStartRef.x;
|
|
369
|
+
const swipeDirections = this.context.swipeDirections;
|
|
370
|
+
// Determine swipe direction if not already locked
|
|
371
|
+
if (!this.swipeDirection() && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) {
|
|
372
|
+
this.swipeDirection.set(Math.abs(xDelta) > Math.abs(yDelta) ? 'x' : 'y');
|
|
373
|
+
}
|
|
374
|
+
const swipeAmount = { x: 0, y: 0 };
|
|
375
|
+
const getDampening = (delta) => {
|
|
376
|
+
const factor = Math.abs(delta) / 20;
|
|
377
|
+
return 1 / (1.5 + factor);
|
|
208
378
|
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
379
|
+
// Only apply swipe in the locked direction
|
|
380
|
+
if (this.swipeDirection() === 'y') {
|
|
381
|
+
// Handle vertical swipes
|
|
382
|
+
if (swipeDirections.includes('top') || swipeDirections.includes('bottom')) {
|
|
383
|
+
if ((swipeDirections.includes('top') && yDelta < 0) ||
|
|
384
|
+
(swipeDirections.includes('bottom') && yDelta > 0)) {
|
|
385
|
+
swipeAmount.y = yDelta;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// Smoothly transition to dampened movement
|
|
389
|
+
const dampenedDelta = yDelta * getDampening(yDelta);
|
|
390
|
+
// Ensure we don't jump when transitioning to dampened movement
|
|
391
|
+
swipeAmount.y = Math.abs(dampenedDelta) < Math.abs(yDelta) ? dampenedDelta : yDelta;
|
|
392
|
+
}
|
|
218
393
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
394
|
+
}
|
|
395
|
+
else if (this.swipeDirection() === 'x') {
|
|
396
|
+
// Handle horizontal swipes
|
|
397
|
+
if (swipeDirections.includes('left') || swipeDirections.includes('right')) {
|
|
398
|
+
if ((swipeDirections.includes('left') && xDelta < 0) ||
|
|
399
|
+
(swipeDirections.includes('right') && xDelta > 0)) {
|
|
400
|
+
swipeAmount.x = xDelta;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
// Smoothly transition to dampened movement
|
|
404
|
+
const dampenedDelta = xDelta * getDampening(xDelta);
|
|
405
|
+
// Ensure we don't jump when transitioning to dampened movement
|
|
406
|
+
swipeAmount.x = Math.abs(dampenedDelta) < Math.abs(xDelta) ? dampenedDelta : xDelta;
|
|
407
|
+
}
|
|
222
408
|
}
|
|
223
409
|
}
|
|
410
|
+
this.swipeAmount.set({ x: swipeAmount.x, y: swipeAmount.y });
|
|
411
|
+
if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) {
|
|
412
|
+
this.swiping.set(true);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
onPointerUp() {
|
|
416
|
+
this.isInteracting.set(false);
|
|
417
|
+
if (!this.config.dismissible ||
|
|
418
|
+
!this.pointerStartRef ||
|
|
419
|
+
!this.swiping() ||
|
|
420
|
+
!this.dragStartTime) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
this.pointerStartRef = null;
|
|
424
|
+
const swipeAmountX = this.swipeAmount().x;
|
|
425
|
+
const swipeAmountY = this.swipeAmount().y;
|
|
426
|
+
const timeTaken = new Date().getTime() - this.dragStartTime.getTime();
|
|
427
|
+
const swipeAmount = this.swipeDirection() === 'x' ? swipeAmountX : swipeAmountY;
|
|
428
|
+
const velocity = Math.abs(swipeAmount) / timeTaken;
|
|
429
|
+
if (Math.abs(swipeAmount) >= this.config.swipeThreshold || velocity > 0.11) {
|
|
430
|
+
afterNextRender({ write: () => this.manager.dismiss(this) }, { injector: this.injector });
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
this.swipeAmount.set({ x: 0, y: 0 });
|
|
435
|
+
}
|
|
436
|
+
// Reset swipe state
|
|
437
|
+
this.swipeDirection.set(null);
|
|
438
|
+
this.swiping.set(false);
|
|
224
439
|
}
|
|
225
440
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpToast, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
226
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
441
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.11", type: NgpToast, isStandalone: true, selector: "[ngpToast]", host: { listeners: { "pointerdown": "onPointerDown($event)", "pointermove": "onPointerMove($event)", "pointerup": "onPointerUp()" }, properties: { "attr.data-position-x": "x", "attr.data-position-y": "y", "attr.data-visible": "visible()", "attr.data-front": "index() === 0", "attr.data-swiping": "swiping()", "attr.data-swipe-direction": "swipeOutDirection()", "attr.data-expanded": "context.expanded()", "style.--ngp-toast-gap.px": "config.gap", "style.--ngp-toast-z-index": "zIndex()", "style.--ngp-toasts-before": "index()", "style.--ngp-toast-index": "index() + 1", "style.--ngp-toast-width.px": "config.width", "style.--ngp-toast-height.px": "dimensions().height", "style.--ngp-toast-offset.px": "offset()", "style.--ngp-toast-front-height.px": "frontToastHeight()", "style.--ngp-toast-swipe-amount-x.px": "swipeAmount().x", "style.--ngp-toast-swipe-amount-y.px": "swipeAmount().y", "style.--ngp-toast-swipe-x": "swipeAmount().x", "style.--ngp-toast-swipe-y": "swipeAmount().y" } }, exportAs: ["ngpToast"], ngImport: i0 }); }
|
|
227
442
|
}
|
|
228
443
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpToast, decorators: [{
|
|
229
444
|
type: Directive,
|
|
230
445
|
args: [{
|
|
231
446
|
selector: '[ngpToast]',
|
|
232
447
|
exportAs: 'ngpToast',
|
|
448
|
+
host: {
|
|
449
|
+
'[attr.data-position-x]': 'x',
|
|
450
|
+
'[attr.data-position-y]': 'y',
|
|
451
|
+
'[attr.data-visible]': 'visible()',
|
|
452
|
+
'[attr.data-front]': 'index() === 0',
|
|
453
|
+
'[attr.data-swiping]': 'swiping()',
|
|
454
|
+
'[attr.data-swipe-direction]': 'swipeOutDirection()',
|
|
455
|
+
'[attr.data-expanded]': 'context.expanded()',
|
|
456
|
+
'[style.--ngp-toast-gap.px]': 'config.gap',
|
|
457
|
+
'[style.--ngp-toast-z-index]': 'zIndex()',
|
|
458
|
+
'[style.--ngp-toasts-before]': 'index()',
|
|
459
|
+
'[style.--ngp-toast-index]': 'index() + 1',
|
|
460
|
+
'[style.--ngp-toast-width.px]': 'config.width',
|
|
461
|
+
'[style.--ngp-toast-height.px]': 'dimensions().height',
|
|
462
|
+
'[style.--ngp-toast-offset.px]': 'offset()',
|
|
463
|
+
'[style.--ngp-toast-front-height.px]': 'frontToastHeight()',
|
|
464
|
+
'[style.--ngp-toast-swipe-amount-x.px]': 'swipeAmount().x',
|
|
465
|
+
'[style.--ngp-toast-swipe-amount-y.px]': 'swipeAmount().y',
|
|
466
|
+
'[style.--ngp-toast-swipe-x]': 'swipeAmount().x',
|
|
467
|
+
'[style.--ngp-toast-swipe-y]': 'swipeAmount().y',
|
|
468
|
+
},
|
|
233
469
|
}]
|
|
234
|
-
}]
|
|
470
|
+
}], ctorParameters: () => [], propDecorators: { onPointerDown: [{
|
|
471
|
+
type: HostListener,
|
|
472
|
+
args: ['pointerdown', ['$event']]
|
|
473
|
+
}], onPointerMove: [{
|
|
474
|
+
type: HostListener,
|
|
475
|
+
args: ['pointermove', ['$event']]
|
|
476
|
+
}], onPointerUp: [{
|
|
477
|
+
type: HostListener,
|
|
478
|
+
args: ['pointerup']
|
|
479
|
+
}] } });
|
|
235
480
|
|
|
236
481
|
/**
|
|
237
482
|
* Generated bundle index. Do not edit.
|
|
238
483
|
*/
|
|
239
484
|
|
|
240
|
-
export { NgpToast,
|
|
485
|
+
export { NgpToast, NgpToastManager, injectToastContext, provideToastConfig };
|
|
241
486
|
//# sourceMappingURL=ng-primitives-toast.mjs.map
|