panelui-native 0.34.0 → 0.36.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 +2 -1
- package/lib/module/components/input/index.js +135 -32
- package/lib/module/components/input/index.js.map +1 -1
- package/lib/module/components/otp-input/index.js +4 -1
- package/lib/module/components/otp-input/index.js.map +1 -1
- package/lib/module/components/panelside/index.js +1 -1
- package/lib/module/components/panelside/index.js.map +1 -1
- package/lib/module/components/progress/index.js +64 -24
- package/lib/module/components/progress/index.js.map +1 -1
- package/lib/module/components/swipe/index.js +537 -0
- package/lib/module/components/swipe/index.js.map +1 -0
- package/lib/module/components/textarea/index.js +3 -1
- package/lib/module/components/textarea/index.js.map +1 -1
- package/lib/module/components/time-picker/index.js +801 -0
- package/lib/module/components/time-picker/index.js.map +1 -0
- package/lib/module/icons/index.js +59 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +4 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/time.js +180 -0
- package/lib/module/utils/time.js.map +1 -0
- package/lib/typescript/src/components/input/index.d.ts +92 -0
- package/lib/typescript/src/components/input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/otp-input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/progress/index.d.ts +25 -4
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
- package/lib/typescript/src/components/swipe/index.d.ts +206 -0
- package/lib/typescript/src/components/swipe/index.d.ts.map +1 -0
- package/lib/typescript/src/components/textarea/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/index.d.ts +104 -0
- package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +4 -0
- package/lib/typescript/src/icons/index.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/utils/time.d.ts +88 -0
- package/lib/typescript/src/utils/time.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/input/index.tsx +171 -29
- package/src/components/otp-input/index.tsx +4 -1
- package/src/components/panelside/index.tsx +1 -1
- package/src/components/progress/index.tsx +93 -25
- package/src/components/swipe/index.tsx +687 -0
- package/src/components/textarea/index.tsx +3 -1
- package/src/components/time-picker/index.tsx +971 -0
- package/src/icons/index.tsx +47 -0
- package/src/index.ts +34 -0
- package/src/theme/use-theme.ts +1 -1
- package/src/utils/time.ts +192 -0
- package/theme.css +32 -0
|
@@ -52,8 +52,21 @@ export interface ProgressProps
|
|
|
52
52
|
extends Omit<ViewProps, 'children'>,
|
|
53
53
|
ProgressVariantProps {
|
|
54
54
|
className?: string;
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Where the work has got to, somewhere between `minValue` and `maxValue`.
|
|
57
|
+
* Ignored when `indeterminate` is set.
|
|
58
|
+
*/
|
|
56
59
|
value?: number;
|
|
60
|
+
/**
|
|
61
|
+
* The bottom of the range — the value at which the bar reads as empty.
|
|
62
|
+
* Defaults to `0`.
|
|
63
|
+
*/
|
|
64
|
+
minValue?: number;
|
|
65
|
+
/**
|
|
66
|
+
* The top of the range — the value at which the bar reads as full. Defaults
|
|
67
|
+
* to `100`, so a bare percentage keeps working with neither bound set.
|
|
68
|
+
*/
|
|
69
|
+
maxValue?: number;
|
|
57
70
|
/** Show a looping animation for unknown-duration work. */
|
|
58
71
|
indeterminate?: boolean;
|
|
59
72
|
/** Extra classes for the moving indicator. */
|
|
@@ -75,37 +88,59 @@ export interface ProgressProps
|
|
|
75
88
|
*/
|
|
76
89
|
valueLabel?: string;
|
|
77
90
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
91
|
+
* How to write the value, through `Intl.NumberFormat`. A `percent` style
|
|
92
|
+
* formats how far along the bar is; every other style formats the value
|
|
93
|
+
* itself, so `{ style: 'currency', currency: 'USD' }` against a `maxValue`
|
|
94
|
+
* of `2000` reads `$1,250.00` rather than a percentage of it. Falls back to
|
|
95
|
+
* a rounded percent when omitted.
|
|
81
96
|
*/
|
|
82
97
|
formatOptions?: Intl.NumberFormatOptions;
|
|
83
98
|
/** Extra classes for the label + value-label row. */
|
|
84
99
|
headerClassName?: string;
|
|
85
100
|
}
|
|
86
101
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (value
|
|
90
|
-
if (value >
|
|
102
|
+
/** `value` held inside the range, so a stray number cannot escape the track. */
|
|
103
|
+
function clamp(value: number, min: number, max: number) {
|
|
104
|
+
if (!(value > min)) return min;
|
|
105
|
+
if (value > max) return max;
|
|
91
106
|
return value;
|
|
92
107
|
}
|
|
93
108
|
|
|
94
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* How far along the bar is, 0–1. An empty or inverted range has no meaningful
|
|
111
|
+
* position in it, so it reads as empty rather than dividing by zero.
|
|
112
|
+
*/
|
|
113
|
+
function fractionOf(value: number, min: number, max: number) {
|
|
114
|
+
const span = max - min;
|
|
115
|
+
if (!(span > 0)) return 0;
|
|
116
|
+
return clamp((value - min) / span, 0, 1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The right-hand caption: an explicit override, an `Intl` rendering, or a
|
|
121
|
+
* rounded percent.
|
|
122
|
+
*
|
|
123
|
+
* A `percent` style is given the fraction, because that is what a percentage
|
|
124
|
+
* of the range means; every other style is given the value, because a currency
|
|
125
|
+
* or a byte count is a quantity and not a proportion.
|
|
126
|
+
*/
|
|
95
127
|
function formatValue(
|
|
96
128
|
value: number,
|
|
129
|
+
fraction: number,
|
|
97
130
|
valueLabel?: string,
|
|
98
131
|
formatOptions?: Intl.NumberFormatOptions
|
|
99
132
|
) {
|
|
100
133
|
if (valueLabel != null) return valueLabel;
|
|
101
134
|
if (formatOptions) {
|
|
102
135
|
try {
|
|
103
|
-
return new Intl.NumberFormat(undefined, formatOptions).format(
|
|
136
|
+
return new Intl.NumberFormat(undefined, formatOptions).format(
|
|
137
|
+
formatOptions.style === 'percent' ? fraction : value
|
|
138
|
+
);
|
|
104
139
|
} catch {
|
|
105
140
|
// Some engines ship a partial Intl; fall through to the plain percent.
|
|
106
141
|
}
|
|
107
142
|
}
|
|
108
|
-
return `${Math.round(
|
|
143
|
+
return `${Math.round(fraction * 100)}%`;
|
|
109
144
|
}
|
|
110
145
|
|
|
111
146
|
/**
|
|
@@ -115,6 +150,12 @@ function formatValue(
|
|
|
115
150
|
*
|
|
116
151
|
* Pass `label` or `showValueLabel` to caption the bar with a header row; the
|
|
117
152
|
* bare track renders when neither is set, so existing call sites are untouched.
|
|
153
|
+
*
|
|
154
|
+
* The value is read against `minValue` / `maxValue`, which default to 0 and
|
|
155
|
+
* 100 — so a percentage needs neither. Set them and the bar speaks in whatever
|
|
156
|
+
* the work is actually counted in: bytes uploaded, seats filled, points
|
|
157
|
+
* scored. Nothing has to be converted to a percent on the way in, and the
|
|
158
|
+
* screen reader is told the real range rather than a derived one.
|
|
118
159
|
*/
|
|
119
160
|
export const Progress = forwardRef<View, ProgressProps>(
|
|
120
161
|
(
|
|
@@ -123,6 +164,8 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
123
164
|
indicatorClassName,
|
|
124
165
|
headerClassName,
|
|
125
166
|
value = 0,
|
|
167
|
+
minValue = 0,
|
|
168
|
+
maxValue = 100,
|
|
126
169
|
indeterminate = false,
|
|
127
170
|
label,
|
|
128
171
|
showValueLabel = false,
|
|
@@ -139,23 +182,33 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
139
182
|
// Yoga mirrors the track; the bar sliding along it is a transform, so it
|
|
140
183
|
// has to be turned around itself.
|
|
141
184
|
const sign = useDirectionSign();
|
|
142
|
-
const
|
|
185
|
+
const target = fractionOf(value, minValue, maxValue);
|
|
186
|
+
const progress = useSharedValue(target);
|
|
143
187
|
const slide = useSharedValue(0);
|
|
144
188
|
|
|
145
|
-
|
|
189
|
+
/**
|
|
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.
|
|
194
|
+
*/
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (!indeterminate) return undefined;
|
|
197
|
+
slide.value = 0;
|
|
198
|
+
slide.value = withRepeat(
|
|
199
|
+
withTiming(1, { duration: 1100, easing: Easing.inOut(Easing.ease) }),
|
|
200
|
+
-1,
|
|
201
|
+
false
|
|
202
|
+
);
|
|
203
|
+
return () => cancelAnimation(slide);
|
|
204
|
+
}, [indeterminate, slide]);
|
|
146
205
|
|
|
206
|
+
// The fill follows the value, and picks up from wherever the loop left it
|
|
207
|
+
// when a bar stops being indeterminate mid-flight.
|
|
147
208
|
useEffect(() => {
|
|
148
|
-
if (indeterminate)
|
|
149
|
-
slide.value = withRepeat(
|
|
150
|
-
withTiming(1, { duration: 1100, easing: Easing.inOut(Easing.ease) }),
|
|
151
|
-
-1,
|
|
152
|
-
false
|
|
153
|
-
);
|
|
154
|
-
return () => cancelAnimation(slide);
|
|
155
|
-
}
|
|
209
|
+
if (indeterminate) return;
|
|
156
210
|
progress.value = withSpring(target, SPRING);
|
|
157
|
-
|
|
158
|
-
}, [indeterminate, target, progress, slide]);
|
|
211
|
+
}, [indeterminate, target, progress]);
|
|
159
212
|
|
|
160
213
|
const onLayout = (event: LayoutChangeEvent) => {
|
|
161
214
|
trackWidth.value = event.nativeEvent.layout.width;
|
|
@@ -188,6 +241,12 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
188
241
|
// The value label is meaningless while looping, so it is dropped there.
|
|
189
242
|
const showValue = showValueLabel && !indeterminate;
|
|
190
243
|
const hasHeader = label != null || showValue;
|
|
244
|
+
const spoken = formatValue(
|
|
245
|
+
clamp(value, minValue, maxValue),
|
|
246
|
+
target,
|
|
247
|
+
valueLabel,
|
|
248
|
+
formatOptions
|
|
249
|
+
);
|
|
191
250
|
|
|
192
251
|
const track = (
|
|
193
252
|
<View
|
|
@@ -195,7 +254,16 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
195
254
|
accessibilityRole="progressbar"
|
|
196
255
|
accessibilityLabel={label}
|
|
197
256
|
accessibilityValue={
|
|
198
|
-
indeterminate
|
|
257
|
+
indeterminate
|
|
258
|
+
? undefined
|
|
259
|
+
: {
|
|
260
|
+
min: minValue,
|
|
261
|
+
max: maxValue,
|
|
262
|
+
now: clamp(value, minValue, maxValue),
|
|
263
|
+
// Without this the range is read as a bare number. `text` is
|
|
264
|
+
// what carries the unit — the percent sign, the currency.
|
|
265
|
+
text: spoken,
|
|
266
|
+
}
|
|
199
267
|
}
|
|
200
268
|
className={slots.track({ className })}
|
|
201
269
|
onLayout={onLayout}
|
|
@@ -222,7 +290,7 @@ export const Progress = forwardRef<View, ProgressProps>(
|
|
|
222
290
|
)}
|
|
223
291
|
{showValue ? (
|
|
224
292
|
<Text size="sm" muted className={cn(label == null && 'ms-auto')}>
|
|
225
|
-
{
|
|
293
|
+
{spoken}
|
|
226
294
|
</Text>
|
|
227
295
|
) : null}
|
|
228
296
|
</View>
|