@cdevhub/ngx-tw 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +108 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs +240 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +287 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +332 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +452 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs +227 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +417 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +232 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +4482 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs +157 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +1454 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +593 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +228 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +510 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +1721 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +721 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs +861 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +1558 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +1833 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +693 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +331 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +289 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +572 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs +36 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +278 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs +355 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs +241 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +433 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +691 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +678 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +335 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +708 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +319 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +1722 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs +115 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +768 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +1147 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +346 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs +186 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs +1082 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +579 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +443 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +391 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +315 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs +994 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +508 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +147 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +178 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +1344 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +1136 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +1311 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +533 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw.mjs +61 -0
- package/fesm2022/cdevhub-ngx-tw.mjs.map +1 -0
- package/package.json +265 -0
- package/theme/_base.css +394 -0
- package/theme/_dark.css +510 -0
- package/theme/_high-contrast.css +290 -0
- package/theme/_semantic.css +317 -0
- package/theme/_typography.css +9 -0
- package/theme/index.css +54 -0
- package/types/cdevhub-ngx-tw-accordion.d.ts +48 -0
- package/types/cdevhub-ngx-tw-alert.d.ts +67 -0
- package/types/cdevhub-ngx-tw-avatar.d.ts +79 -0
- package/types/cdevhub-ngx-tw-badge.d.ts +94 -0
- package/types/cdevhub-ngx-tw-breadcrumbs.d.ts +227 -0
- package/types/cdevhub-ngx-tw-button.d.ts +74 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +200 -0
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +113 -0
- package/types/cdevhub-ngx-tw-calendar.d.ts +1704 -0
- package/types/cdevhub-ngx-tw-card.d.ts +48 -0
- package/types/cdevhub-ngx-tw-carousel.d.ts +406 -0
- package/types/cdevhub-ngx-tw-checkbox.d.ts +136 -0
- package/types/cdevhub-ngx-tw-code-block.d.ts +93 -0
- package/types/cdevhub-ngx-tw-collapsible.d.ts +151 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +389 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +215 -0
- package/types/cdevhub-ngx-tw-core.d.ts +719 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +317 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +313 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +303 -0
- package/types/cdevhub-ngx-tw-empty-state.d.ts +104 -0
- package/types/cdevhub-ngx-tw-flip-card.d.ts +95 -0
- package/types/cdevhub-ngx-tw-form-field.d.ts +210 -0
- package/types/cdevhub-ngx-tw-icon-lucide.d.ts +29 -0
- package/types/cdevhub-ngx-tw-icon.d.ts +134 -0
- package/types/cdevhub-ngx-tw-input.d.ts +110 -0
- package/types/cdevhub-ngx-tw-item.d.ts +70 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +99 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +305 -0
- package/types/cdevhub-ngx-tw-popover.d.ts +157 -0
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +100 -0
- package/types/cdevhub-ngx-tw-radio.d.ts +174 -0
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +66 -0
- package/types/cdevhub-ngx-tw-select.d.ts +384 -0
- package/types/cdevhub-ngx-tw-separator.d.ts +28 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +335 -0
- package/types/cdevhub-ngx-tw-skeleton.d.ts +52 -0
- package/types/cdevhub-ngx-tw-slider.d.ts +258 -0
- package/types/cdevhub-ngx-tw-sort.d.ts +136 -0
- package/types/cdevhub-ngx-tw-spinner.d.ts +50 -0
- package/types/cdevhub-ngx-tw-split.d.ts +265 -0
- package/types/cdevhub-ngx-tw-stat.d.ts +140 -0
- package/types/cdevhub-ngx-tw-stepper.d.ts +127 -0
- package/types/cdevhub-ngx-tw-switch.d.ts +88 -0
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +107 -0
- package/types/cdevhub-ngx-tw-table.d.ts +479 -0
- package/types/cdevhub-ngx-tw-tabs.d.ts +121 -0
- package/types/cdevhub-ngx-tw-textarea.d.ts +62 -0
- package/types/cdevhub-ngx-tw-theme.d.ts +106 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +303 -0
- package/types/cdevhub-ngx-tw-timeline.d.ts +278 -0
- package/types/cdevhub-ngx-tw-toast.d.ts +388 -0
- package/types/cdevhub-ngx-tw-tooltip.d.ts +95 -0
- package/types/cdevhub-ngx-tw.d.ts +49 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, signal, Injector, Injectable, makeEnvironmentProviders, Directive, input } from '@angular/core';
|
|
3
|
+
import { DialogConfig, CdkDialogContainer, Dialog } from '@angular/cdk/dialog';
|
|
4
|
+
import { createBlockScrollStrategy, createNoopScrollStrategy, createRepositionScrollStrategy, createCloseScrollStrategy } from '@angular/cdk/overlay';
|
|
5
|
+
import { ReplaySubject, filter, take, merge, Subject, defer, startWith } from 'rxjs';
|
|
6
|
+
import { CdkPortalOutlet } from '@angular/cdk/portal';
|
|
7
|
+
import { OverlayContainerCoordinator, mergeOverlayPanelClass, coerceOverlayDuration } from '@cdevhub/ngx-tw/core';
|
|
8
|
+
import { tv } from 'tailwind-variants';
|
|
9
|
+
import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
|
|
10
|
+
import { _IdGenerator } from '@angular/cdk/a11y';
|
|
11
|
+
import * as i1 from '@angular/cdk/scrolling';
|
|
12
|
+
import { CdkScrollable } from '@angular/cdk/scrolling';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for opening a dialog with {@link TwDialog.open}.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors the shape of `@angular/cdk/dialog`'s `DialogConfig` with additional
|
|
18
|
+
* tailwind-focused options (`size`, `enterAnimationDuration`, etc.).
|
|
19
|
+
*/
|
|
20
|
+
class TwDialogConfig extends DialogConfig {
|
|
21
|
+
/** Preset size for the dialog panel. Ignored when `width`/`maxWidth` are provided. Defaults to `'md'`. */
|
|
22
|
+
size = 'md';
|
|
23
|
+
/** Duration of the open animation in ms. Defaults to `150`. Use `0` to disable. */
|
|
24
|
+
enterAnimationDuration = 150;
|
|
25
|
+
/**
|
|
26
|
+
* Duration of the close animation in ms. Defaults to `120`. Use `0` to disable.
|
|
27
|
+
* Asymmetric with `enterAnimationDuration` by design: the close transition is
|
|
28
|
+
* slightly faster so the dialog feels dismissive rather than reluctant.
|
|
29
|
+
*/
|
|
30
|
+
exitAnimationDuration = 120;
|
|
31
|
+
/** Scroll strategy preset applied when `scrollStrategy` isn't set. Defaults to `'block'`. */
|
|
32
|
+
scrollBehavior = 'block';
|
|
33
|
+
/** Maximum width. Number values are treated as pixels. Defaults to `'calc(100vw - 32px)'`. */
|
|
34
|
+
maxWidth = 'calc(100vw - 32px)';
|
|
35
|
+
/**
|
|
36
|
+
* Whether the dialog is modal — sets `aria-modal` on the container.
|
|
37
|
+
* Defaults to `true`: a `role="dialog"` opened over the page is modal by
|
|
38
|
+
* default and the rest of the document is inert while it is open. Set to
|
|
39
|
+
* `false` only for non-modal surfaces (rare; consider `popover` instead).
|
|
40
|
+
*/
|
|
41
|
+
ariaModal = true;
|
|
42
|
+
/** Whether Escape and backdrop clicks are blocked. Defaults to `false`. */
|
|
43
|
+
disableClose = false;
|
|
44
|
+
/** Where to move focus when the dialog opens. Defaults to `'first-tabbable'`. */
|
|
45
|
+
autoFocus = 'first-tabbable';
|
|
46
|
+
/** Whether to restore focus to the previously focused element after close. Defaults to `true`. */
|
|
47
|
+
restoreFocus = true;
|
|
48
|
+
/** Whether the dialog renders a backdrop. Defaults to `true`. */
|
|
49
|
+
hasBackdrop = true;
|
|
50
|
+
/** Whether navigation (e.g. router) closes the dialog. Defaults to `true`. */
|
|
51
|
+
closeOnNavigation = true;
|
|
52
|
+
}
|
|
53
|
+
/** Injection token carrying the `data` value passed via {@link TwDialogConfig.data}. */
|
|
54
|
+
const TW_DIALOG_DATA = new InjectionToken('TW_DIALOG_DATA');
|
|
55
|
+
/** Injection token for application-wide default dialog options. Set via `provideTwDialog()`. */
|
|
56
|
+
const TW_DIALOG_DEFAULT_OPTIONS = new InjectionToken('TW_DIALOG_DEFAULT_OPTIONS');
|
|
57
|
+
|
|
58
|
+
// Dialog uses a `data-[state]` driven CSS transition rather than the project's
|
|
59
|
+
// `animate.enter`/`animate.leave` keyframes. Justification:
|
|
60
|
+
// 1. The ref owns the open/close lifecycle (state signal, observables) and
|
|
61
|
+
// needs runtime-configurable enter/exit durations per `TwDialog.open()`
|
|
62
|
+
// call — `animate.enter` only accepts a static class name.
|
|
63
|
+
// 2. The container drives the same CSS variables (opacity + scale) for both
|
|
64
|
+
// transitions, so a single `transition-[opacity,transform]` rule with a
|
|
65
|
+
// data-state attribute is simpler than two keyframe declarations.
|
|
66
|
+
// All other library overlays (popover, menu, tooltip) use animate.enter/leave;
|
|
67
|
+
// this divergence is intentional and isolated to the dialog container.
|
|
68
|
+
const dialogContainerVariants = tv({
|
|
69
|
+
slots: {
|
|
70
|
+
host: 'relative flex flex-col outline-none bg-surface-raised text-fg rounded-lg shadow-md border border-border overflow-hidden transition-[opacity,transform] ease-out motion-reduce:transition-none opacity-0 scale-95 data-[state=open]:opacity-100 data-[state=open]:scale-100 data-[state=closing]:opacity-0 data-[state=closing]:scale-95',
|
|
71
|
+
},
|
|
72
|
+
variants: {
|
|
73
|
+
size: {
|
|
74
|
+
xs: { host: 'w-full max-w-sm max-h-[85vh]' },
|
|
75
|
+
sm: { host: 'w-full max-w-md max-h-[85vh]' },
|
|
76
|
+
md: { host: 'w-full max-w-lg max-h-[85vh]' },
|
|
77
|
+
lg: { host: 'w-full max-w-2xl max-h-[85vh]' },
|
|
78
|
+
xl: { host: 'w-full max-w-4xl max-h-[85vh]' },
|
|
79
|
+
fullscreen: {
|
|
80
|
+
host: 'w-screen h-screen max-w-none max-h-none rounded-none border-0',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
defaultVariants: { size: 'md' },
|
|
85
|
+
}, { twMerge: true });
|
|
86
|
+
/**
|
|
87
|
+
* Dialog container rendered inside the CDK overlay. Wraps the user-provided
|
|
88
|
+
* content in a Tailwind-styled surface and coordinates enter/exit transitions
|
|
89
|
+
* with {@link TwDialogRef}.
|
|
90
|
+
*
|
|
91
|
+
* The animation state machine, `aria-describedby` queue, and panel-class
|
|
92
|
+
* merge are shared with `SheetContainer` via {@link OverlayContainerCoordinator}
|
|
93
|
+
* (component-scoped — see `providers: [OverlayContainerCoordinator]`).
|
|
94
|
+
*
|
|
95
|
+
* @docs-private
|
|
96
|
+
*/
|
|
97
|
+
class DialogContainer extends CdkDialogContainer {
|
|
98
|
+
coordinator = inject(OverlayContainerCoordinator);
|
|
99
|
+
/** Lifecycle state of the dialog's enter/exit animation. */
|
|
100
|
+
state = this.coordinator.state;
|
|
101
|
+
/** Emits whenever the animation state transitions. */
|
|
102
|
+
animationStateChanged = this.coordinator.animationStateChanged;
|
|
103
|
+
/** Resolved enter-animation duration in ms. */
|
|
104
|
+
enterAnimationDuration;
|
|
105
|
+
/** Resolved exit-animation duration in ms. */
|
|
106
|
+
exitAnimationDuration;
|
|
107
|
+
sizeVariant = computed(() => {
|
|
108
|
+
const size = this._config.size ?? 'md';
|
|
109
|
+
return dialogContainerVariants({ size });
|
|
110
|
+
}, ...(ngDevMode ? [{ debugName: "sizeVariant" }] : /* istanbul ignore next */ []));
|
|
111
|
+
hostClasses = computed(() => mergeOverlayPanelClass(this.sizeVariant().host(), this._config.panelClass), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
112
|
+
ariaDescribedByAttr = computed(() => this._config.ariaDescribedBy ||
|
|
113
|
+
this.coordinator.describedByIds()[0] ||
|
|
114
|
+
null, ...(ngDevMode ? [{ debugName: "ariaDescribedByAttr" }] : /* istanbul ignore next */ []));
|
|
115
|
+
transitionDuration = this.coordinator.transitionDuration;
|
|
116
|
+
constructor() {
|
|
117
|
+
super();
|
|
118
|
+
this.enterAnimationDuration = coerceOverlayDuration(this._config.enterAnimationDuration, 150);
|
|
119
|
+
this.exitAnimationDuration = coerceOverlayDuration(this._config.exitAnimationDuration, 120);
|
|
120
|
+
this.coordinator.setDurations(this.enterAnimationDuration, this.exitAnimationDuration);
|
|
121
|
+
}
|
|
122
|
+
_contentAttached() {
|
|
123
|
+
super._contentAttached();
|
|
124
|
+
this.coordinator.startEnterAnimation();
|
|
125
|
+
}
|
|
126
|
+
/** Triggered by the dialog ref to play the exit transition before disposing the overlay. */
|
|
127
|
+
_startExitAnimation() {
|
|
128
|
+
this.coordinator.startExitAnimation();
|
|
129
|
+
}
|
|
130
|
+
/** Registers a description id for the container's `aria-describedby`. */
|
|
131
|
+
_addAriaDescribedBy(id) {
|
|
132
|
+
this.coordinator.addAriaDescribedBy(id);
|
|
133
|
+
}
|
|
134
|
+
/** Removes a previously registered description id. */
|
|
135
|
+
_removeAriaDescribedBy(id) {
|
|
136
|
+
this.coordinator.removeAriaDescribedBy(id);
|
|
137
|
+
}
|
|
138
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
139
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: DialogContainer, isStandalone: true, selector: "tw-dialog-container", host: { attributes: { "tabindex": "-1" }, properties: { "attr.id": "_config.id || null", "attr.role": "_config.role", "attr.aria-modal": "_config.ariaModal", "attr.aria-labelledby": "_config.ariaLabel ? null : _ariaLabelledByQueue[0]", "attr.aria-label": "_config.ariaLabel", "attr.aria-describedby": "ariaDescribedByAttr()", "attr.data-state": "state()", "class": "hostClasses()", "style.transition-duration.ms": "transitionDuration()" } }, providers: [OverlayContainerCoordinator], usesInheritance: true, ngImport: i0, template: '<ng-template cdkPortalOutlet />', isInline: true, dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
140
|
+
}
|
|
141
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogContainer, decorators: [{
|
|
142
|
+
type: Component,
|
|
143
|
+
args: [{
|
|
144
|
+
selector: 'tw-dialog-container',
|
|
145
|
+
template: '<ng-template cdkPortalOutlet />',
|
|
146
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
147
|
+
encapsulation: ViewEncapsulation.None,
|
|
148
|
+
imports: [CdkPortalOutlet],
|
|
149
|
+
providers: [OverlayContainerCoordinator],
|
|
150
|
+
host: {
|
|
151
|
+
tabindex: '-1',
|
|
152
|
+
'[attr.id]': '_config.id || null',
|
|
153
|
+
'[attr.role]': '_config.role',
|
|
154
|
+
'[attr.aria-modal]': '_config.ariaModal',
|
|
155
|
+
'[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledByQueue[0]',
|
|
156
|
+
'[attr.aria-label]': '_config.ariaLabel',
|
|
157
|
+
'[attr.aria-describedby]': 'ariaDescribedByAttr()',
|
|
158
|
+
'[attr.data-state]': 'state()',
|
|
159
|
+
'[class]': 'hostClasses()',
|
|
160
|
+
'[style.transition-duration.ms]': 'transitionDuration()',
|
|
161
|
+
},
|
|
162
|
+
}]
|
|
163
|
+
}], ctorParameters: () => [] });
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Reference to a dialog opened via {@link TwDialog.open}. Drives the dialog
|
|
167
|
+
* lifecycle (close, state, observables) and forwards useful overlay streams.
|
|
168
|
+
*/
|
|
169
|
+
class TwDialogRef {
|
|
170
|
+
cdkRef;
|
|
171
|
+
config;
|
|
172
|
+
containerInstance;
|
|
173
|
+
/** Unique ID of the dialog. */
|
|
174
|
+
id;
|
|
175
|
+
/** Instance of the component rendered inside the dialog, or `null` for template dialogs. */
|
|
176
|
+
componentInstance = null;
|
|
177
|
+
/** `ComponentRef` of the content component, or `null` for template dialogs. */
|
|
178
|
+
componentRef = null;
|
|
179
|
+
/** Current lifecycle state. Reactively readable. */
|
|
180
|
+
state;
|
|
181
|
+
/** When `true`, close-via-escape and close-via-backdrop are disabled. */
|
|
182
|
+
disableClose;
|
|
183
|
+
stateSignal = signal('opening', ...(ngDevMode ? [{ debugName: "stateSignal" }] : /* istanbul ignore next */ []));
|
|
184
|
+
afterOpenedSubject = new ReplaySubject(1);
|
|
185
|
+
beforeClosedSubject = new ReplaySubject(1);
|
|
186
|
+
afterClosedSubject = new ReplaySubject(1);
|
|
187
|
+
pendingResult;
|
|
188
|
+
closeFocusOrigin;
|
|
189
|
+
constructor(cdkRef, config, containerInstance) {
|
|
190
|
+
this.cdkRef = cdkRef;
|
|
191
|
+
this.config = config;
|
|
192
|
+
this.containerInstance = containerInstance;
|
|
193
|
+
this.id = cdkRef.id;
|
|
194
|
+
this.disableClose = config.disableClose;
|
|
195
|
+
this.state = this.stateSignal.asReadonly();
|
|
196
|
+
cdkRef.addPanelClass('tw-dialog-panel');
|
|
197
|
+
const animationChanges = containerInstance.animationStateChanged;
|
|
198
|
+
animationChanges
|
|
199
|
+
.pipe(filter((event) => event.state === 'open'), take(1))
|
|
200
|
+
.subscribe(() => {
|
|
201
|
+
this.stateSignal.set('open');
|
|
202
|
+
this.afterOpenedSubject.next();
|
|
203
|
+
this.afterOpenedSubject.complete();
|
|
204
|
+
});
|
|
205
|
+
// The container owns the exit-animation fallback timer (see
|
|
206
|
+
// ANIMATION_FALLBACK_PADDING in dialog-container.ts), so we trust its
|
|
207
|
+
// `closed` emission and run finishClose exactly once from here.
|
|
208
|
+
animationChanges
|
|
209
|
+
.pipe(filter((event) => event.state === 'closed'), take(1))
|
|
210
|
+
.subscribe(() => this.finishClose());
|
|
211
|
+
cdkRef.overlayRef.detachments().subscribe(() => {
|
|
212
|
+
if (this.stateSignal() !== 'closed') {
|
|
213
|
+
this.beforeClosedSubject.next(this.pendingResult);
|
|
214
|
+
this.beforeClosedSubject.complete();
|
|
215
|
+
this.finishClose();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
merge(cdkRef.backdropClick, cdkRef.keydownEvents.pipe(filter((event) => event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event)))).subscribe((event) => {
|
|
219
|
+
if (this.disableClose)
|
|
220
|
+
return;
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
this.closeWithOrigin(event.type === 'keydown' ? 'keyboard' : 'mouse');
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Closes the dialog. The exit animation runs before the overlay is disposed.
|
|
227
|
+
* @param result Value forwarded to `afterClosed()` subscribers.
|
|
228
|
+
*/
|
|
229
|
+
close(result) {
|
|
230
|
+
this.closeWithOrigin('program', result);
|
|
231
|
+
}
|
|
232
|
+
/** Observable that emits once after the enter animation finishes. */
|
|
233
|
+
afterOpened() {
|
|
234
|
+
return this.afterOpenedSubject.asObservable();
|
|
235
|
+
}
|
|
236
|
+
/** Observable that emits once when the close animation starts. */
|
|
237
|
+
beforeClosed() {
|
|
238
|
+
return this.beforeClosedSubject.asObservable();
|
|
239
|
+
}
|
|
240
|
+
/** Observable that emits once after the dialog has fully closed and the overlay is disposed. */
|
|
241
|
+
afterClosed() {
|
|
242
|
+
return this.afterClosedSubject.asObservable();
|
|
243
|
+
}
|
|
244
|
+
/** Backdrop click stream (emits even when `disableClose` is set). */
|
|
245
|
+
backdropClick() {
|
|
246
|
+
return this.cdkRef.backdropClick;
|
|
247
|
+
}
|
|
248
|
+
/** Keydown event stream for the overlay. */
|
|
249
|
+
keydownEvents() {
|
|
250
|
+
return this.cdkRef.keydownEvents;
|
|
251
|
+
}
|
|
252
|
+
/** Updates the dialog's width/height. Pass empty string to reset a dimension. */
|
|
253
|
+
updateSize(width = '', height = '') {
|
|
254
|
+
this.cdkRef.updateSize(width, height);
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
/** Adds CSS classes to the overlay panel. */
|
|
258
|
+
addPanelClass(classes) {
|
|
259
|
+
this.cdkRef.addPanelClass(classes);
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
/** Removes CSS classes from the overlay panel. */
|
|
263
|
+
removePanelClass(classes) {
|
|
264
|
+
this.cdkRef.removePanelClass(classes);
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
closeWithOrigin(origin, result) {
|
|
268
|
+
if (this.stateSignal() === 'closing' || this.stateSignal() === 'closed')
|
|
269
|
+
return;
|
|
270
|
+
const predicate = this.config.closePredicate;
|
|
271
|
+
if (predicate &&
|
|
272
|
+
!predicate(result, this.config, this.componentInstance)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.pendingResult = result;
|
|
276
|
+
this.closeFocusOrigin = origin;
|
|
277
|
+
this.stateSignal.set('closing');
|
|
278
|
+
this.beforeClosedSubject.next(result);
|
|
279
|
+
this.beforeClosedSubject.complete();
|
|
280
|
+
this.cdkRef.overlayRef.detachBackdrop();
|
|
281
|
+
this.containerInstance._startExitAnimation();
|
|
282
|
+
}
|
|
283
|
+
finishClose() {
|
|
284
|
+
if (this.stateSignal() === 'closed')
|
|
285
|
+
return;
|
|
286
|
+
this.stateSignal.set('closed');
|
|
287
|
+
this.afterClosedSubject.next(this.pendingResult);
|
|
288
|
+
this.afterClosedSubject.complete();
|
|
289
|
+
if (this.cdkRef.containerInstance) {
|
|
290
|
+
this.cdkRef.close(this.pendingResult, { focusOrigin: this.closeFocusOrigin });
|
|
291
|
+
}
|
|
292
|
+
this.componentInstance = null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Opens Tailwind-styled modal dialogs. Composes `@angular/cdk/dialog` for focus
|
|
298
|
+
* trapping, portals, overlay plumbing, and adds a Tailwind container, richer
|
|
299
|
+
* ref API, and animation lifecycle.
|
|
300
|
+
*
|
|
301
|
+
* Not `providedIn: 'root'` — register it via {@link provideTwDialog}.
|
|
302
|
+
*/
|
|
303
|
+
class TwDialog {
|
|
304
|
+
cdkDialog = inject(Dialog);
|
|
305
|
+
injector = inject(Injector);
|
|
306
|
+
defaultOptions = inject(TW_DIALOG_DEFAULT_OPTIONS, { optional: true }) ?? {};
|
|
307
|
+
parentDialog = inject(TwDialog, { optional: true, skipSelf: true });
|
|
308
|
+
openDialogsAtThisLevel = signal([], ...(ngDevMode ? [{ debugName: "openDialogsAtThisLevel" }] : /* istanbul ignore next */ []));
|
|
309
|
+
afterOpenedSubject = new Subject();
|
|
310
|
+
afterAllClosedSubject = new Subject();
|
|
311
|
+
/** Reactively-readable list of currently-open dialogs across the full dialog tree. */
|
|
312
|
+
openDialogs = this.parentDialog
|
|
313
|
+
? this.parentDialog.openDialogs
|
|
314
|
+
: this.openDialogsAtThisLevel.asReadonly();
|
|
315
|
+
/** Emits every time a dialog is opened. */
|
|
316
|
+
afterOpened = this.parentDialog
|
|
317
|
+
? this.parentDialog.afterOpened
|
|
318
|
+
: this.afterOpenedSubject.asObservable();
|
|
319
|
+
/**
|
|
320
|
+
* Emits when every open dialog has closed. Emits immediately on subscribe if
|
|
321
|
+
* there are none open.
|
|
322
|
+
*/
|
|
323
|
+
afterAllClosed = defer(() => this.openDialogs().length
|
|
324
|
+
? this.getAfterAllClosedSource()
|
|
325
|
+
: this.getAfterAllClosedSource().pipe(startWith(undefined)));
|
|
326
|
+
/**
|
|
327
|
+
* Opens a dialog using the given component or template.
|
|
328
|
+
* @param content Component class or `TemplateRef`.
|
|
329
|
+
* @param config Options merged over the application defaults.
|
|
330
|
+
* @returns Reference controlling the opened dialog.
|
|
331
|
+
*/
|
|
332
|
+
open(content, config) {
|
|
333
|
+
const merged = this.resolveConfig(config);
|
|
334
|
+
let twRef;
|
|
335
|
+
const cdkRef = this.cdkDialog.open(content, {
|
|
336
|
+
id: merged.id,
|
|
337
|
+
role: merged.role,
|
|
338
|
+
data: merged.data,
|
|
339
|
+
panelClass: merged.panelClass,
|
|
340
|
+
backdropClass: merged.backdropClass ?? 'tw-dialog-backdrop',
|
|
341
|
+
hasBackdrop: merged.hasBackdrop,
|
|
342
|
+
width: merged.width,
|
|
343
|
+
height: merged.height,
|
|
344
|
+
minWidth: merged.minWidth,
|
|
345
|
+
minHeight: merged.minHeight,
|
|
346
|
+
maxWidth: merged.maxWidth,
|
|
347
|
+
maxHeight: merged.maxHeight,
|
|
348
|
+
direction: merged.direction,
|
|
349
|
+
ariaDescribedBy: merged.ariaDescribedBy,
|
|
350
|
+
ariaLabelledBy: merged.ariaLabelledBy,
|
|
351
|
+
ariaLabel: merged.ariaLabel,
|
|
352
|
+
ariaModal: merged.ariaModal,
|
|
353
|
+
autoFocus: merged.autoFocus,
|
|
354
|
+
restoreFocus: merged.restoreFocus,
|
|
355
|
+
scrollStrategy: merged.scrollStrategy ?? this.resolveScrollStrategy(merged.scrollBehavior),
|
|
356
|
+
closeOnNavigation: merged.closeOnNavigation,
|
|
357
|
+
viewContainerRef: merged.viewContainerRef,
|
|
358
|
+
injector: merged.injector,
|
|
359
|
+
// We handle close/Escape/backdrop ourselves so we can run the exit animation.
|
|
360
|
+
disableClose: true,
|
|
361
|
+
closeOnOverlayDetachments: false,
|
|
362
|
+
container: {
|
|
363
|
+
type: DialogContainer,
|
|
364
|
+
providers: () => [
|
|
365
|
+
{ provide: TwDialogConfig, useValue: merged },
|
|
366
|
+
{ provide: DialogConfig, useValue: merged },
|
|
367
|
+
],
|
|
368
|
+
},
|
|
369
|
+
providers: (_cdkRef, _cdkConfig, container) => {
|
|
370
|
+
twRef = new TwDialogRef(_cdkRef, merged, container);
|
|
371
|
+
const providers = [
|
|
372
|
+
{ provide: TwDialogRef, useValue: twRef },
|
|
373
|
+
{ provide: TW_DIALOG_DATA, useValue: merged.data ?? null },
|
|
374
|
+
];
|
|
375
|
+
if (Array.isArray(merged.providers))
|
|
376
|
+
providers.push(...merged.providers);
|
|
377
|
+
return providers;
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
// After CDK attaches the component, copy references onto our ref.
|
|
381
|
+
twRef.componentRef = cdkRef.componentRef;
|
|
382
|
+
twRef.componentInstance = cdkRef.componentInstance;
|
|
383
|
+
const scope = this.parentDialog ?? this;
|
|
384
|
+
scope.registerOpen(twRef);
|
|
385
|
+
twRef.afterClosed().subscribe(() => {
|
|
386
|
+
scope.unregister(twRef);
|
|
387
|
+
});
|
|
388
|
+
return twRef;
|
|
389
|
+
}
|
|
390
|
+
/** Closes every open dialog managed by this service (and child services). */
|
|
391
|
+
closeAll() {
|
|
392
|
+
const dialogs = [...this.openDialogs()];
|
|
393
|
+
for (let i = dialogs.length - 1; i >= 0; i--)
|
|
394
|
+
dialogs[i].close();
|
|
395
|
+
}
|
|
396
|
+
/** Looks up an open dialog by its id. */
|
|
397
|
+
getDialogById(id) {
|
|
398
|
+
return this.openDialogs().find((dialog) => dialog.id === id);
|
|
399
|
+
}
|
|
400
|
+
ngOnDestroy() {
|
|
401
|
+
const dialogs = [...this.openDialogsAtThisLevel()];
|
|
402
|
+
for (let i = dialogs.length - 1; i >= 0; i--)
|
|
403
|
+
dialogs[i].close();
|
|
404
|
+
this.afterOpenedSubject.complete();
|
|
405
|
+
this.afterAllClosedSubject.complete();
|
|
406
|
+
}
|
|
407
|
+
registerOpen(ref) {
|
|
408
|
+
if (this.parentDialog) {
|
|
409
|
+
this.parentDialog.registerOpen(ref);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
this.openDialogsAtThisLevel.update((list) => [...list, ref]);
|
|
413
|
+
this.afterOpenedSubject.next(ref);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
unregister(ref) {
|
|
417
|
+
if (this.parentDialog) {
|
|
418
|
+
this.parentDialog.unregister(ref);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
this.openDialogsAtThisLevel.update((list) => list.filter((d) => d !== ref));
|
|
422
|
+
if (this.openDialogsAtThisLevel().length === 0) {
|
|
423
|
+
this.afterAllClosedSubject.next();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
getAfterAllClosedSource() {
|
|
428
|
+
return this.parentDialog
|
|
429
|
+
? this.parentDialog.getAfterAllClosedSource()
|
|
430
|
+
: this.afterAllClosedSubject;
|
|
431
|
+
}
|
|
432
|
+
resolveConfig(config) {
|
|
433
|
+
const merged = new TwDialogConfig();
|
|
434
|
+
Object.assign(merged, this.defaultOptions, config);
|
|
435
|
+
return merged;
|
|
436
|
+
}
|
|
437
|
+
resolveScrollStrategy(strategy) {
|
|
438
|
+
switch (strategy) {
|
|
439
|
+
case 'close':
|
|
440
|
+
return createCloseScrollStrategy(this.injector);
|
|
441
|
+
case 'reposition':
|
|
442
|
+
return createRepositionScrollStrategy(this.injector);
|
|
443
|
+
case 'noop':
|
|
444
|
+
return createNoopScrollStrategy();
|
|
445
|
+
case 'block':
|
|
446
|
+
default:
|
|
447
|
+
return createBlockScrollStrategy(this.injector);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TwDialog, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
451
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TwDialog });
|
|
452
|
+
}
|
|
453
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TwDialog, decorators: [{
|
|
454
|
+
type: Injectable
|
|
455
|
+
}] });
|
|
456
|
+
/**
|
|
457
|
+
* Registers the {@link TwDialog} service for dependency injection.
|
|
458
|
+
*
|
|
459
|
+
* @param defaultOptions Optional defaults merged into every `open()` call.
|
|
460
|
+
* @example
|
|
461
|
+
* ```ts
|
|
462
|
+
* provideTwDialog({ size: 'lg', hasBackdrop: true })
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
function provideTwDialog(defaultOptions) {
|
|
466
|
+
const providers = [TwDialog];
|
|
467
|
+
if (defaultOptions) {
|
|
468
|
+
providers.push({ provide: TW_DIALOG_DEFAULT_OPTIONS, useValue: defaultOptions });
|
|
469
|
+
}
|
|
470
|
+
return makeEnvironmentProviders(providers);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Header wrapper for a dialog. Provides consistent padding and spacing for
|
|
475
|
+
* title + subtitle + leading icon, and separates the header from scrollable
|
|
476
|
+
* content below.
|
|
477
|
+
*/
|
|
478
|
+
class DialogHeaderDirective {
|
|
479
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
480
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: DialogHeaderDirective, isStandalone: true, selector: "[twDialogHeader], tw-dialog-header", host: { classAttribute: "flex items-start gap-3 px-6 pt-6 pb-4" }, ngImport: i0 });
|
|
481
|
+
}
|
|
482
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogHeaderDirective, decorators: [{
|
|
483
|
+
type: Directive,
|
|
484
|
+
args: [{
|
|
485
|
+
selector: '[twDialogHeader], tw-dialog-header',
|
|
486
|
+
host: {
|
|
487
|
+
class: 'flex items-start gap-3 px-6 pt-6 pb-4',
|
|
488
|
+
},
|
|
489
|
+
}]
|
|
490
|
+
}] });
|
|
491
|
+
// Slot tokens own light/dark contrast — no `dark:` overrides, no shade picks.
|
|
492
|
+
// See role slot conventions in `projects/ngx-tw/theme/_semantic.css`.
|
|
493
|
+
const ICON_COLOR_CLASSES = {
|
|
494
|
+
primary: 'bg-primary-soft text-primary-icon',
|
|
495
|
+
secondary: 'bg-secondary-soft text-secondary-icon',
|
|
496
|
+
accent: 'bg-accent-soft text-accent-icon',
|
|
497
|
+
neutral: 'bg-neutral-soft text-neutral-icon',
|
|
498
|
+
info: 'bg-info-soft text-info-icon',
|
|
499
|
+
success: 'bg-success-soft text-success-icon',
|
|
500
|
+
warning: 'bg-warning-soft text-warning-icon',
|
|
501
|
+
error: 'bg-error-soft text-error-icon',
|
|
502
|
+
};
|
|
503
|
+
/**
|
|
504
|
+
* Decorative leading icon for a dialog header. Use with a semantic `color` to
|
|
505
|
+
* match destructive / informational / success dialogs.
|
|
506
|
+
*/
|
|
507
|
+
class DialogIconDirective {
|
|
508
|
+
/** Semantic color for the icon container. Defaults to a neutral surface. */
|
|
509
|
+
color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
510
|
+
classes = computed(() => {
|
|
511
|
+
const base = 'flex size-10 shrink-0 items-center justify-center rounded-full';
|
|
512
|
+
const color = this.color();
|
|
513
|
+
return color ? `${base} ${ICON_COLOR_CLASSES[color]}` : `${base} bg-surface-muted text-fg`;
|
|
514
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
515
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
516
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DialogIconDirective, isStandalone: true, selector: "[twDialogIcon]", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0 });
|
|
517
|
+
}
|
|
518
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogIconDirective, decorators: [{
|
|
519
|
+
type: Directive,
|
|
520
|
+
args: [{
|
|
521
|
+
selector: '[twDialogIcon]',
|
|
522
|
+
host: {
|
|
523
|
+
'[class]': 'classes()',
|
|
524
|
+
},
|
|
525
|
+
}]
|
|
526
|
+
}], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }] } });
|
|
527
|
+
/**
|
|
528
|
+
* Dialog title. Registers its ID with the container's `aria-labelledby` queue
|
|
529
|
+
* so screen readers announce it automatically.
|
|
530
|
+
*/
|
|
531
|
+
class DialogTitleDirective {
|
|
532
|
+
generatedId = inject(_IdGenerator).getId('tw-dialog-title-');
|
|
533
|
+
dialogRef = inject(TwDialogRef, {
|
|
534
|
+
optional: true,
|
|
535
|
+
});
|
|
536
|
+
// Ancestor-DI fallback for the rare case where `TwDialogRef` is not in the
|
|
537
|
+
// directive's injector chain (e.g. heavily nested template portals). The
|
|
538
|
+
// container ALWAYS resolves via element-injector traversal because the
|
|
539
|
+
// directive lives inside `<tw-dialog-container>`'s DOM tree.
|
|
540
|
+
container = inject(DialogContainer, { optional: true, skipSelf: true });
|
|
541
|
+
/** Custom id for the title element. Defaults to a generated unique id. */
|
|
542
|
+
id = input(this.generatedId, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
543
|
+
ngOnInit() {
|
|
544
|
+
const containerInstance = this.dialogRef?.containerInstance ?? this.container;
|
|
545
|
+
if (containerInstance)
|
|
546
|
+
containerInstance._addAriaLabelledBy(this.id());
|
|
547
|
+
}
|
|
548
|
+
ngOnDestroy() {
|
|
549
|
+
const containerInstance = this.dialogRef?.containerInstance ?? this.container;
|
|
550
|
+
if (containerInstance)
|
|
551
|
+
containerInstance._removeAriaLabelledBy(this.id());
|
|
552
|
+
}
|
|
553
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogTitleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
554
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DialogTitleDirective, isStandalone: true, selector: "[twDialogTitle], tw-dialog-title", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "id()" }, classAttribute: "text-sm font-semibold text-fg" }, ngImport: i0 });
|
|
555
|
+
}
|
|
556
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogTitleDirective, decorators: [{
|
|
557
|
+
type: Directive,
|
|
558
|
+
args: [{
|
|
559
|
+
selector: '[twDialogTitle], tw-dialog-title',
|
|
560
|
+
host: {
|
|
561
|
+
class: 'text-sm font-semibold text-fg',
|
|
562
|
+
'[id]': 'id()',
|
|
563
|
+
},
|
|
564
|
+
}]
|
|
565
|
+
}], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
|
|
566
|
+
/** Secondary line beneath the dialog title — intended for a short description. */
|
|
567
|
+
class DialogSubtitleDirective {
|
|
568
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogSubtitleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
569
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: DialogSubtitleDirective, isStandalone: true, selector: "[twDialogSubtitle], tw-dialog-subtitle", host: { classAttribute: "text-sm text-fg-muted" }, ngImport: i0 });
|
|
570
|
+
}
|
|
571
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogSubtitleDirective, decorators: [{
|
|
572
|
+
type: Directive,
|
|
573
|
+
args: [{
|
|
574
|
+
selector: '[twDialogSubtitle], tw-dialog-subtitle',
|
|
575
|
+
host: {
|
|
576
|
+
class: 'text-sm text-fg-muted',
|
|
577
|
+
},
|
|
578
|
+
}]
|
|
579
|
+
}] });
|
|
580
|
+
/**
|
|
581
|
+
* Dialog description. Registers its ID with the container's `aria-describedby`
|
|
582
|
+
* queue so screen readers announce the descriptive paragraph after the title.
|
|
583
|
+
* Mirrors {@link DialogTitleDirective} but for `aria-describedby`.
|
|
584
|
+
*/
|
|
585
|
+
class DialogDescriptionDirective {
|
|
586
|
+
generatedId = inject(_IdGenerator).getId('tw-dialog-description-');
|
|
587
|
+
dialogRef = inject(TwDialogRef, {
|
|
588
|
+
optional: true,
|
|
589
|
+
});
|
|
590
|
+
// Ancestor-DI fallback — see DialogTitleDirective.
|
|
591
|
+
container = inject(DialogContainer, { optional: true, skipSelf: true });
|
|
592
|
+
/** Custom id for the description element. Defaults to a generated unique id. */
|
|
593
|
+
id = input(this.generatedId, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
594
|
+
ngOnInit() {
|
|
595
|
+
const containerInstance = this.dialogRef?.containerInstance ?? this.container;
|
|
596
|
+
if (containerInstance)
|
|
597
|
+
containerInstance._addAriaDescribedBy(this.id());
|
|
598
|
+
}
|
|
599
|
+
ngOnDestroy() {
|
|
600
|
+
const containerInstance = this.dialogRef?.containerInstance ?? this.container;
|
|
601
|
+
if (containerInstance)
|
|
602
|
+
containerInstance._removeAriaDescribedBy(this.id());
|
|
603
|
+
}
|
|
604
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogDescriptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
605
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DialogDescriptionDirective, isStandalone: true, selector: "[twDialogDescription], tw-dialog-description", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "id()" } }, ngImport: i0 });
|
|
606
|
+
}
|
|
607
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogDescriptionDirective, decorators: [{
|
|
608
|
+
type: Directive,
|
|
609
|
+
args: [{
|
|
610
|
+
selector: '[twDialogDescription], tw-dialog-description',
|
|
611
|
+
host: {
|
|
612
|
+
'[id]': 'id()',
|
|
613
|
+
},
|
|
614
|
+
}]
|
|
615
|
+
}], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
|
|
616
|
+
/**
|
|
617
|
+
* Scrollable content region of the dialog. Apply between the header and the
|
|
618
|
+
* actions bar. Inherits CDK's `CdkScrollable` to play nicely with scroll
|
|
619
|
+
* strategies and nested scrollables.
|
|
620
|
+
*/
|
|
621
|
+
class DialogContentDirective {
|
|
622
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
623
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: DialogContentDirective, isStandalone: true, selector: "[twDialogContent], tw-dialog-content", host: { classAttribute: "flex-1 overflow-y-auto px-6 py-4 text-sm text-fg" }, hostDirectives: [{ directive: i1.CdkScrollable }], ngImport: i0 });
|
|
624
|
+
}
|
|
625
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogContentDirective, decorators: [{
|
|
626
|
+
type: Directive,
|
|
627
|
+
args: [{
|
|
628
|
+
selector: '[twDialogContent], tw-dialog-content',
|
|
629
|
+
hostDirectives: [CdkScrollable],
|
|
630
|
+
host: {
|
|
631
|
+
class: 'flex-1 overflow-y-auto px-6 py-4 text-sm text-fg',
|
|
632
|
+
},
|
|
633
|
+
}]
|
|
634
|
+
}] });
|
|
635
|
+
/**
|
|
636
|
+
* Bottom action bar of a dialog. Use inside the dialog content or template to
|
|
637
|
+
* host Cancel/Confirm buttons. Stays pinned below scrollable content.
|
|
638
|
+
*/
|
|
639
|
+
class DialogActionsDirective {
|
|
640
|
+
/** Horizontal alignment of the action buttons. Defaults to `'end'`. */
|
|
641
|
+
align = input('end', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
642
|
+
classes = computed(() => {
|
|
643
|
+
const align = this.align();
|
|
644
|
+
const justify = align === 'start' ? 'justify-start' : align === 'center' ? 'justify-center' : 'justify-end';
|
|
645
|
+
return `flex flex-wrap items-center gap-2 px-6 py-4 border-t border-border-muted ${justify}`;
|
|
646
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
647
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogActionsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
648
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DialogActionsDirective, isStandalone: true, selector: "[twDialogActions], tw-dialog-actions", inputs: { align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0 });
|
|
649
|
+
}
|
|
650
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogActionsDirective, decorators: [{
|
|
651
|
+
type: Directive,
|
|
652
|
+
args: [{
|
|
653
|
+
selector: '[twDialogActions], tw-dialog-actions',
|
|
654
|
+
host: {
|
|
655
|
+
'[class]': 'classes()',
|
|
656
|
+
},
|
|
657
|
+
}]
|
|
658
|
+
}], propDecorators: { align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }] } });
|
|
659
|
+
/**
|
|
660
|
+
* Closes the enclosing dialog when the host button is clicked. Provide a
|
|
661
|
+
* `[twDialogClose]` value to pass a result to `afterClosed()` subscribers.
|
|
662
|
+
*/
|
|
663
|
+
class DialogCloseDirective {
|
|
664
|
+
/** Value passed to `afterClosed()` when the button is clicked. */
|
|
665
|
+
twDialogClose = input(undefined, ...(ngDevMode ? [{ debugName: "twDialogClose" }] : /* istanbul ignore next */ []));
|
|
666
|
+
/** Native button `type`. Defaults to `'button'` to avoid accidental form submission. */
|
|
667
|
+
type = input('button', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
668
|
+
dialogRef = inject(TwDialogRef, {
|
|
669
|
+
optional: true,
|
|
670
|
+
});
|
|
671
|
+
onClick() {
|
|
672
|
+
this.dialogRef?.close(this.twDialogClose());
|
|
673
|
+
}
|
|
674
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogCloseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
675
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DialogCloseDirective, isStandalone: true, selector: "[twDialogClose]", inputs: { twDialogClose: { classPropertyName: "twDialogClose", publicName: "twDialogClose", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()" }, properties: { "attr.type": "type()" } }, ngImport: i0 });
|
|
676
|
+
}
|
|
677
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogCloseDirective, decorators: [{
|
|
678
|
+
type: Directive,
|
|
679
|
+
args: [{
|
|
680
|
+
selector: '[twDialogClose]',
|
|
681
|
+
host: {
|
|
682
|
+
'(click)': 'onClick()',
|
|
683
|
+
'[attr.type]': 'type()',
|
|
684
|
+
},
|
|
685
|
+
}]
|
|
686
|
+
}], propDecorators: { twDialogClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "twDialogClose", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Generated bundle index. Do not edit.
|
|
690
|
+
*/
|
|
691
|
+
|
|
692
|
+
export { DialogActionsDirective, DialogCloseDirective, DialogContainer, DialogContentDirective, DialogDescriptionDirective, DialogHeaderDirective, DialogIconDirective, DialogSubtitleDirective, DialogTitleDirective, TW_DIALOG_DATA, TW_DIALOG_DEFAULT_OPTIONS, TwDialog, TwDialogConfig, TwDialogRef, provideTwDialog };
|
|
693
|
+
//# sourceMappingURL=cdevhub-ngx-tw-dialog.mjs.map
|