panelui-native 0.68.0 → 0.73.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 +13 -2
- package/lib/module/components/accordion/index.js +38 -13
- package/lib/module/components/accordion/index.js.map +1 -1
- package/lib/module/components/area-chart/index.js +1 -0
- package/lib/module/components/area-chart/index.js.map +1 -1
- package/lib/module/components/breadcrumb/index.js +2 -1
- package/lib/module/components/breadcrumb/index.js.map +1 -1
- package/lib/module/components/button/index.js +21 -12
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/carousel/index.js +1 -1
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/chip/index.js +2 -2
- package/lib/module/components/chip/index.js.map +1 -1
- package/lib/module/components/code-block/index.js.map +1 -1
- package/lib/module/components/collapsible/index.js +284 -0
- package/lib/module/components/collapsible/index.js.map +1 -0
- package/lib/module/components/color-picker/index.js.map +1 -1
- package/lib/module/components/fab/index.js +7 -6
- package/lib/module/components/fab/index.js.map +1 -1
- package/lib/module/components/flow/index.js +67 -4
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/frame/index.js +2 -2
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/grid-item/index.js +2 -2
- package/lib/module/components/grid-item/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +1 -0
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/hex-chart/index.js +1 -0
- package/lib/module/components/hex-chart/index.js.map +1 -1
- package/lib/module/components/input-group/index.js +7 -2
- package/lib/module/components/input-group/index.js.map +1 -1
- package/lib/module/components/item/index.js +2 -2
- package/lib/module/components/item/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +1 -0
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/live-line-chart/index.js +103 -9
- package/lib/module/components/live-line-chart/index.js.map +1 -1
- package/lib/module/components/live-line-chart/live-line-accessibility.js +47 -0
- package/lib/module/components/live-line-chart/live-line-accessibility.js.map +1 -0
- package/lib/module/components/live-line-chart/live-line-lifecycle.js +33 -0
- package/lib/module/components/live-line-chart/live-line-lifecycle.js.map +1 -0
- package/lib/module/components/marker/index.js +2 -2
- package/lib/module/components/marker/index.js.map +1 -1
- package/lib/module/components/marquee/index.js +191 -0
- package/lib/module/components/marquee/index.js.map +1 -0
- package/lib/module/components/marquee/marquee-math.js +39 -0
- package/lib/module/components/marquee/marquee-math.js.map +1 -0
- package/lib/module/components/menu/index.js +11 -3
- package/lib/module/components/menu/index.js.map +1 -1
- package/lib/module/components/meter/index.js +38 -20
- package/lib/module/components/meter/index.js.map +1 -1
- package/lib/module/components/meter/meter-scale.js +62 -8
- package/lib/module/components/meter/meter-scale.js.map +1 -1
- package/lib/module/components/pagination/index.js +27 -11
- package/lib/module/components/pagination/index.js.map +1 -1
- package/lib/module/components/panelside/index.js +7 -2
- package/lib/module/components/panelside/index.js.map +1 -1
- package/lib/module/components/planner/index.js +159 -64
- package/lib/module/components/planner/index.js.map +1 -1
- package/lib/module/components/planner/planner-announcement.js +30 -0
- package/lib/module/components/planner/planner-announcement.js.map +1 -0
- package/lib/module/components/planner/planner-entries.js +15 -7
- package/lib/module/components/planner/planner-entries.js.map +1 -1
- package/lib/module/components/planner/planner-grid-navigation.js +33 -0
- package/lib/module/components/planner/planner-grid-navigation.js.map +1 -0
- package/lib/module/components/planner/planner-lifecycle.js +45 -0
- package/lib/module/components/planner/planner-lifecycle.js.map +1 -0
- package/lib/module/components/plot/index.js +105 -42
- package/lib/module/components/plot/index.js.map +1 -1
- package/lib/module/components/popover/index.js +2 -2
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/post/index.js +4 -4
- package/lib/module/components/post/index.js.map +1 -1
- package/lib/module/components/search-bar/index.js +331 -0
- package/lib/module/components/search-bar/index.js.map +1 -0
- package/lib/module/components/table/index.js +3 -4
- package/lib/module/components/table/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +288 -39
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/components/toggle-button/index.js +1 -1
- package/lib/module/components/toggle-button/index.js.map +1 -1
- package/lib/module/components/tree/index.js +13 -8
- package/lib/module/components/tree/index.js.map +1 -1
- package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/focus-restoration-store.js +52 -0
- package/lib/module/primitives/focus-restoration-store.js.map +1 -0
- package/lib/module/primitives/portal.js +36 -2
- package/lib/module/primitives/portal.js.map +1 -1
- package/lib/module/utils/chart.js +50 -7
- package/lib/module/utils/chart.js.map +1 -1
- package/lib/typescript/src/components/accordion/index.d.ts +6 -2
- package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/code-block/index.d.ts +3 -3
- package/lib/typescript/src/components/code-block/index.d.ts.map +1 -1
- package/lib/typescript/src/components/collapsible/index.d.ts +76 -0
- package/lib/typescript/src/components/collapsible/index.d.ts.map +1 -0
- package/lib/typescript/src/components/color-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/fab/index.d.ts +4 -2
- package/lib/typescript/src/components/fab/index.d.ts.map +1 -1
- package/lib/typescript/src/components/flow/index.d.ts +7 -1
- package/lib/typescript/src/components/flow/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/hex-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/live-line-chart/index.d.ts +21 -7
- package/lib/typescript/src/components/live-line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/live-line-chart/live-line-accessibility.d.ts +22 -0
- package/lib/typescript/src/components/live-line-chart/live-line-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/live-line-chart/live-line-lifecycle.d.ts +23 -0
- package/lib/typescript/src/components/live-line-chart/live-line-lifecycle.d.ts.map +1 -0
- package/lib/typescript/src/components/marquee/index.d.ts +73 -0
- package/lib/typescript/src/components/marquee/index.d.ts.map +1 -0
- package/lib/typescript/src/components/marquee/marquee-math.d.ts +21 -0
- package/lib/typescript/src/components/marquee/marquee-math.d.ts.map +1 -0
- package/lib/typescript/src/components/menu/index.d.ts +1 -1
- package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
- package/lib/typescript/src/components/meter/index.d.ts +7 -4
- package/lib/typescript/src/components/meter/index.d.ts.map +1 -1
- package/lib/typescript/src/components/meter/meter-scale.d.ts +39 -0
- package/lib/typescript/src/components/meter/meter-scale.d.ts.map +1 -1
- package/lib/typescript/src/components/pagination/index.d.ts.map +1 -1
- package/lib/typescript/src/components/panelside/index.d.ts +1 -1
- package/lib/typescript/src/components/panelside/index.d.ts.map +1 -1
- package/lib/typescript/src/components/plan/index.d.ts +2 -2
- package/lib/typescript/src/components/planner/index.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/planner-announcement.d.ts +15 -0
- package/lib/typescript/src/components/planner/planner-announcement.d.ts.map +1 -0
- package/lib/typescript/src/components/planner/planner-entries.d.ts +1 -1
- package/lib/typescript/src/components/planner/planner-entries.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/planner-grid-navigation.d.ts +4 -0
- package/lib/typescript/src/components/planner/planner-grid-navigation.d.ts.map +1 -0
- package/lib/typescript/src/components/planner/planner-lifecycle.d.ts +20 -0
- package/lib/typescript/src/components/planner/planner-lifecycle.d.ts.map +1 -0
- package/lib/typescript/src/components/plot/index.d.ts +59 -5
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
- package/lib/typescript/src/components/search-bar/index.d.ts +216 -0
- package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/timeline/index.d.ts +35 -2
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tree/index.d.ts +3 -4
- package/lib/typescript/src/components/tree/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +2 -2
- package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/focus-restoration-store.d.ts +24 -0
- package/lib/typescript/src/primitives/focus-restoration-store.d.ts.map +1 -0
- package/lib/typescript/src/primitives/portal.d.ts +4 -0
- package/lib/typescript/src/primitives/portal.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +28 -3
- package/lib/typescript/src/utils/chart.d.ts.map +1 -1
- package/package.json +36 -3
- package/src/components/accordion/index.tsx +48 -15
- package/src/components/area-chart/index.tsx +8 -0
- package/src/components/breadcrumb/index.tsx +2 -1
- package/src/components/button/index.tsx +14 -6
- package/src/components/carousel/index.tsx +1 -1
- package/src/components/chip/index.tsx +2 -2
- package/src/components/code-block/index.tsx +3 -3
- package/src/components/collapsible/index.tsx +287 -0
- package/src/components/color-picker/index.tsx +3 -2
- package/src/components/fab/index.tsx +21 -11
- package/src/components/flow/index.tsx +94 -15
- package/src/components/frame/index.tsx +2 -2
- package/src/components/grid-item/index.tsx +2 -2
- package/src/components/heatmap-chart/index.tsx +7 -1
- package/src/components/hex-chart/index.tsx +7 -1
- package/src/components/input-group/index.tsx +7 -2
- package/src/components/item/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +9 -0
- package/src/components/live-line-chart/index.tsx +130 -13
- package/src/components/live-line-chart/live-line-accessibility.ts +61 -0
- package/src/components/live-line-chart/live-line-lifecycle.ts +63 -0
- package/src/components/marker/index.tsx +2 -2
- package/src/components/marquee/index.tsx +234 -0
- package/src/components/marquee/marquee-math.ts +37 -0
- package/src/components/menu/index.tsx +11 -3
- package/src/components/meter/index.tsx +49 -26
- package/src/components/meter/meter-scale.ts +84 -8
- package/src/components/pagination/index.tsx +26 -13
- package/src/components/panelside/index.tsx +7 -2
- package/src/components/plan/index.tsx +2 -2
- package/src/components/planner/index.tsx +202 -76
- package/src/components/planner/planner-announcement.ts +37 -0
- package/src/components/planner/planner-entries.ts +16 -7
- package/src/components/planner/planner-grid-navigation.ts +45 -0
- package/src/components/planner/planner-lifecycle.ts +76 -0
- package/src/components/plot/index.tsx +183 -39
- package/src/components/popover/index.tsx +3 -3
- package/src/components/post/index.tsx +4 -4
- package/src/components/search-bar/index.tsx +436 -0
- package/src/components/table/index.tsx +2 -2
- package/src/components/timeline/index.tsx +335 -39
- package/src/components/toggle-button/index.tsx +1 -1
- package/src/components/tree/index.tsx +17 -12
- package/src/hooks/use-keyboard-avoidance.ts +7 -2
- package/src/index.ts +15 -0
- package/src/primitives/focus-restoration-store.ts +70 -0
- package/src/primitives/portal.tsx +34 -2
- package/src/utils/chart.ts +66 -7
|
@@ -451,6 +451,8 @@ export function MenuItem({
|
|
|
451
451
|
closeOnSelect = true,
|
|
452
452
|
onSelect,
|
|
453
453
|
onPress,
|
|
454
|
+
onPressIn,
|
|
455
|
+
onPressOut,
|
|
454
456
|
...props
|
|
455
457
|
}: MenuItemProps) {
|
|
456
458
|
const { close, haptics } = useMenu('Menu.Item');
|
|
@@ -467,15 +469,21 @@ export function MenuItem({
|
|
|
467
469
|
|
|
468
470
|
return (
|
|
469
471
|
<AnimatedPressable
|
|
472
|
+
{...props}
|
|
470
473
|
accessibilityRole="menuitem"
|
|
471
474
|
accessibilityState={{ disabled }}
|
|
472
475
|
disabled={disabled}
|
|
473
476
|
onPress={handlePress}
|
|
474
|
-
onPressIn={
|
|
475
|
-
|
|
477
|
+
onPressIn={(event) => {
|
|
478
|
+
onPressIn?.(event);
|
|
479
|
+
press.onPressIn();
|
|
480
|
+
}}
|
|
481
|
+
onPressOut={(event) => {
|
|
482
|
+
onPressOut?.(event);
|
|
483
|
+
press.onPressOut();
|
|
484
|
+
}}
|
|
476
485
|
style={press.rowStyle}
|
|
477
486
|
className={cn(slots.item(), 'overflow-hidden', className)}
|
|
478
|
-
{...props}
|
|
479
487
|
>
|
|
480
488
|
<Animated.View
|
|
481
489
|
pointerEvents="none"
|
|
@@ -11,16 +11,21 @@ import Animated, {
|
|
|
11
11
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
12
12
|
import { Text } from '../../primitives/text';
|
|
13
13
|
import {
|
|
14
|
-
clamp,
|
|
15
14
|
colorFor,
|
|
16
|
-
fractionOf,
|
|
17
|
-
formatValue,
|
|
18
15
|
litSegments,
|
|
16
|
+
meterSemantics,
|
|
17
|
+
normalizeScale,
|
|
18
|
+
normalizeSegments,
|
|
19
19
|
type MeterColor,
|
|
20
20
|
type MeterThreshold,
|
|
21
21
|
} from './meter-scale';
|
|
22
22
|
|
|
23
|
-
const SPRING = {
|
|
23
|
+
const SPRING = {
|
|
24
|
+
damping: 20,
|
|
25
|
+
stiffness: 180,
|
|
26
|
+
mass: 0.6,
|
|
27
|
+
overshootClamping: true,
|
|
28
|
+
} as const;
|
|
24
29
|
/** Milliseconds for a segment to light or go out. */
|
|
25
30
|
const SEGMENT_DURATION = 180;
|
|
26
31
|
/** How faint an unlit segment sits. Present, but plainly not counted. */
|
|
@@ -32,7 +37,7 @@ const meterVariants = tv({
|
|
|
32
37
|
header: 'flex-row items-center justify-between gap-2',
|
|
33
38
|
track: 'w-full overflow-hidden rounded-full',
|
|
34
39
|
indicator: 'h-full rounded-full',
|
|
35
|
-
segments: 'w-full flex-row',
|
|
40
|
+
segments: 'w-full flex-row overflow-hidden',
|
|
36
41
|
segment: 'flex-1 rounded-full',
|
|
37
42
|
},
|
|
38
43
|
variants: {
|
|
@@ -70,7 +75,7 @@ export interface MeterProps
|
|
|
70
75
|
extends Omit<ViewProps, 'children'>,
|
|
71
76
|
MeterVariantProps {
|
|
72
77
|
className?: string;
|
|
73
|
-
/** The measurement
|
|
78
|
+
/** The measurement. Values outside the scale are clamped to its ends. */
|
|
74
79
|
value: number;
|
|
75
80
|
/**
|
|
76
81
|
* The bottom of the scale — the value at which the bar reads as empty.
|
|
@@ -111,7 +116,8 @@ export interface MeterProps
|
|
|
111
116
|
/**
|
|
112
117
|
* Points on the scale where the colour changes, each `{ from, color }`. The
|
|
113
118
|
* highest one the reading has reached wins, so the order you list them in
|
|
114
|
-
* does not matter; below all of them the `color` prop applies.
|
|
119
|
+
* does not matter; below all of them the `color` prop applies. Non-finite
|
|
120
|
+
* `from` values are ignored.
|
|
115
121
|
*
|
|
116
122
|
* This is the difference between a meter and a bar: the colour is a
|
|
117
123
|
* judgement about the reading. Which direction is bad is yours to say —
|
|
@@ -125,8 +131,10 @@ export interface MeterProps
|
|
|
125
131
|
* say "three out of four" where a bar says "about seventy percent", and a
|
|
126
132
|
* password is not seventy percent strong.
|
|
127
133
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
134
|
+
* Fractional counts round down and counts above 100 clamp to 100. Invalid,
|
|
135
|
+
* non-positive, or sub-one counts use the continuous bar. Any value above
|
|
136
|
+
* the floor lights at least one block, so a reading that is not empty never
|
|
137
|
+
* looks it.
|
|
130
138
|
*/
|
|
131
139
|
segments?: number;
|
|
132
140
|
/** Extra classes for the fill, or for a lit segment. */
|
|
@@ -207,8 +215,9 @@ export const Meter = forwardRef<View, MeterProps>(
|
|
|
207
215
|
},
|
|
208
216
|
ref
|
|
209
217
|
) => {
|
|
210
|
-
const
|
|
211
|
-
const
|
|
218
|
+
const scale = normalizeScale(value, minValue, maxValue);
|
|
219
|
+
const { value: held, fraction } = scale;
|
|
220
|
+
const segmentCount = normalizeSegments(segments);
|
|
212
221
|
const resolvedColor = colorFor(held, color ?? 'primary', thresholds);
|
|
213
222
|
const slots = meterVariants({ color: resolvedColor, size });
|
|
214
223
|
const reducedMotion = useReducedMotion();
|
|
@@ -218,7 +227,7 @@ export const Meter = forwardRef<View, MeterProps>(
|
|
|
218
227
|
// Any reading above the floor lights a block, so "a little" never looks
|
|
219
228
|
// like "none". Rounding down would leave the first quarter of a
|
|
220
229
|
// four-block meter dark, which is the reading it is least able to afford.
|
|
221
|
-
const litCount =
|
|
230
|
+
const litCount = litSegments(fraction, segmentCount);
|
|
222
231
|
const lit = useSharedValue(litCount);
|
|
223
232
|
|
|
224
233
|
// Reduce motion lands on the value instead of springing to it — the
|
|
@@ -232,39 +241,47 @@ export const Meter = forwardRef<View, MeterProps>(
|
|
|
232
241
|
}, [lit, litCount]);
|
|
233
242
|
|
|
234
243
|
const onLayout = (event: LayoutChangeEvent) => {
|
|
235
|
-
|
|
244
|
+
const width = event.nativeEvent.layout.width;
|
|
245
|
+
trackWidth.value = Number.isFinite(width) && width > 0 ? width : 0;
|
|
236
246
|
};
|
|
237
247
|
|
|
238
248
|
const fillStyle = useAnimatedStyle(() => ({
|
|
239
249
|
width: trackWidth.value * progress.value,
|
|
240
250
|
}));
|
|
241
251
|
|
|
242
|
-
const
|
|
252
|
+
const semantics = meterSemantics({
|
|
253
|
+
value: held,
|
|
254
|
+
fraction,
|
|
255
|
+
// The normalized ends, not the raw props: an invalid scale is repaired
|
|
256
|
+
// for the bar, and the spoken reading has to describe the same one.
|
|
257
|
+
minValue: scale.min,
|
|
258
|
+
maxValue: scale.max,
|
|
259
|
+
label,
|
|
260
|
+
accessibilityLabel,
|
|
261
|
+
valueLabel,
|
|
262
|
+
formatOptions,
|
|
263
|
+
});
|
|
264
|
+
const spoken = semantics.text;
|
|
243
265
|
const showValue = showValueLabel;
|
|
244
266
|
const hasHeader = label != null || showValue;
|
|
245
267
|
|
|
246
268
|
const accessibility = {
|
|
247
269
|
accessibilityRole: 'progressbar' as const,
|
|
248
|
-
accessibilityLabel:
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
now: held,
|
|
253
|
-
// Without this the scale is read as a bare number. `text` is what
|
|
254
|
-
// carries the unit — the percent sign, the gigabytes, the word.
|
|
255
|
-
text: spoken,
|
|
256
|
-
},
|
|
270
|
+
accessibilityLabel: semantics.label,
|
|
271
|
+
// `text` carries the unit or word and intentionally overrides the
|
|
272
|
+
// platform's generic rendering of this numeric range.
|
|
273
|
+
accessibilityValue: semantics.value,
|
|
257
274
|
};
|
|
258
275
|
|
|
259
276
|
const bar =
|
|
260
|
-
|
|
277
|
+
segmentCount > 0 ? (
|
|
261
278
|
<View
|
|
262
279
|
ref={ref}
|
|
263
280
|
{...accessibility}
|
|
264
281
|
className={slots.segments({ className })}
|
|
265
282
|
{...props}
|
|
266
283
|
>
|
|
267
|
-
{Array.from({ length:
|
|
284
|
+
{Array.from({ length: segmentCount }, (_, index) => (
|
|
268
285
|
<Segment
|
|
269
286
|
key={index}
|
|
270
287
|
index={index}
|
|
@@ -293,7 +310,13 @@ export const Meter = forwardRef<View, MeterProps>(
|
|
|
293
310
|
|
|
294
311
|
return (
|
|
295
312
|
<View className={slots.root()}>
|
|
296
|
-
<View
|
|
313
|
+
<View
|
|
314
|
+
className={slots.header({ className: headerClassName })}
|
|
315
|
+
// This is the visual rendering of the name and value already owned
|
|
316
|
+
// by the bar below. Hiding the subtree keeps one concise focus stop.
|
|
317
|
+
accessibilityElementsHidden
|
|
318
|
+
importantForAccessibility="no-hide-descendants"
|
|
319
|
+
>
|
|
297
320
|
{label != null ? (
|
|
298
321
|
<Text size="sm" weight="medium" numberOfLines={1}>
|
|
299
322
|
{label}
|
|
@@ -29,11 +29,55 @@ export interface MeterThreshold {
|
|
|
29
29
|
color: MeterColor;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** A bounded block count keeps malformed input from allocating without limit. */
|
|
33
|
+
export const MAX_METER_SEGMENTS = 100;
|
|
34
|
+
|
|
35
|
+
export interface MeterScale {
|
|
36
|
+
min: number;
|
|
37
|
+
max: number;
|
|
38
|
+
value: number;
|
|
39
|
+
fraction: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Make a public scale safe for layout, animation and native accessibility.
|
|
44
|
+
* Invalid bounds fall back to their documented defaults; if those still do
|
|
45
|
+
* not produce a positive span, the scale honestly collapses at its floor.
|
|
46
|
+
*/
|
|
47
|
+
export function normalizeScale(value: number, min: number, max: number): MeterScale {
|
|
48
|
+
const floor = Number.isFinite(min) ? min : 0;
|
|
49
|
+
const candidateMax = Number.isFinite(max) ? max : 100;
|
|
50
|
+
const ceiling = candidateMax > floor ? candidateMax : floor;
|
|
51
|
+
const held = Number.isNaN(value)
|
|
52
|
+
? floor
|
|
53
|
+
: value === Number.POSITIVE_INFINITY
|
|
54
|
+
? ceiling
|
|
55
|
+
: value === Number.NEGATIVE_INFINITY
|
|
56
|
+
? floor
|
|
57
|
+
: Math.min(Math.max(value, floor), ceiling);
|
|
58
|
+
const magnitude = Math.max(Math.abs(floor), Math.abs(ceiling), 1);
|
|
59
|
+
const position =
|
|
60
|
+
ceiling > floor
|
|
61
|
+
? (held / magnitude - floor / magnitude) /
|
|
62
|
+
(ceiling / magnitude - floor / magnitude)
|
|
63
|
+
: 0;
|
|
64
|
+
return {
|
|
65
|
+
min: floor,
|
|
66
|
+
max: ceiling,
|
|
67
|
+
value: held,
|
|
68
|
+
fraction: Math.min(Math.max(position, 0), 1),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Positive counts are whole and bounded; zero means the continuous meter. */
|
|
73
|
+
export function normalizeSegments(segments: number | undefined) {
|
|
74
|
+
if (segments == null || !Number.isFinite(segments) || !(segments > 0)) return 0;
|
|
75
|
+
return Math.min(Math.floor(segments), MAX_METER_SEGMENTS);
|
|
76
|
+
}
|
|
77
|
+
|
|
32
78
|
/** `value` held inside the scale, so a stray number cannot escape the track. */
|
|
33
79
|
export function clamp(value: number, min: number, max: number) {
|
|
34
|
-
|
|
35
|
-
if (value > max) return max;
|
|
36
|
-
return value;
|
|
80
|
+
return normalizeScale(value, min, max).value;
|
|
37
81
|
}
|
|
38
82
|
|
|
39
83
|
/**
|
|
@@ -41,9 +85,7 @@ export function clamp(value: number, min: number, max: number) {
|
|
|
41
85
|
* meaningful position in it, so it reads as empty rather than dividing by zero.
|
|
42
86
|
*/
|
|
43
87
|
export function fractionOf(value: number, min: number, max: number) {
|
|
44
|
-
|
|
45
|
-
if (!(span > 0)) return 0;
|
|
46
|
-
return clamp((value - min) / span, 0, 1);
|
|
88
|
+
return normalizeScale(value, min, max).fraction;
|
|
47
89
|
}
|
|
48
90
|
|
|
49
91
|
/**
|
|
@@ -62,6 +104,7 @@ export function colorFor(
|
|
|
62
104
|
if (!thresholds?.length) return base;
|
|
63
105
|
let winner: MeterThreshold | undefined;
|
|
64
106
|
for (const threshold of thresholds) {
|
|
107
|
+
if (!Number.isFinite(threshold.from)) continue;
|
|
65
108
|
if (value < threshold.from) continue;
|
|
66
109
|
if (!winner || threshold.from > winner.from) winner = threshold;
|
|
67
110
|
}
|
|
@@ -77,8 +120,10 @@ export function colorFor(
|
|
|
77
120
|
* wrong.
|
|
78
121
|
*/
|
|
79
122
|
export function litSegments(fraction: number, segments: number) {
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
const count = normalizeSegments(segments);
|
|
124
|
+
if (count === 0 || Number.isNaN(fraction) || fraction <= 0) return 0;
|
|
125
|
+
if (!Number.isFinite(fraction) || fraction >= 1) return count;
|
|
126
|
+
return Math.min(Math.ceil(fraction * count), count);
|
|
82
127
|
}
|
|
83
128
|
|
|
84
129
|
/**
|
|
@@ -107,3 +152,34 @@ export function formatValue(
|
|
|
107
152
|
}
|
|
108
153
|
return `${Math.round(fraction * 100)}%`;
|
|
109
154
|
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The meter's single spoken contract. Visual treatments such as segments,
|
|
158
|
+
* thresholds and motion do not belong here: they do not change the reading.
|
|
159
|
+
*/
|
|
160
|
+
export function meterSemantics({
|
|
161
|
+
value,
|
|
162
|
+
fraction,
|
|
163
|
+
minValue,
|
|
164
|
+
maxValue,
|
|
165
|
+
label,
|
|
166
|
+
accessibilityLabel,
|
|
167
|
+
valueLabel,
|
|
168
|
+
formatOptions,
|
|
169
|
+
}: {
|
|
170
|
+
value: number;
|
|
171
|
+
fraction: number;
|
|
172
|
+
minValue: number;
|
|
173
|
+
maxValue: number;
|
|
174
|
+
label?: string;
|
|
175
|
+
accessibilityLabel?: string;
|
|
176
|
+
valueLabel?: string;
|
|
177
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
178
|
+
}) {
|
|
179
|
+
const text = formatValue(value, fraction, valueLabel, formatOptions);
|
|
180
|
+
return {
|
|
181
|
+
label: accessibilityLabel ?? label,
|
|
182
|
+
text,
|
|
183
|
+
value: { min: minValue, max: maxValue, now: value, text },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -367,7 +367,7 @@ export interface PaginationItemProps
|
|
|
367
367
|
* are on, so the current page is spoken as state rather than only painted.
|
|
368
368
|
*/
|
|
369
369
|
const PaginationItem = forwardRef<View, PaginationItemProps>(
|
|
370
|
-
({ className, labelClassName, page, children, ...props }, ref) => {
|
|
370
|
+
({ className, labelClassName, page, children, onPress, ...props }, ref) => {
|
|
371
371
|
const { page: current, size, disabled, goTo } = usePagination('Pagination.Item');
|
|
372
372
|
const isCurrent = page === current;
|
|
373
373
|
const { item, itemLabel } = paginationVariants({ size, current: isCurrent, disabled });
|
|
@@ -375,14 +375,17 @@ const PaginationItem = forwardRef<View, PaginationItemProps>(
|
|
|
375
375
|
return (
|
|
376
376
|
<AnimatedPressable
|
|
377
377
|
ref={ref}
|
|
378
|
+
{...props}
|
|
378
379
|
accessibilityRole="button"
|
|
379
380
|
accessibilityLabel={`Page ${page}`}
|
|
380
381
|
accessibilityState={{ selected: isCurrent, disabled }}
|
|
381
382
|
disabled={disabled}
|
|
382
383
|
hitSlop={size === 'sm' ? SMALL_HIT_SLOP : undefined}
|
|
383
|
-
onPress={() =>
|
|
384
|
+
onPress={(event) => {
|
|
385
|
+
onPress?.(event);
|
|
386
|
+
goTo(page);
|
|
387
|
+
}}
|
|
384
388
|
className={item({ className })}
|
|
385
|
-
{...props}
|
|
386
389
|
>
|
|
387
390
|
{textChildren(children ?? String(page), (text) => (
|
|
388
391
|
<Text className={itemLabel({ className: labelClassName })}>{text}</Text>
|
|
@@ -437,23 +440,29 @@ function PaginationArrow({
|
|
|
437
440
|
word,
|
|
438
441
|
className,
|
|
439
442
|
label,
|
|
443
|
+
disabled,
|
|
444
|
+
onPress,
|
|
440
445
|
...props
|
|
441
446
|
}: PaginationArrowProps & { step: -1 | 1; part: string; word: string }) {
|
|
442
447
|
const { Glyph, size, spent, color, press } = useArrow(step, part);
|
|
443
|
-
const
|
|
448
|
+
const isDisabled = Boolean(spent || disabled);
|
|
449
|
+
const { item, itemLabel } = paginationVariants({ size, disabled: isDisabled });
|
|
444
450
|
|
|
445
451
|
return (
|
|
446
452
|
<AnimatedPressable
|
|
453
|
+
{...props}
|
|
447
454
|
accessibilityRole="button"
|
|
448
455
|
accessibilityLabel={word}
|
|
449
|
-
accessibilityState={{ disabled:
|
|
450
|
-
disabled={
|
|
456
|
+
accessibilityState={{ disabled: isDisabled }}
|
|
457
|
+
disabled={isDisabled}
|
|
451
458
|
hitSlop={size === 'sm' ? SMALL_HIT_SLOP : undefined}
|
|
452
|
-
onPress={
|
|
459
|
+
onPress={(event) => {
|
|
460
|
+
onPress?.(event);
|
|
461
|
+
press();
|
|
462
|
+
}}
|
|
453
463
|
className={item({
|
|
454
464
|
className: cn('flex-row gap-1', label && (size === 'sm' ? 'px-2' : 'px-3'), className),
|
|
455
465
|
})}
|
|
456
|
-
{...props}
|
|
457
466
|
>
|
|
458
467
|
{step === -1 ? <Glyph size={iconSize[size]} color={color} /> : null}
|
|
459
468
|
{label ? <Text className={itemLabel()}>{word}</Text> : null}
|
|
@@ -489,24 +498,28 @@ export interface PaginationEllipsisProps
|
|
|
489
498
|
* that always went forwards would strand anyone reading the row right to left.
|
|
490
499
|
*/
|
|
491
500
|
const PaginationEllipsis = forwardRef<View, PaginationEllipsisProps>(
|
|
492
|
-
({ className, direction = 1, jump = 5, ...props }, ref) => {
|
|
501
|
+
({ className, direction = 1, jump = 5, disabled: disabledProp, onPress, ...props }, ref) => {
|
|
493
502
|
const { page, size, disabled, goTo } = usePagination('Pagination.Ellipsis');
|
|
494
503
|
const color = useCSSVariable('--color-muted-foreground');
|
|
495
504
|
const { ellipsis } = paginationVariants({ size });
|
|
505
|
+
const isDisabled = Boolean(disabled || disabledProp);
|
|
496
506
|
|
|
497
507
|
return (
|
|
498
508
|
<AnimatedPressable
|
|
499
509
|
ref={ref}
|
|
510
|
+
{...props}
|
|
500
511
|
accessibilityRole="button"
|
|
501
512
|
accessibilityLabel={
|
|
502
513
|
direction === -1 ? `Back ${jump} pages` : `Forward ${jump} pages`
|
|
503
514
|
}
|
|
504
|
-
accessibilityState={{ disabled }}
|
|
505
|
-
disabled={
|
|
515
|
+
accessibilityState={{ disabled: isDisabled }}
|
|
516
|
+
disabled={isDisabled}
|
|
506
517
|
hitSlop={size === 'sm' ? SMALL_HIT_SLOP : undefined}
|
|
507
|
-
onPress={() =>
|
|
518
|
+
onPress={(event) => {
|
|
519
|
+
onPress?.(event);
|
|
520
|
+
goTo(page + direction * jump);
|
|
521
|
+
}}
|
|
508
522
|
className={ellipsis({ className })}
|
|
509
|
-
{...props}
|
|
510
523
|
>
|
|
511
524
|
<EllipsisIcon
|
|
512
525
|
size={iconSize[size]}
|
|
@@ -1412,6 +1412,7 @@ function PanelsideTrigger({
|
|
|
1412
1412
|
className,
|
|
1413
1413
|
label = 'Open navigation panel',
|
|
1414
1414
|
children,
|
|
1415
|
+
onPress,
|
|
1415
1416
|
...props
|
|
1416
1417
|
}: PanelsideTriggerProps) {
|
|
1417
1418
|
const { toggle, docked } = usePanelsideContext('Panelside.Trigger');
|
|
@@ -1426,6 +1427,7 @@ function PanelsideTrigger({
|
|
|
1426
1427
|
return cloneElement(children, {
|
|
1427
1428
|
onPress: (...args: unknown[]) => {
|
|
1428
1429
|
children.props.onPress?.(...args);
|
|
1430
|
+
onPress?.(...(args as Parameters<NonNullable<PressableProps['onPress']>>));
|
|
1429
1431
|
toggle();
|
|
1430
1432
|
},
|
|
1431
1433
|
});
|
|
@@ -1433,11 +1435,14 @@ function PanelsideTrigger({
|
|
|
1433
1435
|
|
|
1434
1436
|
return (
|
|
1435
1437
|
<AnimatedPressable
|
|
1436
|
-
|
|
1438
|
+
{...props}
|
|
1439
|
+
onPress={(event) => {
|
|
1440
|
+
onPress?.(event);
|
|
1441
|
+
toggle();
|
|
1442
|
+
}}
|
|
1437
1443
|
className={cn('h-10 w-10 items-center justify-center rounded-full', className)}
|
|
1438
1444
|
accessibilityRole="button"
|
|
1439
1445
|
accessibilityLabel={label}
|
|
1440
|
-
{...props}
|
|
1441
1446
|
>
|
|
1442
1447
|
<MenuIcon size={20} color={color} />
|
|
1443
1448
|
</AnimatedPressable>
|
|
@@ -285,7 +285,7 @@ function PlanIcon({ className, children, ...props }: PlanIconProps) {
|
|
|
285
285
|
);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
export interface PlanTitleProps extends
|
|
288
|
+
export interface PlanTitleProps extends TextProps {
|
|
289
289
|
className?: string;
|
|
290
290
|
children?: ReactNode;
|
|
291
291
|
}
|
|
@@ -306,7 +306,7 @@ function PlanTitle({ className, children, ...props }: PlanTitleProps) {
|
|
|
306
306
|
);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
export interface PlanDescriptionProps extends
|
|
309
|
+
export interface PlanDescriptionProps extends TextProps {
|
|
310
310
|
className?: string;
|
|
311
311
|
children?: ReactNode;
|
|
312
312
|
}
|