@blotch/dolan 0.0.0-stage → 22.2.1
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/dist/commands/add.d.ts +22 -0
- package/dist/commands/add.js +132 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/add.test.d.ts +1 -0
- package/dist/commands/add.test.js +209 -0
- package/dist/commands/add.test.js.map +1 -0
- package/dist/commands/diff.d.ts +13 -0
- package/dist/commands/diff.js +155 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/diff.test.d.ts +1 -0
- package/dist/commands/diff.test.js +327 -0
- package/dist/commands/diff.test.js.map +1 -0
- package/dist/commands/doctor.d.ts +15 -0
- package/dist/commands/doctor.js +248 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +1 -0
- package/dist/commands/doctor.test.js +301 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/eject.d.ts +11 -0
- package/dist/commands/eject.js +55 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/eject.test.d.ts +1 -0
- package/dist/commands/eject.test.js +151 -0
- package/dist/commands/eject.test.js.map +1 -0
- package/dist/commands/info.d.ts +1 -0
- package/dist/commands/info.js +73 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/info.test.d.ts +1 -0
- package/dist/commands/info.test.js +143 -0
- package/dist/commands/info.test.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +98 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +1 -0
- package/dist/commands/init.test.js +154 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/list.d.ts +16 -0
- package/dist/commands/list.js +124 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/list.test.d.ts +1 -0
- package/dist/commands/list.test.js +285 -0
- package/dist/commands/list.test.js.map +1 -0
- package/dist/commands/outdated.d.ts +13 -0
- package/dist/commands/outdated.js +116 -0
- package/dist/commands/outdated.js.map +1 -0
- package/dist/commands/outdated.test.d.ts +1 -0
- package/dist/commands/outdated.test.js +208 -0
- package/dist/commands/outdated.test.js.map +1 -0
- package/dist/commands/remove.d.ts +18 -0
- package/dist/commands/remove.js +161 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/remove.test.d.ts +1 -0
- package/dist/commands/remove.test.js +361 -0
- package/dist/commands/remove.test.js.map +1 -0
- package/dist/commands/theme.d.ts +1 -0
- package/dist/commands/theme.js +66 -0
- package/dist/commands/theme.js.map +1 -0
- package/dist/commands/theme.test.d.ts +1 -0
- package/dist/commands/theme.test.js +133 -0
- package/dist/commands/theme.test.js.map +1 -0
- package/dist/commands/update.d.ts +31 -0
- package/dist/commands/update.js +601 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/update.test.d.ts +1 -0
- package/dist/commands/update.test.js +1233 -0
- package/dist/commands/update.test.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +48 -0
- package/dist/config.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/lockfile.d.ts +21 -0
- package/dist/lockfile.js +39 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/registry.d.ts +14 -0
- package/dist/registry.js +85 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +1 -0
- package/dist/registry.test.js +95 -0
- package/dist/registry.test.js.map +1 -0
- package/dist/test-utils.d.ts +16 -0
- package/dist/test-utils.js +65 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/ui/components/accordion/accordion.test.ts +194 -0
- package/dist/ui/components/accordion/accordion.ts +173 -0
- package/dist/ui/components/accordion/index.ts +6 -0
- package/dist/ui/components/action-sheet/action-sheet.test.ts +211 -0
- package/dist/ui/components/action-sheet/action-sheet.ts +353 -0
- package/dist/ui/components/action-sheet/index.ts +6 -0
- package/dist/ui/components/alert/alert.test.ts +48 -0
- package/dist/ui/components/alert/alert.ts +104 -0
- package/dist/ui/components/alert/index.ts +1 -0
- package/dist/ui/components/alert-dialog/alert-dialog.test.ts +141 -0
- package/dist/ui/components/alert-dialog/alert-dialog.ts +243 -0
- package/dist/ui/components/alert-dialog/index.ts +7 -0
- package/dist/ui/components/area-chart/area-chart-render.test.ts +121 -0
- package/dist/ui/components/area-chart/area-chart.test.ts +158 -0
- package/dist/ui/components/area-chart/area-chart.ts +349 -0
- package/dist/ui/components/area-chart/index.ts +6 -0
- package/dist/ui/components/aspect-ratio/aspect-ratio.test.ts +10 -0
- package/dist/ui/components/aspect-ratio/aspect-ratio.ts +28 -0
- package/dist/ui/components/aspect-ratio/index.ts +1 -0
- package/dist/ui/components/avatar/avatar.test.ts +69 -0
- package/dist/ui/components/avatar/avatar.ts +131 -0
- package/dist/ui/components/avatar/index.ts +1 -0
- package/dist/ui/components/badge/badge.test.ts +49 -0
- package/dist/ui/components/badge/badge.ts +97 -0
- package/dist/ui/components/badge/index.ts +1 -0
- package/dist/ui/components/bar-chart/bar-chart-render.test.ts +174 -0
- package/dist/ui/components/bar-chart/bar-chart.test.ts +73 -0
- package/dist/ui/components/bar-chart/bar-chart.ts +327 -0
- package/dist/ui/components/bar-chart/index.ts +6 -0
- package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +163 -0
- package/dist/ui/components/bottom-sheet/bottom-sheet.ts +339 -0
- package/dist/ui/components/bottom-sheet/index.ts +1 -0
- package/dist/ui/components/button/button-render.test.ts +113 -0
- package/dist/ui/components/button/button.test.ts +70 -0
- package/dist/ui/components/button/button.ts +170 -0
- package/dist/ui/components/button/index.ts +6 -0
- package/dist/ui/components/button-group/button-group-render.test.ts +146 -0
- package/dist/ui/components/button-group/button-group.test.ts +54 -0
- package/dist/ui/components/button-group/button-group.ts +209 -0
- package/dist/ui/components/button-group/index.ts +8 -0
- package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +174 -0
- package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +96 -0
- package/dist/ui/components/candlestick-chart/candlestick-chart.ts +355 -0
- package/dist/ui/components/candlestick-chart/index.ts +7 -0
- package/dist/ui/components/card/card.test.ts +110 -0
- package/dist/ui/components/card/card.ts +162 -0
- package/dist/ui/components/card/index.ts +8 -0
- package/dist/ui/components/cartesian-chart/cartesian-chart-render.test.ts +188 -0
- package/dist/ui/components/cartesian-chart/cartesian-chart.test.ts +549 -0
- package/dist/ui/components/cartesian-chart/cartesian-chart.ts +1140 -0
- package/dist/ui/components/cartesian-chart/index.ts +18 -0
- package/dist/ui/components/checkbox/checkbox.test.ts +59 -0
- package/dist/ui/components/checkbox/checkbox.ts +96 -0
- package/dist/ui/components/checkbox/index.ts +1 -0
- package/dist/ui/components/collapsible/collapsible.test.ts +112 -0
- package/dist/ui/components/collapsible/collapsible.ts +122 -0
- package/dist/ui/components/collapsible/index.ts +5 -0
- package/dist/ui/components/dialog/dialog.test.ts +157 -0
- package/dist/ui/components/dialog/dialog.ts +248 -0
- package/dist/ui/components/dialog/index.ts +7 -0
- package/dist/ui/components/empty-state/empty-state.test.ts +60 -0
- package/dist/ui/components/empty-state/empty-state.ts +46 -0
- package/dist/ui/components/empty-state/index.ts +1 -0
- package/dist/ui/components/icon/icon.test.ts +52 -0
- package/dist/ui/components/icon/icon.ts +54 -0
- package/dist/ui/components/icon/icons.ts +569 -0
- package/dist/ui/components/icon/index.ts +2 -0
- package/dist/ui/components/input/index.ts +1 -0
- package/dist/ui/components/input/input.test.ts +134 -0
- package/dist/ui/components/input/input.ts +189 -0
- package/dist/ui/components/label/index.ts +1 -0
- package/dist/ui/components/label/label.test.ts +38 -0
- package/dist/ui/components/label/label.ts +24 -0
- package/dist/ui/components/line-chart/index.ts +6 -0
- package/dist/ui/components/line-chart/line-chart-render.test.ts +121 -0
- package/dist/ui/components/line-chart/line-chart.test.ts +81 -0
- package/dist/ui/components/line-chart/line-chart.ts +273 -0
- package/dist/ui/components/list/index.ts +1 -0
- package/dist/ui/components/list/list.test.ts +35 -0
- package/dist/ui/components/list/list.ts +71 -0
- package/dist/ui/components/nav-drawer/index.ts +8 -0
- package/dist/ui/components/nav-drawer/nav-drawer.test.ts +267 -0
- package/dist/ui/components/nav-drawer/nav-drawer.ts +504 -0
- package/dist/ui/components/pagination/index.ts +1 -0
- package/dist/ui/components/pagination/pagination.test.ts +96 -0
- package/dist/ui/components/pagination/pagination.ts +147 -0
- package/dist/ui/components/progress/index.ts +1 -0
- package/dist/ui/components/progress/progress.test.ts +71 -0
- package/dist/ui/components/progress/progress.ts +118 -0
- package/dist/ui/components/radio-group/index.ts +1 -0
- package/dist/ui/components/radio-group/radio-group.test.ts +115 -0
- package/dist/ui/components/radio-group/radio-group.ts +129 -0
- package/dist/ui/components/scatter-chart/index.ts +6 -0
- package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +157 -0
- package/dist/ui/components/scatter-chart/scatter-chart.test.ts +57 -0
- package/dist/ui/components/scatter-chart/scatter-chart.ts +277 -0
- package/dist/ui/components/scroll-area/index.ts +1 -0
- package/dist/ui/components/scroll-area/scroll-area.test.ts +40 -0
- package/dist/ui/components/scroll-area/scroll-area.ts +73 -0
- package/dist/ui/components/select/index.ts +1 -0
- package/dist/ui/components/select/select.test.ts +107 -0
- package/dist/ui/components/select/select.ts +146 -0
- package/dist/ui/components/separator/index.ts +1 -0
- package/dist/ui/components/separator/separator.test.ts +26 -0
- package/dist/ui/components/separator/separator.ts +45 -0
- package/dist/ui/components/sheet/index.ts +7 -0
- package/dist/ui/components/sheet/sheet.test.ts +44 -0
- package/dist/ui/components/sheet/sheet.ts +114 -0
- package/dist/ui/components/skeleton/index.ts +1 -0
- package/dist/ui/components/skeleton/skeleton.test.ts +13 -0
- package/dist/ui/components/skeleton/skeleton.ts +45 -0
- package/dist/ui/components/spinner/index.ts +1 -0
- package/dist/ui/components/spinner/spinner.test.ts +39 -0
- package/dist/ui/components/spinner/spinner.ts +83 -0
- package/dist/ui/components/switch/index.ts +1 -0
- package/dist/ui/components/switch/switch.test.ts +63 -0
- package/dist/ui/components/switch/switch.ts +98 -0
- package/dist/ui/components/tabs/index.ts +1 -0
- package/dist/ui/components/tabs/tabs.test.ts +199 -0
- package/dist/ui/components/tabs/tabs.ts +317 -0
- package/dist/ui/components/textarea/index.ts +1 -0
- package/dist/ui/components/textarea/textarea.test.ts +138 -0
- package/dist/ui/components/textarea/textarea.ts +221 -0
- package/dist/ui/components/toast/index.ts +9 -0
- package/dist/ui/components/toast/toast-state.test.ts +54 -0
- package/dist/ui/components/toast/toast-state.ts +46 -0
- package/dist/ui/components/toast/toast.test.ts +342 -0
- package/dist/ui/components/toast/toast.ts +527 -0
- package/dist/ui/components/toggle/index.ts +7 -0
- package/dist/ui/components/toggle/toggle.test.ts +186 -0
- package/dist/ui/components/toggle/toggle.ts +218 -0
- package/dist/ui/components/typography/index.ts +5 -0
- package/dist/ui/components/typography/typography.test.ts +55 -0
- package/dist/ui/components/typography/typography.ts +44 -0
- package/dist/ui/theme/dark.css +41 -0
- package/dist/ui/theme/default.css +56 -0
- package/dist/ui/theme/tailwind-plugin.test.ts +124 -0
- package/dist/ui/theme/tailwind-plugin.ts +151 -0
- package/dist/ui/utils/animate.test.ts +324 -0
- package/dist/ui/utils/animate.ts +544 -0
- package/dist/ui/utils/cn.test.ts +27 -0
- package/dist/ui/utils/cn.ts +6 -0
- package/dist/utils/analyze.d.ts +32 -0
- package/dist/utils/analyze.js +109 -0
- package/dist/utils/analyze.js.map +1 -0
- package/dist/utils/analyze.test.d.ts +1 -0
- package/dist/utils/analyze.test.js +146 -0
- package/dist/utils/analyze.test.js.map +1 -0
- package/dist/utils/animate.d.ts +210 -0
- package/dist/utils/animate.js +423 -0
- package/dist/utils/animate.js.map +1 -0
- package/dist/utils/animate.test.d.ts +1 -0
- package/dist/utils/animate.test.js +365 -0
- package/dist/utils/animate.test.js.map +1 -0
- package/dist/utils/banner.d.ts +1 -0
- package/dist/utils/banner.js +15 -0
- package/dist/utils/banner.js.map +1 -0
- package/dist/utils/banner.test.d.ts +1 -0
- package/dist/utils/banner.test.js +29 -0
- package/dist/utils/banner.test.js.map +1 -0
- package/dist/utils/cn.d.ts +2 -0
- package/dist/utils/cn.js +6 -0
- package/dist/utils/cn.js.map +1 -0
- package/dist/utils/cn.test.d.ts +1 -0
- package/dist/utils/cn.test.js +21 -0
- package/dist/utils/cn.test.js.map +1 -0
- package/dist/utils/format.d.ts +31 -0
- package/dist/utils/format.js +95 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/format.test.d.ts +1 -0
- package/dist/utils/format.test.js +71 -0
- package/dist/utils/format.test.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +16 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/logger.test.d.ts +1 -0
- package/dist/utils/logger.test.js +36 -0
- package/dist/utils/logger.test.js.map +1 -0
- package/dist/utils/resolve-paths.d.ts +12 -0
- package/dist/utils/resolve-paths.js +29 -0
- package/dist/utils/resolve-paths.js.map +1 -0
- package/dist/utils/resolve-paths.test.d.ts +1 -0
- package/dist/utils/resolve-paths.test.js +52 -0
- package/dist/utils/resolve-paths.test.js.map +1 -0
- package/package.json +56 -4
- package/README.md +0 -3
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ElementRef,
|
|
3
|
+
Component,
|
|
4
|
+
ViewEncapsulation,
|
|
5
|
+
computed,
|
|
6
|
+
effect,
|
|
7
|
+
inject,
|
|
8
|
+
input,
|
|
9
|
+
model,
|
|
10
|
+
output,
|
|
11
|
+
signal,
|
|
12
|
+
viewChild,
|
|
13
|
+
} from '@angular/core';
|
|
14
|
+
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
type AnimationHandle,
|
|
18
|
+
DURATION,
|
|
19
|
+
EASING,
|
|
20
|
+
SCALE,
|
|
21
|
+
fadeIn,
|
|
22
|
+
fadeOut,
|
|
23
|
+
pressDown,
|
|
24
|
+
pressRelease,
|
|
25
|
+
slideIn,
|
|
26
|
+
slideOut,
|
|
27
|
+
} from '../../utils/animate';
|
|
28
|
+
import { cn } from '../../utils/cn';
|
|
29
|
+
|
|
30
|
+
@Component({
|
|
31
|
+
selector: 'ui-action-sheet',
|
|
32
|
+
standalone: true,
|
|
33
|
+
imports: [LYNX_ELEMENTS],
|
|
34
|
+
encapsulation: ViewEncapsulation.None,
|
|
35
|
+
template: `
|
|
36
|
+
<overlay [attr.visible]="overlayVisible()" [style]="overlayStyle()">
|
|
37
|
+
<!-- bg-black/50 dims the content behind the sheet. The fadeIn/fadeOut in
|
|
38
|
+
#animateIn/#animateOut already tween this element's opacity over
|
|
39
|
+
DURATION.normal, so the dim animates in lockstep with the panel slide —
|
|
40
|
+
matching the dialog/bottom-sheet backdrop. A named color with an opacity
|
|
41
|
+
modifier is required here: bg-black/50 compiles to rgb(0 0 0 / 0.5),
|
|
42
|
+
which Lynx accepts, whereas opacity modifiers on semantic tokens don't. -->
|
|
43
|
+
<view
|
|
44
|
+
#backdrop
|
|
45
|
+
class="h-full w-full bg-black/50"
|
|
46
|
+
(bindtap)="onBackdropTap()"
|
|
47
|
+
>
|
|
48
|
+
<view
|
|
49
|
+
#panel
|
|
50
|
+
[class]="panelClass()"
|
|
51
|
+
[style]="panelPositionStyle()"
|
|
52
|
+
(catchtap)="onPanelTap()"
|
|
53
|
+
>
|
|
54
|
+
<!-- The elevation shadow is an inline rgba() box-shadow rather than a
|
|
55
|
+
\`shadow-*\` Tailwind class: the Lynx tailwind preset doesn't wire
|
|
56
|
+
up the \`--tw-shadow\`/\`--tw-ring-*\` variables those utilities
|
|
57
|
+
depend on, so \`shadow-*\` renders nothing on Lynx. \`overflow-hidden\`
|
|
58
|
+
clips this tile's children to the rounded corners but not its own
|
|
59
|
+
box-shadow (painted outside the border box), so the shadow shows. -->
|
|
60
|
+
<view
|
|
61
|
+
class="flex-col rounded-lg border border-border bg-card flex overflow-hidden"
|
|
62
|
+
style="box-shadow: 0 2px 16px rgba(0, 0, 0, 0.15);"
|
|
63
|
+
>
|
|
64
|
+
<ng-content />
|
|
65
|
+
</view>
|
|
66
|
+
<!-- Cancel button is slotted separately so it renders outside the
|
|
67
|
+
rounded card — matching the iOS action sheet visual convention
|
|
68
|
+
where cancel is a distinct tile below the action group. -->
|
|
69
|
+
<ng-content select="ui-action-sheet-cancel" />
|
|
70
|
+
</view>
|
|
71
|
+
</view>
|
|
72
|
+
</overlay>
|
|
73
|
+
`,
|
|
74
|
+
})
|
|
75
|
+
export class UiActionSheet {
|
|
76
|
+
readonly open = model(false);
|
|
77
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
78
|
+
|
|
79
|
+
readonly closed = output<void>();
|
|
80
|
+
|
|
81
|
+
protected readonly overlayVisible = signal(false);
|
|
82
|
+
/**
|
|
83
|
+
* `width`/`height`/`top`/`left` are web-only necessities: on web
|
|
84
|
+
* `<overlay>` has no intrinsic full-screen sizing (unlike native), so
|
|
85
|
+
* without them it collapses to 0x0 and nothing renders. `z-index` keeps it
|
|
86
|
+
* above sibling content that would otherwise occasionally paint on top.
|
|
87
|
+
*/
|
|
88
|
+
protected readonly overlayStyle = computed(() =>
|
|
89
|
+
this.overlayVisible()
|
|
90
|
+
? 'position: fixed; overflow: visible; width: 100vw; height: 100vh; top: 0; left: 0; z-index: 999;'
|
|
91
|
+
: 'position: fixed; overflow: visible; width: 100vw; height: 100vh; top: 0; left: 0; z-index: 999; display: none;',
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
readonly backdropRef = viewChild<ElementRef>('backdrop');
|
|
95
|
+
readonly panelRef = viewChild<ElementRef>('panel');
|
|
96
|
+
#backdropAnim?: AnimationHandle;
|
|
97
|
+
#panelAnim?: AnimationHandle;
|
|
98
|
+
#hasBeenOpen = false;
|
|
99
|
+
|
|
100
|
+
constructor() {
|
|
101
|
+
// `#hasBeenOpen` prevents the close animation from running on the initial
|
|
102
|
+
// effect evaluation when `open` starts as false. Without this guard, the
|
|
103
|
+
// first run would call #doClose() and emit `closed` before the action
|
|
104
|
+
// sheet has ever been opened.
|
|
105
|
+
effect(() => {
|
|
106
|
+
const isOpen = this.open();
|
|
107
|
+
if (isOpen) {
|
|
108
|
+
this.#hasBeenOpen = true;
|
|
109
|
+
this.#doOpen();
|
|
110
|
+
} else if (this.#hasBeenOpen) {
|
|
111
|
+
this.#doClose();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
protected readonly panelClass = computed(() =>
|
|
117
|
+
cn('w-full flex-col gap-2 px-4 pb-6 flex', this.userClass()),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
protected readonly panelPositionStyle = computed(
|
|
121
|
+
() => 'position: absolute; bottom: 0; left: 0; right: 0;',
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
protected onBackdropTap(): void {
|
|
125
|
+
this.open.set(false);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
close(): void {
|
|
129
|
+
this.open.set(false);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* No-op tap handler for the panel. `catchtap` (vs `bindtap`) already stops
|
|
134
|
+
* the tap from bubbling to the backdrop — Lynx controls propagation via the
|
|
135
|
+
* event prefix, not at runtime. (The renderer shims `event.stopPropagation()`
|
|
136
|
+
* as a no-op so DOM-style handlers don't crash, but it has no effect here.)
|
|
137
|
+
*/
|
|
138
|
+
protected onPanelTap(): void {}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Two-phase open: make overlay visible first (so native elements exist in
|
|
142
|
+
* the tree), then animate on the next frame. See nav-drawer for the same
|
|
143
|
+
* pattern — Lynx animate() requires elements to be mounted before use.
|
|
144
|
+
*/
|
|
145
|
+
#doOpen(): void {
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
this.overlayVisible.set(true);
|
|
148
|
+
setTimeout(() => this.#animateIn(), 0);
|
|
149
|
+
}, 0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Animate out before hiding; +20ms guards against Lynx timer imprecision
|
|
154
|
+
* causing elements to disappear before the animation finishes.
|
|
155
|
+
*/
|
|
156
|
+
#doClose(): void {
|
|
157
|
+
setTimeout(() => {
|
|
158
|
+
this.#animateOut();
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
this.overlayVisible.set(false);
|
|
161
|
+
this.closed.emit();
|
|
162
|
+
}, DURATION.normal + 20);
|
|
163
|
+
}, 0);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#animateIn(): void {
|
|
167
|
+
const backdrop = this.backdropRef()?.nativeElement;
|
|
168
|
+
const panel = this.panelRef()?.nativeElement;
|
|
169
|
+
if (!backdrop || !panel) return;
|
|
170
|
+
|
|
171
|
+
this.#backdropAnim?.cancel();
|
|
172
|
+
this.#panelAnim?.cancel();
|
|
173
|
+
|
|
174
|
+
this.#backdropAnim = fadeIn(backdrop, { duration: DURATION.normal });
|
|
175
|
+
this.#panelAnim = slideIn(panel, 'up', {
|
|
176
|
+
duration: DURATION.slow,
|
|
177
|
+
easing: EASING.sheet,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#animateOut(): void {
|
|
182
|
+
const backdrop = this.backdropRef()?.nativeElement;
|
|
183
|
+
const panel = this.panelRef()?.nativeElement;
|
|
184
|
+
if (!backdrop || !panel) return;
|
|
185
|
+
|
|
186
|
+
this.#backdropAnim?.cancel();
|
|
187
|
+
this.#panelAnim?.cancel();
|
|
188
|
+
|
|
189
|
+
this.#backdropAnim = fadeOut(backdrop, { duration: DURATION.normal });
|
|
190
|
+
this.#panelAnim = slideOut(panel, 'down', {
|
|
191
|
+
duration: DURATION.normal,
|
|
192
|
+
easing: EASING.accelerate,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@Component({
|
|
198
|
+
selector: 'ui-action-sheet-title',
|
|
199
|
+
standalone: true,
|
|
200
|
+
imports: [LYNX_ELEMENTS],
|
|
201
|
+
encapsulation: ViewEncapsulation.None,
|
|
202
|
+
template: `
|
|
203
|
+
<view [class]="containerClass()">
|
|
204
|
+
<text [class]="textClass()"><ng-content /></text>
|
|
205
|
+
</view>
|
|
206
|
+
`,
|
|
207
|
+
})
|
|
208
|
+
export class UiActionSheetTitle {
|
|
209
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
210
|
+
|
|
211
|
+
protected readonly containerClass = computed(() =>
|
|
212
|
+
cn('items-center px-4 py-3 flex justify-center', this.userClass()),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
protected readonly textClass = computed(() =>
|
|
216
|
+
cn('text-sm text-muted-foreground'),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@Component({
|
|
221
|
+
selector: 'ui-action-sheet-item',
|
|
222
|
+
standalone: true,
|
|
223
|
+
imports: [LYNX_ELEMENTS],
|
|
224
|
+
encapsulation: ViewEncapsulation.None,
|
|
225
|
+
template: `
|
|
226
|
+
<view
|
|
227
|
+
#container
|
|
228
|
+
[class]="containerClass()"
|
|
229
|
+
(bindtouchstart)="onPressStart()"
|
|
230
|
+
(bindtouchend)="onPressEnd()"
|
|
231
|
+
(bindtouchcancel)="onPressCancel()"
|
|
232
|
+
(bindmousedown)="onPressStart()"
|
|
233
|
+
(bindmouseup)="onPressEnd()"
|
|
234
|
+
(bindmouseleave)="onPressCancel()"
|
|
235
|
+
(bindtap)="onTap()"
|
|
236
|
+
>
|
|
237
|
+
<text [class]="textClass()"><ng-content /></text>
|
|
238
|
+
</view>
|
|
239
|
+
`,
|
|
240
|
+
})
|
|
241
|
+
export class UiActionSheetItem {
|
|
242
|
+
readonly #sheet = inject(UiActionSheet);
|
|
243
|
+
|
|
244
|
+
readonly variant = input<'default' | 'destructive'>('default');
|
|
245
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
246
|
+
|
|
247
|
+
readonly pressed = output<void>();
|
|
248
|
+
|
|
249
|
+
readonly containerRef = viewChild<ElementRef>('container');
|
|
250
|
+
#pressAnim?: AnimationHandle;
|
|
251
|
+
|
|
252
|
+
protected readonly containerClass = computed(() =>
|
|
253
|
+
cn(
|
|
254
|
+
'items-center border-t border-border px-4 py-3 flex justify-center',
|
|
255
|
+
this.userClass(),
|
|
256
|
+
),
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
protected readonly textClass = computed(() =>
|
|
260
|
+
cn(
|
|
261
|
+
'text-base',
|
|
262
|
+
this.variant() === 'destructive' ? 'text-destructive' : 'text-primary',
|
|
263
|
+
),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
protected onPressStart(): void {
|
|
267
|
+
this.#pressAnim?.cancel();
|
|
268
|
+
this.#pressAnim = pressDown(
|
|
269
|
+
this.containerRef()?.nativeElement,
|
|
270
|
+
SCALE.pressDownLight,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
protected onPressEnd(): void {
|
|
275
|
+
this.#pressAnim?.cancel();
|
|
276
|
+
this.#pressAnim = pressRelease(this.containerRef()?.nativeElement);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
protected onPressCancel(): void {
|
|
280
|
+
this.#pressAnim?.cancel();
|
|
281
|
+
this.#pressAnim = pressRelease(this.containerRef()?.nativeElement);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
protected onTap(): void {
|
|
285
|
+
this.pressed.emit();
|
|
286
|
+
this.#sheet.close();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@Component({
|
|
291
|
+
selector: 'ui-action-sheet-cancel',
|
|
292
|
+
standalone: true,
|
|
293
|
+
imports: [LYNX_ELEMENTS],
|
|
294
|
+
encapsulation: ViewEncapsulation.None,
|
|
295
|
+
template: `
|
|
296
|
+
<view
|
|
297
|
+
#container
|
|
298
|
+
[class]="containerClass()"
|
|
299
|
+
style="box-shadow: 0 2px 16px rgba(0, 0, 0, 0.15);"
|
|
300
|
+
(bindtouchstart)="onPressStart()"
|
|
301
|
+
(bindtouchend)="onPressEnd()"
|
|
302
|
+
(bindtouchcancel)="onPressCancel()"
|
|
303
|
+
(bindmousedown)="onPressStart()"
|
|
304
|
+
(bindmouseup)="onPressEnd()"
|
|
305
|
+
(bindmouseleave)="onPressCancel()"
|
|
306
|
+
(bindtap)="onTap()"
|
|
307
|
+
>
|
|
308
|
+
<text [class]="textClass()">{{ label() }}</text>
|
|
309
|
+
</view>
|
|
310
|
+
`,
|
|
311
|
+
})
|
|
312
|
+
export class UiActionSheetCancel {
|
|
313
|
+
readonly #sheet = inject(UiActionSheet);
|
|
314
|
+
|
|
315
|
+
readonly label = input('Cancel');
|
|
316
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
317
|
+
|
|
318
|
+
readonly containerRef = viewChild<ElementRef>('container');
|
|
319
|
+
#pressAnim?: AnimationHandle;
|
|
320
|
+
|
|
321
|
+
protected readonly containerClass = computed(() =>
|
|
322
|
+
cn(
|
|
323
|
+
'items-center rounded-lg border border-border bg-card px-4 py-3 flex justify-center',
|
|
324
|
+
this.userClass(),
|
|
325
|
+
),
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
protected readonly textClass = computed(() =>
|
|
329
|
+
cn('text-base font-semibold text-primary'),
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
protected onPressStart(): void {
|
|
333
|
+
this.#pressAnim?.cancel();
|
|
334
|
+
this.#pressAnim = pressDown(
|
|
335
|
+
this.containerRef()?.nativeElement,
|
|
336
|
+
SCALE.pressDownLight,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
protected onPressEnd(): void {
|
|
341
|
+
this.#pressAnim?.cancel();
|
|
342
|
+
this.#pressAnim = pressRelease(this.containerRef()?.nativeElement);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
protected onPressCancel(): void {
|
|
346
|
+
this.#pressAnim?.cancel();
|
|
347
|
+
this.#pressAnim = pressRelease(this.containerRef()?.nativeElement);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
protected onTap(): void {
|
|
351
|
+
this.#sheet.close();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
3
|
+
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { setInputSignal } from '../../../test-utils/set-input-signal';
|
|
6
|
+
import { UiAlert, alertVariants } from './alert';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
standalone: true,
|
|
10
|
+
imports: [UiAlert, LYNX_ELEMENTS],
|
|
11
|
+
template: `<ui-alert [variant]="variant"><text>Message</text></ui-alert>`,
|
|
12
|
+
})
|
|
13
|
+
class AlertHost {
|
|
14
|
+
variant: 'default' | 'destructive' = 'default';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('alertVariants', () => {
|
|
18
|
+
it('returns default variant classes', () => {
|
|
19
|
+
expect(alertVariants()).toContain('bg-background');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('returns destructive variant classes', () => {
|
|
23
|
+
expect(alertVariants({ variant: 'destructive' })).toContain(
|
|
24
|
+
'bg-destructive-subtle',
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('UiAlert', () => {
|
|
30
|
+
it('renders projected content', async () => {
|
|
31
|
+
const { getByText } = await render(AlertHost);
|
|
32
|
+
expect(getByText('Message')).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders title and description slots when their inputs are set', async () => {
|
|
36
|
+
// JIT template bindings don't propagate through input(); drive the signals
|
|
37
|
+
// directly so the @if(title()) / @if(description()) branches render and
|
|
38
|
+
// their computed class functions run.
|
|
39
|
+
const { componentRef, container } = await render(UiAlert);
|
|
40
|
+
const inst = componentRef.instance as UiAlert;
|
|
41
|
+
setInputSignal(inst.title, 'Heads up');
|
|
42
|
+
setInputSignal(inst.description, 'Your download is ready.');
|
|
43
|
+
await waitForUpdate();
|
|
44
|
+
|
|
45
|
+
const texts = container.querySelectorAll('text');
|
|
46
|
+
expect(texts.length).toBeGreaterThanOrEqual(2);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ElementRef,
|
|
3
|
+
Component,
|
|
4
|
+
ViewEncapsulation,
|
|
5
|
+
computed,
|
|
6
|
+
effect,
|
|
7
|
+
input,
|
|
8
|
+
viewChild,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
11
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
12
|
+
|
|
13
|
+
import { revealIn } from '../../utils/animate';
|
|
14
|
+
import { cn } from '../../utils/cn';
|
|
15
|
+
|
|
16
|
+
export const alertVariants = cva('flex-col rounded-lg border p-4 flex', {
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
default: 'border-border bg-background',
|
|
20
|
+
// Opacity modifiers on semantic colors don't work on Lynx (opaque rgba
|
|
21
|
+
// values with no separable channels): use a solid destructive border and
|
|
22
|
+
// the pre-composed translucent bg-destructive-subtle for the tint.
|
|
23
|
+
destructive: 'border-destructive bg-destructive-subtle',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: 'default',
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const alertTitleVariants = cva('mb-1 font-medium', {
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
default: 'text-foreground',
|
|
35
|
+
destructive: 'text-destructive',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: { variant: 'default' },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const alertDescriptionVariants = cva('text-sm', {
|
|
42
|
+
variants: {
|
|
43
|
+
variant: {
|
|
44
|
+
default: 'text-muted-foreground',
|
|
45
|
+
destructive: 'text-destructive',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
defaultVariants: { variant: 'default' },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type AlertVariant = NonNullable<
|
|
52
|
+
VariantProps<typeof alertVariants>['variant']
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
@Component({
|
|
56
|
+
selector: 'ui-alert',
|
|
57
|
+
standalone: true,
|
|
58
|
+
imports: [LYNX_ELEMENTS],
|
|
59
|
+
encapsulation: ViewEncapsulation.None,
|
|
60
|
+
template: `
|
|
61
|
+
<view #container [class]="containerClass()">
|
|
62
|
+
@if (title()) {
|
|
63
|
+
<text [class]="titleClass()">{{ title() }}</text>
|
|
64
|
+
}
|
|
65
|
+
@if (description()) {
|
|
66
|
+
<text [class]="descriptionClass()">{{ description() }}</text>
|
|
67
|
+
}
|
|
68
|
+
<ng-content />
|
|
69
|
+
</view>
|
|
70
|
+
`,
|
|
71
|
+
})
|
|
72
|
+
export class UiAlert {
|
|
73
|
+
readonly variant = input<AlertVariant>('default');
|
|
74
|
+
readonly title = input<string>('');
|
|
75
|
+
readonly description = input<string>('');
|
|
76
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
77
|
+
|
|
78
|
+
readonly containerRef = viewChild<ElementRef>('container');
|
|
79
|
+
|
|
80
|
+
constructor() {
|
|
81
|
+
// Subtle entrance animation on first render
|
|
82
|
+
effect(() => {
|
|
83
|
+
const el = this.containerRef()?.nativeElement;
|
|
84
|
+
// revealIn calls el.animate() which is inert in jsdom — the animation
|
|
85
|
+
// never plays and the effect can't be verified from a JIT render test.
|
|
86
|
+
/* v8 ignore next 3 */
|
|
87
|
+
if (el) {
|
|
88
|
+
revealIn(el, { fromY: 4 });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected readonly containerClass = computed(() =>
|
|
94
|
+
cn(alertVariants({ variant: this.variant() }), this.userClass()),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
protected readonly titleClass = computed(() =>
|
|
98
|
+
cn(alertTitleVariants({ variant: this.variant() })),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
protected readonly descriptionClass = computed(() =>
|
|
102
|
+
cn(alertDescriptionVariants({ variant: this.variant() })),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { UiAlert, alertVariants, type AlertVariant } from './alert';
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
|
+
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
3
|
+
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { setInputSignal } from '../../../test-utils/set-input-signal';
|
|
6
|
+
import {
|
|
7
|
+
UiAlertDialog,
|
|
8
|
+
UiAlertDialogDescription,
|
|
9
|
+
UiAlertDialogFooter,
|
|
10
|
+
UiAlertDialogHeader,
|
|
11
|
+
UiAlertDialogTitle,
|
|
12
|
+
} from './alert-dialog';
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
standalone: true,
|
|
16
|
+
imports: [
|
|
17
|
+
UiAlertDialog,
|
|
18
|
+
UiAlertDialogHeader,
|
|
19
|
+
UiAlertDialogTitle,
|
|
20
|
+
UiAlertDialogDescription,
|
|
21
|
+
UiAlertDialogFooter,
|
|
22
|
+
LYNX_ELEMENTS,
|
|
23
|
+
],
|
|
24
|
+
template: `
|
|
25
|
+
<ui-alert-dialog [(open)]="open">
|
|
26
|
+
<ui-alert-dialog-header>
|
|
27
|
+
<ui-alert-dialog-title>Sure?</ui-alert-dialog-title>
|
|
28
|
+
<ui-alert-dialog-description>Cannot undo</ui-alert-dialog-description>
|
|
29
|
+
</ui-alert-dialog-header>
|
|
30
|
+
<ui-alert-dialog-footer><text>Buttons</text></ui-alert-dialog-footer>
|
|
31
|
+
</ui-alert-dialog>
|
|
32
|
+
`,
|
|
33
|
+
})
|
|
34
|
+
class AlertDialogHost {
|
|
35
|
+
open = true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('UiAlertDialog', () => {
|
|
39
|
+
it('renders header, title, description, and footer', async () => {
|
|
40
|
+
const { getByText } = await render(AlertDialogHost);
|
|
41
|
+
expect(getByText('Sure?')).toBeTruthy();
|
|
42
|
+
expect(getByText('Cannot undo')).toBeTruthy();
|
|
43
|
+
expect(getByText('Buttons')).toBeTruthy();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('panel tap is a no-op (catchtap stops bubbling)', async () => {
|
|
47
|
+
const { componentRef } = await render(UiAlertDialog);
|
|
48
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
49
|
+
expect(() =>
|
|
50
|
+
(inst as unknown as { onPanelTap: () => void }).onPanelTap(),
|
|
51
|
+
).not.toThrow();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('open effect: showing then hiding runs the animate-in/out lifecycles', async () => {
|
|
55
|
+
// Monkey-patch backdrop/panel refs so #animateIn / #animateOut execute in
|
|
56
|
+
// full instead of bailing on !backdrop || !panel.
|
|
57
|
+
const { componentRef, container } = await render(UiAlertDialog);
|
|
58
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
59
|
+
const view = container.querySelector('view') as Element;
|
|
60
|
+
(inst as unknown as { backdropRef: unknown }).backdropRef = signal({
|
|
61
|
+
nativeElement: view,
|
|
62
|
+
});
|
|
63
|
+
(inst as unknown as { panelRef: unknown }).panelRef = signal({
|
|
64
|
+
nativeElement: view,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
inst.open.set(true);
|
|
68
|
+
await waitForUpdate();
|
|
69
|
+
await waitForUpdate();
|
|
70
|
+
await waitForUpdate();
|
|
71
|
+
|
|
72
|
+
inst.open.set(false);
|
|
73
|
+
await new Promise((r) => setTimeout(r, 320));
|
|
74
|
+
await waitForUpdate();
|
|
75
|
+
expect(inst.open()).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('open effect bails when backdrop/panel refs are undefined', async () => {
|
|
79
|
+
// Covers the early-return branches in #animateIn / #animateOut.
|
|
80
|
+
const { componentRef } = await render(UiAlertDialog);
|
|
81
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
82
|
+
inst.open.set(true);
|
|
83
|
+
await waitForUpdate();
|
|
84
|
+
await waitForUpdate();
|
|
85
|
+
inst.open.set(false);
|
|
86
|
+
await waitForUpdate();
|
|
87
|
+
expect(inst.open()).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('overlayStyle switches between visible and hidden variants', async () => {
|
|
91
|
+
const { componentRef, container } = await render(UiAlertDialog);
|
|
92
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
93
|
+
let overlay = container.querySelector('overlay');
|
|
94
|
+
expect(overlay?.getAttribute('style')).toContain('display: none');
|
|
95
|
+
|
|
96
|
+
const view = container.querySelector('view') as Element;
|
|
97
|
+
(inst as unknown as { backdropRef: unknown }).backdropRef = signal({
|
|
98
|
+
nativeElement: view,
|
|
99
|
+
});
|
|
100
|
+
(inst as unknown as { panelRef: unknown }).panelRef = signal({
|
|
101
|
+
nativeElement: view,
|
|
102
|
+
});
|
|
103
|
+
inst.open.set(true);
|
|
104
|
+
for (let i = 0; i < 10; i++) await waitForUpdate();
|
|
105
|
+
overlay = container.querySelector('overlay');
|
|
106
|
+
expect(overlay?.getAttribute('style')).not.toContain('display: none');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('accepts a user class on the panel', async () => {
|
|
110
|
+
const { componentRef, container } = await render(UiAlertDialog);
|
|
111
|
+
const inst = componentRef.instance as UiAlertDialog;
|
|
112
|
+
setInputSignal(inst.userClass, 'custom-panel');
|
|
113
|
+
await waitForUpdate();
|
|
114
|
+
const views = Array.from(container.querySelectorAll('view'));
|
|
115
|
+
expect(
|
|
116
|
+
views.some((el) => el.getAttribute('class')?.includes('custom-panel')),
|
|
117
|
+
).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('Alert dialog sub-components', () => {
|
|
122
|
+
it('UiAlertDialogHeader renders projected content', async () => {
|
|
123
|
+
const { container } = await render(UiAlertDialogHeader);
|
|
124
|
+
expect(container.querySelector('view')).toBeTruthy();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('UiAlertDialogTitle renders projected content', async () => {
|
|
128
|
+
const { container } = await render(UiAlertDialogTitle);
|
|
129
|
+
expect(container.querySelector('text')).toBeTruthy();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('UiAlertDialogDescription renders projected content', async () => {
|
|
133
|
+
const { container } = await render(UiAlertDialogDescription);
|
|
134
|
+
expect(container.querySelector('text')).toBeTruthy();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('UiAlertDialogFooter renders projected content', async () => {
|
|
138
|
+
const { container } = await render(UiAlertDialogFooter);
|
|
139
|
+
expect(container.querySelector('view')).toBeTruthy();
|
|
140
|
+
});
|
|
141
|
+
});
|