@cdevhub/ngx-tw 0.4.0 → 0.6.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 +78 -15
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +16 -5
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +26 -15
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +17 -5
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +27 -6
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-button.mjs +23 -11
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +5 -5
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +3 -3
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +427 -122
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-card.mjs +21 -11
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +215 -96
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +108 -64
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +18 -6
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +55 -30
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs +218 -0
- package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +236 -211
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs +200 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +34 -7
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-core.mjs +135 -15
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs +203 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +160 -102
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs +209 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +182 -107
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-dialog-dialog-renderer-DoIhoV3d.mjs +198 -0
- package/fesm2022/cdevhub-ngx-tw-dialog-dialog-renderer-DoIhoV3d.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
- package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +199 -220
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
- package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-item.mjs +18 -3
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
- package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
- package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
- package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs +256 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
- package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +197 -278
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
- package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
- package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs +616 -0
- package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +556 -945
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
- package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
- package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
- package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
- package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
- package/index.json +1 -1
- package/package.json +63 -2
- package/theme/_base.css +13 -0
- package/theme/_dark.css +110 -36
- package/theme/_high-contrast-dark.css +367 -0
- package/theme/_high-contrast.css +13 -2
- package/theme/_light.css +276 -0
- package/theme/_semantic.css +47 -12
- package/theme/_typography.css +17 -0
- package/theme/index.css +86 -7
- package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
- package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
- package/types/cdevhub-ngx-tw-button.d.ts +1 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
- package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
- package/types/cdevhub-ngx-tw-card.d.ts +11 -3
- package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
- package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
- package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
- package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
- package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
- package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
- package/types/cdevhub-ngx-tw-core.d.ts +152 -21
- package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
- package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
- package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +89 -19
- package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
- package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
- package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
- package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
- package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
- package/types/cdevhub-ngx-tw-input.d.ts +5 -8
- package/types/cdevhub-ngx-tw-item.d.ts +2 -0
- package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
- package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
- package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
- package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
- package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
- package/types/cdevhub-ngx-tw-select.d.ts +104 -33
- package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +96 -21
- package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
- package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
- package/types/cdevhub-ngx-tw-split.d.ts +32 -2
- package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
- package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
- package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
- package/types/cdevhub-ngx-tw-table.d.ts +53 -10
- package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
- package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
- package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
- package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
- package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
- package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
- package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
- package/types/cdevhub-ngx-tw-toast.d.ts +61 -12
- package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
- package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
- package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
- package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { signal, inject, Injector, ElementRef, DestroyRef, computed, TemplateRef, untracked, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { Overlay, createGlobalPositionStrategy } from '@angular/cdk/overlay';
|
|
4
|
+
import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
|
|
5
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
6
|
+
import { LiveAnnouncer } from '@angular/cdk/a11y';
|
|
7
|
+
import { TW_TOAST_REF, TW_TOAST_DATA, ToastComponent, ToastActionDirective } from './cdevhub-ngx-tw-toast.mjs';
|
|
8
|
+
|
|
9
|
+
const POSITION_AXIS = {
|
|
10
|
+
'top-right': 'right',
|
|
11
|
+
'bottom-right': 'right',
|
|
12
|
+
'top-left': 'left',
|
|
13
|
+
'bottom-left': 'left',
|
|
14
|
+
'top-center': 'top',
|
|
15
|
+
'bottom-center': 'bottom',
|
|
16
|
+
};
|
|
17
|
+
const POSITION_HOST_CLASSES = {
|
|
18
|
+
'top-right': 'items-end',
|
|
19
|
+
'bottom-right': 'items-end',
|
|
20
|
+
'top-left': 'items-start',
|
|
21
|
+
'bottom-left': 'items-start',
|
|
22
|
+
'top-center': 'items-center',
|
|
23
|
+
'bottom-center': 'items-center',
|
|
24
|
+
};
|
|
25
|
+
const POSITION_ORDER_REVERSED = {
|
|
26
|
+
'top-right': false,
|
|
27
|
+
'top-left': false,
|
|
28
|
+
'top-center': false,
|
|
29
|
+
'bottom-right': true,
|
|
30
|
+
'bottom-left': true,
|
|
31
|
+
'bottom-center': true,
|
|
32
|
+
};
|
|
33
|
+
const SWIPE_DISMISS_FRACTION = 0.4;
|
|
34
|
+
const SWIPE_MAX_OPACITY_FADE = 0.6;
|
|
35
|
+
/**
|
|
36
|
+
* Internal flex-column host rendered inside each per-position CDK overlay.
|
|
37
|
+
* Stacks visible toasts via `@for`, wires pause-on-interaction, Escape
|
|
38
|
+
* dismissal, swipe gestures, and `LiveAnnouncer` announcements.
|
|
39
|
+
*
|
|
40
|
+
* @docs-private
|
|
41
|
+
*/
|
|
42
|
+
class ToastContainerComponent {
|
|
43
|
+
/** Per-position stacking anchor. Mutated from {@link ToastService} via `.instance.position.set(...)`. */
|
|
44
|
+
position = signal('bottom-right', /* @ts-ignore */
|
|
45
|
+
...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
46
|
+
/** Accessible label applied to the `role="region"` host. Mutated from the service on init. */
|
|
47
|
+
regionLabel = signal('Notifications', /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "regionLabel" }] : /* istanbul ignore next */ []));
|
|
49
|
+
/**
|
|
50
|
+
* Toasts currently assigned to this container. The service pushes toasts
|
|
51
|
+
* into this signal; the container filters by state to drive `animate.leave`.
|
|
52
|
+
*/
|
|
53
|
+
visibleRefs = signal([], /* @ts-ignore */
|
|
54
|
+
...(ngDevMode ? [{ debugName: "visibleRefs" }] : /* istanbul ignore next */ []));
|
|
55
|
+
injector = inject(Injector);
|
|
56
|
+
liveAnnouncer = inject(LiveAnnouncer);
|
|
57
|
+
host = inject((ElementRef));
|
|
58
|
+
/**
|
|
59
|
+
* Teardown for every swipe currently in flight.
|
|
60
|
+
*
|
|
61
|
+
* `swipeSessions` is a WeakMap and the listeners are bound to the toast
|
|
62
|
+
* element, so neither outlives the DOM — but a swipe interrupted by destroy
|
|
63
|
+
* (the toast auto-dismisses mid-drag) would otherwise leave the pointer
|
|
64
|
+
* capture unreleased. Each entry removes its listeners and releases capture.
|
|
65
|
+
*/
|
|
66
|
+
activeSwipeTeardowns = new Set();
|
|
67
|
+
/** Per-toast handle into {@link activeSwipeTeardowns}. */
|
|
68
|
+
swipeTeardowns = new WeakMap();
|
|
69
|
+
swipeSessions = new WeakMap();
|
|
70
|
+
constructor() {
|
|
71
|
+
// A toast can auto-dismiss (or the whole container can be torn down) while
|
|
72
|
+
// a drag is still in flight, in which case `onSwipeEnd` never runs.
|
|
73
|
+
inject(DestroyRef).onDestroy(() => {
|
|
74
|
+
for (const teardown of [...this.activeSwipeTeardowns])
|
|
75
|
+
teardown();
|
|
76
|
+
this.activeSwipeTeardowns.clear();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
entries = computed(() => {
|
|
80
|
+
const pos = this.position();
|
|
81
|
+
const axis = POSITION_AXIS[pos];
|
|
82
|
+
const enter = `toast-enter-${axis}`;
|
|
83
|
+
const leave = `toast-leave-${axis}`;
|
|
84
|
+
const refs = this.visibleRefs().filter((ref) => {
|
|
85
|
+
const s = ref.state();
|
|
86
|
+
return s === 'entering' || s === 'visible' || s === 'paused';
|
|
87
|
+
});
|
|
88
|
+
return refs.map((ref) => ({
|
|
89
|
+
ref,
|
|
90
|
+
kind: resolveKind(ref.content()),
|
|
91
|
+
enterClass: enter,
|
|
92
|
+
leaveClass: leave,
|
|
93
|
+
}));
|
|
94
|
+
}, /* @ts-ignore */
|
|
95
|
+
...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
96
|
+
orderedEntries = computed(() => {
|
|
97
|
+
const list = this.entries();
|
|
98
|
+
return POSITION_ORDER_REVERSED[this.position()] ? [...list].reverse() : list;
|
|
99
|
+
}, /* @ts-ignore */
|
|
100
|
+
...(ngDevMode ? [{ debugName: "orderedEntries" }] : /* istanbul ignore next */ []));
|
|
101
|
+
hostClasses = computed(() => {
|
|
102
|
+
const base = 'flex flex-col gap-2 w-full max-w-sm';
|
|
103
|
+
const align = POSITION_HOST_CLASSES[this.position()];
|
|
104
|
+
return `${base} ${align}`;
|
|
105
|
+
}, /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
107
|
+
/** @internal Called by the service right after attaching a new toast. Runs `LiveAnnouncer`. */
|
|
108
|
+
_announceOpen(ref) {
|
|
109
|
+
const politeness = this.resolvePoliteness(ref);
|
|
110
|
+
if (politeness === 'off')
|
|
111
|
+
return;
|
|
112
|
+
const msg = this.resolveAnnouncementText(ref);
|
|
113
|
+
if (msg)
|
|
114
|
+
this.liveAnnouncer.announce(msg, politeness);
|
|
115
|
+
}
|
|
116
|
+
/** @internal Called by the service when `ref.update()` fires — re-announces with the new severity / content. */
|
|
117
|
+
_announceUpdate(ref) {
|
|
118
|
+
this._announceOpen(ref);
|
|
119
|
+
}
|
|
120
|
+
/** @internal Captures the component instance after `cdkPortalOutlet` attaches. */
|
|
121
|
+
onPortalAttached(ref, attached) {
|
|
122
|
+
if (attached && 'instance' in attached) {
|
|
123
|
+
ref.componentInstance = attached.instance;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** @internal Injector factory for component-class content. Provides `TW_TOAST_DATA` + `TW_TOAST_REF`. */
|
|
127
|
+
_createContentInjector(ref) {
|
|
128
|
+
return Injector.create({
|
|
129
|
+
parent: this.injector,
|
|
130
|
+
providers: [
|
|
131
|
+
{ provide: TW_TOAST_REF, useValue: ref },
|
|
132
|
+
{ provide: TW_TOAST_DATA, useValue: ref.data() },
|
|
133
|
+
],
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// ── Template helpers ──
|
|
137
|
+
asString(content) {
|
|
138
|
+
return typeof content === 'string' ? content : '';
|
|
139
|
+
}
|
|
140
|
+
asTemplate(content) {
|
|
141
|
+
return content instanceof TemplateRef ? content : null;
|
|
142
|
+
}
|
|
143
|
+
templateContext(ref) {
|
|
144
|
+
return { $implicit: ref.data(), ref };
|
|
145
|
+
}
|
|
146
|
+
panelClassFor(ref) {
|
|
147
|
+
const raw = ref.config.panelClass;
|
|
148
|
+
if (!raw)
|
|
149
|
+
return '';
|
|
150
|
+
return Array.isArray(raw) ? raw.join(' ') : raw;
|
|
151
|
+
}
|
|
152
|
+
// ── Interaction handlers ──
|
|
153
|
+
onPointerEnter(ref) {
|
|
154
|
+
ref._setHovered(true);
|
|
155
|
+
}
|
|
156
|
+
onPointerLeave(ref) {
|
|
157
|
+
ref._setHovered(false);
|
|
158
|
+
}
|
|
159
|
+
onFocusIn(ref) {
|
|
160
|
+
ref._setFocused(true);
|
|
161
|
+
}
|
|
162
|
+
onFocusOut(ref, event) {
|
|
163
|
+
const related = event.relatedTarget;
|
|
164
|
+
const target = event.currentTarget;
|
|
165
|
+
if (related && target && target.contains(related))
|
|
166
|
+
return;
|
|
167
|
+
ref._setFocused(false);
|
|
168
|
+
}
|
|
169
|
+
onEscape(ref, event) {
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
ref._dismissWith('manual');
|
|
173
|
+
}
|
|
174
|
+
onSwipeStart(ref, event, el) {
|
|
175
|
+
if (!ref.config.swipeToDismiss)
|
|
176
|
+
return;
|
|
177
|
+
if (event.button !== 0)
|
|
178
|
+
return;
|
|
179
|
+
const target = event.target;
|
|
180
|
+
if (target?.closest('button, a, input, select, textarea, [role="button"]'))
|
|
181
|
+
return;
|
|
182
|
+
const width = el.getBoundingClientRect().width;
|
|
183
|
+
this.swipeSessions.set(ref, {
|
|
184
|
+
pointerId: event.pointerId,
|
|
185
|
+
startX: event.clientX,
|
|
186
|
+
width,
|
|
187
|
+
active: false,
|
|
188
|
+
});
|
|
189
|
+
try {
|
|
190
|
+
el.setPointerCapture(event.pointerId);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// setPointerCapture throws when the pointerId is no longer active, and
|
|
194
|
+
// is absent entirely in jsdom. The swipe still tracks via the listeners
|
|
195
|
+
// below — capture is an enhancement, not a prerequisite. Matches the
|
|
196
|
+
// guards already on both `releasePointerCapture` call sites.
|
|
197
|
+
}
|
|
198
|
+
const onMove = (e) => this.onSwipeMove(ref, e, el);
|
|
199
|
+
const onUp = (e) => this.onSwipeEnd(ref, e, el, onMove, onUp);
|
|
200
|
+
el.addEventListener('pointermove', onMove);
|
|
201
|
+
el.addEventListener('pointerup', onUp);
|
|
202
|
+
el.addEventListener('pointercancel', onUp);
|
|
203
|
+
const teardown = () => {
|
|
204
|
+
el.removeEventListener('pointermove', onMove);
|
|
205
|
+
el.removeEventListener('pointerup', onUp);
|
|
206
|
+
el.removeEventListener('pointercancel', onUp);
|
|
207
|
+
try {
|
|
208
|
+
el.releasePointerCapture(event.pointerId);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
/* capture already released, or the element is detached */
|
|
212
|
+
}
|
|
213
|
+
this.swipeSessions.delete(ref);
|
|
214
|
+
this.activeSwipeTeardowns.delete(teardown);
|
|
215
|
+
};
|
|
216
|
+
this.swipeTeardowns.set(ref, teardown);
|
|
217
|
+
this.activeSwipeTeardowns.add(teardown);
|
|
218
|
+
}
|
|
219
|
+
onSwipeMove(ref, event, _el) {
|
|
220
|
+
const session = this.swipeSessions.get(ref);
|
|
221
|
+
if (!session || event.pointerId !== session.pointerId)
|
|
222
|
+
return;
|
|
223
|
+
const dx = event.clientX - session.startX;
|
|
224
|
+
if (!session.active && Math.abs(dx) < 6)
|
|
225
|
+
return;
|
|
226
|
+
session.active = true;
|
|
227
|
+
ref.swipeTransform.set(`translate3d(${dx}px, 0, 0)`);
|
|
228
|
+
const fade = 1 - Math.min(Math.abs(dx) / session.width, 1) * SWIPE_MAX_OPACITY_FADE;
|
|
229
|
+
ref.swipeOpacity.set(fade);
|
|
230
|
+
}
|
|
231
|
+
onSwipeEnd(ref, event, el, onMove, onUp) {
|
|
232
|
+
const session = this.swipeSessions.get(ref);
|
|
233
|
+
el.removeEventListener('pointermove', onMove);
|
|
234
|
+
el.removeEventListener('pointerup', onUp);
|
|
235
|
+
el.removeEventListener('pointercancel', onUp);
|
|
236
|
+
const teardown = this.swipeTeardowns.get(ref);
|
|
237
|
+
if (teardown) {
|
|
238
|
+
this.activeSwipeTeardowns.delete(teardown);
|
|
239
|
+
this.swipeTeardowns.delete(ref);
|
|
240
|
+
}
|
|
241
|
+
if (!session || event.pointerId !== session.pointerId)
|
|
242
|
+
return;
|
|
243
|
+
try {
|
|
244
|
+
el.releasePointerCapture(session.pointerId);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
/* no-op */
|
|
248
|
+
}
|
|
249
|
+
this.swipeSessions.delete(ref);
|
|
250
|
+
if (!session.active)
|
|
251
|
+
return;
|
|
252
|
+
const dx = event.clientX - session.startX;
|
|
253
|
+
const threshold = session.width * SWIPE_DISMISS_FRACTION;
|
|
254
|
+
const allowed = this.swipeDirectionAllowed(dx);
|
|
255
|
+
if (Math.abs(dx) >= threshold && allowed) {
|
|
256
|
+
untracked(() => {
|
|
257
|
+
ref.swipeTransform.set(`translate3d(${Math.sign(dx) * session.width * 1.2}px, 0, 0)`);
|
|
258
|
+
ref.swipeOpacity.set(0);
|
|
259
|
+
ref.leaveAnimationOverride.set('fade-out');
|
|
260
|
+
});
|
|
261
|
+
ref._dismissWith('swipe');
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
ref.swipeTransform.set(null);
|
|
265
|
+
ref.swipeOpacity.set(null);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
swipeDirectionAllowed(dx) {
|
|
269
|
+
const pos = this.position();
|
|
270
|
+
if (pos === 'top-right' || pos === 'bottom-right')
|
|
271
|
+
return dx > 0;
|
|
272
|
+
if (pos === 'top-left' || pos === 'bottom-left')
|
|
273
|
+
return dx < 0;
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
resolvePoliteness(ref) {
|
|
277
|
+
if (ref.config.politeness)
|
|
278
|
+
return ref.config.politeness;
|
|
279
|
+
return ref.severity() === 'error' ? 'assertive' : 'polite';
|
|
280
|
+
}
|
|
281
|
+
resolveAnnouncementText(ref) {
|
|
282
|
+
const explicit = ref.ariaLabel();
|
|
283
|
+
if (explicit)
|
|
284
|
+
return explicit;
|
|
285
|
+
const content = ref.content();
|
|
286
|
+
if (typeof content === 'string')
|
|
287
|
+
return content;
|
|
288
|
+
const host = this.host.nativeElement.querySelector(`[data-toast-id="${ref.id}"]`);
|
|
289
|
+
if (host)
|
|
290
|
+
return host.textContent?.trim() ?? '';
|
|
291
|
+
return this.host.nativeElement.textContent?.trim() ?? '';
|
|
292
|
+
}
|
|
293
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: ToastContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
294
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: ToastContainerComponent, isStandalone: true, selector: "tw-toast-container", host: { attributes: { "role": "region" }, properties: { "class": "hostClasses()", "attr.aria-label": "regionLabel()", "attr.data-position": "position()" } }, ngImport: i0, template: `
|
|
295
|
+
@for (entry of orderedEntries(); track entry.ref.id) {
|
|
296
|
+
<!--
|
|
297
|
+
Toast entry wrapper. Owns the keyboard and pointer contract for one toast.
|
|
298
|
+
|
|
299
|
+
Focus policy (SC 2.2.1 Timing Adjustable, SC 2.1.1 Keyboard):
|
|
300
|
+
|
|
301
|
+
- tabindex="0" makes the wrapper itself a tab stop. Auto-dismiss pauses on
|
|
302
|
+
focus-within, so without this a toast whose only affordances are opted
|
|
303
|
+
out (dismissible false, no action) has nothing focusable at all and a
|
|
304
|
+
keyboard-only user can never stop its 5s clock. Radix Toast puts the tab
|
|
305
|
+
stop on the toast root for the same reason.
|
|
306
|
+
- No focus trap. A toast is not modal; trapping would strand a user who
|
|
307
|
+
never asked for it.
|
|
308
|
+
- No autofocus. Moving focus on open would yank the caret out of whatever
|
|
309
|
+
the user was doing, which is worse than the delay it saves.
|
|
310
|
+
- No summon hotkey. Deliberately deferred: a document-level key would have
|
|
311
|
+
to pick one of six position containers, and the region landmark already
|
|
312
|
+
gives screen-reader users a direct route in.
|
|
313
|
+
- Escape dismisses the focused toast; the browser returns focus to the
|
|
314
|
+
document flow position on its own once the element is removed.
|
|
315
|
+
|
|
316
|
+
Pointer and focus events are forwarded to the ref so the container can
|
|
317
|
+
pause the auto-dismiss timer while the user is reading or interacting.
|
|
318
|
+
-->
|
|
319
|
+
<div
|
|
320
|
+
class="pointer-events-auto w-full max-w-sm rounded-lg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500"
|
|
321
|
+
tabindex="0"
|
|
322
|
+
[attr.data-toast-id]="entry.ref.id"
|
|
323
|
+
[style.transform]="entry.ref.swipeTransform() || null"
|
|
324
|
+
[style.opacity]="entry.ref.swipeOpacity() ?? null"
|
|
325
|
+
[style.touch-action]="'pan-y'"
|
|
326
|
+
[animate.enter]="entry.enterClass"
|
|
327
|
+
[animate.leave]="entry.ref.leaveAnimationOverride() ?? entry.leaveClass"
|
|
328
|
+
(pointerenter)="onPointerEnter(entry.ref)"
|
|
329
|
+
(pointerleave)="onPointerLeave(entry.ref)"
|
|
330
|
+
(focusin)="onFocusIn(entry.ref)"
|
|
331
|
+
(focusout)="onFocusOut(entry.ref, $event)"
|
|
332
|
+
(keydown.escape)="onEscape(entry.ref, $event)"
|
|
333
|
+
(pointerdown)="onSwipeStart(entry.ref, $event, swipeEl)"
|
|
334
|
+
#swipeEl
|
|
335
|
+
>
|
|
336
|
+
@switch (entry.kind) {
|
|
337
|
+
@case ('string') {
|
|
338
|
+
<tw-toast
|
|
339
|
+
[severity]="entry.ref.severity()"
|
|
340
|
+
[dismissible]="entry.ref.dismissible()"
|
|
341
|
+
[icon]="entry.ref.icon()"
|
|
342
|
+
[aria-label]="entry.ref.ariaLabel()"
|
|
343
|
+
[class]="panelClassFor(entry.ref)"
|
|
344
|
+
(dismissed)="entry.ref._dismissWith('manual')"
|
|
345
|
+
(actionClicked)="entry.ref.triggerAction()"
|
|
346
|
+
>
|
|
347
|
+
{{ asString(entry.ref.content()) }}
|
|
348
|
+
@if (entry.ref.action(); as action) {
|
|
349
|
+
<button twToastAction>{{ action.label }}</button>
|
|
350
|
+
}
|
|
351
|
+
</tw-toast>
|
|
352
|
+
}
|
|
353
|
+
@case ('template') {
|
|
354
|
+
<tw-toast
|
|
355
|
+
[severity]="entry.ref.severity()"
|
|
356
|
+
[dismissible]="entry.ref.dismissible()"
|
|
357
|
+
[icon]="entry.ref.icon()"
|
|
358
|
+
[aria-label]="entry.ref.ariaLabel()"
|
|
359
|
+
[class]="panelClassFor(entry.ref)"
|
|
360
|
+
(dismissed)="entry.ref._dismissWith('manual')"
|
|
361
|
+
(actionClicked)="entry.ref.triggerAction()"
|
|
362
|
+
>
|
|
363
|
+
<ng-container
|
|
364
|
+
[ngTemplateOutlet]="asTemplate(entry.ref.content())"
|
|
365
|
+
[ngTemplateOutletContext]="templateContext(entry.ref)"
|
|
366
|
+
/>
|
|
367
|
+
@if (entry.ref.action(); as action) {
|
|
368
|
+
<button twToastAction>{{ action.label }}</button>
|
|
369
|
+
}
|
|
370
|
+
</tw-toast>
|
|
371
|
+
}
|
|
372
|
+
@case ('component') {
|
|
373
|
+
<ng-template
|
|
374
|
+
[cdkPortalOutlet]="entry.ref._portal"
|
|
375
|
+
(attached)="onPortalAttached(entry.ref, $event)"
|
|
376
|
+
/>
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
</div>
|
|
380
|
+
}
|
|
381
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: ToastComponent, selector: "tw-toast", inputs: ["severity", "dismissible", "icon", "aria-label"], outputs: ["dismissed", "actionClicked"] }, { kind: "directive", type: ToastActionDirective, selector: "[twToastAction]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
382
|
+
}
|
|
383
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: ToastContainerComponent, decorators: [{
|
|
384
|
+
type: Component,
|
|
385
|
+
args: [{
|
|
386
|
+
selector: 'tw-toast-container',
|
|
387
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
388
|
+
encapsulation: ViewEncapsulation.None,
|
|
389
|
+
imports: [NgTemplateOutlet, CdkPortalOutlet, ToastComponent, ToastActionDirective],
|
|
390
|
+
host: {
|
|
391
|
+
role: 'region',
|
|
392
|
+
'[class]': 'hostClasses()',
|
|
393
|
+
'[attr.aria-label]': 'regionLabel()',
|
|
394
|
+
'[attr.data-position]': 'position()',
|
|
395
|
+
},
|
|
396
|
+
template: `
|
|
397
|
+
@for (entry of orderedEntries(); track entry.ref.id) {
|
|
398
|
+
<!--
|
|
399
|
+
Toast entry wrapper. Owns the keyboard and pointer contract for one toast.
|
|
400
|
+
|
|
401
|
+
Focus policy (SC 2.2.1 Timing Adjustable, SC 2.1.1 Keyboard):
|
|
402
|
+
|
|
403
|
+
- tabindex="0" makes the wrapper itself a tab stop. Auto-dismiss pauses on
|
|
404
|
+
focus-within, so without this a toast whose only affordances are opted
|
|
405
|
+
out (dismissible false, no action) has nothing focusable at all and a
|
|
406
|
+
keyboard-only user can never stop its 5s clock. Radix Toast puts the tab
|
|
407
|
+
stop on the toast root for the same reason.
|
|
408
|
+
- No focus trap. A toast is not modal; trapping would strand a user who
|
|
409
|
+
never asked for it.
|
|
410
|
+
- No autofocus. Moving focus on open would yank the caret out of whatever
|
|
411
|
+
the user was doing, which is worse than the delay it saves.
|
|
412
|
+
- No summon hotkey. Deliberately deferred: a document-level key would have
|
|
413
|
+
to pick one of six position containers, and the region landmark already
|
|
414
|
+
gives screen-reader users a direct route in.
|
|
415
|
+
- Escape dismisses the focused toast; the browser returns focus to the
|
|
416
|
+
document flow position on its own once the element is removed.
|
|
417
|
+
|
|
418
|
+
Pointer and focus events are forwarded to the ref so the container can
|
|
419
|
+
pause the auto-dismiss timer while the user is reading or interacting.
|
|
420
|
+
-->
|
|
421
|
+
<div
|
|
422
|
+
class="pointer-events-auto w-full max-w-sm rounded-lg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500"
|
|
423
|
+
tabindex="0"
|
|
424
|
+
[attr.data-toast-id]="entry.ref.id"
|
|
425
|
+
[style.transform]="entry.ref.swipeTransform() || null"
|
|
426
|
+
[style.opacity]="entry.ref.swipeOpacity() ?? null"
|
|
427
|
+
[style.touch-action]="'pan-y'"
|
|
428
|
+
[animate.enter]="entry.enterClass"
|
|
429
|
+
[animate.leave]="entry.ref.leaveAnimationOverride() ?? entry.leaveClass"
|
|
430
|
+
(pointerenter)="onPointerEnter(entry.ref)"
|
|
431
|
+
(pointerleave)="onPointerLeave(entry.ref)"
|
|
432
|
+
(focusin)="onFocusIn(entry.ref)"
|
|
433
|
+
(focusout)="onFocusOut(entry.ref, $event)"
|
|
434
|
+
(keydown.escape)="onEscape(entry.ref, $event)"
|
|
435
|
+
(pointerdown)="onSwipeStart(entry.ref, $event, swipeEl)"
|
|
436
|
+
#swipeEl
|
|
437
|
+
>
|
|
438
|
+
@switch (entry.kind) {
|
|
439
|
+
@case ('string') {
|
|
440
|
+
<tw-toast
|
|
441
|
+
[severity]="entry.ref.severity()"
|
|
442
|
+
[dismissible]="entry.ref.dismissible()"
|
|
443
|
+
[icon]="entry.ref.icon()"
|
|
444
|
+
[aria-label]="entry.ref.ariaLabel()"
|
|
445
|
+
[class]="panelClassFor(entry.ref)"
|
|
446
|
+
(dismissed)="entry.ref._dismissWith('manual')"
|
|
447
|
+
(actionClicked)="entry.ref.triggerAction()"
|
|
448
|
+
>
|
|
449
|
+
{{ asString(entry.ref.content()) }}
|
|
450
|
+
@if (entry.ref.action(); as action) {
|
|
451
|
+
<button twToastAction>{{ action.label }}</button>
|
|
452
|
+
}
|
|
453
|
+
</tw-toast>
|
|
454
|
+
}
|
|
455
|
+
@case ('template') {
|
|
456
|
+
<tw-toast
|
|
457
|
+
[severity]="entry.ref.severity()"
|
|
458
|
+
[dismissible]="entry.ref.dismissible()"
|
|
459
|
+
[icon]="entry.ref.icon()"
|
|
460
|
+
[aria-label]="entry.ref.ariaLabel()"
|
|
461
|
+
[class]="panelClassFor(entry.ref)"
|
|
462
|
+
(dismissed)="entry.ref._dismissWith('manual')"
|
|
463
|
+
(actionClicked)="entry.ref.triggerAction()"
|
|
464
|
+
>
|
|
465
|
+
<ng-container
|
|
466
|
+
[ngTemplateOutlet]="asTemplate(entry.ref.content())"
|
|
467
|
+
[ngTemplateOutletContext]="templateContext(entry.ref)"
|
|
468
|
+
/>
|
|
469
|
+
@if (entry.ref.action(); as action) {
|
|
470
|
+
<button twToastAction>{{ action.label }}</button>
|
|
471
|
+
}
|
|
472
|
+
</tw-toast>
|
|
473
|
+
}
|
|
474
|
+
@case ('component') {
|
|
475
|
+
<ng-template
|
|
476
|
+
[cdkPortalOutlet]="entry.ref._portal"
|
|
477
|
+
(attached)="onPortalAttached(entry.ref, $event)"
|
|
478
|
+
/>
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
</div>
|
|
482
|
+
}
|
|
483
|
+
`,
|
|
484
|
+
}]
|
|
485
|
+
}], ctorParameters: () => [] });
|
|
486
|
+
function resolveKind(content) {
|
|
487
|
+
if (typeof content === 'string')
|
|
488
|
+
return 'string';
|
|
489
|
+
if (content instanceof TemplateRef)
|
|
490
|
+
return 'template';
|
|
491
|
+
return 'component';
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const OVERLAY_EDGE_OFFSET = '1rem';
|
|
495
|
+
/**
|
|
496
|
+
* Rendering half of the toast feature: owns the CDK overlays, the per-position
|
|
497
|
+
* {@link ToastContainerComponent} instances, and every `LiveAnnouncer` call.
|
|
498
|
+
*
|
|
499
|
+
* This module is reached only through a dynamic `import()` in `ToastService`,
|
|
500
|
+
* which is what keeps `@angular/cdk/overlay`, the toast components, and
|
|
501
|
+
* `tailwind-variants` out of a consumer's initial bundle. Nothing here may be
|
|
502
|
+
* imported statically from `toast.ts` — a value import (as opposed to a
|
|
503
|
+
* `import type`) would pull the whole graph back into the eager chunk and
|
|
504
|
+
* silently undo the split. See `toast.spec.ts` for the guard test.
|
|
505
|
+
*
|
|
506
|
+
* @docs-private
|
|
507
|
+
*/
|
|
508
|
+
class ToastRenderer {
|
|
509
|
+
injector;
|
|
510
|
+
regionAriaLabel;
|
|
511
|
+
overlay;
|
|
512
|
+
positionOverlays = new Map();
|
|
513
|
+
disposed = false;
|
|
514
|
+
constructor(injector, regionAriaLabel) {
|
|
515
|
+
this.injector = injector;
|
|
516
|
+
this.regionAriaLabel = regionAriaLabel;
|
|
517
|
+
// Resolved here rather than injected into `ToastService`, so the `Overlay`
|
|
518
|
+
// symbol lives in this lazily-loaded chunk. It is `providedIn: 'root'`, so
|
|
519
|
+
// no eager provider is required.
|
|
520
|
+
this.overlay = injector.get(Overlay);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Render a toast into its position container, creating the overlay on first
|
|
524
|
+
* use. Returns `false` if the renderer has already been disposed.
|
|
525
|
+
*/
|
|
526
|
+
attach(ref, position) {
|
|
527
|
+
if (this.disposed)
|
|
528
|
+
return false;
|
|
529
|
+
const entry = this.getContainerForPosition(position);
|
|
530
|
+
const instance = entry.containerRef.instance;
|
|
531
|
+
// Component-class content needs an injector built from the container, so
|
|
532
|
+
// the portal cannot be created until the container exists.
|
|
533
|
+
const content = ref.content();
|
|
534
|
+
if (isComponentConstructor(content)) {
|
|
535
|
+
ref._portal = new ComponentPortal(content, null, instance._createContentInjector(ref));
|
|
536
|
+
}
|
|
537
|
+
instance.visibleRefs.update((list) => [...list, ref]);
|
|
538
|
+
ref._overlayPanelElement = entry.overlayRef.overlayElement;
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
/** Announce a freshly attached toast to assistive technology. */
|
|
542
|
+
announceOpen(ref) {
|
|
543
|
+
this.containerFor(ref)?._announceOpen(ref);
|
|
544
|
+
}
|
|
545
|
+
/** Re-announce a toast whose content or severity changed via `update()`. */
|
|
546
|
+
announceUpdate(ref) {
|
|
547
|
+
this.containerFor(ref)?._announceUpdate(ref);
|
|
548
|
+
}
|
|
549
|
+
/** Drop a dismissed toast from its container's render list. */
|
|
550
|
+
detach(ref) {
|
|
551
|
+
const instance = this.containerFor(ref);
|
|
552
|
+
if (!instance)
|
|
553
|
+
return;
|
|
554
|
+
instance.visibleRefs.update((list) => list.filter((r) => r !== ref));
|
|
555
|
+
}
|
|
556
|
+
/** Tear down every overlay this renderer created. */
|
|
557
|
+
dispose() {
|
|
558
|
+
this.disposed = true;
|
|
559
|
+
for (const { overlayRef } of this.positionOverlays.values()) {
|
|
560
|
+
overlayRef.dispose();
|
|
561
|
+
}
|
|
562
|
+
this.positionOverlays.clear();
|
|
563
|
+
}
|
|
564
|
+
containerFor(ref) {
|
|
565
|
+
return this.positionOverlays.get(ref.config.position ?? 'bottom-right')?.containerRef
|
|
566
|
+
.instance;
|
|
567
|
+
}
|
|
568
|
+
getContainerForPosition(position) {
|
|
569
|
+
const existing = this.positionOverlays.get(position);
|
|
570
|
+
if (existing)
|
|
571
|
+
return existing;
|
|
572
|
+
const overlayRef = this.overlay.create({
|
|
573
|
+
positionStrategy: this.buildPositionStrategy(position),
|
|
574
|
+
scrollStrategy: this.overlay.scrollStrategies.noop(),
|
|
575
|
+
hasBackdrop: false,
|
|
576
|
+
panelClass: ['tw-toast-overlay', `tw-toast-overlay-${position}`],
|
|
577
|
+
});
|
|
578
|
+
const containerPortal = new ComponentPortal(ToastContainerComponent, null, this.injector);
|
|
579
|
+
const containerRef = overlayRef.attach(containerPortal);
|
|
580
|
+
containerRef.instance.position.set(position);
|
|
581
|
+
containerRef.instance.regionLabel.set(this.regionAriaLabel);
|
|
582
|
+
const entry = { overlayRef, containerRef };
|
|
583
|
+
this.positionOverlays.set(position, entry);
|
|
584
|
+
return entry;
|
|
585
|
+
}
|
|
586
|
+
buildPositionStrategy(position) {
|
|
587
|
+
const strategy = createGlobalPositionStrategy(this.injector);
|
|
588
|
+
switch (position) {
|
|
589
|
+
case 'top-right':
|
|
590
|
+
strategy.top(OVERLAY_EDGE_OFFSET).right(OVERLAY_EDGE_OFFSET);
|
|
591
|
+
break;
|
|
592
|
+
case 'top-left':
|
|
593
|
+
strategy.top(OVERLAY_EDGE_OFFSET).left(OVERLAY_EDGE_OFFSET);
|
|
594
|
+
break;
|
|
595
|
+
case 'top-center':
|
|
596
|
+
strategy.top(OVERLAY_EDGE_OFFSET).centerHorizontally();
|
|
597
|
+
break;
|
|
598
|
+
case 'bottom-right':
|
|
599
|
+
strategy.bottom(OVERLAY_EDGE_OFFSET).right(OVERLAY_EDGE_OFFSET);
|
|
600
|
+
break;
|
|
601
|
+
case 'bottom-left':
|
|
602
|
+
strategy.bottom(OVERLAY_EDGE_OFFSET).left(OVERLAY_EDGE_OFFSET);
|
|
603
|
+
break;
|
|
604
|
+
case 'bottom-center':
|
|
605
|
+
strategy.bottom(OVERLAY_EDGE_OFFSET).centerHorizontally();
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
return strategy;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function isComponentConstructor(value) {
|
|
612
|
+
return typeof value === 'function' && !(value instanceof TemplateRef);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export { ToastRenderer };
|
|
616
|
+
//# sourceMappingURL=cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map
|