@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,544 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared animation utility for @blotch/ui components.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes all motion constants (durations, easing curves, scale factors)
|
|
5
|
+
* and provides reusable animation helpers that wrap the Lynx element.animate() API.
|
|
6
|
+
* This ensures a consistent, cohesive motion language across the entire component library.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Constants
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Duration constants in milliseconds
|
|
15
|
+
*/
|
|
16
|
+
export const DURATION = {
|
|
17
|
+
/**
|
|
18
|
+
* Micro-interactions: checkbox tick, radio dot
|
|
19
|
+
*/
|
|
20
|
+
instant: 100,
|
|
21
|
+
/**
|
|
22
|
+
* Press/release, thumb slide
|
|
23
|
+
*/
|
|
24
|
+
fast: 150,
|
|
25
|
+
/**
|
|
26
|
+
* Overlays, content transitions
|
|
27
|
+
*/
|
|
28
|
+
normal: 250,
|
|
29
|
+
/**
|
|
30
|
+
* Sheet/drawer slide, accordion expand
|
|
31
|
+
*/
|
|
32
|
+
slow: 350,
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Easing curves for different animation contexts
|
|
37
|
+
*/
|
|
38
|
+
export const EASING = {
|
|
39
|
+
/**
|
|
40
|
+
* Entering elements — fast start, gentle stop
|
|
41
|
+
*/
|
|
42
|
+
decelerate: 'cubic-bezier(0.0, 0.0, 0.2, 1)',
|
|
43
|
+
/**
|
|
44
|
+
* Exiting elements — gentle start, fast stop
|
|
45
|
+
*/
|
|
46
|
+
accelerate: 'cubic-bezier(0.4, 0.0, 1, 1)',
|
|
47
|
+
/**
|
|
48
|
+
* General purpose movement
|
|
49
|
+
*/
|
|
50
|
+
standard: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
|
|
51
|
+
/**
|
|
52
|
+
* Bouncy overshoot — playful, energetic entrance
|
|
53
|
+
*/
|
|
54
|
+
spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
55
|
+
/**
|
|
56
|
+
* Gentle overshoot — refined entrance
|
|
57
|
+
*/
|
|
58
|
+
springSubtle: 'cubic-bezier(0.22, 1.2, 0.36, 1)',
|
|
59
|
+
/**
|
|
60
|
+
* iOS-style sheet/drawer slide
|
|
61
|
+
*/
|
|
62
|
+
sheet: 'cubic-bezier(0.32, 0.72, 0, 1)',
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Scale factors for transform animations
|
|
67
|
+
*/
|
|
68
|
+
export const SCALE = {
|
|
69
|
+
/**
|
|
70
|
+
* Button/toggle press
|
|
71
|
+
*/
|
|
72
|
+
pressDown: 0.97,
|
|
73
|
+
/**
|
|
74
|
+
* List item / card press (lighter)
|
|
75
|
+
*/
|
|
76
|
+
pressDownLight: 0.985,
|
|
77
|
+
/**
|
|
78
|
+
* Small elements popping in (check, radio dot)
|
|
79
|
+
*/
|
|
80
|
+
popFrom: 0.6,
|
|
81
|
+
/**
|
|
82
|
+
* Dialog entrance starting scale
|
|
83
|
+
*/
|
|
84
|
+
dialogFrom: 0.9,
|
|
85
|
+
/**
|
|
86
|
+
* Dialog exit ending scale
|
|
87
|
+
*/
|
|
88
|
+
dialogTo: 0.95,
|
|
89
|
+
} as const;
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Types
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Options shared by most animation helpers
|
|
97
|
+
*/
|
|
98
|
+
export type AnimateOptions = {
|
|
99
|
+
duration?: number;
|
|
100
|
+
easing?: string;
|
|
101
|
+
fill?: 'none' | 'forwards' | 'backwards' | 'both';
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A cancelable animation handle returned by Lynx element.animate()
|
|
106
|
+
*/
|
|
107
|
+
export type AnimationHandle = {
|
|
108
|
+
cancel(): void;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Primitive Helpers
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Fades an element in (opacity 0 → 1).
|
|
117
|
+
* Used for backdrops, content reveals, and soft entrances.
|
|
118
|
+
*/
|
|
119
|
+
export const fadeIn = (
|
|
120
|
+
el: any,
|
|
121
|
+
options?: AnimateOptions,
|
|
122
|
+
): AnimationHandle | undefined => {
|
|
123
|
+
if (!el) return undefined;
|
|
124
|
+
return el.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
125
|
+
duration: options?.duration ?? DURATION.normal,
|
|
126
|
+
easing: options?.easing ?? EASING.decelerate,
|
|
127
|
+
fill: options?.fill ?? 'forwards',
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Fades an element out (opacity 1 → 0).
|
|
133
|
+
* Used for backdrops, content dismissals, and soft exits.
|
|
134
|
+
*/
|
|
135
|
+
export const fadeOut = (
|
|
136
|
+
el: any,
|
|
137
|
+
options?: AnimateOptions,
|
|
138
|
+
): AnimationHandle | undefined => {
|
|
139
|
+
if (!el) return undefined;
|
|
140
|
+
return el.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
141
|
+
duration: options?.duration ?? DURATION.fast,
|
|
142
|
+
easing: options?.easing ?? EASING.accelerate,
|
|
143
|
+
fill: options?.fill ?? 'forwards',
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Scales and fades an element in — used for overlay panels (dialog, alert-dialog).
|
|
149
|
+
* Animates from a smaller scale + offset to full size.
|
|
150
|
+
*/
|
|
151
|
+
export const scaleIn = (
|
|
152
|
+
el: any,
|
|
153
|
+
options?: AnimateOptions & { fromScale?: number; fromY?: number },
|
|
154
|
+
): AnimationHandle | undefined => {
|
|
155
|
+
if (!el) return undefined;
|
|
156
|
+
const fromScale = options?.fromScale ?? SCALE.dialogFrom;
|
|
157
|
+
const fromY = options?.fromY ?? 10;
|
|
158
|
+
return el.animate(
|
|
159
|
+
[
|
|
160
|
+
{ transform: `scale(${fromScale}) translateY(${fromY}px)`, opacity: 0 },
|
|
161
|
+
{ transform: 'scale(1) translateY(0px)', opacity: 1 },
|
|
162
|
+
],
|
|
163
|
+
{
|
|
164
|
+
duration: options?.duration ?? DURATION.slow,
|
|
165
|
+
easing: options?.easing ?? EASING.springSubtle,
|
|
166
|
+
fill: options?.fill ?? 'forwards',
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Scales and fades an element out — used for overlay panel exit.
|
|
173
|
+
*/
|
|
174
|
+
export const scaleOut = (
|
|
175
|
+
el: any,
|
|
176
|
+
options?: AnimateOptions & { toScale?: number; toY?: number },
|
|
177
|
+
): AnimationHandle | undefined => {
|
|
178
|
+
if (!el) return undefined;
|
|
179
|
+
const toScale = options?.toScale ?? SCALE.dialogTo;
|
|
180
|
+
const toY = options?.toY ?? 10;
|
|
181
|
+
return el.animate(
|
|
182
|
+
[
|
|
183
|
+
{ transform: 'scale(1) translateY(0px)', opacity: 1 },
|
|
184
|
+
{ transform: `scale(${toScale}) translateY(${toY}px)`, opacity: 0 },
|
|
185
|
+
],
|
|
186
|
+
{
|
|
187
|
+
duration: options?.duration ?? DURATION.fast,
|
|
188
|
+
easing: options?.easing ?? EASING.accelerate,
|
|
189
|
+
fill: options?.fill ?? 'forwards',
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Slides an element in from a direction.
|
|
196
|
+
* Used for sheets (bottom), nav-drawers (left/right), toasts (bottom).
|
|
197
|
+
*/
|
|
198
|
+
export const slideIn = (
|
|
199
|
+
el: any,
|
|
200
|
+
direction: 'up' | 'down' | 'left' | 'right',
|
|
201
|
+
options?: AnimateOptions,
|
|
202
|
+
): AnimationHandle | undefined => {
|
|
203
|
+
if (!el) return undefined;
|
|
204
|
+
const translate = {
|
|
205
|
+
up: ['translateY(100%)', 'translateY(0%)'],
|
|
206
|
+
down: ['translateY(-100%)', 'translateY(0%)'],
|
|
207
|
+
left: ['translateX(-100%)', 'translateX(0%)'],
|
|
208
|
+
right: ['translateX(100%)', 'translateX(0%)'],
|
|
209
|
+
}[direction];
|
|
210
|
+
return el.animate(
|
|
211
|
+
[
|
|
212
|
+
{ transform: translate[0], opacity: 0.8 },
|
|
213
|
+
{ transform: translate[1], opacity: 1 },
|
|
214
|
+
],
|
|
215
|
+
{
|
|
216
|
+
duration: options?.duration ?? DURATION.slow,
|
|
217
|
+
easing: options?.easing ?? EASING.sheet,
|
|
218
|
+
fill: options?.fill ?? 'forwards',
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Slides an element out to a direction.
|
|
225
|
+
*/
|
|
226
|
+
export const slideOut = (
|
|
227
|
+
el: any,
|
|
228
|
+
direction: 'up' | 'down' | 'left' | 'right',
|
|
229
|
+
options?: AnimateOptions,
|
|
230
|
+
): AnimationHandle | undefined => {
|
|
231
|
+
if (!el) return undefined;
|
|
232
|
+
const translate = {
|
|
233
|
+
up: ['translateY(0%)', 'translateY(-100%)'],
|
|
234
|
+
down: ['translateY(0%)', 'translateY(100%)'],
|
|
235
|
+
left: ['translateX(0%)', 'translateX(-100%)'],
|
|
236
|
+
right: ['translateX(0%)', 'translateX(100%)'],
|
|
237
|
+
}[direction];
|
|
238
|
+
return el.animate(
|
|
239
|
+
[
|
|
240
|
+
{ transform: translate[0], opacity: 1 },
|
|
241
|
+
{ transform: translate[1], opacity: 0.8 },
|
|
242
|
+
],
|
|
243
|
+
{
|
|
244
|
+
duration: options?.duration ?? DURATION.normal,
|
|
245
|
+
easing: options?.easing ?? EASING.accelerate,
|
|
246
|
+
fill: options?.fill ?? 'forwards',
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Pops a small element in with spring physics.
|
|
253
|
+
* Used for checkbox marks, radio dots, badges.
|
|
254
|
+
*/
|
|
255
|
+
export const popIn = (
|
|
256
|
+
el: any,
|
|
257
|
+
options?: AnimateOptions & { fromScale?: number },
|
|
258
|
+
): AnimationHandle | undefined => {
|
|
259
|
+
if (!el) return undefined;
|
|
260
|
+
const from = options?.fromScale ?? SCALE.popFrom;
|
|
261
|
+
return el.animate(
|
|
262
|
+
[
|
|
263
|
+
{ transform: `scale(${from})`, opacity: 0 },
|
|
264
|
+
{ transform: 'scale(1)', opacity: 1 },
|
|
265
|
+
],
|
|
266
|
+
{
|
|
267
|
+
duration: options?.duration ?? DURATION.fast,
|
|
268
|
+
easing: options?.easing ?? EASING.spring,
|
|
269
|
+
fill: options?.fill ?? 'forwards',
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Pops a small element out (reverse of popIn).
|
|
276
|
+
*/
|
|
277
|
+
export const popOut = (
|
|
278
|
+
el: any,
|
|
279
|
+
options?: AnimateOptions & { toScale?: number },
|
|
280
|
+
): AnimationHandle | undefined => {
|
|
281
|
+
if (!el) return undefined;
|
|
282
|
+
const to = options?.toScale ?? 0.8;
|
|
283
|
+
return el.animate(
|
|
284
|
+
[
|
|
285
|
+
{ transform: 'scale(1)', opacity: 1 },
|
|
286
|
+
{ transform: `scale(${to})`, opacity: 0 },
|
|
287
|
+
],
|
|
288
|
+
{
|
|
289
|
+
duration: options?.duration ?? DURATION.instant,
|
|
290
|
+
easing: options?.easing ?? EASING.accelerate,
|
|
291
|
+
fill: options?.fill ?? 'forwards',
|
|
292
|
+
},
|
|
293
|
+
);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// Press Feedback Helpers
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Drives a press-feedback transform with an inline CSS transition instead of
|
|
302
|
+
* the imperative `element.animate()` (Web Animations) API.
|
|
303
|
+
*
|
|
304
|
+
* WHY NOT `el.animate()`: it plays on iOS/Android (native runs the renderer on
|
|
305
|
+
* the main thread, where `__ElementAnimate` maps to a real native animation)
|
|
306
|
+
* but is a DEAD no-op on Lynx web. On web the app runs inside `@lynx-js/web-core`'s
|
|
307
|
+
* worker against an OFFSCREEN document; `element.animate()` there creates a live
|
|
308
|
+
* Web-Animation on the offscreen node that is never serialized to the real DOM,
|
|
309
|
+
* so the on-screen element never moves. (Verified in a headless browser: the
|
|
310
|
+
* press handlers fire and `el.element.animate()` runs without error, yet the
|
|
311
|
+
* rendered node's computed transform stays `none`.) Inline STYLE mutations, by
|
|
312
|
+
* contrast, ARE serialized offscreen→real — that is how every `[style.*]`
|
|
313
|
+
* binding renders on web — so a `transition` + `transform` written via
|
|
314
|
+
* `setStyle` animates identically on native and web.
|
|
315
|
+
*
|
|
316
|
+
* The "from" state is always the element's current transform, so setting the
|
|
317
|
+
* `transition` and the new `transform` together reliably triggers the
|
|
318
|
+
* transition on both platforms (no from-state reflow needed, unlike an
|
|
319
|
+
* entrance animation would require).
|
|
320
|
+
*/
|
|
321
|
+
const transitionTransform = (
|
|
322
|
+
el: any,
|
|
323
|
+
transform: string,
|
|
324
|
+
duration: number,
|
|
325
|
+
easing: string,
|
|
326
|
+
): AnimationHandle | undefined => {
|
|
327
|
+
if (!el?.setStyle) return undefined;
|
|
328
|
+
el.setStyle('transition', `transform ${duration}ms ${easing}`);
|
|
329
|
+
el.setStyle('transform', transform);
|
|
330
|
+
// CSS transitions have no imperative handle to stop; re-pressing simply
|
|
331
|
+
// re-transitions from the current value. cancel() is a no-op kept for API
|
|
332
|
+
// parity with callers that store and cancel the previous handle.
|
|
333
|
+
return { cancel() {} };
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Animates an element being pressed down (scale shrink).
|
|
338
|
+
*
|
|
339
|
+
* Wire this to BOTH the touch and mouse press-start events —
|
|
340
|
+
* `(bindtouchstart)` and `(bindmousedown)`. Native (iOS/Android) only ever
|
|
341
|
+
* fires touch events; Lynx web runs in the browser, where a mouse press fires
|
|
342
|
+
* `mousedown` and NEVER `touchstart`. Mouse events are inert on touch devices,
|
|
343
|
+
* so binding both yields identical press-and-hold feedback across platforms.
|
|
344
|
+
* See `transitionTransform` for why this uses a CSS transition, not
|
|
345
|
+
* `el.animate()`.
|
|
346
|
+
*/
|
|
347
|
+
export const pressDown = (
|
|
348
|
+
el: any,
|
|
349
|
+
scale: number = SCALE.pressDown,
|
|
350
|
+
): AnimationHandle | undefined =>
|
|
351
|
+
transitionTransform(el, `scale(${scale})`, DURATION.instant, EASING.standard);
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Animates an element releasing from a press (scale back with spring).
|
|
355
|
+
*
|
|
356
|
+
* Wire this to the touch AND mouse release events — `(bindtouchend)` +
|
|
357
|
+
* `(bindtouchcancel)` and `(bindmouseup)` + `(bindmouseleave)`. See `pressDown`
|
|
358
|
+
* for why the mouse bindings are required on Lynx web. `mouseleave` is the
|
|
359
|
+
* `touchcancel` analogue: it restores the scale if the cursor leaves the
|
|
360
|
+
* element while still pressed.
|
|
361
|
+
*/
|
|
362
|
+
export const pressRelease = (el: any): AnimationHandle | undefined =>
|
|
363
|
+
transitionTransform(el, 'scale(1)', DURATION.fast, EASING.spring);
|
|
364
|
+
|
|
365
|
+
// ---------------------------------------------------------------------------
|
|
366
|
+
// Composite Helpers
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Orchestrates opening an overlay (backdrop + panel entrance).
|
|
371
|
+
* Provides staggered timing — backdrop fades first, then panel enters.
|
|
372
|
+
*/
|
|
373
|
+
export const overlayOpen = (
|
|
374
|
+
backdrop: any,
|
|
375
|
+
panel: any,
|
|
376
|
+
type: 'center' | 'bottom' | 'left' | 'right',
|
|
377
|
+
): { backdrop?: AnimationHandle; panel?: AnimationHandle } => {
|
|
378
|
+
// Backdrop fades in immediately
|
|
379
|
+
const backdropAnim = fadeIn(backdrop, { duration: DURATION.normal });
|
|
380
|
+
|
|
381
|
+
// Panel enters with a 30ms stagger after backdrop
|
|
382
|
+
let panelAnim: AnimationHandle | undefined;
|
|
383
|
+
setTimeout(() => {
|
|
384
|
+
if (type === 'center') {
|
|
385
|
+
panelAnim = scaleIn(panel);
|
|
386
|
+
} else if (type === 'bottom') {
|
|
387
|
+
panelAnim = slideIn(panel, 'up');
|
|
388
|
+
} else if (type === 'left') {
|
|
389
|
+
panelAnim = slideIn(panel, 'left');
|
|
390
|
+
} else {
|
|
391
|
+
panelAnim = slideIn(panel, 'right');
|
|
392
|
+
}
|
|
393
|
+
}, 30);
|
|
394
|
+
|
|
395
|
+
return { backdrop: backdropAnim, panel: panelAnim };
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Orchestrates closing an overlay (panel exits, then backdrop fades).
|
|
400
|
+
* Returns total animation duration for cleanup scheduling.
|
|
401
|
+
*/
|
|
402
|
+
export const overlayClose = (
|
|
403
|
+
backdrop: any,
|
|
404
|
+
panel: any,
|
|
405
|
+
type: 'center' | 'bottom' | 'left' | 'right',
|
|
406
|
+
): number => {
|
|
407
|
+
// Panel exits immediately
|
|
408
|
+
if (type === 'center') {
|
|
409
|
+
scaleOut(panel, { duration: 180 });
|
|
410
|
+
} else if (type === 'bottom') {
|
|
411
|
+
slideOut(panel, 'down', { duration: DURATION.normal });
|
|
412
|
+
} else if (type === 'left') {
|
|
413
|
+
slideOut(panel, 'left', { duration: DURATION.normal });
|
|
414
|
+
} else {
|
|
415
|
+
slideOut(panel, 'right', { duration: DURATION.normal });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Backdrop fades out in parallel
|
|
419
|
+
fadeOut(backdrop, { duration: DURATION.normal });
|
|
420
|
+
|
|
421
|
+
// Return the longest duration for setTimeout cleanup
|
|
422
|
+
return type === 'center' ? 180 : DURATION.normal;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
// Continuous Animation Helpers
|
|
427
|
+
// ---------------------------------------------------------------------------
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Creates a continuous pulse animation (for skeleton loading states).
|
|
431
|
+
* Oscillates opacity between 1 and 0.5 indefinitely.
|
|
432
|
+
*/
|
|
433
|
+
export const pulse = (
|
|
434
|
+
el: any,
|
|
435
|
+
options?: { duration?: number },
|
|
436
|
+
): AnimationHandle | undefined => {
|
|
437
|
+
if (!el) return undefined;
|
|
438
|
+
return el.animate([{ opacity: 1 }, { opacity: 0.5 }, { opacity: 1 }], {
|
|
439
|
+
duration: options?.duration ?? 1500,
|
|
440
|
+
easing: 'ease-in-out',
|
|
441
|
+
iterations: Infinity,
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Creates a horizontal shake animation (for error states on inputs).
|
|
447
|
+
* Shakes ±4px then ±2px before settling.
|
|
448
|
+
*/
|
|
449
|
+
export const shake = (
|
|
450
|
+
el: any,
|
|
451
|
+
options?: AnimateOptions,
|
|
452
|
+
): AnimationHandle | undefined => {
|
|
453
|
+
if (!el) return undefined;
|
|
454
|
+
return el.animate(
|
|
455
|
+
[
|
|
456
|
+
{ transform: 'translateX(0)' },
|
|
457
|
+
{ transform: 'translateX(-4px)' },
|
|
458
|
+
{ transform: 'translateX(4px)' },
|
|
459
|
+
{ transform: 'translateX(-2px)' },
|
|
460
|
+
{ transform: 'translateX(2px)' },
|
|
461
|
+
{ transform: 'translateX(0)' },
|
|
462
|
+
],
|
|
463
|
+
{
|
|
464
|
+
duration: options?.duration ?? DURATION.slow,
|
|
465
|
+
easing: options?.easing ?? EASING.standard,
|
|
466
|
+
fill: 'forwards',
|
|
467
|
+
},
|
|
468
|
+
);
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Animates a spring-based translation between two X positions.
|
|
473
|
+
* Used for switch thumbs with a squish effect at midpoint.
|
|
474
|
+
*/
|
|
475
|
+
export const springTranslateX = (
|
|
476
|
+
el: any,
|
|
477
|
+
fromX: number,
|
|
478
|
+
toX: number,
|
|
479
|
+
options?: AnimateOptions,
|
|
480
|
+
): AnimationHandle | undefined => {
|
|
481
|
+
if (!el) return undefined;
|
|
482
|
+
const midX = (fromX + toX) / 2;
|
|
483
|
+
return el.animate(
|
|
484
|
+
[
|
|
485
|
+
{ transform: `translateX(${fromX}px) scale(1)` },
|
|
486
|
+
{ transform: `translateX(${midX}px) scale(1.1)` },
|
|
487
|
+
{ transform: `translateX(${toX}px) scale(1)` },
|
|
488
|
+
],
|
|
489
|
+
{
|
|
490
|
+
duration: options?.duration ?? 200,
|
|
491
|
+
easing: options?.easing ?? EASING.spring,
|
|
492
|
+
fill: options?.fill ?? 'forwards',
|
|
493
|
+
},
|
|
494
|
+
);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Content reveal animation — fade in with upward slide.
|
|
499
|
+
* Used for accordion content, tab content, alerts, etc.
|
|
500
|
+
*/
|
|
501
|
+
export const revealIn = (
|
|
502
|
+
el: any,
|
|
503
|
+
options?: AnimateOptions & { fromY?: number },
|
|
504
|
+
): AnimationHandle | undefined => {
|
|
505
|
+
if (!el) return undefined;
|
|
506
|
+
const fromY = options?.fromY ?? 8;
|
|
507
|
+
return el.animate(
|
|
508
|
+
[
|
|
509
|
+
{ transform: `translateY(${fromY}px)`, opacity: 0 },
|
|
510
|
+
{ transform: 'translateY(0)', opacity: 1 },
|
|
511
|
+
],
|
|
512
|
+
{
|
|
513
|
+
duration: options?.duration ?? DURATION.normal,
|
|
514
|
+
easing: options?.easing ?? EASING.decelerate,
|
|
515
|
+
fill: options?.fill ?? 'forwards',
|
|
516
|
+
},
|
|
517
|
+
);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Direction-aware slide + fade for tab content transitions.
|
|
522
|
+
* Slides from left or right based on navigation direction, creating
|
|
523
|
+
* a spatial relationship between tab panels.
|
|
524
|
+
*/
|
|
525
|
+
export const directionalSlideIn = (
|
|
526
|
+
el: any,
|
|
527
|
+
direction: 1 | -1,
|
|
528
|
+
options?: AnimateOptions & { distance?: number },
|
|
529
|
+
): AnimationHandle | undefined => {
|
|
530
|
+
if (!el) return undefined;
|
|
531
|
+
const distance = options?.distance ?? 24;
|
|
532
|
+
const fromX = direction * distance;
|
|
533
|
+
return el.animate(
|
|
534
|
+
[
|
|
535
|
+
{ transform: `translateX(${fromX}px)`, opacity: 0 },
|
|
536
|
+
{ transform: 'translateX(0px)', opacity: 1 },
|
|
537
|
+
],
|
|
538
|
+
{
|
|
539
|
+
duration: options?.duration ?? DURATION.slow,
|
|
540
|
+
easing: options?.easing ?? EASING.springSubtle,
|
|
541
|
+
fill: options?.fill ?? 'forwards',
|
|
542
|
+
},
|
|
543
|
+
);
|
|
544
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { cn } from './cn';
|
|
4
|
+
|
|
5
|
+
describe('cn', () => {
|
|
6
|
+
it('merges class names', () => {
|
|
7
|
+
expect(cn('foo', 'bar')).toBe('foo bar');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('handles conditional classes via clsx', () => {
|
|
11
|
+
expect(cn('base', false, 'visible')).toBe('base visible');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('resolves tailwind conflicts via twMerge', () => {
|
|
15
|
+
expect(cn('px-4', 'px-2')).toBe('px-2');
|
|
16
|
+
expect(cn('text-red-500', 'text-blue-500')).toBe('text-blue-500');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('handles undefined and null inputs', () => {
|
|
20
|
+
expect(cn('foo', undefined, null, 'bar')).toBe('foo bar');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('handles empty inputs', () => {
|
|
24
|
+
expect(cn()).toBe('');
|
|
25
|
+
expect(cn('')).toBe('');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Hunk } from 'diff';
|
|
2
|
+
export type FileStatus = 'up-to-date' | 'auto-update' | 'user-modified' | 'conflict' | 'new-upstream';
|
|
3
|
+
export type FileAnalysis = {
|
|
4
|
+
file: string;
|
|
5
|
+
status: FileStatus;
|
|
6
|
+
currentContent: string;
|
|
7
|
+
newContent: string;
|
|
8
|
+
};
|
|
9
|
+
export type ComponentAnalysis = {
|
|
10
|
+
name: string;
|
|
11
|
+
files: FileAnalysis[];
|
|
12
|
+
};
|
|
13
|
+
export declare const STATUS_ICONS: Record<FileStatus, string>;
|
|
14
|
+
export declare const STATUS_LABELS: Record<FileStatus, string>;
|
|
15
|
+
/**
|
|
16
|
+
* Three-way merge status detection. The lockfile stores the hash of each
|
|
17
|
+
* file at install time (the "base"). Comparing current (user's file),
|
|
18
|
+
* upstream (bundled source), and base (lockfile hash) lets us determine
|
|
19
|
+
* who changed what:
|
|
20
|
+
* base == current, base != upstream → upstream changed → safe auto-update
|
|
21
|
+
* base != current, base == upstream → user changed → keep theirs
|
|
22
|
+
* base != current, base != upstream → both changed → conflict
|
|
23
|
+
* When no base exists (missing lockfile entry), we can't tell who diverged,
|
|
24
|
+
* so any difference is flagged as a conflict to be safe.
|
|
25
|
+
*/
|
|
26
|
+
export declare const analyzeFile: (currentContent: string | null, newContent: string, storedHash: string | null, hashContent: (content: string) => string) => FileStatus;
|
|
27
|
+
export declare const formatDiff: (currentContent: string, newContent: string) => string;
|
|
28
|
+
export declare const summarizeComponent: (analysis: ComponentAnalysis) => FileStatus;
|
|
29
|
+
export { type Hunk } from 'diff';
|
|
30
|
+
export declare const getHunks: (currentContent: string, newContent: string, context?: number) => Hunk[];
|
|
31
|
+
export declare const formatHunk: (hunk: Hunk) => string;
|
|
32
|
+
export declare const applySelectedHunks: (currentContent: string, newContent: string, acceptedIndices: number[]) => string | false;
|