panelui-native 0.77.0 → 0.78.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.
Files changed (102) hide show
  1. package/lib/module/components/area-chart/index.js +32 -25
  2. package/lib/module/components/area-chart/index.js.map +1 -1
  3. package/lib/module/components/bottom-sheet/index.js +97 -15
  4. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  5. package/lib/module/components/funnel-chart/index.js +15 -2
  6. package/lib/module/components/funnel-chart/index.js.map +1 -1
  7. package/lib/module/components/heatmap-chart/index.js +19 -27
  8. package/lib/module/components/heatmap-chart/index.js.map +1 -1
  9. package/lib/module/components/hex-chart/index.js +2 -0
  10. package/lib/module/components/hex-chart/index.js.map +1 -1
  11. package/lib/module/components/line-chart/index.js +38 -31
  12. package/lib/module/components/line-chart/index.js.map +1 -1
  13. package/lib/module/components/marquee/index.js +91 -5
  14. package/lib/module/components/marquee/index.js.map +1 -1
  15. package/lib/module/components/pie-chart/index.js +15 -2
  16. package/lib/module/components/pie-chart/index.js.map +1 -1
  17. package/lib/module/components/plot/index.js +42 -44
  18. package/lib/module/components/plot/index.js.map +1 -1
  19. package/lib/module/components/progress-button/index.js +390 -0
  20. package/lib/module/components/progress-button/index.js.map +1 -0
  21. package/lib/module/components/progress-button/progress-button-hold.js +57 -0
  22. package/lib/module/components/progress-button/progress-button-hold.js.map +1 -0
  23. package/lib/module/components/scatter-chart/index.js +6 -25
  24. package/lib/module/components/scatter-chart/index.js.map +1 -1
  25. package/lib/module/components/section-rail/index.js +40 -17
  26. package/lib/module/components/section-rail/index.js.map +1 -1
  27. package/lib/module/components/split-view/index.js +54 -10
  28. package/lib/module/components/split-view/index.js.map +1 -1
  29. package/lib/module/components/timeline/index.js +145 -14
  30. package/lib/module/components/timeline/index.js.map +1 -1
  31. package/lib/module/components/toast/index.js +45 -11
  32. package/lib/module/components/toast/index.js.map +1 -1
  33. package/lib/module/components/treemap-chart/index.js +63 -6
  34. package/lib/module/components/treemap-chart/index.js.map +1 -1
  35. package/lib/module/components/waterfall-chart/index.js +20 -4
  36. package/lib/module/components/waterfall-chart/index.js.map +1 -1
  37. package/lib/module/hooks/index.js +1 -0
  38. package/lib/module/hooks/index.js.map +1 -1
  39. package/lib/module/hooks/use-scroll-sections.js +44 -2
  40. package/lib/module/hooks/use-scroll-sections.js.map +1 -1
  41. package/lib/module/hooks/use-skeleton-handoff.js +58 -0
  42. package/lib/module/hooks/use-skeleton-handoff.js.map +1 -0
  43. package/lib/module/index.js +1 -0
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/typescript/src/components/area-chart/index.d.ts +1 -1
  46. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
  47. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  48. package/lib/typescript/src/components/funnel-chart/index.d.ts.map +1 -1
  49. package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
  50. package/lib/typescript/src/components/hex-chart/index.d.ts.map +1 -1
  51. package/lib/typescript/src/components/line-chart/index.d.ts +1 -1
  52. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  53. package/lib/typescript/src/components/marquee/index.d.ts +55 -2
  54. package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
  55. package/lib/typescript/src/components/pie-chart/index.d.ts.map +1 -1
  56. package/lib/typescript/src/components/plot/index.d.ts +1 -3
  57. package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
  58. package/lib/typescript/src/components/progress-button/index.d.ts +295 -0
  59. package/lib/typescript/src/components/progress-button/index.d.ts.map +1 -0
  60. package/lib/typescript/src/components/progress-button/progress-button-hold.d.ts +40 -0
  61. package/lib/typescript/src/components/progress-button/progress-button-hold.d.ts.map +1 -0
  62. package/lib/typescript/src/components/scatter-chart/index.d.ts +1 -1
  63. package/lib/typescript/src/components/scatter-chart/index.d.ts.map +1 -1
  64. package/lib/typescript/src/components/section-rail/index.d.ts.map +1 -1
  65. package/lib/typescript/src/components/split-view/index.d.ts +10 -1
  66. package/lib/typescript/src/components/split-view/index.d.ts.map +1 -1
  67. package/lib/typescript/src/components/timeline/index.d.ts +7 -0
  68. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  69. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  70. package/lib/typescript/src/components/treemap-chart/index.d.ts.map +1 -1
  71. package/lib/typescript/src/components/waterfall-chart/index.d.ts.map +1 -1
  72. package/lib/typescript/src/hooks/index.d.ts +1 -0
  73. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  74. package/lib/typescript/src/hooks/use-scroll-sections.d.ts +1 -0
  75. package/lib/typescript/src/hooks/use-scroll-sections.d.ts.map +1 -1
  76. package/lib/typescript/src/hooks/use-skeleton-handoff.d.ts +11 -0
  77. package/lib/typescript/src/hooks/use-skeleton-handoff.d.ts.map +1 -0
  78. package/lib/typescript/src/index.d.ts +3 -2
  79. package/lib/typescript/src/index.d.ts.map +1 -1
  80. package/package.json +1 -1
  81. package/src/components/area-chart/index.tsx +49 -37
  82. package/src/components/bottom-sheet/index.tsx +109 -11
  83. package/src/components/funnel-chart/index.tsx +10 -2
  84. package/src/components/heatmap-chart/index.tsx +27 -22
  85. package/src/components/hex-chart/index.tsx +22 -6
  86. package/src/components/line-chart/index.tsx +57 -43
  87. package/src/components/marquee/index.tsx +129 -6
  88. package/src/components/pie-chart/index.tsx +10 -2
  89. package/src/components/plot/index.tsx +66 -50
  90. package/src/components/progress-button/index.tsx +465 -0
  91. package/src/components/progress-button/progress-button-hold.ts +57 -0
  92. package/src/components/scatter-chart/index.tsx +4 -25
  93. package/src/components/section-rail/index.tsx +39 -19
  94. package/src/components/split-view/index.tsx +53 -8
  95. package/src/components/timeline/index.tsx +143 -17
  96. package/src/components/toast/index.tsx +60 -11
  97. package/src/components/treemap-chart/index.tsx +65 -8
  98. package/src/components/waterfall-chart/index.tsx +14 -4
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/use-scroll-sections.ts +52 -3
  101. package/src/hooks/use-skeleton-handoff.ts +69 -0
  102. package/src/index.ts +10 -0
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * ProgressButton — press and hold to confirm, with the wait drawn on the button.
5
+ *
6
+ * ```tsx
7
+ * <ProgressButton onComplete={wipe}>
8
+ * <ProgressButton.Label>Hold to erase</ProgressButton.Label>
9
+ * </ProgressButton>
10
+ * ```
11
+ *
12
+ * For the action a confirmation dialog exists to slow down. A dialog asks the
13
+ * question somewhere else and takes the answer as a tap — which is two taps,
14
+ * and two taps in a row is a rhythm a hand falls into. A hold cannot be
15
+ * completed by accident and cannot be completed by habit: it has to be
16
+ * sustained, and the fill says for how much longer.
17
+ *
18
+ * ## The fill is the promise
19
+ *
20
+ * Nothing fires until the fill reaches the end. There is no tolerance near the
21
+ * top, because a tolerance means the button sometimes commits after the reader
22
+ * has deliberately let go — which is the one failure a confirmation control
23
+ * cannot have. Released early, the fill drains back, in proportion to how far
24
+ * it got.
25
+ *
26
+ * ## How it is drawn
27
+ *
28
+ * The fill is a clipped copy of the button in inverted colours, growing from
29
+ * the leading edge. That is what keeps the label legible across the boundary:
30
+ * a single label under a translucent wash goes muddy in the middle of the
31
+ * wipe, exactly where the eye is. Two labels, each at full contrast on its own
32
+ * ground, never do.
33
+ *
34
+ * The clip is a view under `overflow: 'hidden'`, and its width is animated.
35
+ * Animating a width is normally a layout pass per frame; this view is
36
+ * absolutely positioned over the button and its copy of the label is pinned to
37
+ * the button's own width, so nothing it contains reflows as it grows.
38
+ *
39
+ * ## Reduced motion
40
+ *
41
+ * The wipe is replaced by a stepped fill that advances in fifths. The hold
42
+ * still has to be legible — a control that asks you to wait and shows nothing
43
+ * is a broken button — so this is a coarser indicator, not the absence of one.
44
+ */
45
+ import { createContext, forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
46
+ import { Pressable, View } from 'react-native';
47
+ import Animated, { Easing, cancelAnimation, runOnJS, useAnimatedReaction, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from 'react-native-reanimated';
48
+ import { tv } from 'tailwind-variants';
49
+ import { Text, textChildren } from "../../primitives/text.js";
50
+ import { impactKnock, selectionTick } from "../../utils/haptics.js";
51
+ import { DEFAULT_AUTO_RESET_DELAY, DEFAULT_RELEASE_DURATION, releaseDuration, resolveHoldDuration } from "./progress-button-hold.js";
52
+
53
+ /** How many steps the reduced-motion fill advances in. */
54
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
55
+ const REDUCED_STEPS = 5;
56
+ const progressButtonVariants = tv({
57
+ slots: {
58
+ /*
59
+ * A pill, not the `rounded-lg` the other buttons take.
60
+ *
61
+ * The fill is clipped by this radius, so the shape of the button is also
62
+ * the shape of the wipe's leading edge as it comes out of the corner. On a
63
+ * small radius that edge emerges square from a rounded box, which reads as
64
+ * a rectangle sliding out from under the button rather than as the button
65
+ * filling up.
66
+ */
67
+ root: 'relative overflow-hidden rounded-full border',
68
+ /*
69
+ * The row inside the button. It is separate from `root` because the fill
70
+ * has to sit over the whole button including its padding — a wipe that
71
+ * stops at the text's own box leaves an unfilled margin down each side and
72
+ * reads as a progress bar someone put inside a button.
73
+ */
74
+ content: 'flex-row items-center justify-center gap-2',
75
+ label: 'min-w-0 shrink text-center font-medium',
76
+ /** The inverted copy, drawn on the filled ground. */
77
+ fill: '',
78
+ fillLabel: 'min-w-0 shrink text-center font-medium'
79
+ },
80
+ variants: {
81
+ variant: {
82
+ primary: {
83
+ root: 'border-primary bg-transparent',
84
+ label: 'text-primary',
85
+ fill: 'bg-primary',
86
+ fillLabel: 'text-primary-foreground'
87
+ },
88
+ secondary: {
89
+ root: 'border-transparent bg-secondary',
90
+ label: 'text-secondary-foreground',
91
+ fill: 'bg-foreground',
92
+ fillLabel: 'text-background'
93
+ },
94
+ destructive: {
95
+ root: 'border-destructive bg-transparent',
96
+ label: 'text-destructive',
97
+ fill: 'bg-destructive',
98
+ fillLabel: 'text-destructive-solid-foreground'
99
+ },
100
+ success: {
101
+ root: 'border-success bg-transparent',
102
+ label: 'text-success',
103
+ fill: 'bg-success',
104
+ fillLabel: 'text-success-solid-foreground'
105
+ }
106
+ },
107
+ size: {
108
+ // Matched to Button's boxes, and `min-h-*` for the same reason: the
109
+ // label's glyphs grow with the system text size and the box has to grow
110
+ // with them.
111
+ // Wider than the equivalent Button, because the corner is a half-circle
112
+ // rather than a small radius: the curve eats into the side padding, and
113
+ // at Button's values the first and last glyphs sit against it.
114
+ sm: {
115
+ root: 'min-h-9',
116
+ content: 'min-h-9 gap-1.5 px-3.5 py-2',
117
+ label: 'text-[14px]',
118
+ fillLabel: 'text-[14px]'
119
+ },
120
+ md: {
121
+ root: 'min-h-11',
122
+ content: 'min-h-11 px-5 py-2.5',
123
+ label: 'text-[16px]',
124
+ fillLabel: 'text-[16px]'
125
+ },
126
+ lg: {
127
+ root: 'min-h-12',
128
+ content: 'min-h-12 px-7 py-2.5',
129
+ label: 'text-[18px]',
130
+ fillLabel: 'text-[18px]'
131
+ }
132
+ },
133
+ fullWidth: {
134
+ true: {
135
+ root: 'w-full'
136
+ }
137
+ },
138
+ disabled: {
139
+ true: {
140
+ root: 'opacity-[0.64]'
141
+ }
142
+ }
143
+ },
144
+ defaultVariants: {
145
+ variant: 'primary',
146
+ size: 'md'
147
+ }
148
+ });
149
+
150
+ /** How a progress button looks. */
151
+
152
+ /** How big a progress button is. */
153
+
154
+ const ProgressButtonContext = /*#__PURE__*/createContext(null);
155
+ function useProgressButtonContext(component) {
156
+ const context = useContext(ProgressButtonContext);
157
+ if (!context) throw new Error(`${component} must be used within a <ProgressButton>`);
158
+ return context;
159
+ }
160
+
161
+ /** How far through the hold this button is, for something rendered inside it. */
162
+ export function useProgressButton() {
163
+ const {
164
+ progress,
165
+ completed
166
+ } = useProgressButtonContext('useProgressButton');
167
+ return {
168
+ progress,
169
+ completed
170
+ };
171
+ }
172
+ const ProgressButtonRoot = /*#__PURE__*/forwardRef(function ProgressButtonRoot({
173
+ className,
174
+ variant = 'primary',
175
+ size = 'md',
176
+ fullWidth,
177
+ holdDuration,
178
+ onComplete,
179
+ onCompletedChange,
180
+ completed: completedProp,
181
+ autoReset = false,
182
+ autoResetDelay = DEFAULT_AUTO_RESET_DELAY,
183
+ disabled = false,
184
+ haptics = false,
185
+ accessibilityState,
186
+ children,
187
+ ...props
188
+ }, ref) {
189
+ const slots = progressButtonVariants({
190
+ variant,
191
+ size,
192
+ fullWidth,
193
+ disabled
194
+ });
195
+ const duration = resolveHoldDuration(holdDuration);
196
+ const reducedMotion = useReducedMotion();
197
+ const progress = useSharedValue(0);
198
+ const [width, setWidth] = useState(0);
199
+ const [internalCompleted, setInternalCompleted] = useState(false);
200
+ const resetTimer = useRef(null);
201
+ const isControlled = completedProp !== undefined;
202
+ const completed = isControlled ? completedProp : internalCompleted;
203
+ const finish = useCallback(() => {
204
+ if (!isControlled) setInternalCompleted(true);
205
+ onCompletedChange?.(true);
206
+ onComplete?.();
207
+ if (haptics) impactKnock();
208
+ }, [isControlled, onComplete, onCompletedChange, haptics]);
209
+
210
+ /*
211
+ * Completion is read off the animation rather than timed alongside it.
212
+ *
213
+ * A `setTimeout` for `holdDuration` and a fill for `holdDuration` are two
214
+ * clocks that agree only while the app is idle: the moment the JS thread is
215
+ * busy the button fires before the fill arrives, or the fill sits full while
216
+ * nothing happens. The reaction fires on the frame the value actually
217
+ * reaches the end, which is the frame the reader saw it get there.
218
+ */
219
+ useAnimatedReaction(() => progress.value >= 1, (full, was) => {
220
+ if (full && !was) runOnJS(finish)();
221
+ });
222
+ const begin = useCallback(() => {
223
+ if (disabled || completed) return;
224
+ if (haptics) selectionTick();
225
+ cancelAnimation(progress);
226
+ if (reducedMotion) {
227
+ /*
228
+ * Stepped rather than smooth, and still a real indicator. What reduced
229
+ * motion is about is continuous movement, not the button telling you how
230
+ * much longer to wait — take that away and the control asks for a hold
231
+ * with nothing on screen to say why.
232
+ */
233
+ progress.value = withTiming(1, {
234
+ duration,
235
+ easing: Easing.steps(REDUCED_STEPS, true)
236
+ });
237
+ return;
238
+ }
239
+ // Linear. A fill is constant motion, and an eased one misreports the wait:
240
+ // it races the first half and crawls the second, or the reverse.
241
+ progress.value = withTiming(1, {
242
+ duration,
243
+ easing: Easing.linear
244
+ });
245
+ }, [disabled, completed, haptics, progress, reducedMotion, duration]);
246
+ const abandon = useCallback(() => {
247
+ cancelAnimation(progress);
248
+ if (progress.value <= 0) return;
249
+ progress.value = withTiming(0, {
250
+ // Proportional to how far it got. A fixed release makes a hold abandoned
251
+ // after a moment feel sticky, which reads as the button resisting.
252
+ duration: releaseDuration(progress.value, DEFAULT_RELEASE_DURATION),
253
+ easing: Easing.out(Easing.cubic)
254
+ });
255
+ }, [progress]);
256
+ const reset = useCallback(() => {
257
+ cancelAnimation(progress);
258
+ progress.value = 0;
259
+ if (!isControlled) setInternalCompleted(false);
260
+ onCompletedChange?.(false);
261
+ }, [progress, isControlled, onCompletedChange]);
262
+ useEffect(() => {
263
+ if (!completed || !autoReset) return;
264
+ resetTimer.current = setTimeout(reset, autoResetDelay);
265
+ return () => {
266
+ if (resetTimer.current) clearTimeout(resetTimer.current);
267
+ resetTimer.current = null;
268
+ };
269
+ }, [completed, autoReset, autoResetDelay, reset]);
270
+
271
+ // A controlled button told it is no longer complete has to empty its fill,
272
+ // or the next hold starts from a bar that is already full.
273
+ useEffect(() => {
274
+ if (!completed) return;
275
+ progress.value = 1;
276
+ }, [completed, progress]);
277
+ useEffect(() => () => {
278
+ cancelAnimation(progress);
279
+ if (resetTimer.current) clearTimeout(resetTimer.current);
280
+ }, [progress]);
281
+ const onLayout = event => {
282
+ const next = Math.round(event.nativeEvent.layout.width);
283
+ setWidth(current => current === next ? current : next);
284
+ props.onLayout?.(event);
285
+ };
286
+ const context = useMemo(() => ({
287
+ progress,
288
+ width,
289
+ completed,
290
+ variant,
291
+ size,
292
+ slots
293
+ }), [progress, width, completed, variant, size, slots]);
294
+ const body = children ?? /*#__PURE__*/_jsx(_Fragment, {
295
+ children: /*#__PURE__*/_jsx(ProgressButtonLabel, {
296
+ children: "Hold to confirm"
297
+ })
298
+ });
299
+ return /*#__PURE__*/_jsx(ProgressButtonContext.Provider, {
300
+ value: context,
301
+ children: /*#__PURE__*/_jsx(Pressable, {
302
+ ref: ref,
303
+ accessibilityRole: "button"
304
+ /*
305
+ * Spoken as what it is. A button whose label says "Hold to confirm"
306
+ * still announces as an ordinary button to anyone who cannot see the
307
+ * fill, and a single activation does nothing — so the hint carries the
308
+ * instruction and the state carries the outcome.
309
+ */,
310
+ accessibilityHint: "Press and hold to confirm",
311
+ accessibilityState: {
312
+ ...accessibilityState,
313
+ disabled,
314
+ checked: completed
315
+ },
316
+ disabled: disabled,
317
+ onPressIn: begin,
318
+ onPressOut: abandon
319
+ // A few points of drift should not abandon a hold the reader is
320
+ // still making. Fingers move.
321
+ ,
322
+ pressRetentionOffset: 16,
323
+ ...props,
324
+ onLayout: onLayout,
325
+ className: slots.root({
326
+ className
327
+ }),
328
+ children: body
329
+ })
330
+ });
331
+ });
332
+ /**
333
+ * What the button says, drawn twice.
334
+ *
335
+ * The second copy is the one inside the fill, in the inverted colour, pinned to
336
+ * the button's own width and clipped to however far the hold has got. Both are
337
+ * the same text at the same position, so the boundary between them falls in the
338
+ * middle of a glyph rather than between two differently laid-out lines.
339
+ */
340
+ function ProgressButtonLabel({
341
+ className,
342
+ children,
343
+ ...props
344
+ }) {
345
+ const {
346
+ progress,
347
+ width,
348
+ slots
349
+ } = useProgressButtonContext('ProgressButton.Label');
350
+ const fillStyle = useAnimatedStyle(() => ({
351
+ width: width * progress.value
352
+ }));
353
+ return /*#__PURE__*/_jsxs(View, {
354
+ ...props,
355
+ className: slots.content({
356
+ className
357
+ }),
358
+ children: [textChildren(children, text => /*#__PURE__*/_jsx(Text, {
359
+ className: slots.label(),
360
+ children: text
361
+ })), /*#__PURE__*/_jsx(Animated.View, {
362
+ pointerEvents: "none",
363
+ style: [{
364
+ position: 'absolute',
365
+ top: 0,
366
+ bottom: 0,
367
+ left: 0,
368
+ overflow: 'hidden'
369
+ }, fillStyle],
370
+ className: slots.fill(),
371
+ children: /*#__PURE__*/_jsx(View, {
372
+ style: {
373
+ width
374
+ },
375
+ className: slots.content(),
376
+ children: textChildren(children, text => /*#__PURE__*/_jsx(Text, {
377
+ className: slots.fillLabel(),
378
+ children: text
379
+ }))
380
+ })
381
+ })]
382
+ });
383
+ }
384
+ ProgressButtonLabel.displayName = 'ProgressButton.Label';
385
+ ProgressButtonRoot.displayName = 'ProgressButton';
386
+ export const ProgressButton = Object.assign(ProgressButtonRoot, {
387
+ Label: ProgressButtonLabel
388
+ });
389
+ export { DEFAULT_AUTO_RESET_DELAY, DEFAULT_HOLD_DURATION, DEFAULT_RELEASE_DURATION, isComplete, releaseDuration, resolveHoldDuration } from "./progress-button-hold.js";
390
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createContext","forwardRef","useCallback","useContext","useEffect","useMemo","useRef","useState","Pressable","View","Animated","Easing","cancelAnimation","runOnJS","useAnimatedReaction","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","tv","Text","textChildren","impactKnock","selectionTick","DEFAULT_AUTO_RESET_DELAY","DEFAULT_RELEASE_DURATION","releaseDuration","resolveHoldDuration","jsx","_jsx","Fragment","_Fragment","jsxs","_jsxs","REDUCED_STEPS","progressButtonVariants","slots","root","content","label","fill","fillLabel","variants","variant","primary","secondary","destructive","success","size","sm","md","lg","fullWidth","true","disabled","defaultVariants","ProgressButtonContext","useProgressButtonContext","component","context","Error","useProgressButton","progress","completed","ProgressButtonRoot","className","holdDuration","onComplete","onCompletedChange","completedProp","autoReset","autoResetDelay","haptics","accessibilityState","children","props","ref","duration","reducedMotion","width","setWidth","internalCompleted","setInternalCompleted","resetTimer","isControlled","undefined","finish","value","full","was","begin","easing","steps","linear","abandon","out","cubic","reset","current","setTimeout","clearTimeout","onLayout","event","next","Math","round","nativeEvent","layout","body","ProgressButtonLabel","Provider","accessibilityRole","accessibilityHint","checked","onPressIn","onPressOut","pressRetentionOffset","fillStyle","text","pointerEvents","style","position","top","bottom","left","overflow","displayName","ProgressButton","Object","assign","Label","DEFAULT_HOLD_DURATION","isComplete"],"sourceRoot":"../../../../src","sources":["components/progress-button/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * The arithmetic behind a hold, kept out of the component so it can be tested
5
+ * without a renderer — the same split `splitter-math` and `ai-input-growth` use.
6
+ *
7
+ * None of it is worklet-only, so the component can call it from either runtime.
8
+ */
9
+
10
+ /** Milliseconds a hold has to be sustained before it counts. */
11
+ export const DEFAULT_HOLD_DURATION = 2000;
12
+
13
+ /** Milliseconds the fill takes to drain when a hold is abandoned. */
14
+ export const DEFAULT_RELEASE_DURATION = 240;
15
+
16
+ /** Milliseconds before an `autoReset` button offers itself again. */
17
+ export const DEFAULT_AUTO_RESET_DELAY = 1000;
18
+
19
+ /**
20
+ * A hold duration that cannot produce a broken animation.
21
+ *
22
+ * Zero would complete on touch-down, which defeats the point of the control:
23
+ * a hold-to-confirm that confirms on a tap is a button with extra steps. A
24
+ * floor rather than a rejection, because the caller asking for `0` wants it
25
+ * fast, not broken.
26
+ */
27
+ export function resolveHoldDuration(duration) {
28
+ if (duration === undefined) return DEFAULT_HOLD_DURATION;
29
+ if (!Number.isFinite(duration)) return DEFAULT_HOLD_DURATION;
30
+ return Math.max(200, duration);
31
+ }
32
+
33
+ /**
34
+ * How long the fill should take to drain from where it is.
35
+ *
36
+ * Proportional to how far it got, so abandoning a hold at a tenth of the way
37
+ * does not take the same quarter-second as abandoning it at nine tenths. A
38
+ * fixed release makes a barely-started hold feel sticky, which reads as the
39
+ * control resisting being let go.
40
+ */
41
+ export function releaseDuration(progress, full = DEFAULT_RELEASE_DURATION) {
42
+ const travelled = Math.min(1, Math.max(0, progress));
43
+ return Math.max(80, full * travelled);
44
+ }
45
+
46
+ /**
47
+ * Whether a hold that has been released should still be honoured.
48
+ *
49
+ * The fill is the promise, so anything short of the end is not a confirmation —
50
+ * there is no "close enough" here. It exists as a named function because the
51
+ * tempting version of this rule is a tolerance, and a tolerance means the
52
+ * button sometimes fires when the reader let go early on purpose.
53
+ */
54
+ export function isComplete(progress) {
55
+ return progress >= 1;
56
+ }
57
+ //# sourceMappingURL=progress-button-hold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DEFAULT_HOLD_DURATION","DEFAULT_RELEASE_DURATION","DEFAULT_AUTO_RESET_DELAY","resolveHoldDuration","duration","undefined","Number","isFinite","Math","max","releaseDuration","progress","full","travelled","min","isComplete"],"sourceRoot":"../../../../src","sources":["components/progress-button/progress-button-hold.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,OAAO,MAAMA,qBAAqB,GAAG,IAAI;;AAEzC;AACA,OAAO,MAAMC,wBAAwB,GAAG,GAAG;;AAE3C;AACA,OAAO,MAAMC,wBAAwB,GAAG,IAAI;;AAE5C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,mBAAmBA,CAACC,QAA4B,EAAU;EACxE,IAAIA,QAAQ,KAAKC,SAAS,EAAE,OAAOL,qBAAqB;EACxD,IAAI,CAACM,MAAM,CAACC,QAAQ,CAACH,QAAQ,CAAC,EAAE,OAAOJ,qBAAqB;EAC5D,OAAOQ,IAAI,CAACC,GAAG,CAAC,GAAG,EAAEL,QAAQ,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASM,eAAeA,CAC7BC,QAAgB,EAChBC,IAAY,GAAGX,wBAAwB,EAC/B;EACR,MAAMY,SAAS,GAAGL,IAAI,CAACM,GAAG,CAAC,CAAC,EAAEN,IAAI,CAACC,GAAG,CAAC,CAAC,EAAEE,QAAQ,CAAC,CAAC;EACpD,OAAOH,IAAI,CAACC,GAAG,CAAC,EAAE,EAAEG,IAAI,GAAGC,SAAS,CAAC;AACvC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,UAAUA,CAACJ,QAAgB,EAAW;EACpD,OAAOA,QAAQ,IAAI,CAAC;AACtB","ignoreList":[]}
@@ -50,6 +50,7 @@ import { Text } from "../../primitives/text.js";
50
50
  import { ChartAccessibilityData } from "../../primitives/chart-accessibility.js";
51
51
  import { compactNumber, useSeriesColor, xAt, yOf } from "../../utils/chart.js";
52
52
  import { cn } from "../../utils/cn.js";
53
+ import { useSkeletonHandoff } from "../../hooks/use-skeleton-handoff.js";
53
54
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
54
55
  const AnimatedCircle = Animated.createAnimatedComponent(Circle);
55
56
  const AnimatedG = Animated.createAnimatedComponent(G);
@@ -61,9 +62,6 @@ const AnimatedG = Animated.createAnimatedComponent(G);
61
62
  */
62
63
  const STAGGER = 0.4;
63
64
 
64
- /** Milliseconds for the placeholder field to dissolve once the data arrives. */
65
- const SKELETON_FADE = 220;
66
-
67
65
  /** Milliseconds for a point to swell as it is selected, and settle as it is not. */
68
66
  const SELECT_DURATION = 140;
69
67
 
@@ -153,7 +151,7 @@ const ScatterChartRoot = /*#__PURE__*/forwardRef(function ScatterChartRoot({
153
151
  xDataKey = 'x',
154
152
  status = 'ready',
155
153
  aspectRatio = 1,
156
- animationDuration = 900,
154
+ animationDuration = 650,
157
155
  domainDuration = 500,
158
156
  xDomain,
159
157
  yDomain,
@@ -678,27 +676,10 @@ function ScatterChartSkeleton({
678
676
  } = useChart('ScatterChart.Skeleton');
679
677
  const token = useCSSVariable('--color-skeleton');
680
678
  const fill = color ?? (typeof token === 'string' ? token : 'rgba(128,128,128,0.2)');
681
- const reducedMotion = useReducedMotion();
682
- const loading = status === 'loading';
683
- const fade = useSharedValue(1);
684
- // Mounted a beat longer than `loading`, so there is something to fade.
685
- const [mounted, setMounted] = useState(loading);
686
- useEffect(() => {
687
- if (loading) {
688
- fade.value = 1;
689
- setMounted(true);
690
- return;
691
- }
692
- if (reducedMotion) {
693
- setMounted(false);
694
- return;
695
- }
696
- fade.value = withTiming(0, {
697
- duration: SKELETON_FADE
698
- }, finished => {
699
- if (finished) runOnJS(setMounted)(false);
700
- });
701
- }, [loading, reducedMotion, fade]);
679
+ const {
680
+ mounted,
681
+ opacity: fade
682
+ } = useSkeletonHandoff(status === 'loading');
702
683
  const dots = useMemo(() => {
703
684
  // A cheap deterministic scatter: two irrational-ish strides that do not
704
685
  // share a factor, so the points spread instead of falling into a lattice.