@blotch/dolan 0.0.0-stage → 22.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/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,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ElementRef,
|
|
3
|
+
Component,
|
|
4
|
+
ViewEncapsulation,
|
|
5
|
+
computed,
|
|
6
|
+
effect,
|
|
7
|
+
input,
|
|
8
|
+
model,
|
|
9
|
+
output,
|
|
10
|
+
signal,
|
|
11
|
+
viewChild,
|
|
12
|
+
} from '@angular/core';
|
|
13
|
+
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
type AnimationHandle,
|
|
17
|
+
DURATION,
|
|
18
|
+
EASING,
|
|
19
|
+
SCALE,
|
|
20
|
+
fadeIn,
|
|
21
|
+
fadeOut,
|
|
22
|
+
scaleIn,
|
|
23
|
+
scaleOut,
|
|
24
|
+
} from '../../utils/animate';
|
|
25
|
+
import { cn } from '../../utils/cn';
|
|
26
|
+
|
|
27
|
+
@Component({
|
|
28
|
+
selector: 'ui-alert-dialog',
|
|
29
|
+
standalone: true,
|
|
30
|
+
imports: [LYNX_ELEMENTS],
|
|
31
|
+
encapsulation: ViewEncapsulation.None,
|
|
32
|
+
template: `
|
|
33
|
+
<overlay [attr.visible]="overlayVisible()" [style]="overlayStyle()">
|
|
34
|
+
<view #backdrop [class]="backdropClass()">
|
|
35
|
+
<view #panel [class]="panelClass()" (catchtap)="onPanelTap()">
|
|
36
|
+
<ng-content />
|
|
37
|
+
</view>
|
|
38
|
+
</view>
|
|
39
|
+
</overlay>
|
|
40
|
+
`,
|
|
41
|
+
})
|
|
42
|
+
export class UiAlertDialog {
|
|
43
|
+
readonly open = model(false);
|
|
44
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
45
|
+
|
|
46
|
+
readonly closed = output<void>();
|
|
47
|
+
|
|
48
|
+
protected readonly overlayVisible = signal(false);
|
|
49
|
+
|
|
50
|
+
readonly backdropRef = viewChild<ElementRef>('backdrop');
|
|
51
|
+
readonly panelRef = viewChild<ElementRef>('panel');
|
|
52
|
+
#backdropAnim?: AnimationHandle;
|
|
53
|
+
#panelAnim?: AnimationHandle;
|
|
54
|
+
#hasBeenOpen = false;
|
|
55
|
+
|
|
56
|
+
constructor() {
|
|
57
|
+
// `#hasBeenOpen` prevents the close animation from running on the initial
|
|
58
|
+
// effect evaluation when `open` starts as false. Without this guard, the
|
|
59
|
+
// first run would call #doClose() immediately and emit `closed` before the
|
|
60
|
+
// dialog has ever been opened.
|
|
61
|
+
effect(() => {
|
|
62
|
+
const isOpen = this.open();
|
|
63
|
+
if (isOpen) {
|
|
64
|
+
this.#hasBeenOpen = true;
|
|
65
|
+
this.#doOpen();
|
|
66
|
+
} else if (this.#hasBeenOpen) {
|
|
67
|
+
this.#doClose();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* `width`/`height`/`top`/`left` are web-only necessities: on web
|
|
74
|
+
* `<overlay>` has no intrinsic full-screen sizing (unlike native), so
|
|
75
|
+
* without them it collapses to 0x0 and nothing renders. `z-index` keeps it
|
|
76
|
+
* above sibling content that would otherwise occasionally paint on top.
|
|
77
|
+
*/
|
|
78
|
+
protected readonly overlayStyle = computed(() =>
|
|
79
|
+
this.overlayVisible()
|
|
80
|
+
? 'position: fixed; overflow: visible; width: 100vw; height: 100vh; top: 0; left: 0; z-index: 999;'
|
|
81
|
+
: 'position: fixed; overflow: visible; width: 100vw; height: 100vh; top: 0; left: 0; z-index: 999; display: none;',
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// `bg-black/50` supplies the dim. It lives on the backdrop specifically so
|
|
85
|
+
// the existing fadeIn/fadeOut (opacity 0↔1, staggered 30ms ahead of the
|
|
86
|
+
// panel scale-in) animate the dim in lockstep with the dialog appearing —
|
|
87
|
+
// no separate animation needed. A named color with an opacity modifier is
|
|
88
|
+
// used deliberately: it compiles to `rgb(0 0 0 / 0.5)`, which Lynx accepts,
|
|
89
|
+
// whereas a semantic token like `bg-background/50` would render transparent.
|
|
90
|
+
protected readonly backdropClass = computed(() =>
|
|
91
|
+
cn('items-center flex justify-center', 'h-full w-full', 'bg-black/50'),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
protected readonly panelClass = computed(() =>
|
|
95
|
+
cn(
|
|
96
|
+
'w-4/5 flex-col rounded-lg border border-border bg-background p-6 flex',
|
|
97
|
+
this.userClass(),
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* No-op tap handler for the panel. `catchtap` (vs `bindtap`) already stops
|
|
103
|
+
* the tap from bubbling to the backdrop — Lynx controls propagation via the
|
|
104
|
+
* event prefix, not at runtime. (The renderer shims `event.stopPropagation()`
|
|
105
|
+
* as a no-op so DOM-style handlers don't crash, but it has no effect here.)
|
|
106
|
+
*/
|
|
107
|
+
protected onPanelTap(): void {}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Two-phase open: make overlay visible first (so native elements exist in
|
|
111
|
+
* the tree), then animate on the next frame. See nav-drawer for the same
|
|
112
|
+
* pattern — Lynx animate() requires elements to be mounted before use.
|
|
113
|
+
*/
|
|
114
|
+
#doOpen(): void {
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
this.overlayVisible.set(true);
|
|
117
|
+
setTimeout(() => this.#animateIn(), 0);
|
|
118
|
+
}, 0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Animate out before hiding; +20ms guards against Lynx timer imprecision
|
|
123
|
+
* causing elements to disappear before the animation finishes.
|
|
124
|
+
*/
|
|
125
|
+
#doClose(): void {
|
|
126
|
+
setTimeout(() => {
|
|
127
|
+
this.#animateOut();
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
this.overlayVisible.set(false);
|
|
130
|
+
this.closed.emit();
|
|
131
|
+
}, DURATION.normal + 20);
|
|
132
|
+
}, 0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#animateIn(): void {
|
|
136
|
+
const backdrop = this.backdropRef()?.nativeElement;
|
|
137
|
+
const panel = this.panelRef()?.nativeElement;
|
|
138
|
+
if (!backdrop || !panel) return;
|
|
139
|
+
|
|
140
|
+
this.#backdropAnim?.cancel();
|
|
141
|
+
this.#panelAnim?.cancel();
|
|
142
|
+
|
|
143
|
+
this.#backdropAnim = fadeIn(backdrop, { duration: DURATION.normal });
|
|
144
|
+
|
|
145
|
+
// 30ms stagger: let the backdrop fade start first so the panel scale-in
|
|
146
|
+
// feels like it's emerging from behind the dimmed overlay rather than
|
|
147
|
+
// appearing simultaneously with it.
|
|
148
|
+
setTimeout(() => {
|
|
149
|
+
this.#panelAnim = scaleIn(panel, {
|
|
150
|
+
duration: DURATION.slow,
|
|
151
|
+
easing: EASING.springSubtle,
|
|
152
|
+
fromScale: SCALE.dialogFrom,
|
|
153
|
+
fromY: 10,
|
|
154
|
+
});
|
|
155
|
+
}, 30);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#animateOut(): void {
|
|
159
|
+
const backdrop = this.backdropRef()?.nativeElement;
|
|
160
|
+
const panel = this.panelRef()?.nativeElement;
|
|
161
|
+
if (!backdrop || !panel) return;
|
|
162
|
+
|
|
163
|
+
this.#backdropAnim?.cancel();
|
|
164
|
+
this.#panelAnim?.cancel();
|
|
165
|
+
|
|
166
|
+
this.#panelAnim = scaleOut(panel, {
|
|
167
|
+
duration: 180,
|
|
168
|
+
easing: EASING.accelerate,
|
|
169
|
+
toScale: SCALE.dialogTo,
|
|
170
|
+
toY: 10,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
this.#backdropAnim = fadeOut(backdrop, { duration: DURATION.normal });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@Component({
|
|
178
|
+
selector: 'ui-alert-dialog-header',
|
|
179
|
+
standalone: true,
|
|
180
|
+
imports: [LYNX_ELEMENTS],
|
|
181
|
+
encapsulation: ViewEncapsulation.None,
|
|
182
|
+
template: `
|
|
183
|
+
<view [class]="containerClass()">
|
|
184
|
+
<ng-content />
|
|
185
|
+
</view>
|
|
186
|
+
`,
|
|
187
|
+
})
|
|
188
|
+
export class UiAlertDialogHeader {
|
|
189
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
190
|
+
|
|
191
|
+
protected readonly containerClass = computed(() =>
|
|
192
|
+
cn('flex-col gap-1.5 flex', this.userClass()),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@Component({
|
|
197
|
+
selector: 'ui-alert-dialog-title',
|
|
198
|
+
standalone: true,
|
|
199
|
+
imports: [LYNX_ELEMENTS],
|
|
200
|
+
encapsulation: ViewEncapsulation.None,
|
|
201
|
+
template: `<text [class]="textClass()"><ng-content /></text>`,
|
|
202
|
+
})
|
|
203
|
+
export class UiAlertDialogTitle {
|
|
204
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
205
|
+
|
|
206
|
+
protected readonly textClass = computed(() =>
|
|
207
|
+
cn('text-lg font-semibold text-foreground', this.userClass()),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@Component({
|
|
212
|
+
selector: 'ui-alert-dialog-description',
|
|
213
|
+
standalone: true,
|
|
214
|
+
imports: [LYNX_ELEMENTS],
|
|
215
|
+
encapsulation: ViewEncapsulation.None,
|
|
216
|
+
template: `<text [class]="textClass()"><ng-content /></text>`,
|
|
217
|
+
})
|
|
218
|
+
export class UiAlertDialogDescription {
|
|
219
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
220
|
+
|
|
221
|
+
protected readonly textClass = computed(() =>
|
|
222
|
+
cn('text-sm text-muted-foreground', this.userClass()),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@Component({
|
|
227
|
+
selector: 'ui-alert-dialog-footer',
|
|
228
|
+
standalone: true,
|
|
229
|
+
imports: [LYNX_ELEMENTS],
|
|
230
|
+
encapsulation: ViewEncapsulation.None,
|
|
231
|
+
template: `
|
|
232
|
+
<view [class]="containerClass()">
|
|
233
|
+
<ng-content />
|
|
234
|
+
</view>
|
|
235
|
+
`,
|
|
236
|
+
})
|
|
237
|
+
export class UiAlertDialogFooter {
|
|
238
|
+
readonly userClass = input<string>('', { alias: 'class' });
|
|
239
|
+
|
|
240
|
+
protected readonly containerClass = computed(() =>
|
|
241
|
+
cn('flex-row gap-2 pt-4 flex justify-end', this.userClass()),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { setInputSignal } from '../../../test-utils/set-input-signal';
|
|
4
|
+
import { UiCartesianChart } from '../cartesian-chart/cartesian-chart';
|
|
5
|
+
import { UiAreaChart, UiAreaSeries } from './area-chart';
|
|
6
|
+
|
|
7
|
+
describe('UiAreaChart', () => {
|
|
8
|
+
it('renders with default configuration', async () => {
|
|
9
|
+
const { container } = await render(UiAreaChart);
|
|
10
|
+
expect(container).toBeTruthy();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('folds the baseline into the y-axis domain', async () => {
|
|
14
|
+
const { componentRef } = await render(UiAreaChart);
|
|
15
|
+
const inst = componentRef.instance as UiAreaChart;
|
|
16
|
+
setInputSignal(inst.data, [
|
|
17
|
+
{ x: 0, y: 5 },
|
|
18
|
+
{ x: 5, y: 10 },
|
|
19
|
+
{ x: 10, y: 8 },
|
|
20
|
+
]);
|
|
21
|
+
setInputSignal(inst.baseline, 0);
|
|
22
|
+
await waitForUpdate();
|
|
23
|
+
const cast = inst as unknown as {
|
|
24
|
+
xAxis: () => { domain: readonly [number, number]; ticks: number[] };
|
|
25
|
+
yAxis: () => { domain: readonly [number, number]; ticks: number[] };
|
|
26
|
+
};
|
|
27
|
+
expect(cast.xAxis().domain[0]).toBeLessThanOrEqual(0);
|
|
28
|
+
expect(cast.xAxis().domain[1]).toBeGreaterThanOrEqual(10);
|
|
29
|
+
expect(cast.yAxis().domain[0]).toBeLessThanOrEqual(0);
|
|
30
|
+
expect(cast.yAxis().domain[1]).toBeGreaterThanOrEqual(10);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('honors explicit xMin / xMax / yMin / yMax overrides', async () => {
|
|
34
|
+
const { componentRef } = await render(UiAreaChart);
|
|
35
|
+
const inst = componentRef.instance as UiAreaChart;
|
|
36
|
+
setInputSignal(inst.data, [
|
|
37
|
+
{ x: 0, y: 0 },
|
|
38
|
+
{ x: 10, y: 10 },
|
|
39
|
+
]);
|
|
40
|
+
setInputSignal(inst.xMin, -5);
|
|
41
|
+
setInputSignal(inst.xMax, 20);
|
|
42
|
+
setInputSignal(inst.yMin, -1);
|
|
43
|
+
setInputSignal(inst.yMax, 15);
|
|
44
|
+
await waitForUpdate();
|
|
45
|
+
const cast = inst as unknown as {
|
|
46
|
+
xAxis: () => { domain: readonly [number, number] };
|
|
47
|
+
yAxis: () => { domain: readonly [number, number] };
|
|
48
|
+
};
|
|
49
|
+
expect(cast.xAxis().domain[0]).toBeLessThanOrEqual(-5);
|
|
50
|
+
expect(cast.xAxis().domain[1]).toBeGreaterThanOrEqual(20);
|
|
51
|
+
expect(cast.yAxis().domain[0]).toBeLessThanOrEqual(-1);
|
|
52
|
+
expect(cast.yAxis().domain[1]).toBeGreaterThanOrEqual(15);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('exposes the default tick formatter used by the child cartesian chart', async () => {
|
|
56
|
+
const { componentRef } = await render(UiAreaChart);
|
|
57
|
+
const inst = componentRef.instance as UiAreaChart;
|
|
58
|
+
// JIT doesn't wire the child cartesian chart's [xTickFormat] input, so the
|
|
59
|
+
// default formatter never fires through the template. Invoke it directly.
|
|
60
|
+
expect(inst.xTickFormat()(3.14159)).toBe('3.14');
|
|
61
|
+
expect(inst.yTickFormat()(1.005)).toBe('1');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('UiAreaSeries', () => {
|
|
66
|
+
const renderSeries = async () => {
|
|
67
|
+
const parentResult = await render(UiCartesianChart);
|
|
68
|
+
const parent = parentResult.componentRef.instance as UiCartesianChart;
|
|
69
|
+
setInputSignal(parent.xDomain, [0, 10]);
|
|
70
|
+
setInputSignal(parent.yDomain, [0, 10]);
|
|
71
|
+
await waitForUpdate();
|
|
72
|
+
const seriesResult = await render(UiAreaSeries, {
|
|
73
|
+
providers: [{ provide: UiCartesianChart, useValue: parent }],
|
|
74
|
+
});
|
|
75
|
+
const series = seriesResult.componentRef.instance as UiAreaSeries;
|
|
76
|
+
setInputSignal(series.data, [
|
|
77
|
+
{ x: 0, y: 0 },
|
|
78
|
+
{ x: 5, y: 5 },
|
|
79
|
+
{ x: 10, y: 10 },
|
|
80
|
+
]);
|
|
81
|
+
await waitForUpdate();
|
|
82
|
+
return { parent, series };
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
it('renders fill columns and a translucent fill layer', async () => {
|
|
86
|
+
const { series } = await renderSeries();
|
|
87
|
+
const cast = series as unknown as {
|
|
88
|
+
columns: () => { style: string }[];
|
|
89
|
+
fillLayerStyle: () => string;
|
|
90
|
+
};
|
|
91
|
+
expect(cast.columns().length).toBeGreaterThan(0);
|
|
92
|
+
expect(cast.fillLayerStyle()).toContain('opacity');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('smooth mode densely resamples the fill columns', async () => {
|
|
96
|
+
const { series } = await renderSeries();
|
|
97
|
+
const cast = series as unknown as { columns: () => { style: string }[] };
|
|
98
|
+
const straight = cast.columns().length;
|
|
99
|
+
setInputSignal(series.smooth, true);
|
|
100
|
+
await waitForUpdate();
|
|
101
|
+
// Smoothed curve produces at least as many columns as the straight one.
|
|
102
|
+
expect(cast.columns().length).toBeGreaterThanOrEqual(straight);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('emits pointTap when the nested line series taps', async () => {
|
|
106
|
+
const { series } = await renderSeries();
|
|
107
|
+
const cb = vi.fn();
|
|
108
|
+
series.pointTap.subscribe(cb);
|
|
109
|
+
series.pointTap.emit({ x: 1, y: 2 });
|
|
110
|
+
expect(cb).toHaveBeenCalledWith({ x: 1, y: 2 });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('clamps the baseline into the plot when it lies outside the domain', async () => {
|
|
114
|
+
const { series } = await renderSeries();
|
|
115
|
+
// baseline > yDomain upper bound → clamp to top of plot; columns still render
|
|
116
|
+
setInputSignal(series.baseline, 999);
|
|
117
|
+
await waitForUpdate();
|
|
118
|
+
const cast = series as unknown as { columns: () => { style: string }[] };
|
|
119
|
+
expect(Array.isArray(cast.columns())).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock Angular (and the transitively-loaded line-chart / cartesian-chart Angular
|
|
4
|
+
// usage) so importing the module needs no framework runtime. Only the exported
|
|
5
|
+
// pure `computeAreaColumns` is exercised; components are never built.
|
|
6
|
+
vi.mock('@angular/core', () => ({
|
|
7
|
+
Component: () => () => {},
|
|
8
|
+
ViewEncapsulation: { None: 0 },
|
|
9
|
+
computed: () => () => {},
|
|
10
|
+
inject: () => ({}),
|
|
11
|
+
input: () => () => {},
|
|
12
|
+
output: () => () => {},
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock('@blotch/angular-lynx', () => ({
|
|
16
|
+
LYNX_ELEMENTS: [],
|
|
17
|
+
// cartesian-chart (imported transitively) registers these in its decorator.
|
|
18
|
+
LynxGestureDetector: class {},
|
|
19
|
+
PanGesture: class {},
|
|
20
|
+
PinchGesture: class {},
|
|
21
|
+
Gesture: { Simultaneous: () => ({}) },
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
const { computeAreaColumns } = await import('./area-chart');
|
|
25
|
+
|
|
26
|
+
describe('computeAreaColumns', () => {
|
|
27
|
+
it('fills a flat line down to the baseline with uniform columns', () => {
|
|
28
|
+
// Horizontal line at pixel y=20, baseline at y=100, 30px plot, 10px strips.
|
|
29
|
+
const columns = computeAreaColumns(
|
|
30
|
+
[
|
|
31
|
+
{ x: 0, y: 20 },
|
|
32
|
+
{ x: 30, y: 20 },
|
|
33
|
+
],
|
|
34
|
+
100,
|
|
35
|
+
30,
|
|
36
|
+
10,
|
|
37
|
+
);
|
|
38
|
+
expect(columns.map((c) => c.x)).toEqual([0, 10, 20]);
|
|
39
|
+
for (const column of columns) {
|
|
40
|
+
expect(column.top).toBeCloseTo(20);
|
|
41
|
+
expect(column.height).toBeCloseTo(80);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('fills on both sides when the line crosses the baseline', () => {
|
|
46
|
+
// Line y = x from (0,0) to (100,100); baseline pixel y = 50.
|
|
47
|
+
const columns = computeAreaColumns(
|
|
48
|
+
[
|
|
49
|
+
{ x: 0, y: 0 },
|
|
50
|
+
{ x: 100, y: 100 },
|
|
51
|
+
],
|
|
52
|
+
50,
|
|
53
|
+
100,
|
|
54
|
+
50,
|
|
55
|
+
);
|
|
56
|
+
expect(columns).toHaveLength(2);
|
|
57
|
+
// Strip centered at x=25 → line above the baseline: top at the line, down to it.
|
|
58
|
+
expect(columns[0].top).toBeCloseTo(25);
|
|
59
|
+
expect(columns[0].height).toBeCloseTo(25);
|
|
60
|
+
// Strip centered at x=75 → line below the baseline: top at the baseline.
|
|
61
|
+
expect(columns[1].top).toBeCloseTo(50);
|
|
62
|
+
expect(columns[1].height).toBeCloseTo(25);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('skips strips whose center falls outside the data x-range', () => {
|
|
66
|
+
// Data spans x=[40,60] but the plot is 100 wide → only strips inside fill.
|
|
67
|
+
const columns = computeAreaColumns(
|
|
68
|
+
[
|
|
69
|
+
{ x: 40, y: 10 },
|
|
70
|
+
{ x: 60, y: 10 },
|
|
71
|
+
],
|
|
72
|
+
100,
|
|
73
|
+
100,
|
|
74
|
+
10,
|
|
75
|
+
);
|
|
76
|
+
expect(columns.map((c) => c.x)).toEqual([40, 50]);
|
|
77
|
+
expect(columns.every((c) => c.x >= 40 && c.x <= 60)).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('skips columns where the line sits on the baseline', () => {
|
|
81
|
+
// Flat line exactly at the baseline → zero height everywhere, nothing to fill.
|
|
82
|
+
const columns = computeAreaColumns(
|
|
83
|
+
[
|
|
84
|
+
{ x: 0, y: 50 },
|
|
85
|
+
{ x: 30, y: 50 },
|
|
86
|
+
],
|
|
87
|
+
50,
|
|
88
|
+
30,
|
|
89
|
+
10,
|
|
90
|
+
);
|
|
91
|
+
expect(columns).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('returns no columns for fewer than two points', () => {
|
|
95
|
+
expect(computeAreaColumns([], 100, 100, 10)).toEqual([]);
|
|
96
|
+
expect(computeAreaColumns([{ x: 0, y: 0 }], 100, 100, 10)).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('returns no columns for a non-positive plot width', () => {
|
|
100
|
+
expect(
|
|
101
|
+
computeAreaColumns(
|
|
102
|
+
[
|
|
103
|
+
{ x: 0, y: 0 },
|
|
104
|
+
{ x: 10, y: 10 },
|
|
105
|
+
],
|
|
106
|
+
100,
|
|
107
|
+
0,
|
|
108
|
+
10,
|
|
109
|
+
),
|
|
110
|
+
).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('handles duplicate consecutive x values without dividing by zero', () => {
|
|
114
|
+
// Two points share x=10 → span === 0 branch. Choose plotWidth/stripWidth
|
|
115
|
+
// so that a strip's center lands exactly on x=10: stripWidth=20 puts the
|
|
116
|
+
// very first strip's center at x=0 + 20/2 = 10.
|
|
117
|
+
const columns = computeAreaColumns(
|
|
118
|
+
[
|
|
119
|
+
{ x: 10, y: 20 },
|
|
120
|
+
{ x: 10, y: 40 },
|
|
121
|
+
],
|
|
122
|
+
100,
|
|
123
|
+
21,
|
|
124
|
+
20,
|
|
125
|
+
);
|
|
126
|
+
// Strip at x=0 has center=10 which is inside the (degenerate) segment.
|
|
127
|
+
expect(columns.length).toBeGreaterThan(0);
|
|
128
|
+
expect(columns[0].top).toBeCloseTo(20);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('falls back to the final y when interpolation hits the last-point edge', () => {
|
|
132
|
+
// Single-point case: points.length - 1 === 0 so the for-loop never runs.
|
|
133
|
+
// With x === first.x === last.x, `interpolateY` falls through to the
|
|
134
|
+
// defensive `return last.y` branch. Since a single point can't produce a
|
|
135
|
+
// fill on its own, `computeAreaColumns` returns [] due to the `< 2` guard —
|
|
136
|
+
// so drive the branch via a duplicate-x-at-both-ends 3-point series where
|
|
137
|
+
// the intermediate segment is degenerate but sits inside the domain.
|
|
138
|
+
// Easier: 3 collinear points where the middle segment has span=0 and the
|
|
139
|
+
// sample lands there but is *equal* to the endpoints.
|
|
140
|
+
// {x:0}, {x:5}, {x:5}, {x:10} - with samples at x=5.
|
|
141
|
+
// Actually the simplest reachable case is: [{x:5,y:1},{x:5,y:2}] with a
|
|
142
|
+
// strip whose center = 5. Then first.x=5, last.x=5, x >= first.x && x <= last.x
|
|
143
|
+
// enters loop, p1=p2 with span=0 → returns p1.y in the loop. Not the fallback.
|
|
144
|
+
// The fallback is only reachable if points contains a "gap": e.g. after the
|
|
145
|
+
// duplicate, but that violates the sorted-input contract. Accept as
|
|
146
|
+
// defensive — see the v8 ignore in the source.
|
|
147
|
+
const columns = computeAreaColumns(
|
|
148
|
+
[
|
|
149
|
+
{ x: 0, y: 0 },
|
|
150
|
+
{ x: 10, y: 10 },
|
|
151
|
+
],
|
|
152
|
+
100,
|
|
153
|
+
10,
|
|
154
|
+
10,
|
|
155
|
+
);
|
|
156
|
+
expect(columns.length).toBeGreaterThan(0);
|
|
157
|
+
});
|
|
158
|
+
});
|