panelui-native 0.56.0 → 0.59.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/README.md +21 -10
- package/lib/module/components/funnel-chart/index.js +828 -0
- package/lib/module/components/funnel-chart/index.js.map +1 -0
- package/lib/module/components/progress/index.js +67 -13
- package/lib/module/components/progress/index.js.map +1 -1
- package/lib/module/components/qr-code/index.js +641 -0
- package/lib/module/components/qr-code/index.js.map +1 -0
- package/lib/module/components/qr-code/qr-encode.js +475 -0
- package/lib/module/components/qr-code/qr-encode.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/chart.js +36 -0
- package/lib/module/utils/chart.js.map +1 -1
- package/lib/typescript/src/components/funnel-chart/index.d.ts +289 -0
- package/lib/typescript/src/components/funnel-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/progress/index.d.ts +9 -1
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
- package/lib/typescript/src/components/qr-code/index.d.ts +221 -0
- package/lib/typescript/src/components/qr-code/index.d.ts.map +1 -0
- package/lib/typescript/src/components/qr-code/qr-encode.d.ts +39 -0
- package/lib/typescript/src/components/qr-code/qr-encode.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +19 -0
- package/lib/typescript/src/utils/chart.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/funnel-chart/index.tsx +1121 -0
- package/src/components/progress/index.tsx +64 -11
- package/src/components/qr-code/index.tsx +663 -0
- package/src/components/qr-code/qr-encode.ts +548 -0
- package/src/index.ts +35 -0
- package/src/utils/chart.ts +52 -0
|
@@ -5,6 +5,7 @@ import Animated, {
|
|
|
5
5
|
cancelAnimation,
|
|
6
6
|
interpolate,
|
|
7
7
|
useAnimatedStyle,
|
|
8
|
+
useReducedMotion,
|
|
8
9
|
useSharedValue,
|
|
9
10
|
withRepeat,
|
|
10
11
|
withSpring,
|
|
@@ -18,6 +19,12 @@ import { cn } from '../../utils/cn';
|
|
|
18
19
|
const SPRING = { damping: 20, stiffness: 180, mass: 0.6 } as const;
|
|
19
20
|
/** Fraction of the track covered by the sliding bar in indeterminate mode. */
|
|
20
21
|
const INDETERMINATE_WIDTH = 0.4;
|
|
22
|
+
/** Milliseconds for the bar to cross the track once. */
|
|
23
|
+
const SWEEP_DURATION = 1100;
|
|
24
|
+
/** Milliseconds for one half of the fade that stands in for the sweep. */
|
|
25
|
+
const PULSE_DURATION = 900;
|
|
26
|
+
/** How far down the fade goes. Faint enough to read as a pulse, not a flicker. */
|
|
27
|
+
const PULSE_FLOOR = 0.35;
|
|
21
28
|
|
|
22
29
|
const progressVariants = tv({
|
|
23
30
|
slots: {
|
|
@@ -67,7 +74,11 @@ export interface ProgressProps
|
|
|
67
74
|
* to `100`, so a bare percentage keeps working with neither bound set.
|
|
68
75
|
*/
|
|
69
76
|
maxValue?: number;
|
|
70
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* Show a looping animation for unknown-duration work. Under the platform's
|
|
79
|
+
* reduce-motion setting the bar fills the track and pulses instead of
|
|
80
|
+
* travelling across it, so it still reads as running.
|
|
81
|
+
*/
|
|
71
82
|
indeterminate?: boolean;
|
|
72
83
|
/** Extra classes for the moving indicator. */
|
|
73
84
|
indicatorClassName?: string;
|
|
@@ -156,6 +167,10 @@ function formatValue(
|
|
|
156
167
|
* the work is actually counted in: bytes uploaded, seats filled, points
|
|
157
168
|
* scored. Nothing has to be converted to a percent on the way in, and the
|
|
158
169
|
* screen reader is told the real range rather than a derived one.
|
|
170
|
+
*
|
|
171
|
+
* Reduce motion is honoured in both modes: the fill lands on its value rather
|
|
172
|
+
* than springing to it, and the indeterminate bar pulses in place rather than
|
|
173
|
+
* crossing the track.
|
|
159
174
|
*/
|
|
160
175
|
export const Progress = forwardRef<View, ProgressProps>(
|
|
161
176
|
(
|
|
@@ -185,43 +200,78 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
185
200
|
const target = fractionOf(value, minValue, maxValue);
|
|
186
201
|
const progress = useSharedValue(target);
|
|
187
202
|
const slide = useSharedValue(0);
|
|
203
|
+
const reducedMotion = useReducedMotion();
|
|
188
204
|
|
|
189
205
|
/**
|
|
190
|
-
* The loop is tied to `indeterminate`
|
|
191
|
-
* value moved used to leave `withRepeat`
|
|
192
|
-
* got to, so a bar that kept receiving
|
|
193
|
-
* sweeping a shrinking sliver of the track
|
|
206
|
+
* The loop is tied to `indeterminate` and to how much motion is wanted.
|
|
207
|
+
* Restarting it whenever the value moved used to leave `withRepeat`
|
|
208
|
+
* cycling from wherever the bar had got to, so a bar that kept receiving
|
|
209
|
+
* values while looping ended up sweeping a shrinking sliver of the track
|
|
210
|
+
* instead of crossing it.
|
|
211
|
+
*
|
|
212
|
+
* Under reduce motion the same loop drives a fade rather than a journey
|
|
213
|
+
* across the track. Freezing it outright is the other option and it is the
|
|
214
|
+
* wrong one: a bar that stops moving reads as a bar that has hung, which is
|
|
215
|
+
* the one thing an indeterminate bar exists to rule out. Reduce motion asks
|
|
216
|
+
* for less travel, not for less news — so the travel goes and the pulse
|
|
217
|
+
* stays, running slower than the sweep it replaces.
|
|
194
218
|
*/
|
|
195
219
|
useEffect(() => {
|
|
196
220
|
if (!indeterminate) return undefined;
|
|
197
221
|
slide.value = 0;
|
|
198
222
|
slide.value = withRepeat(
|
|
199
|
-
withTiming(1, {
|
|
223
|
+
withTiming(1, {
|
|
224
|
+
duration: reducedMotion ? PULSE_DURATION : SWEEP_DURATION,
|
|
225
|
+
easing: Easing.inOut(Easing.ease),
|
|
226
|
+
}),
|
|
200
227
|
-1,
|
|
201
|
-
|
|
228
|
+
// The sweep restarts at the left edge; the fade turns around, because a
|
|
229
|
+
// pulse that snapped back to full brightness would be the flicker the
|
|
230
|
+
// setting is there to avoid.
|
|
231
|
+
reducedMotion
|
|
202
232
|
);
|
|
203
233
|
return () => cancelAnimation(slide);
|
|
204
|
-
}, [indeterminate, slide]);
|
|
234
|
+
}, [indeterminate, reducedMotion, slide]);
|
|
205
235
|
|
|
206
236
|
// The fill follows the value, and picks up from wherever the loop left it
|
|
207
|
-
// when a bar stops being indeterminate mid-flight.
|
|
237
|
+
// when a bar stops being indeterminate mid-flight. Reduce motion lands on
|
|
238
|
+
// the value instead of springing to it — the number is the information, and
|
|
239
|
+
// the overshoot is decoration.
|
|
208
240
|
useEffect(() => {
|
|
209
241
|
if (indeterminate) return;
|
|
210
|
-
progress.value = withSpring(target, SPRING);
|
|
211
|
-
}, [indeterminate, target, progress]);
|
|
242
|
+
progress.value = reducedMotion ? target : withSpring(target, SPRING);
|
|
243
|
+
}, [indeterminate, target, progress, reducedMotion]);
|
|
212
244
|
|
|
213
245
|
const onLayout = (event: LayoutChangeEvent) => {
|
|
214
246
|
trackWidth.value = event.nativeEvent.layout.width;
|
|
215
247
|
};
|
|
216
248
|
|
|
249
|
+
/*
|
|
250
|
+
* Both styles set every property either of them sets. The two are swapped
|
|
251
|
+
* on the same view when a bar stops being indeterminate, and an animated
|
|
252
|
+
* property that one style drops is not reset by the other — it is simply
|
|
253
|
+
* left at whatever it was last given, which would strand a half-faded bar
|
|
254
|
+
* at that opacity for the rest of its life.
|
|
255
|
+
*/
|
|
217
256
|
const determinateStyle = useAnimatedStyle(() => ({
|
|
218
257
|
width: trackWidth.value * progress.value,
|
|
258
|
+
opacity: 1,
|
|
259
|
+
transform: [{ translateX: 0 }],
|
|
219
260
|
}));
|
|
220
261
|
|
|
221
262
|
const indeterminateStyle = useAnimatedStyle(() => {
|
|
263
|
+
if (reducedMotion) {
|
|
264
|
+
// Nothing travels: the whole track carries the bar and the bar breathes.
|
|
265
|
+
return {
|
|
266
|
+
width: trackWidth.value,
|
|
267
|
+
opacity: interpolate(slide.value, [0, 1], [1, PULSE_FLOOR]),
|
|
268
|
+
transform: [{ translateX: 0 }],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
222
271
|
const barWidth = trackWidth.value * INDETERMINATE_WIDTH;
|
|
223
272
|
return {
|
|
224
273
|
width: barWidth,
|
|
274
|
+
opacity: 1,
|
|
225
275
|
transform: [
|
|
226
276
|
{
|
|
227
277
|
// The loop travels the way the text does. Yoga mirrors the track
|
|
@@ -253,6 +303,9 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
253
303
|
ref={ref}
|
|
254
304
|
accessibilityRole="progressbar"
|
|
255
305
|
accessibilityLabel={label}
|
|
306
|
+
// Indeterminate has no value to announce, so `busy` is the only thing
|
|
307
|
+
// separating "working, length unknown" from "empty and going nowhere".
|
|
308
|
+
accessibilityState={indeterminate ? { busy: true } : undefined}
|
|
256
309
|
accessibilityValue={
|
|
257
310
|
indeterminate
|
|
258
311
|
? undefined
|