panelui-native 0.73.0 → 0.75.1
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 +3 -1
- package/lib/module/components/avatar/avatar-group.js +64 -0
- package/lib/module/components/avatar/avatar-group.js.map +1 -0
- package/lib/module/components/avatar/index.js +84 -2
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/color-picker/color-picker-accessibility.js +12 -0
- package/lib/module/components/color-picker/color-picker-accessibility.js.map +1 -0
- package/lib/module/components/color-picker/index.js +9 -5
- package/lib/module/components/color-picker/index.js.map +1 -1
- package/lib/module/components/date-time-picker/index.js +11 -3
- package/lib/module/components/date-time-picker/index.js.map +1 -1
- package/lib/module/components/drawer/index.js +62 -13
- package/lib/module/components/drawer/index.js.map +1 -1
- package/lib/module/components/input/index.js +7 -4
- package/lib/module/components/input/index.js.map +1 -1
- package/lib/module/components/input/input-content-padding.js +6 -0
- package/lib/module/components/input/input-content-padding.js.map +1 -0
- package/lib/module/components/input-group/index.js +14 -9
- package/lib/module/components/input-group/index.js.map +1 -1
- package/lib/module/components/input-group/input-group-measurements.js +60 -0
- package/lib/module/components/input-group/input-group-measurements.js.map +1 -0
- package/lib/module/components/otp-input/index.js +3 -1
- package/lib/module/components/otp-input/index.js.map +1 -1
- package/lib/module/components/otp-input/otp-input-state.js +7 -0
- package/lib/module/components/otp-input/otp-input-state.js.map +1 -0
- package/lib/module/components/planner/index.js +20 -6
- package/lib/module/components/planner/index.js.map +1 -1
- package/lib/module/components/planner/planner-today.js +8 -0
- package/lib/module/components/planner/planner-today.js.map +1 -0
- package/lib/module/components/plot/index.js +8 -11
- package/lib/module/components/plot/index.js.map +1 -1
- package/lib/module/components/plot/plot-series-registry.js +32 -0
- package/lib/module/components/plot/plot-series-registry.js.map +1 -0
- package/lib/module/components/radio-group/index.js +2 -1
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/radio-group/radio-group-value.js +8 -0
- package/lib/module/components/radio-group/radio-group-value.js.map +1 -0
- package/lib/module/components/rating/index.js +7 -4
- package/lib/module/components/rating/index.js.map +1 -1
- package/lib/module/components/rating/rating-inputs.js +15 -0
- package/lib/module/components/rating/rating-inputs.js.map +1 -0
- package/lib/module/components/search-bar/index.js +7 -7
- package/lib/module/components/search-bar/index.js.map +1 -1
- package/lib/module/components/search-bar/search-bar-debounce.js +23 -0
- package/lib/module/components/search-bar/search-bar-debounce.js.map +1 -0
- package/lib/module/components/select/index.js +9 -1
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/select/native-select-contract.js +11 -0
- package/lib/module/components/select/native-select-contract.js.map +1 -0
- package/lib/module/components/split-view/index.js +439 -0
- package/lib/module/components/split-view/index.js.map +1 -0
- package/lib/module/components/split-view/split-view-math.js +132 -0
- package/lib/module/components/split-view/split-view-math.js.map +1 -0
- package/lib/module/components/splitter/index.js +433 -0
- package/lib/module/components/splitter/index.js.map +1 -0
- package/lib/module/components/splitter/splitter-math.js +206 -0
- package/lib/module/components/splitter/splitter-math.js.map +1 -0
- package/lib/module/components/steps/index.js +16 -5
- package/lib/module/components/steps/index.js.map +1 -1
- package/lib/module/components/steps/steps-trigger.js +6 -0
- package/lib/module/components/steps/steps-trigger.js.map +1 -0
- package/lib/module/components/tag-input/index.js +15 -10
- package/lib/module/components/tag-input/index.js.map +1 -1
- package/lib/module/components/tag-input/tag-input-state.js +14 -0
- package/lib/module/components/tag-input/tag-input-state.js.map +1 -0
- package/lib/module/components/textarea/index.js +9 -4
- package/lib/module/components/textarea/index.js.map +1 -1
- package/lib/module/components/textarea/textarea-disabled.js +6 -0
- package/lib/module/components/textarea/textarea-disabled.js.map +1 -0
- package/lib/module/components/timeline/index.js +83 -27
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/components/timeline/timeline-geometry.js +11 -0
- package/lib/module/components/timeline/timeline-geometry.js.map +1 -0
- package/lib/module/components/toast/index.js +18 -1
- package/lib/module/components/toast/index.js.map +1 -1
- package/lib/module/components/toast/toast-store.js +36 -4
- package/lib/module/components/toast/toast-store.js.map +1 -1
- package/lib/module/components/tour/index.js +4 -2
- package/lib/module/components/tour/index.js.map +1 -1
- package/lib/module/components/tour/tour-card-measurement.js +24 -0
- package/lib/module/components/tour/tour-card-measurement.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/components/avatar/avatar-group.d.ts +46 -0
- package/lib/typescript/src/components/avatar/avatar-group.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/index.d.ts +23 -1
- package/lib/typescript/src/components/avatar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/color-picker/color-picker-accessibility.d.ts +8 -0
- package/lib/typescript/src/components/color-picker/color-picker-accessibility.d.ts.map +1 -0
- package/lib/typescript/src/components/color-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/date-time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/drawer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input/input-content-padding.d.ts +2 -0
- package/lib/typescript/src/components/input/input-content-padding.d.ts.map +1 -0
- package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input-group/input-group-measurements.d.ts +16 -0
- package/lib/typescript/src/components/input-group/input-group-measurements.d.ts.map +1 -0
- package/lib/typescript/src/components/otp-input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/otp-input/otp-input-state.d.ts +3 -0
- package/lib/typescript/src/components/otp-input/otp-input-state.d.ts.map +1 -0
- package/lib/typescript/src/components/planner/index.d.ts.map +1 -1
- package/lib/typescript/src/components/planner/planner-today.d.ts +2 -0
- package/lib/typescript/src/components/planner/planner-today.d.ts.map +1 -0
- package/lib/typescript/src/components/plot/index.d.ts +1 -1
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
- package/lib/typescript/src/components/plot/plot-series-registry.d.ts +8 -0
- package/lib/typescript/src/components/plot/plot-series-registry.d.ts.map +1 -0
- package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/radio-group/radio-group-value.d.ts +2 -0
- package/lib/typescript/src/components/radio-group/radio-group-value.d.ts.map +1 -0
- package/lib/typescript/src/components/rating/index.d.ts.map +1 -1
- package/lib/typescript/src/components/rating/rating-inputs.d.ts +4 -0
- package/lib/typescript/src/components/rating/rating-inputs.d.ts.map +1 -0
- package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/search-bar/search-bar-debounce.d.ts +7 -0
- package/lib/typescript/src/components/search-bar/search-bar-debounce.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/select/native-select-contract.d.ts +11 -0
- package/lib/typescript/src/components/select/native-select-contract.d.ts.map +1 -0
- package/lib/typescript/src/components/split-view/index.d.ts +159 -0
- package/lib/typescript/src/components/split-view/index.d.ts.map +1 -0
- package/lib/typescript/src/components/split-view/split-view-math.d.ts +54 -0
- package/lib/typescript/src/components/split-view/split-view-math.d.ts.map +1 -0
- package/lib/typescript/src/components/splitter/index.d.ts +78 -0
- package/lib/typescript/src/components/splitter/index.d.ts.map +1 -0
- package/lib/typescript/src/components/splitter/splitter-math.d.ts +67 -0
- package/lib/typescript/src/components/splitter/splitter-math.d.ts.map +1 -0
- package/lib/typescript/src/components/steps/index.d.ts +2 -2
- package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
- package/lib/typescript/src/components/steps/steps-trigger.d.ts +2 -0
- package/lib/typescript/src/components/steps/steps-trigger.d.ts.map +1 -0
- package/lib/typescript/src/components/tag-input/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tag-input/tag-input-state.d.ts +12 -0
- package/lib/typescript/src/components/tag-input/tag-input-state.d.ts.map +1 -0
- package/lib/typescript/src/components/textarea/index.d.ts.map +1 -1
- package/lib/typescript/src/components/textarea/textarea-disabled.d.ts +2 -0
- package/lib/typescript/src/components/textarea/textarea-disabled.d.ts.map +1 -0
- package/lib/typescript/src/components/timeline/index.d.ts +1 -0
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/timeline-geometry.d.ts +4 -0
- package/lib/typescript/src/components/timeline/timeline-geometry.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/toast-store.d.ts +7 -2
- package/lib/typescript/src/components/toast/toast-store.d.ts.map +1 -1
- package/lib/typescript/src/components/tour/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tour/tour-card-measurement.d.ts +14 -0
- package/lib/typescript/src/components/tour/tour-card-measurement.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/avatar/avatar-group.ts +78 -0
- package/src/components/avatar/index.tsx +121 -1
- package/src/components/color-picker/color-picker-accessibility.ts +12 -0
- package/src/components/color-picker/index.tsx +27 -8
- package/src/components/date-time-picker/index.tsx +11 -3
- package/src/components/drawer/index.tsx +47 -15
- package/src/components/input/index.tsx +6 -2
- package/src/components/input/input-content-padding.ts +3 -0
- package/src/components/input-group/index.tsx +38 -10
- package/src/components/input-group/input-group-measurements.ts +98 -0
- package/src/components/otp-input/index.tsx +3 -1
- package/src/components/otp-input/otp-input-state.ts +7 -0
- package/src/components/planner/index.tsx +24 -6
- package/src/components/planner/planner-today.ts +5 -0
- package/src/components/plot/index.tsx +16 -12
- package/src/components/plot/plot-series-registry.ts +41 -0
- package/src/components/radio-group/index.tsx +4 -1
- package/src/components/radio-group/radio-group-value.ts +9 -0
- package/src/components/rating/index.tsx +11 -4
- package/src/components/rating/rating-inputs.ts +19 -0
- package/src/components/search-bar/index.tsx +12 -7
- package/src/components/search-bar/search-bar-debounce.ts +35 -0
- package/src/components/select/index.tsx +10 -1
- package/src/components/select/native-select-contract.ts +12 -0
- package/src/components/split-view/index.tsx +565 -0
- package/src/components/split-view/split-view-math.ts +146 -0
- package/src/components/splitter/index.tsx +589 -0
- package/src/components/splitter/splitter-math.ts +244 -0
- package/src/components/steps/index.tsx +35 -9
- package/src/components/steps/steps-trigger.ts +6 -0
- package/src/components/tag-input/index.tsx +13 -11
- package/src/components/tag-input/tag-input-state.ts +18 -0
- package/src/components/textarea/index.tsx +8 -4
- package/src/components/textarea/textarea-disabled.ts +6 -0
- package/src/components/timeline/index.tsx +89 -26
- package/src/components/timeline/timeline-geometry.ts +9 -0
- package/src/components/toast/index.tsx +26 -1
- package/src/components/toast/toast-store.ts +43 -8
- package/src/components/tour/index.tsx +11 -3
- package/src/components/tour/tour-card-measurement.ts +29 -0
- package/src/index.ts +17 -0
|
@@ -38,6 +38,7 @@ import Animated, {
|
|
|
38
38
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
39
39
|
import { useCSSVariable } from 'uniwind';
|
|
40
40
|
import { Text } from '../../primitives/text';
|
|
41
|
+
import { resolveOtpEditable } from './otp-input-state';
|
|
41
42
|
|
|
42
43
|
/** Long enough to read as a transition, short enough not to lag a fast type. */
|
|
43
44
|
const FOCUS_DURATION = 150;
|
|
@@ -245,6 +246,7 @@ export const OtpInput = forwardRef<TextInput, OtpInputProps>(
|
|
|
245
246
|
groupEvery = 0,
|
|
246
247
|
size,
|
|
247
248
|
disabled,
|
|
249
|
+
editable,
|
|
248
250
|
isInvalid,
|
|
249
251
|
errorMessage,
|
|
250
252
|
accessibilityLabel,
|
|
@@ -351,7 +353,7 @@ export const OtpInput = forwardRef<TextInput, OtpInputProps>(
|
|
|
351
353
|
ref={setRef}
|
|
352
354
|
value={value}
|
|
353
355
|
onChangeText={handleChange}
|
|
354
|
-
editable={
|
|
356
|
+
editable={resolveOtpEditable(editable, disabled)}
|
|
355
357
|
keyboardType={type === 'numeric' ? 'number-pad' : 'default'}
|
|
356
358
|
// The OS one-time-code affordances: iOS surfaces the SMS code above
|
|
357
359
|
// the keyboard, Android offers to autofill it.
|
|
@@ -115,6 +115,7 @@ import {
|
|
|
115
115
|
usePlannerMonthLifecycle,
|
|
116
116
|
usePlannerSelectionLifecycle,
|
|
117
117
|
} from './planner-lifecycle';
|
|
118
|
+
import { millisecondsUntilNextLocalDay } from './planner-today';
|
|
118
119
|
|
|
119
120
|
/** How many of a day's entries a cell draws before it stops and counts. */
|
|
120
121
|
const DEFAULT_ENTRY_LIMIT = 2;
|
|
@@ -249,21 +250,38 @@ function usePlanner(component: string): PlannerContextValue {
|
|
|
249
250
|
return context;
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
/** Today, recomputed
|
|
253
|
+
/** Today, recomputed at local midnight and whenever the app comes back. */
|
|
253
254
|
function useToday(): Date {
|
|
254
255
|
const [today, setToday] = useState(() => startOfDay(new Date()));
|
|
255
256
|
|
|
256
257
|
useEffect(() => {
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
259
|
+
|
|
260
|
+
const clearTimer = () => {
|
|
261
|
+
if (timer === null) return;
|
|
262
|
+
clearTimeout(timer);
|
|
263
|
+
timer = null;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const refreshAndSchedule = () => {
|
|
267
|
+
const current = new Date();
|
|
268
|
+
const now = startOfDay(current);
|
|
259
269
|
// A new object every resume would invalidate every memo below it for a
|
|
260
270
|
// date that has not changed.
|
|
261
|
-
setToday((
|
|
271
|
+
setToday((previous) => (previous.getTime() === now.getTime() ? previous : now));
|
|
272
|
+
clearTimer();
|
|
273
|
+
timer = setTimeout(refreshAndSchedule, millisecondsUntilNextLocalDay(current));
|
|
262
274
|
};
|
|
275
|
+
|
|
276
|
+
refreshAndSchedule();
|
|
263
277
|
const subscription = AppState.addEventListener('change', (state) => {
|
|
264
|
-
if (state === 'active')
|
|
278
|
+
if (state === 'active') refreshAndSchedule();
|
|
279
|
+
else clearTimer();
|
|
265
280
|
});
|
|
266
|
-
return () =>
|
|
281
|
+
return () => {
|
|
282
|
+
clearTimer();
|
|
283
|
+
subscription.remove();
|
|
284
|
+
};
|
|
267
285
|
}, []);
|
|
268
286
|
|
|
269
287
|
return today;
|
|
@@ -112,6 +112,12 @@ import {
|
|
|
112
112
|
type Plot as PlotBox,
|
|
113
113
|
} from '../../utils/chart';
|
|
114
114
|
import { cn } from '../../utils/cn';
|
|
115
|
+
import {
|
|
116
|
+
registerPlotSeries,
|
|
117
|
+
unregisterPlotSeries,
|
|
118
|
+
visiblePlotSeries,
|
|
119
|
+
type PlotSeriesRegistration,
|
|
120
|
+
} from './plot-series-registry';
|
|
115
121
|
|
|
116
122
|
/*
|
|
117
123
|
* The scale functions, re-exported so a mark written outside this file can sit
|
|
@@ -214,7 +220,7 @@ export interface PlotGeometry {
|
|
|
214
220
|
/** Every mark that registered itself, as `[dataKey, colour]`. */
|
|
215
221
|
series: [string, string][];
|
|
216
222
|
registerSeries: (key: string, color: string) => void;
|
|
217
|
-
unregisterSeries: (key: string) => void;
|
|
223
|
+
unregisterSeries: (key: string, color?: string) => void;
|
|
218
224
|
/** Row under the cursor, or `-1`. On the UI thread. */
|
|
219
225
|
activeIndex: SharedValue<number>;
|
|
220
226
|
/** The same index on the JS thread, for anything that has to re-render. */
|
|
@@ -340,7 +346,7 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
340
346
|
ref
|
|
341
347
|
) {
|
|
342
348
|
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
343
|
-
const [
|
|
349
|
+
const [registrations, setRegistrations] = useState<PlotSeriesRegistration[]>([]);
|
|
344
350
|
const [activeIndexJS, setActiveIndexJS] = useState(-1);
|
|
345
351
|
const clipId = `panelui-plot-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
|
|
346
352
|
|
|
@@ -352,19 +358,18 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
352
358
|
|
|
353
359
|
const registerSeries = useMemo(
|
|
354
360
|
() => (key: string, color: string) =>
|
|
355
|
-
|
|
356
|
-
const existing = current.find(([k]) => k === key);
|
|
357
|
-
if (existing?.[1] === color) return current;
|
|
358
|
-
return [...current.filter(([k]) => k !== key), [key, color]];
|
|
359
|
-
}),
|
|
361
|
+
setRegistrations((current) => registerPlotSeries(current, key, color)),
|
|
360
362
|
[]
|
|
361
363
|
);
|
|
362
364
|
|
|
363
365
|
const unregisterSeries = useMemo(
|
|
364
|
-
() => (key: string
|
|
366
|
+
() => (key: string, color?: string) =>
|
|
367
|
+
setRegistrations((current) => unregisterPlotSeries(current, key, color)),
|
|
365
368
|
[]
|
|
366
369
|
);
|
|
367
370
|
|
|
371
|
+
const series = useMemo(() => visiblePlotSeries(registrations), [registrations]);
|
|
372
|
+
|
|
368
373
|
/*
|
|
369
374
|
* What the children need decided before anything is laid out.
|
|
370
375
|
*
|
|
@@ -404,7 +409,7 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
404
409
|
* magnitude apart look alike, which is the chart lying rather than the chart
|
|
405
410
|
* being convenient.
|
|
406
411
|
*/
|
|
407
|
-
const seriesKeys = series.map(([key]) => key)
|
|
412
|
+
const seriesKeys = useMemo(() => series.map(([key]) => key), [series]);
|
|
408
413
|
const extent = useMemo<[number, number]>(() => {
|
|
409
414
|
const lowPin = yDomain?.[0];
|
|
410
415
|
const highPin = yDomain?.[1];
|
|
@@ -412,11 +417,10 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
412
417
|
return [lowPin, highPin];
|
|
413
418
|
}
|
|
414
419
|
|
|
415
|
-
const keys = seriesKeys ? seriesKeys.split('|') : [];
|
|
416
420
|
let min = Infinity;
|
|
417
421
|
let max = -Infinity;
|
|
418
422
|
for (const row of data) {
|
|
419
|
-
for (const key of
|
|
423
|
+
for (const key of seriesKeys) {
|
|
420
424
|
const value = row[key];
|
|
421
425
|
if (typeof value !== 'number' || Number.isNaN(value)) continue;
|
|
422
426
|
if (value < min) min = value;
|
|
@@ -692,7 +696,7 @@ function useMark(dataKey: string, color: string | undefined, colorIndex: number)
|
|
|
692
696
|
|
|
693
697
|
useEffect(() => {
|
|
694
698
|
registerSeries(dataKey, resolved);
|
|
695
|
-
return () => unregisterSeries(dataKey);
|
|
699
|
+
return () => unregisterSeries(dataKey, resolved);
|
|
696
700
|
}, [dataKey, resolved, registerSeries, unregisterSeries]);
|
|
697
701
|
|
|
698
702
|
return resolved;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface PlotSeriesRegistration {
|
|
2
|
+
key: string;
|
|
3
|
+
colors: readonly string[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function registerPlotSeries(
|
|
7
|
+
current: readonly PlotSeriesRegistration[],
|
|
8
|
+
key: string,
|
|
9
|
+
color: string
|
|
10
|
+
): PlotSeriesRegistration[] {
|
|
11
|
+
const existing = current.find((entry) => entry.key === key);
|
|
12
|
+
if (!existing) return [...current, { key, colors: [color] }];
|
|
13
|
+
|
|
14
|
+
return current.map((entry) =>
|
|
15
|
+
entry === existing ? { ...entry, colors: [...entry.colors, color] } : entry
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function unregisterPlotSeries(
|
|
20
|
+
current: readonly PlotSeriesRegistration[],
|
|
21
|
+
key: string,
|
|
22
|
+
color?: string
|
|
23
|
+
): PlotSeriesRegistration[] {
|
|
24
|
+
const existing = current.find((entry) => entry.key === key);
|
|
25
|
+
if (!existing) return current as PlotSeriesRegistration[];
|
|
26
|
+
|
|
27
|
+
const index =
|
|
28
|
+
color === undefined ? existing.colors.length - 1 : existing.colors.lastIndexOf(color);
|
|
29
|
+
if (index < 0) return current as PlotSeriesRegistration[];
|
|
30
|
+
|
|
31
|
+
const colors = existing.colors.filter((_, candidate) => candidate !== index);
|
|
32
|
+
if (colors.length === 0) return current.filter((entry) => entry !== existing);
|
|
33
|
+
|
|
34
|
+
return current.map((entry) => (entry === existing ? { ...entry, colors } : entry));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function visiblePlotSeries(
|
|
38
|
+
registrations: readonly PlotSeriesRegistration[]
|
|
39
|
+
): [string, string][] {
|
|
40
|
+
return registrations.map(({ key, colors }) => [key, colors[colors.length - 1]!]);
|
|
41
|
+
}
|
|
@@ -36,6 +36,7 @@ import Animated, {
|
|
|
36
36
|
import { tv, type VariantProps } from 'tailwind-variants';
|
|
37
37
|
import { cn } from '../../utils/cn';
|
|
38
38
|
import { Text, textChildren } from '../../primitives/text';
|
|
39
|
+
import { requestRadioValueChange } from './radio-group-value';
|
|
39
40
|
|
|
40
41
|
type RadioVariant = 'dot' | 'card';
|
|
41
42
|
type RadioOrientation = 'vertical' | 'horizontal';
|
|
@@ -247,7 +248,9 @@ const RadioGroupItem = forwardRef<View, RadioGroupItemProps>(
|
|
|
247
248
|
accessibilityState={{ selected, disabled: !!disabled }}
|
|
248
249
|
accessibilityLabel={label}
|
|
249
250
|
disabled={disabled}
|
|
250
|
-
onPress={() =>
|
|
251
|
+
onPress={() =>
|
|
252
|
+
requestRadioValueChange(context.value, value, context.onValueChange)
|
|
253
|
+
}
|
|
251
254
|
className={slots.row({ className })}
|
|
252
255
|
>
|
|
253
256
|
{body}
|
|
@@ -43,6 +43,11 @@ import { StarIcon } from '../../icons';
|
|
|
43
43
|
import { Text } from '../../primitives/text';
|
|
44
44
|
import { cn } from '../../utils/cn';
|
|
45
45
|
import { selectionTick } from '../../utils/haptics';
|
|
46
|
+
import {
|
|
47
|
+
normalizeRatingMax,
|
|
48
|
+
normalizeRatingPrecision,
|
|
49
|
+
normalizeRatingValue,
|
|
50
|
+
} from './rating-inputs';
|
|
46
51
|
|
|
47
52
|
/** Springs the fill onto its resting value after a tap or the end of a drag. */
|
|
48
53
|
const SPRING = { damping: 18, stiffness: 220, mass: 0.6 } as const;
|
|
@@ -197,8 +202,8 @@ export const Rating = forwardRef<View, RatingProps>(
|
|
|
197
202
|
rowClassName,
|
|
198
203
|
value: valueProp,
|
|
199
204
|
defaultValue = 0,
|
|
200
|
-
max = 5,
|
|
201
|
-
precision = 1,
|
|
205
|
+
max: maxProp = 5,
|
|
206
|
+
precision: precisionProp = 1,
|
|
202
207
|
onValueChange,
|
|
203
208
|
onValueCommit,
|
|
204
209
|
readOnly = false,
|
|
@@ -213,9 +218,11 @@ export const Rating = forwardRef<View, RatingProps>(
|
|
|
213
218
|
},
|
|
214
219
|
ref
|
|
215
220
|
) => {
|
|
221
|
+
const max = normalizeRatingMax(maxProp);
|
|
222
|
+
const precision = normalizeRatingPrecision(precisionProp);
|
|
216
223
|
const isControlled = valueProp !== undefined;
|
|
217
|
-
const [internal, setInternal] = useState(defaultValue);
|
|
218
|
-
const value =
|
|
224
|
+
const [internal, setInternal] = useState(() => normalizeRatingValue(defaultValue, max));
|
|
225
|
+
const value = normalizeRatingValue(isControlled ? valueProp : internal, max);
|
|
219
226
|
|
|
220
227
|
const interactive = !readOnly && !disabled;
|
|
221
228
|
const slots = ratingVariants({ size, disabled });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const DEFAULT_MAX = 5;
|
|
2
|
+
const DEFAULT_PRECISION = 1;
|
|
3
|
+
|
|
4
|
+
export function normalizeRatingMax(max: number | undefined): number {
|
|
5
|
+
return typeof max === 'number' && Number.isFinite(max) && max >= 1
|
|
6
|
+
? Math.floor(max)
|
|
7
|
+
: DEFAULT_MAX;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function normalizeRatingPrecision(precision: number | undefined): number {
|
|
11
|
+
return typeof precision === 'number' && Number.isFinite(precision) && precision > 0
|
|
12
|
+
? precision
|
|
13
|
+
: DEFAULT_PRECISION;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function normalizeRatingValue(value: number | undefined, max: number): number {
|
|
17
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return 0;
|
|
18
|
+
return Math.min(Math.max(value, 0), max);
|
|
19
|
+
}
|
|
@@ -70,6 +70,12 @@ import { AnimatedPressable } from '../../primitives/animated-pressable';
|
|
|
70
70
|
import { Text } from '../../primitives/text';
|
|
71
71
|
import { Input, type InputProps } from '../input';
|
|
72
72
|
import { Spinner } from '../spinner';
|
|
73
|
+
import {
|
|
74
|
+
cancelSearchBarDebounce,
|
|
75
|
+
flushSearchBarDebounce,
|
|
76
|
+
scheduleSearchBarDebounce,
|
|
77
|
+
type SearchBarDebounceTimer,
|
|
78
|
+
} from './search-bar-debounce';
|
|
73
79
|
|
|
74
80
|
/** Matches the field's own focus crossfade, so the row settles as one thing. */
|
|
75
81
|
const CANCEL_DURATION = 180;
|
|
@@ -251,6 +257,7 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(
|
|
|
251
257
|
* render and push the pause out forever.
|
|
252
258
|
*/
|
|
253
259
|
const debouncedRef = useRef(onDebouncedChange);
|
|
260
|
+
const debounceTimerRef = useRef<SearchBarDebounceTimer['current']>(null);
|
|
254
261
|
useEffect(() => {
|
|
255
262
|
debouncedRef.current = onDebouncedChange;
|
|
256
263
|
});
|
|
@@ -263,12 +270,10 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(
|
|
|
263
270
|
settled.current = true;
|
|
264
271
|
return;
|
|
265
272
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
const timer = setTimeout(() => debouncedRef.current?.(text), debounce);
|
|
271
|
-
return () => clearTimeout(timer);
|
|
273
|
+
scheduleSearchBarDebounce(debounceTimerRef, debouncedRef.current, text, debounce);
|
|
274
|
+
return () => {
|
|
275
|
+
cancelSearchBarDebounce(debounceTimerRef);
|
|
276
|
+
};
|
|
272
277
|
}, [text, debounce]);
|
|
273
278
|
|
|
274
279
|
const handleFocus = useCallback<NonNullable<InputProps['onFocus']>>(
|
|
@@ -291,7 +296,7 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(
|
|
|
291
296
|
(event: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => {
|
|
292
297
|
// A return key is somebody saying they are done waiting, so the
|
|
293
298
|
// pending pause is spent rather than waited out.
|
|
294
|
-
debouncedRef.current
|
|
299
|
+
flushSearchBarDebounce(debounceTimerRef, debouncedRef.current, text);
|
|
295
300
|
onSubmit?.(text);
|
|
296
301
|
onSubmitEditing?.(event);
|
|
297
302
|
},
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface SearchBarDebounceTimer {
|
|
2
|
+
current: ReturnType<typeof setTimeout> | null;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function cancelSearchBarDebounce(timer: SearchBarDebounceTimer): void {
|
|
6
|
+
if (timer.current === null) return;
|
|
7
|
+
clearTimeout(timer.current);
|
|
8
|
+
timer.current = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function scheduleSearchBarDebounce(
|
|
12
|
+
timer: SearchBarDebounceTimer,
|
|
13
|
+
callback: ((value: string) => void) | undefined,
|
|
14
|
+
value: string,
|
|
15
|
+
delay: number
|
|
16
|
+
): void {
|
|
17
|
+
cancelSearchBarDebounce(timer);
|
|
18
|
+
if (!Number.isFinite(delay) || delay <= 0) {
|
|
19
|
+
callback?.(value);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
timer.current = setTimeout(() => {
|
|
23
|
+
timer.current = null;
|
|
24
|
+
callback?.(value);
|
|
25
|
+
}, delay);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function flushSearchBarDebounce(
|
|
29
|
+
timer: SearchBarDebounceTimer,
|
|
30
|
+
callback: ((value: string) => void) | undefined,
|
|
31
|
+
value: string
|
|
32
|
+
): void {
|
|
33
|
+
cancelSearchBarDebounce(timer);
|
|
34
|
+
callback?.(value);
|
|
35
|
+
}
|
|
@@ -70,6 +70,7 @@ import { useBackHandler } from '../../hooks/use-back-handler';
|
|
|
70
70
|
import { cn } from '../../utils/cn';
|
|
71
71
|
import { BottomSheet } from '../bottom-sheet';
|
|
72
72
|
import { InputGroup } from '../input-group';
|
|
73
|
+
import { nativeSelectSupportsOptions } from './native-select-contract';
|
|
73
74
|
|
|
74
75
|
const selectVariants = tv({
|
|
75
76
|
slots: {
|
|
@@ -339,7 +340,6 @@ function SelectRoot({
|
|
|
339
340
|
const triggerRef = useRef<View>(null);
|
|
340
341
|
const chevron = useSharedValue(0);
|
|
341
342
|
const { height: screenHeight } = useWindowDimensions();
|
|
342
|
-
const nativeUI = native ? getNativeUI() : null;
|
|
343
343
|
|
|
344
344
|
const options = useMemo(() => {
|
|
345
345
|
const collected: SelectItemProps[] = [];
|
|
@@ -347,6 +347,15 @@ function SelectRoot({
|
|
|
347
347
|
return collected;
|
|
348
348
|
}, [children]);
|
|
349
349
|
|
|
350
|
+
/*
|
|
351
|
+
* @expo/ui's portable Picker only has a control-wide `enabled` flag. Its
|
|
352
|
+
* items accept label and value, but no disabled state, so handing it a
|
|
353
|
+
* disabled Select.Item would make that option selectable again. Preserve
|
|
354
|
+
* the public item contract by using the styled Select for that list.
|
|
355
|
+
*/
|
|
356
|
+
const nativeUI =
|
|
357
|
+
native && nativeSelectSupportsOptions(options) ? getNativeUI() : null;
|
|
358
|
+
|
|
350
359
|
const selectedLabel = useMemo(
|
|
351
360
|
() => options.find((option) => option.value === value)?.label,
|
|
352
361
|
[options, value]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface NativeSelectOption {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The portable @expo/ui picker can disable the whole control, but not one
|
|
7
|
+
* option. Keep the styled Select when an item needs that distinction rather
|
|
8
|
+
* than silently turning a disabled choice back into a selectable one.
|
|
9
|
+
*/
|
|
10
|
+
export function nativeSelectSupportsOptions(options: NativeSelectOption[]): boolean {
|
|
11
|
+
return options.every((option) => !option.disabled);
|
|
12
|
+
}
|