panelui-native 0.75.1 → 0.77.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/lib/module/components/ai-input/ai-input-growth.js +62 -0
- package/lib/module/components/ai-input/ai-input-growth.js.map +1 -0
- package/lib/module/components/ai-input/index.js +1584 -0
- package/lib/module/components/ai-input/index.js.map +1 -0
- package/lib/module/components/alert/index.js +1 -1
- package/lib/module/components/alert/index.js.map +1 -1
- package/lib/module/components/avatar/avatar-source.js +15 -0
- package/lib/module/components/avatar/avatar-source.js.map +1 -0
- package/lib/module/components/avatar/index.js +32 -31
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/bottom-sheet/index.js +4 -2
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/button/index.js +7 -1
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/button-group/index.js +2 -3
- package/lib/module/components/button-group/index.js.map +1 -1
- package/lib/module/components/carousel/index.js +10 -7
- package/lib/module/components/carousel/index.js.map +1 -1
- package/lib/module/components/collapsible/index.js +1 -1
- package/lib/module/components/collapsible/index.js.map +1 -1
- package/lib/module/components/field/index.js +7 -8
- package/lib/module/components/field/index.js.map +1 -1
- package/lib/module/components/label/index.js +1 -1
- package/lib/module/components/label/index.js.map +1 -1
- package/lib/module/components/marquee/index.js +36 -11
- package/lib/module/components/marquee/index.js.map +1 -1
- package/lib/module/components/marquee/marquee-math.js +22 -6
- package/lib/module/components/marquee/marquee-math.js.map +1 -1
- package/lib/module/components/plan/index.js +2 -1
- package/lib/module/components/plan/index.js.map +1 -1
- package/lib/module/components/plot/index.js +27 -9
- package/lib/module/components/plot/index.js.map +1 -1
- package/lib/module/components/search-bar/index.js +11 -3
- package/lib/module/components/search-bar/index.js.map +1 -1
- package/lib/module/components/select/index.js +1 -0
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/slider/index.js +1 -0
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/split-view/index.js +26 -27
- package/lib/module/components/split-view/index.js.map +1 -1
- package/lib/module/components/split-view/split-view-lifecycle.js +32 -0
- package/lib/module/components/split-view/split-view-lifecycle.js.map +1 -0
- package/lib/module/components/splitter/index.js +7 -13
- package/lib/module/components/splitter/index.js.map +1 -1
- package/lib/module/components/splitter/splitter-math.js +14 -0
- package/lib/module/components/splitter/splitter-math.js.map +1 -1
- package/lib/module/components/switch/index.js +1 -0
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/tabs/index.js +1 -1
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +156 -42
- 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 -1
- package/lib/module/components/toast/index.js +1 -1
- package/lib/module/components/toast/index.js.map +1 -1
- package/lib/module/components/tooltip/index.js +2 -2
- package/lib/module/components/tooltip/index.js.map +1 -1
- package/lib/module/components/tree/index.js +2 -2
- package/lib/module/components/tree/index.js.map +1 -1
- package/lib/module/components/typography/index.js +2 -1
- package/lib/module/components/typography/index.js.map +1 -1
- package/lib/module/components/waterfall-chart/index.js +22 -3
- package/lib/module/components/waterfall-chart/index.js.map +1 -1
- package/lib/module/hooks/use-disclosure.js +10 -8
- package/lib/module/hooks/use-disclosure.js.map +1 -1
- package/lib/module/icons/index.js +135 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/primitives/controllable-state.js +61 -0
- package/lib/module/primitives/controllable-state.js.map +1 -0
- package/lib/module/primitives/finite-chart.js +15 -0
- package/lib/module/primitives/finite-chart.js.map +1 -0
- package/lib/module/primitives/glass.js +136 -0
- package/lib/module/primitives/glass.js.map +1 -0
- package/lib/module/primitives/scrim.js +8 -1
- package/lib/module/primitives/scrim.js.map +1 -1
- package/lib/typescript/src/components/ai-input/ai-input-growth.d.ts +59 -0
- package/lib/typescript/src/components/ai-input/ai-input-growth.d.ts.map +1 -0
- package/lib/typescript/src/components/ai-input/index.d.ts +442 -0
- package/lib/typescript/src/components/ai-input/index.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/avatar-source.d.ts +4 -0
- package/lib/typescript/src/components/avatar/avatar-source.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +10 -1
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts +15 -0
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/index.d.ts +15 -6
- package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/marquee-math.d.ts +6 -0
- package/lib/typescript/src/components/marquee/marquee-math.d.ts.map +1 -1
- package/lib/typescript/src/components/plan/index.d.ts.map +1 -1
- package/lib/typescript/src/components/plot/index.d.ts +2 -1
- package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
- package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/split-view/index.d.ts.map +1 -1
- package/lib/typescript/src/components/split-view/split-view-lifecycle.d.ts +14 -0
- package/lib/typescript/src/components/split-view/split-view-lifecycle.d.ts.map +1 -0
- package/lib/typescript/src/components/splitter/index.d.ts +2 -2
- package/lib/typescript/src/components/splitter/index.d.ts.map +1 -1
- package/lib/typescript/src/components/splitter/splitter-math.d.ts +2 -0
- package/lib/typescript/src/components/splitter/splitter-math.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/index.d.ts +24 -2
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/timeline-geometry.d.ts +2 -0
- package/lib/typescript/src/components/timeline/timeline-geometry.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
- package/lib/typescript/src/components/waterfall-chart/index.d.ts +2 -1
- package/lib/typescript/src/components/waterfall-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-disclosure.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +18 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +17 -0
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/controllable-state.d.ts +33 -0
- package/lib/typescript/src/primitives/controllable-state.d.ts.map +1 -0
- package/lib/typescript/src/primitives/finite-chart.d.ts +5 -0
- package/lib/typescript/src/primitives/finite-chart.d.ts.map +1 -0
- package/lib/typescript/src/primitives/glass.d.ts +43 -0
- package/lib/typescript/src/primitives/glass.d.ts.map +1 -0
- package/lib/typescript/src/primitives/scrim.d.ts +7 -0
- package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
- package/package.json +7 -2
- package/src/components/ai-input/ai-input-growth.ts +65 -0
- package/src/components/ai-input/index.tsx +1971 -0
- package/src/components/alert/index.tsx +1 -1
- package/src/components/avatar/avatar-source.ts +18 -0
- package/src/components/avatar/index.tsx +35 -27
- package/src/components/bottom-sheet/index.tsx +16 -4
- package/src/components/button/index.tsx +20 -1
- package/src/components/button-group/index.tsx +1 -1
- package/src/components/carousel/index.tsx +10 -11
- package/src/components/collapsible/index.tsx +1 -1
- package/src/components/field/index.tsx +4 -4
- package/src/components/label/index.tsx +1 -1
- package/src/components/marquee/index.tsx +56 -12
- package/src/components/marquee/marquee-math.ts +26 -3
- package/src/components/plan/index.tsx +6 -2
- package/src/components/plot/index.tsx +37 -11
- package/src/components/search-bar/index.tsx +9 -3
- package/src/components/select/index.tsx +1 -1
- package/src/components/slider/index.tsx +1 -1
- package/src/components/split-view/index.tsx +24 -33
- package/src/components/split-view/split-view-lifecycle.ts +43 -0
- package/src/components/splitter/index.tsx +7 -11
- package/src/components/splitter/splitter-math.ts +28 -0
- package/src/components/switch/index.tsx +1 -1
- package/src/components/tabs/index.tsx +1 -1
- package/src/components/timeline/index.tsx +189 -34
- package/src/components/timeline/timeline-geometry.ts +11 -0
- package/src/components/toast/index.tsx +6 -1
- package/src/components/tooltip/index.tsx +1 -1
- package/src/components/tree/index.tsx +2 -2
- package/src/components/typography/index.tsx +2 -2
- package/src/components/waterfall-chart/index.tsx +37 -4
- package/src/hooks/use-disclosure.ts +7 -12
- package/src/icons/index.tsx +106 -0
- package/src/index.ts +27 -0
- package/src/native/index.ts +17 -0
- package/src/primitives/controllable-state.ts +112 -0
- package/src/primitives/finite-chart.ts +14 -0
- package/src/primitives/glass.tsx +171 -0
- package/src/primitives/scrim.tsx +7 -1
|
@@ -97,7 +97,9 @@ import Svg, {
|
|
|
97
97
|
Rect,
|
|
98
98
|
} from 'react-native-svg';
|
|
99
99
|
import { useCSSVariable } from 'uniwind';
|
|
100
|
+
import { ChartAccessibilityData, type ChartAccessibilityProps } from '../../primitives/chart-accessibility';
|
|
100
101
|
import { Text } from '../../primitives/text';
|
|
102
|
+
import { finiteChartNumber } from '../../primitives/finite-chart';
|
|
101
103
|
import {
|
|
102
104
|
areaPath,
|
|
103
105
|
bandOf,
|
|
@@ -260,7 +262,7 @@ export function usePlotCursor(): { activeIndex: number; activePoint: PlotDatum |
|
|
|
260
262
|
};
|
|
261
263
|
}
|
|
262
264
|
|
|
263
|
-
export interface PlotProps extends ViewProps {
|
|
265
|
+
export interface PlotProps extends ViewProps, ChartAccessibilityProps<PlotDatum> {
|
|
264
266
|
className?: string;
|
|
265
267
|
/** The rows. Each one is a position along the x-axis. */
|
|
266
268
|
data: PlotDatum[];
|
|
@@ -339,6 +341,11 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
339
341
|
xScale: xScaleProp,
|
|
340
342
|
curve = 'monotone',
|
|
341
343
|
onActiveIndexChange,
|
|
344
|
+
accessible,
|
|
345
|
+
accessibilityLabel,
|
|
346
|
+
accessibilityHint,
|
|
347
|
+
accessibilityLabelForDatum,
|
|
348
|
+
onAccessibilityDatumPress,
|
|
342
349
|
compact = false,
|
|
343
350
|
children,
|
|
344
351
|
...props
|
|
@@ -411,9 +418,9 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
411
418
|
*/
|
|
412
419
|
const seriesKeys = useMemo(() => series.map(([key]) => key), [series]);
|
|
413
420
|
const extent = useMemo<[number, number]>(() => {
|
|
414
|
-
const lowPin = yDomain?.[0];
|
|
415
|
-
const highPin = yDomain?.[1];
|
|
416
|
-
if (
|
|
421
|
+
const lowPin = finiteChartNumber(yDomain?.[0]);
|
|
422
|
+
const highPin = finiteChartNumber(yDomain?.[1]);
|
|
423
|
+
if (lowPin !== undefined && highPin !== undefined) {
|
|
417
424
|
return [lowPin, highPin];
|
|
418
425
|
}
|
|
419
426
|
|
|
@@ -421,8 +428,8 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
421
428
|
let max = -Infinity;
|
|
422
429
|
for (const row of data) {
|
|
423
430
|
for (const key of seriesKeys) {
|
|
424
|
-
const value = row[key];
|
|
425
|
-
if (
|
|
431
|
+
const value = finiteChartNumber(row[key]);
|
|
432
|
+
if (value === undefined) continue;
|
|
426
433
|
if (value < min) min = value;
|
|
427
434
|
if (value > max) max = value;
|
|
428
435
|
}
|
|
@@ -432,8 +439,8 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
432
439
|
// Nothing to measure. Fall back to whatever was pinned, and to a unit
|
|
433
440
|
// domain when nothing was — dividing by a span of zero is how a mark
|
|
434
441
|
// ends up drawn at infinity.
|
|
435
|
-
const low =
|
|
436
|
-
const high =
|
|
442
|
+
const low = lowPin ?? 0;
|
|
443
|
+
const high = highPin ?? low + 1;
|
|
437
444
|
return [low, high];
|
|
438
445
|
}
|
|
439
446
|
|
|
@@ -477,8 +484,8 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
477
484
|
const derivedHigh = nice ? niceHigh : max + headroom * highRoom;
|
|
478
485
|
|
|
479
486
|
return [
|
|
480
|
-
|
|
481
|
-
|
|
487
|
+
lowPin ?? derivedLow,
|
|
488
|
+
highPin ?? derivedHigh,
|
|
482
489
|
];
|
|
483
490
|
}, [data, yDomain, seriesKeys, hasBars, nice]);
|
|
484
491
|
|
|
@@ -619,7 +626,26 @@ const PlotRoot = forwardRef<PlotHandle, PlotProps>(function PlotRoot(
|
|
|
619
626
|
<PlotContext.Provider value={context}>
|
|
620
627
|
<View {...props} style={props.style} className={cn('w-full', className)}>
|
|
621
628
|
{header}
|
|
622
|
-
<
|
|
629
|
+
<ChartAccessibilityData
|
|
630
|
+
chart="Plot"
|
|
631
|
+
data={data}
|
|
632
|
+
disabled={accessible === false || status === 'loading'}
|
|
633
|
+
accessibilityLabel={accessibilityLabel}
|
|
634
|
+
accessibilityHint={accessibilityHint}
|
|
635
|
+
accessibilityLabelForDatum={accessibilityLabelForDatum}
|
|
636
|
+
onAccessibilityDatumPress={onAccessibilityDatumPress}
|
|
637
|
+
valueOf={(datum) => [
|
|
638
|
+
[xDataKey, datum[xDataKey]],
|
|
639
|
+
...seriesKeys.map((key) => [key, datum[key]] as [string, unknown]),
|
|
640
|
+
]}
|
|
641
|
+
/>
|
|
642
|
+
<View
|
|
643
|
+
onLayout={onLayout}
|
|
644
|
+
style={{ aspectRatio }}
|
|
645
|
+
className="w-full"
|
|
646
|
+
accessibilityElementsHidden
|
|
647
|
+
importantForAccessibility="no-hide-descendants"
|
|
648
|
+
>
|
|
623
649
|
{plot.width > 0 ? (
|
|
624
650
|
<>
|
|
625
651
|
<Svg width="100%" height="100%" style={StyleSheet.absoluteFill}>
|
|
@@ -294,28 +294,31 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(
|
|
|
294
294
|
|
|
295
295
|
const handleSubmit = useCallback(
|
|
296
296
|
(event: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => {
|
|
297
|
+
if (disabled) return;
|
|
297
298
|
// A return key is somebody saying they are done waiting, so the
|
|
298
299
|
// pending pause is spent rather than waited out.
|
|
299
300
|
flushSearchBarDebounce(debounceTimerRef, debouncedRef.current, text);
|
|
300
301
|
onSubmit?.(text);
|
|
301
302
|
onSubmitEditing?.(event);
|
|
302
303
|
},
|
|
303
|
-
[onSubmit, onSubmitEditing, text]
|
|
304
|
+
[disabled, onSubmit, onSubmitEditing, text]
|
|
304
305
|
);
|
|
305
306
|
|
|
306
307
|
const handleClear = useCallback(() => {
|
|
308
|
+
if (disabled) return;
|
|
307
309
|
setText('');
|
|
308
310
|
onClear?.();
|
|
309
311
|
// The keyboard stays: clearing a query is the start of the next one.
|
|
310
312
|
// Android takes focus away with the press, so it is asked back.
|
|
311
313
|
inputRef.current?.focus();
|
|
312
|
-
}, [onClear, setText]);
|
|
314
|
+
}, [disabled, onClear, setText]);
|
|
313
315
|
|
|
314
316
|
const handleCancel = useCallback(() => {
|
|
317
|
+
if (disabled) return;
|
|
315
318
|
setText('');
|
|
316
319
|
inputRef.current?.blur();
|
|
317
320
|
onCancel?.();
|
|
318
|
-
}, [onCancel, setText]);
|
|
321
|
+
}, [disabled, onCancel, setText]);
|
|
319
322
|
|
|
320
323
|
/*
|
|
321
324
|
* Cancel's width is measured once and driven from a shared value, so the
|
|
@@ -426,8 +429,11 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(
|
|
|
426
429
|
<AnimatedPressable
|
|
427
430
|
onLayout={handleCancelLayout}
|
|
428
431
|
onPress={handleCancel}
|
|
432
|
+
disabled={disabled}
|
|
433
|
+
focusable={!disabled}
|
|
429
434
|
hitSlop={8}
|
|
430
435
|
accessibilityRole="button"
|
|
436
|
+
accessibilityState={{ disabled: !!disabled }}
|
|
431
437
|
className={slots.cancelButton()}
|
|
432
438
|
>
|
|
433
439
|
<Text className={slots.cancelLabel()}>{cancelLabel}</Text>
|
|
@@ -440,7 +440,7 @@ function SelectRoot({
|
|
|
440
440
|
// A picker fills the width of the row it sits in and reports its own
|
|
441
441
|
// height — a menu is a compact button, a wheel a full rotor, and the
|
|
442
442
|
// platform is the only thing that knows which by how much.
|
|
443
|
-
<Host matchContents={{ vertical: true }}>
|
|
443
|
+
<Host matchContents={{ vertical: true }} ignoreSafeArea="keyboard">
|
|
444
444
|
<Picker
|
|
445
445
|
selectedValue={value ?? firstEnabled?.value ?? ''}
|
|
446
446
|
onValueChange={(next: string) => onValueChange(next)}
|
|
@@ -567,7 +567,7 @@ export const Slider = forwardRef<View, SliderProps>(
|
|
|
567
567
|
{/* A slider has no intrinsic width — it fills whatever it is given —
|
|
568
568
|
so the width comes from ordinary layout and only the height is
|
|
569
569
|
matched to the platform's content. */}
|
|
570
|
-
<Host matchContents={{ vertical: true }}>
|
|
570
|
+
<Host matchContents={{ vertical: true }} ignoreSafeArea="keyboard">
|
|
571
571
|
<NativeSlider
|
|
572
572
|
value={value}
|
|
573
573
|
onValueChange={(next: number) => {
|
|
@@ -56,10 +56,10 @@ import {
|
|
|
56
56
|
DEFAULT_MIN_HEIGHT,
|
|
57
57
|
clamp,
|
|
58
58
|
nearestSnapIndex,
|
|
59
|
-
normalizeSnapIndex,
|
|
60
59
|
resolveLength,
|
|
61
60
|
resolveSnapPoints,
|
|
62
61
|
} from './split-view-math';
|
|
62
|
+
import { useSplitViewIndexLifecycle } from './split-view-lifecycle';
|
|
63
63
|
|
|
64
64
|
/** Settles the pane onto a snap point without overshooting past its limits. */
|
|
65
65
|
const SPRING = {
|
|
@@ -189,14 +189,16 @@ function SplitViewRoot({
|
|
|
189
189
|
[pointsKey]
|
|
190
190
|
);
|
|
191
191
|
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
const {
|
|
193
|
+
index: snapIndex,
|
|
194
|
+
requestIndex,
|
|
195
|
+
requestToken,
|
|
196
|
+
} = useSplitViewIndexLifecycle({
|
|
197
|
+
snapIndex: snapIndexProp,
|
|
198
|
+
defaultSnapIndex,
|
|
199
|
+
count: points.length,
|
|
200
|
+
onSnapIndexChange,
|
|
201
|
+
});
|
|
200
202
|
|
|
201
203
|
const onSnapRef = useRef(onSnap);
|
|
202
204
|
onSnapRef.current = onSnap;
|
|
@@ -207,30 +209,15 @@ function SplitViewRoot({
|
|
|
207
209
|
}, []);
|
|
208
210
|
|
|
209
211
|
/*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* to spring, to report, and to leave a controlled caller's index alone.
|
|
212
|
+
* Every modality makes the same request. The synchronization effect below
|
|
213
|
+
* moves only to the index the owner accepts, then reports that settlement.
|
|
213
214
|
*/
|
|
214
215
|
const moveTo = useCallback(
|
|
215
|
-
(index: number
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!controlled) setInternalIndex(index);
|
|
220
|
-
onSnapIndexChange?.(index);
|
|
221
|
-
|
|
222
|
-
const finish = () => report(index, target);
|
|
223
|
-
if (options?.animated === false || !animate) {
|
|
224
|
-
topHeight.value = target;
|
|
225
|
-
finish();
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
topHeight.value = withSpring(target, SPRING, (finished) => {
|
|
229
|
-
'worklet';
|
|
230
|
-
if (finished) runOnJS(finish)();
|
|
231
|
-
});
|
|
216
|
+
(index: number) => {
|
|
217
|
+
if (points[index] === undefined) return;
|
|
218
|
+
requestIndex(index);
|
|
232
219
|
},
|
|
233
|
-
[
|
|
220
|
+
[points, requestIndex]
|
|
234
221
|
);
|
|
235
222
|
|
|
236
223
|
const snapTo = useCallback((index: number) => moveTo(index), [moveTo]);
|
|
@@ -260,11 +247,15 @@ function SplitViewRoot({
|
|
|
260
247
|
report(snapIndex, target);
|
|
261
248
|
return;
|
|
262
249
|
}
|
|
263
|
-
|
|
250
|
+
const finish = () => report(snapIndex, target);
|
|
251
|
+
topHeight.value = withSpring(target, SPRING, (finished) => {
|
|
252
|
+
'worklet';
|
|
253
|
+
if (finished) runOnJS(finish)();
|
|
254
|
+
});
|
|
264
255
|
// Only when the resolved layout moves — a re-render that changes nothing
|
|
265
256
|
// must not restart a spring the reader is watching.
|
|
266
257
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
267
|
-
}, [points, snapIndex, animate, animateOnMount]);
|
|
258
|
+
}, [points, snapIndex, animate, animateOnMount, requestToken]);
|
|
268
259
|
|
|
269
260
|
const handleLayout = useCallback(
|
|
270
261
|
(event: LayoutChangeEvent) => {
|
|
@@ -471,6 +462,7 @@ function SplitViewDragArea({
|
|
|
471
462
|
return (
|
|
472
463
|
<GestureDetector gesture={pan}>
|
|
473
464
|
<View
|
|
465
|
+
{...props}
|
|
474
466
|
accessible
|
|
475
467
|
accessibilityRole="adjustable"
|
|
476
468
|
accessibilityLabel={accessibilityLabel}
|
|
@@ -493,7 +485,6 @@ function SplitViewDragArea({
|
|
|
493
485
|
}
|
|
494
486
|
className={dragArea({ className })}
|
|
495
487
|
onLayout={handleLayout}
|
|
496
|
-
{...props}
|
|
497
488
|
>
|
|
498
489
|
{children}
|
|
499
490
|
</View>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
function normalizeIndex(index: number, count: number): number {
|
|
4
|
+
if (!Number.isFinite(index) || count <= 0) return 0;
|
|
5
|
+
return Math.max(0, Math.min(Math.round(index), count - 1));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface SplitViewIndexLifecycleOptions {
|
|
9
|
+
snapIndex: number | undefined;
|
|
10
|
+
defaultSnapIndex: number;
|
|
11
|
+
count: number;
|
|
12
|
+
onSnapIndexChange?: (index: number) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Keeps requested indices intact until the measured snap-point list exists. */
|
|
16
|
+
export function useSplitViewIndexLifecycle({
|
|
17
|
+
snapIndex,
|
|
18
|
+
defaultSnapIndex,
|
|
19
|
+
count,
|
|
20
|
+
onSnapIndexChange,
|
|
21
|
+
}: SplitViewIndexLifecycleOptions) {
|
|
22
|
+
const controlled = snapIndex !== undefined;
|
|
23
|
+
const [internalIndex, setInternalIndex] = useState(defaultSnapIndex);
|
|
24
|
+
// Every request must settle, including one that resolves to the current index
|
|
25
|
+
// after a drag and a controlled request its owner rejects.
|
|
26
|
+
const [requestToken, setRequestToken] = useState(0);
|
|
27
|
+
|
|
28
|
+
const index = normalizeIndex(
|
|
29
|
+
controlled ? snapIndex : internalIndex,
|
|
30
|
+
count || 1
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const requestIndex = useCallback(
|
|
34
|
+
(next: number) => {
|
|
35
|
+
if (!controlled) setInternalIndex(next);
|
|
36
|
+
setRequestToken((token) => token + 1);
|
|
37
|
+
onSnapIndexChange?.(next);
|
|
38
|
+
},
|
|
39
|
+
[controlled, onSnapIndexChange]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return { index, requestIndex, requestToken };
|
|
43
|
+
}
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
isCollapsed,
|
|
59
59
|
layoutOffset,
|
|
60
60
|
normalizeConstraint,
|
|
61
|
+
resetLayout,
|
|
61
62
|
resizeLayout,
|
|
62
63
|
resolveLayout,
|
|
63
64
|
type SplitterConstraint,
|
|
@@ -388,6 +389,7 @@ function SplitterHandle({
|
|
|
388
389
|
disabled,
|
|
389
390
|
withGrip = true,
|
|
390
391
|
accessibilityLabel = 'Resize panels',
|
|
392
|
+
style,
|
|
391
393
|
...props
|
|
392
394
|
}: SplitterHandleProps) {
|
|
393
395
|
const context = useSplitter('Splitter.Handle');
|
|
@@ -468,17 +470,10 @@ function SplitterHandle({
|
|
|
468
470
|
* they already have between them.
|
|
469
471
|
*/
|
|
470
472
|
const reset = useCallback(() => {
|
|
471
|
-
const
|
|
472
|
-
const next = layout.value.slice();
|
|
473
|
-
const room = (next[pair] ?? 0) + (next[pair + 1] ?? 0);
|
|
474
|
-
const first = initial[pair] ?? 0;
|
|
475
|
-
const span = first + (initial[pair + 1] ?? 0);
|
|
476
|
-
if (span <= 0) return;
|
|
477
|
-
next[pair] = (first / span) * room;
|
|
478
|
-
next[pair + 1] = room - next[pair]!;
|
|
473
|
+
const next = resetLayout(layout.value, initial, boundary, constraints);
|
|
479
474
|
layout.value = next;
|
|
480
475
|
commit(next);
|
|
481
|
-
}, [boundary, commit, initial, layout]);
|
|
476
|
+
}, [boundary, commit, constraints, initial, layout]);
|
|
482
477
|
|
|
483
478
|
const doubleTap = useMemo(
|
|
484
479
|
() =>
|
|
@@ -548,6 +543,7 @@ function SplitterHandle({
|
|
|
548
543
|
return (
|
|
549
544
|
<GestureDetector gesture={gesture}>
|
|
550
545
|
<Animated.View
|
|
546
|
+
{...props}
|
|
551
547
|
accessible
|
|
552
548
|
accessibilityRole="adjustable"
|
|
553
549
|
accessibilityLabel={accessibilityLabel}
|
|
@@ -556,8 +552,7 @@ function SplitterHandle({
|
|
|
556
552
|
accessibilityActions={frozen ? undefined : [{ name: 'increment' }, { name: 'decrement' }]}
|
|
557
553
|
onAccessibilityAction={frozen ? undefined : onAccessibilityAction}
|
|
558
554
|
className={handle({ className })}
|
|
559
|
-
style={animatedStyle}
|
|
560
|
-
{...props}
|
|
555
|
+
style={[style, animatedStyle]}
|
|
561
556
|
>
|
|
562
557
|
<View className={line()} />
|
|
563
558
|
{withGrip ? <View className={grip()} /> : null}
|
|
@@ -582,6 +577,7 @@ export {
|
|
|
582
577
|
layoutOffset,
|
|
583
578
|
normalizeConstraint,
|
|
584
579
|
panelFloor,
|
|
580
|
+
resetLayout,
|
|
585
581
|
resizeLayout,
|
|
586
582
|
resolveLayout,
|
|
587
583
|
type SplitterConstraint,
|
|
@@ -227,6 +227,34 @@ export function resizeLayout(
|
|
|
227
227
|
return next;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
/** Restores one pair's initial ratio without escaping its current constraints. */
|
|
231
|
+
export function resetLayout(
|
|
232
|
+
layout: number[],
|
|
233
|
+
initial: number[],
|
|
234
|
+
boundary: number,
|
|
235
|
+
constraints: SplitterConstraint[]
|
|
236
|
+
): number[] {
|
|
237
|
+
const next = layout.slice();
|
|
238
|
+
if (boundary < 0 || boundary + 1 >= next.length) return next;
|
|
239
|
+
|
|
240
|
+
const pair = next[boundary]! + next[boundary + 1]!;
|
|
241
|
+
const first = initial[boundary];
|
|
242
|
+
const second = initial[boundary + 1];
|
|
243
|
+
if (
|
|
244
|
+
!Number.isFinite(pair) ||
|
|
245
|
+
typeof first !== 'number' ||
|
|
246
|
+
!Number.isFinite(first) ||
|
|
247
|
+
typeof second !== 'number' ||
|
|
248
|
+
!Number.isFinite(second) ||
|
|
249
|
+
first + second <= 0
|
|
250
|
+
) {
|
|
251
|
+
return next;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const target = (first / (first + second)) * pair;
|
|
255
|
+
return resizeLayout(next, boundary, target - next[boundary]!, constraints);
|
|
256
|
+
}
|
|
257
|
+
|
|
230
258
|
/** Where a seam sits, in percent, measured from the start of the splitter. */
|
|
231
259
|
export function layoutOffset(layout: number[], boundary: number): number {
|
|
232
260
|
'worklet';
|
|
@@ -128,7 +128,7 @@ export const Switch = forwardRef<View, SwitchProps>(
|
|
|
128
128
|
// so the host is left to follow it. Pinning the host to a number
|
|
129
129
|
// instead is what leaves the control laid out against a box it never
|
|
130
130
|
// agreed to, and settling into it a frame later.
|
|
131
|
-
<Host matchContents>
|
|
131
|
+
<Host matchContents ignoreSafeArea="keyboard">
|
|
132
132
|
<NativeSwitch
|
|
133
133
|
value={value}
|
|
134
134
|
onValueChange={(next: boolean) => onValueChange?.(next)}
|
|
@@ -778,7 +778,7 @@ function TabsList({ className, scrollable = false, children, ...props }: TabsLis
|
|
|
778
778
|
}, [scrollable, activeLayout?.x, activeLayout]);
|
|
779
779
|
|
|
780
780
|
const row = (
|
|
781
|
-
<View accessibilityRole="tablist" className={cn(list(), className)}
|
|
781
|
+
<View {...props} accessibilityRole="tablist" className={cn(list(), className)}>
|
|
782
782
|
{/* Nothing to slide: in `expanding` every tab draws its own pill, and the
|
|
783
783
|
open one is the shape that moves. */}
|
|
784
784
|
{variant === 'expanding' ? null : <TabsIndicator />}
|