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.
Files changed (32) hide show
  1. package/README.md +21 -10
  2. package/lib/module/components/funnel-chart/index.js +828 -0
  3. package/lib/module/components/funnel-chart/index.js.map +1 -0
  4. package/lib/module/components/progress/index.js +67 -13
  5. package/lib/module/components/progress/index.js.map +1 -1
  6. package/lib/module/components/qr-code/index.js +641 -0
  7. package/lib/module/components/qr-code/index.js.map +1 -0
  8. package/lib/module/components/qr-code/qr-encode.js +475 -0
  9. package/lib/module/components/qr-code/qr-encode.js.map +1 -0
  10. package/lib/module/index.js +2 -0
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/module/utils/chart.js +36 -0
  13. package/lib/module/utils/chart.js.map +1 -1
  14. package/lib/typescript/src/components/funnel-chart/index.d.ts +289 -0
  15. package/lib/typescript/src/components/funnel-chart/index.d.ts.map +1 -0
  16. package/lib/typescript/src/components/progress/index.d.ts +9 -1
  17. package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
  18. package/lib/typescript/src/components/qr-code/index.d.ts +221 -0
  19. package/lib/typescript/src/components/qr-code/index.d.ts.map +1 -0
  20. package/lib/typescript/src/components/qr-code/qr-encode.d.ts +39 -0
  21. package/lib/typescript/src/components/qr-code/qr-encode.d.ts.map +1 -0
  22. package/lib/typescript/src/index.d.ts +2 -0
  23. package/lib/typescript/src/index.d.ts.map +1 -1
  24. package/lib/typescript/src/utils/chart.d.ts +19 -0
  25. package/lib/typescript/src/utils/chart.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/components/funnel-chart/index.tsx +1121 -0
  28. package/src/components/progress/index.tsx +64 -11
  29. package/src/components/qr-code/index.tsx +663 -0
  30. package/src/components/qr-code/qr-encode.ts +548 -0
  31. package/src/index.ts +35 -0
  32. 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
- /** Show a looping animation for unknown-duration work. */
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` alone. Restarting it whenever the
191
- * value moved used to leave `withRepeat` cycling from wherever the bar had
192
- * got to, so a bar that kept receiving values while looping ended up
193
- * sweeping a shrinking sliver of the track instead of crossing it.
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, { duration: 1100, easing: Easing.inOut(Easing.ease) }),
223
+ withTiming(1, {
224
+ duration: reducedMotion ? PULSE_DURATION : SWEEP_DURATION,
225
+ easing: Easing.inOut(Easing.ease),
226
+ }),
200
227
  -1,
201
- false
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