panelui-native 0.71.0 → 0.75.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 +5 -2
- package/lib/module/components/accordion/index.js +10 -4
- 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/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/breadcrumb/index.js +2 -1
- package/lib/module/components/breadcrumb/index.js.map +1 -1
- package/lib/module/components/button/index.js +7 -5
- 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 +5 -1
- package/lib/module/components/collapsible/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 +58 -13
- package/lib/module/components/drawer/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 +1 -1
- 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/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 +21 -11
- 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/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/marker/index.js +2 -2
- package/lib/module/components/marker/index.js.map +1 -1
- package/lib/module/components/marquee/index.js +9 -12
- package/lib/module/components/marquee/index.js.map +1 -1
- 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/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/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 +46 -11
- 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 +113 -53
- 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/post/index.js +4 -4
- package/lib/module/components/post/index.js.map +1 -1
- 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 +331 -0
- package/lib/module/components/search-bar/index.js.map +1 -0
- 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/table/index.js +3 -4
- package/lib/module/components/table/index.js.map +1 -1
- 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 +352 -47
- 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/toggle-button/index.js +1 -1
- package/lib/module/components/toggle-button/index.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/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/utils/chart.js +50 -7
- package/lib/module/utils/chart.js.map +1 -1
- package/lib/typescript/src/components/accordion/index.d.ts +2 -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/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/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 +2 -2
- package/lib/typescript/src/components/collapsible/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/fab/index.d.ts +4 -2
- package/lib/typescript/src/components/fab/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/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/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/marquee/index.d.ts +1 -1
- package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
- 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/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/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-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 +60 -6
- 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 +216 -0
- package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -0
- 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 +36 -2
- 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/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 +5 -2
- package/lib/typescript/src/index.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 +1 -1
- package/src/components/accordion/index.tsx +17 -7
- package/src/components/area-chart/index.tsx +8 -0
- package/src/components/avatar/avatar-group.ts +78 -0
- package/src/components/avatar/index.tsx +121 -1
- package/src/components/breadcrumb/index.tsx +2 -1
- package/src/components/button/index.tsx +3 -2
- 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 +13 -4
- package/src/components/color-picker/color-picker-accessibility.ts +12 -0
- package/src/components/color-picker/index.tsx +30 -10
- package/src/components/date-time-picker/index.tsx +11 -3
- package/src/components/drawer/index.tsx +42 -14
- package/src/components/fab/index.tsx +21 -11
- package/src/components/flow/index.tsx +1 -1
- 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/index.tsx +6 -2
- package/src/components/input/input-content-padding.ts +3 -0
- package/src/components/input-group/index.tsx +45 -12
- package/src/components/input-group/input-group-measurements.ts +98 -0
- package/src/components/item/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +9 -0
- package/src/components/marker/index.tsx +2 -2
- package/src/components/marquee/index.tsx +14 -12
- package/src/components/marquee/marquee-math.ts +37 -0
- package/src/components/menu/index.tsx +11 -3
- package/src/components/otp-input/index.tsx +3 -1
- package/src/components/otp-input/otp-input-state.ts +7 -0
- 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 +44 -11
- package/src/components/planner/planner-today.ts +5 -0
- package/src/components/plot/index.tsx +199 -51
- package/src/components/plot/plot-series-registry.ts +41 -0
- package/src/components/post/index.tsx +4 -4
- 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 +441 -0
- 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/table/index.tsx +2 -2
- 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 +405 -46
- 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/toggle-button/index.tsx +1 -1
- package/src/components/tour/index.tsx +11 -3
- package/src/components/tour/tour-card-measurement.ts +29 -0
- package/src/components/tree/index.tsx +17 -12
- package/src/hooks/use-keyboard-avoidance.ts +7 -2
- package/src/index.ts +19 -0
- package/src/utils/chart.ts +66 -7
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splitter — panes that share a container, with a seam between them the reader
|
|
3
|
+
* can drag.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <Splitter className="h-64" defaultLayout={[65, 35]}>
|
|
7
|
+
* <Splitter.Panel minSize={30}>{list}</Splitter.Panel>
|
|
8
|
+
* <Splitter.Handle />
|
|
9
|
+
* <Splitter.Panel minSize={20} collapsible>{detail}</Splitter.Panel>
|
|
10
|
+
* </Splitter>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Sizes are percentages of the splitter, so a layout dragged in portrait is
|
|
14
|
+
* still the same layout in landscape. The splitter itself has no height of its
|
|
15
|
+
* own — give it one, or put it in something that does, or it collapses to
|
|
16
|
+
* nothing and takes its panes with it.
|
|
17
|
+
*
|
|
18
|
+
* The seams are drawn over the panes rather than between them. A handle that
|
|
19
|
+
* took layout space would have to be measured before the panes could be sized,
|
|
20
|
+
* which puts the whole layout downstream of a number nobody controls; floating
|
|
21
|
+
* it means the panes add up to exactly the container and the handle can be as
|
|
22
|
+
* wide a target as a finger needs without changing anything.
|
|
23
|
+
*
|
|
24
|
+
* Dragging runs on the UI thread. `onLayoutChange` fires when the seam is let
|
|
25
|
+
* go rather than on every frame, because a layout that round-trips through
|
|
26
|
+
* React sixty times a second is the one thing that makes this feel slow.
|
|
27
|
+
*/
|
|
28
|
+
import {
|
|
29
|
+
Children,
|
|
30
|
+
createContext,
|
|
31
|
+
isValidElement,
|
|
32
|
+
useCallback,
|
|
33
|
+
useContext,
|
|
34
|
+
useEffect,
|
|
35
|
+
useMemo,
|
|
36
|
+
useRef,
|
|
37
|
+
useState,
|
|
38
|
+
type ReactElement,
|
|
39
|
+
type ReactNode,
|
|
40
|
+
} from 'react';
|
|
41
|
+
import {
|
|
42
|
+
View,
|
|
43
|
+
type AccessibilityActionEvent,
|
|
44
|
+
type LayoutChangeEvent,
|
|
45
|
+
type ViewProps,
|
|
46
|
+
} from 'react-native';
|
|
47
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
48
|
+
import Animated, {
|
|
49
|
+
runOnJS,
|
|
50
|
+
useAnimatedStyle,
|
|
51
|
+
useSharedValue,
|
|
52
|
+
type SharedValue,
|
|
53
|
+
} from 'react-native-reanimated';
|
|
54
|
+
import { tv } from 'tailwind-variants';
|
|
55
|
+
import { useDirectionSign } from '../../hooks/use-direction';
|
|
56
|
+
import { selectionTick } from '../../utils/haptics';
|
|
57
|
+
import {
|
|
58
|
+
isCollapsed,
|
|
59
|
+
layoutOffset,
|
|
60
|
+
normalizeConstraint,
|
|
61
|
+
resizeLayout,
|
|
62
|
+
resolveLayout,
|
|
63
|
+
type SplitterConstraint,
|
|
64
|
+
} from './splitter-math';
|
|
65
|
+
|
|
66
|
+
export type SplitterOrientation = 'horizontal' | 'vertical';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* How thick the seam's touch target is, in points. Wider than the line it
|
|
70
|
+
* draws, because the line is a hairline and a hairline is not a target.
|
|
71
|
+
*/
|
|
72
|
+
const HANDLE_THICKNESS = 24;
|
|
73
|
+
|
|
74
|
+
const splitterVariants = tv({
|
|
75
|
+
slots: {
|
|
76
|
+
root: 'relative overflow-hidden',
|
|
77
|
+
panel: 'overflow-hidden',
|
|
78
|
+
handle: 'absolute items-center justify-center',
|
|
79
|
+
line: 'bg-border',
|
|
80
|
+
grip: 'absolute rounded-full bg-muted-foreground/30',
|
|
81
|
+
},
|
|
82
|
+
variants: {
|
|
83
|
+
orientation: {
|
|
84
|
+
horizontal: {
|
|
85
|
+
// `w-full`, because the panes are shares of a width the splitter has to
|
|
86
|
+
// already know. Left to size itself, a row asks its children how wide
|
|
87
|
+
// they are and the children answer with a share of that answer — a
|
|
88
|
+
// circle that resolves to zero and takes the whole splitter with it.
|
|
89
|
+
// Inside anything that centres its children, that is what happens.
|
|
90
|
+
root: 'w-full flex-row',
|
|
91
|
+
// Over the pane that follows it, not under. A seam is drawn between two
|
|
92
|
+
// panes but written between them too, and a later sibling paints on top
|
|
93
|
+
// — which left the outer half of every touch target dead.
|
|
94
|
+
handle: 'bottom-0 start-0 top-0 z-10 w-6',
|
|
95
|
+
line: 'h-full w-px',
|
|
96
|
+
grip: 'h-8 w-1',
|
|
97
|
+
},
|
|
98
|
+
vertical: {
|
|
99
|
+
root: 'flex-col',
|
|
100
|
+
handle: 'end-0 start-0 top-0 z-10 h-6',
|
|
101
|
+
line: 'h-px w-full',
|
|
102
|
+
grip: 'h-1 w-8',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
defaultVariants: {
|
|
107
|
+
orientation: 'horizontal',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
interface SplitterContextValue {
|
|
112
|
+
orientation: SplitterOrientation;
|
|
113
|
+
/** Live percentages, one per panel. Written by drags, read by every pane. */
|
|
114
|
+
layout: SharedValue<number[]>;
|
|
115
|
+
/** The splitter's own size along its axis, in points. `0` until measured. */
|
|
116
|
+
available: SharedValue<number>;
|
|
117
|
+
/** Whether that measurement has landed. Panes size themselves by flex until it has. */
|
|
118
|
+
measured: boolean;
|
|
119
|
+
/** The last settled layout, for the props that have to be React state. */
|
|
120
|
+
settled: number[];
|
|
121
|
+
/** The layout the splitter resolved to before anybody dragged it. */
|
|
122
|
+
initial: number[];
|
|
123
|
+
constraints: SplitterConstraint[];
|
|
124
|
+
disabled: boolean;
|
|
125
|
+
step: number;
|
|
126
|
+
sign: 1 | -1;
|
|
127
|
+
commit: (next: number[]) => void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const SplitterContext = createContext<SplitterContextValue | null>(null);
|
|
131
|
+
const PanelIndexContext = createContext(0);
|
|
132
|
+
const BoundaryIndexContext = createContext(-1);
|
|
133
|
+
|
|
134
|
+
function useSplitter(component: string): SplitterContextValue {
|
|
135
|
+
const context = useContext(SplitterContext);
|
|
136
|
+
if (!context) throw new Error(`${component} must be used within a <Splitter>`);
|
|
137
|
+
return context;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface SplitterProps extends ViewProps {
|
|
141
|
+
className?: string;
|
|
142
|
+
/** Which way the panes are laid out. Defaults to `horizontal`. */
|
|
143
|
+
orientation?: SplitterOrientation;
|
|
144
|
+
/**
|
|
145
|
+
* Controlled layout, as one percentage per panel. Pair it with
|
|
146
|
+
* `onLayoutChange`: a seam that is let go snaps back to this unless the value
|
|
147
|
+
* moves with it.
|
|
148
|
+
*/
|
|
149
|
+
layout?: number[];
|
|
150
|
+
/**
|
|
151
|
+
* Starting layout when uncontrolled, as one percentage per panel. Panels left
|
|
152
|
+
* out of it fall back to their own `defaultSize`, and then to an even share.
|
|
153
|
+
*/
|
|
154
|
+
defaultLayout?: number[];
|
|
155
|
+
/** Called with the new layout once a seam is let go, or stepped. */
|
|
156
|
+
onLayoutChange?: (layout: number[]) => void;
|
|
157
|
+
/** Freezes every seam. */
|
|
158
|
+
disabled?: boolean;
|
|
159
|
+
/** How far one accessibility step moves a seam, in percent. Defaults to `5`. */
|
|
160
|
+
step?: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isElementOfType(child: ReactNode, type: unknown): child is ReactElement<Record<string, unknown>> {
|
|
164
|
+
return isValidElement(child) && child.type === type;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function SplitterRoot({
|
|
168
|
+
className,
|
|
169
|
+
orientation = 'horizontal',
|
|
170
|
+
layout: layoutProp,
|
|
171
|
+
defaultLayout,
|
|
172
|
+
onLayoutChange,
|
|
173
|
+
disabled = false,
|
|
174
|
+
step = 5,
|
|
175
|
+
children,
|
|
176
|
+
onLayout,
|
|
177
|
+
...props
|
|
178
|
+
}: SplitterProps) {
|
|
179
|
+
const horizontal = orientation === 'horizontal';
|
|
180
|
+
const sign = useDirectionSign();
|
|
181
|
+
const { root } = splitterVariants({ orientation });
|
|
182
|
+
|
|
183
|
+
const items = Children.toArray(children);
|
|
184
|
+
const panels = items.filter((child) => isElementOfType(child, SplitterPanel)) as ReactElement<SplitterPanelProps>[];
|
|
185
|
+
|
|
186
|
+
// The panes' limits are read straight off their elements rather than
|
|
187
|
+
// registered from an effect. A layout that is only correct after the children
|
|
188
|
+
// have mounted and reported in is a layout that is wrong on the first frame,
|
|
189
|
+
// and this one is knowable before any of them render.
|
|
190
|
+
const constraintsKey = JSON.stringify(
|
|
191
|
+
panels.map((panel) => [
|
|
192
|
+
panel.props.minSize,
|
|
193
|
+
panel.props.maxSize,
|
|
194
|
+
panel.props.collapsible === true,
|
|
195
|
+
panel.props.collapsedSize,
|
|
196
|
+
panel.props.defaultSize,
|
|
197
|
+
])
|
|
198
|
+
);
|
|
199
|
+
const constraints = useMemo(
|
|
200
|
+
() => panels.map((panel) => normalizeConstraint(panel.props)),
|
|
201
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
202
|
+
[constraintsKey]
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const sizesKey = JSON.stringify([
|
|
206
|
+
layoutProp ?? null,
|
|
207
|
+
defaultLayout ?? null,
|
|
208
|
+
panels.map((panel) => panel.props.defaultSize ?? null),
|
|
209
|
+
]);
|
|
210
|
+
const resolved = useMemo(() => {
|
|
211
|
+
const declared = layoutProp ?? defaultLayout;
|
|
212
|
+
return resolveLayout(
|
|
213
|
+
panels.map((panel, index) => declared?.[index] ?? panel.props.defaultSize),
|
|
214
|
+
constraints
|
|
215
|
+
);
|
|
216
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
217
|
+
}, [sizesKey, constraints]);
|
|
218
|
+
|
|
219
|
+
const layout = useSharedValue<number[]>(resolved);
|
|
220
|
+
const available = useSharedValue(0);
|
|
221
|
+
const [measured, setMeasured] = useState(false);
|
|
222
|
+
const [settled, setSettled] = useState(resolved);
|
|
223
|
+
// Bumped when a controlled splitter is let go, so a caller that ignored the
|
|
224
|
+
// change gets the seam put back where its props still say it is.
|
|
225
|
+
const [syncToken, setSyncToken] = useState(0);
|
|
226
|
+
|
|
227
|
+
const controlled = layoutProp !== undefined;
|
|
228
|
+
const controlledRef = useRef(controlled);
|
|
229
|
+
controlledRef.current = controlled;
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
layout.value = resolved;
|
|
233
|
+
setSettled(resolved);
|
|
234
|
+
}, [resolved, syncToken, layout]);
|
|
235
|
+
|
|
236
|
+
const commit = useCallback(
|
|
237
|
+
(next: number[]) => {
|
|
238
|
+
if (controlledRef.current) setSyncToken((token) => token + 1);
|
|
239
|
+
else setSettled(next);
|
|
240
|
+
onLayoutChange?.(next);
|
|
241
|
+
},
|
|
242
|
+
[onLayoutChange]
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const handleLayout = useCallback(
|
|
246
|
+
(event: LayoutChangeEvent) => {
|
|
247
|
+
const box = event.nativeEvent.layout;
|
|
248
|
+
const size = horizontal ? box.width : box.height;
|
|
249
|
+
available.value = size;
|
|
250
|
+
if (size > 0) setMeasured(true);
|
|
251
|
+
onLayout?.(event);
|
|
252
|
+
},
|
|
253
|
+
[available, horizontal, onLayout]
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const context = useMemo<SplitterContextValue>(
|
|
257
|
+
() => ({
|
|
258
|
+
orientation,
|
|
259
|
+
layout,
|
|
260
|
+
available,
|
|
261
|
+
measured,
|
|
262
|
+
settled,
|
|
263
|
+
initial: resolved,
|
|
264
|
+
constraints,
|
|
265
|
+
disabled,
|
|
266
|
+
// A step of zero or less is a seam that cannot be moved without a drag,
|
|
267
|
+
// which is the one thing the accessibility actions exist to avoid.
|
|
268
|
+
step: Number.isFinite(step) && step > 0 ? step : 5,
|
|
269
|
+
sign,
|
|
270
|
+
commit,
|
|
271
|
+
}),
|
|
272
|
+
[
|
|
273
|
+
orientation,
|
|
274
|
+
layout,
|
|
275
|
+
available,
|
|
276
|
+
measured,
|
|
277
|
+
settled,
|
|
278
|
+
resolved,
|
|
279
|
+
constraints,
|
|
280
|
+
disabled,
|
|
281
|
+
step,
|
|
282
|
+
sign,
|
|
283
|
+
commit,
|
|
284
|
+
]
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Panes learn their index and seams learn which pair they sit between from
|
|
288
|
+
// where they appear, so nothing has to be numbered by hand and a pane cannot
|
|
289
|
+
// be given the wrong one.
|
|
290
|
+
let panelIndex = -1;
|
|
291
|
+
const laidOut = items.map((child, index) => {
|
|
292
|
+
if (isElementOfType(child, SplitterPanel)) {
|
|
293
|
+
panelIndex += 1;
|
|
294
|
+
return (
|
|
295
|
+
<PanelIndexContext.Provider key={`panel-${panelIndex}`} value={panelIndex}>
|
|
296
|
+
{child}
|
|
297
|
+
</PanelIndexContext.Provider>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
if (isElementOfType(child, SplitterHandle)) {
|
|
301
|
+
return (
|
|
302
|
+
<BoundaryIndexContext.Provider key={`handle-${index}`} value={panelIndex}>
|
|
303
|
+
{child}
|
|
304
|
+
</BoundaryIndexContext.Provider>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return child;
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<SplitterContext.Provider value={context}>
|
|
312
|
+
<View className={root({ className })} onLayout={handleLayout} {...props}>
|
|
313
|
+
{laidOut}
|
|
314
|
+
</View>
|
|
315
|
+
</SplitterContext.Provider>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface SplitterPanelProps extends ViewProps {
|
|
320
|
+
className?: string;
|
|
321
|
+
/** Starting share of the splitter, in percent. Unsized panes split the rest. */
|
|
322
|
+
defaultSize?: number;
|
|
323
|
+
/** Smallest share this pane may hold while open, in percent. Defaults to `10`. */
|
|
324
|
+
minSize?: number;
|
|
325
|
+
/** Largest share this pane may hold, in percent. Defaults to `100`. */
|
|
326
|
+
maxSize?: number;
|
|
327
|
+
/** Lets a drag past `minSize` shut the pane rather than stopping at it. */
|
|
328
|
+
collapsible?: boolean;
|
|
329
|
+
/** Share this pane holds while shut, in percent. Defaults to `0`. */
|
|
330
|
+
collapsedSize?: number;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* One pane.
|
|
335
|
+
*
|
|
336
|
+
* It clips what is inside it, so a pane dragged narrower hides its content
|
|
337
|
+
* rather than pushing it into the pane beside it.
|
|
338
|
+
*/
|
|
339
|
+
function SplitterPanel({ className, children, style, ...props }: SplitterPanelProps) {
|
|
340
|
+
const context = useSplitter('Splitter.Panel');
|
|
341
|
+
const index = useContext(PanelIndexContext);
|
|
342
|
+
const { panel } = splitterVariants({ orientation: context.orientation });
|
|
343
|
+
const horizontal = context.orientation === 'horizontal';
|
|
344
|
+
|
|
345
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
346
|
+
const size = (context.available.value * (context.layout.value[index] ?? 0)) / 100;
|
|
347
|
+
return horizontal ? { width: size } : { height: size };
|
|
348
|
+
}, [index, horizontal]);
|
|
349
|
+
|
|
350
|
+
// Nothing is measured on the first frame, and a pane sized from a container of
|
|
351
|
+
// zero is a pane nobody can see. Until the measurement lands, a definite flex
|
|
352
|
+
// basis of zero takes precedence over that width and the shares below carry
|
|
353
|
+
// the layout — so the first frame is already in proportion, and the switch to
|
|
354
|
+
// measured points changes nothing anybody can see.
|
|
355
|
+
const unmeasured = { flexGrow: context.settled[index] ?? 0, flexBasis: 0, flexShrink: 1 };
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<Animated.View
|
|
359
|
+
className={panel({ className })}
|
|
360
|
+
style={[context.measured ? null : unmeasured, style, animatedStyle]}
|
|
361
|
+
{...props}
|
|
362
|
+
>
|
|
363
|
+
{children}
|
|
364
|
+
</Animated.View>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export interface SplitterHandleProps extends ViewProps {
|
|
369
|
+
className?: string;
|
|
370
|
+
/** Freezes this seam on its own, leaving the others draggable. */
|
|
371
|
+
disabled?: boolean;
|
|
372
|
+
/** Draws the grip in the middle of the seam. Defaults to `true`. */
|
|
373
|
+
withGrip?: boolean;
|
|
374
|
+
/** What a screen reader calls the seam. Defaults to "Resize panels". */
|
|
375
|
+
accessibilityLabel?: string;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* The seam between two panes.
|
|
380
|
+
*
|
|
381
|
+
* Put one between each pair. It is `adjustable` to a screen reader, so the
|
|
382
|
+
* layout can be moved a step at a time without a drag, and a double tap puts
|
|
383
|
+
* the pair back where it started — the fastest way out of a pane dragged shut
|
|
384
|
+
* by accident.
|
|
385
|
+
*/
|
|
386
|
+
function SplitterHandle({
|
|
387
|
+
className,
|
|
388
|
+
disabled,
|
|
389
|
+
withGrip = true,
|
|
390
|
+
accessibilityLabel = 'Resize panels',
|
|
391
|
+
...props
|
|
392
|
+
}: SplitterHandleProps) {
|
|
393
|
+
const context = useSplitter('Splitter.Handle');
|
|
394
|
+
const boundary = useContext(BoundaryIndexContext);
|
|
395
|
+
const { handle, line, grip } = splitterVariants({ orientation: context.orientation });
|
|
396
|
+
const horizontal = context.orientation === 'horizontal';
|
|
397
|
+
const frozen = context.disabled || disabled === true;
|
|
398
|
+
|
|
399
|
+
const { layout, available, constraints, sign, commit } = context;
|
|
400
|
+
const start = useSharedValue<number[]>([]);
|
|
401
|
+
const collapsed = useSharedValue(false);
|
|
402
|
+
const moved = useSharedValue(false);
|
|
403
|
+
|
|
404
|
+
const pan = useMemo(() => {
|
|
405
|
+
const gesture = Gesture.Pan()
|
|
406
|
+
.enabled(!frozen && boundary >= 0)
|
|
407
|
+
.onBegin(() => {
|
|
408
|
+
start.value = layout.value.slice();
|
|
409
|
+
moved.value = false;
|
|
410
|
+
const constraint = constraints[boundary];
|
|
411
|
+
collapsed.value = constraint
|
|
412
|
+
? isCollapsed(layout.value[boundary] ?? 0, constraint)
|
|
413
|
+
: false;
|
|
414
|
+
})
|
|
415
|
+
.onUpdate((event) => {
|
|
416
|
+
if (available.value <= 0) return;
|
|
417
|
+
const travelled = horizontal ? event.translationX * sign : event.translationY;
|
|
418
|
+
const delta = (travelled / available.value) * 100;
|
|
419
|
+
const next = resizeLayout(start.value, boundary, delta, constraints);
|
|
420
|
+
moved.value = true;
|
|
421
|
+
layout.value = next;
|
|
422
|
+
|
|
423
|
+
// The one moment in a drag worth feeling: a pane arriving at shut, or
|
|
424
|
+
// leaving it. Both are a jump the finger did not make, so the tick is
|
|
425
|
+
// what says the jump was the control and not a stutter.
|
|
426
|
+
const constraint = constraints[boundary];
|
|
427
|
+
if (constraint) {
|
|
428
|
+
const shut = isCollapsed(next[boundary] ?? 0, constraint);
|
|
429
|
+
if (shut !== collapsed.value) {
|
|
430
|
+
collapsed.value = shut;
|
|
431
|
+
runOnJS(selectionTick)();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
})
|
|
435
|
+
// A seam that was pressed and not moved has nothing to report, and a
|
|
436
|
+
// layout change nobody made is a re-render nobody asked for.
|
|
437
|
+
.onFinalize(() => {
|
|
438
|
+
if (moved.value) runOnJS(commit)(layout.value.slice());
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
/*
|
|
442
|
+
* A seam only answers to the axis it moves on. Without that it claims any
|
|
443
|
+
* movement at all — which is a splitter inside a scroller taking the
|
|
444
|
+
* scroll, and a drag with no way to fail, which is the one thing the double
|
|
445
|
+
* tap behind it is waiting for.
|
|
446
|
+
*/
|
|
447
|
+
return horizontal
|
|
448
|
+
? gesture.activeOffsetX([-8, 8]).failOffsetY([-16, 16])
|
|
449
|
+
: gesture.activeOffsetY([-8, 8]).failOffsetX([-16, 16]);
|
|
450
|
+
}, [
|
|
451
|
+
available,
|
|
452
|
+
boundary,
|
|
453
|
+
collapsed,
|
|
454
|
+
commit,
|
|
455
|
+
constraints,
|
|
456
|
+
frozen,
|
|
457
|
+
horizontal,
|
|
458
|
+
layout,
|
|
459
|
+
moved,
|
|
460
|
+
sign,
|
|
461
|
+
start,
|
|
462
|
+
]);
|
|
463
|
+
|
|
464
|
+
const { initial } = context;
|
|
465
|
+
/**
|
|
466
|
+
* Puts this pair back to the proportions the splitter started with, without
|
|
467
|
+
* touching the panes further along — the two of them share exactly the room
|
|
468
|
+
* they already have between them.
|
|
469
|
+
*/
|
|
470
|
+
const reset = useCallback(() => {
|
|
471
|
+
const pair = boundary >= 0 ? boundary : 0;
|
|
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]!;
|
|
479
|
+
layout.value = next;
|
|
480
|
+
commit(next);
|
|
481
|
+
}, [boundary, commit, initial, layout]);
|
|
482
|
+
|
|
483
|
+
const doubleTap = useMemo(
|
|
484
|
+
() =>
|
|
485
|
+
Gesture.Tap()
|
|
486
|
+
.numberOfTaps(2)
|
|
487
|
+
// A tap that has travelled this far is a drag, and saying so is what
|
|
488
|
+
// lets it give up on the movement rather than on a timer.
|
|
489
|
+
.maxDistance(HANDLE_THICKNESS / 2)
|
|
490
|
+
.enabled(!frozen && boundary >= 0)
|
|
491
|
+
.onEnd((_event, success) => {
|
|
492
|
+
if (success) runOnJS(reset)();
|
|
493
|
+
}),
|
|
494
|
+
[boundary, frozen, reset]
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
/*
|
|
498
|
+
* The drag comes first, and the order is the whole of it.
|
|
499
|
+
*
|
|
500
|
+
* A race gives its first gesture the priority and makes the rest wait for it
|
|
501
|
+
* to fail. With the tap in front, every drag was held back until the tap gave
|
|
502
|
+
* up — which a two-tap gesture only does once its window has expired, half a
|
|
503
|
+
* second after the finger lands. A slow first drag outlasted that and worked;
|
|
504
|
+
* the quicker one after it did not, and the seam ignored it.
|
|
505
|
+
*
|
|
506
|
+
* Round this way the pan activates on movement, and a press that never moves
|
|
507
|
+
* fails it and hands the touch on, which is all the tap was waiting for.
|
|
508
|
+
*/
|
|
509
|
+
const gesture = useMemo(() => Gesture.Race(pan, doubleTap), [doubleTap, pan]);
|
|
510
|
+
|
|
511
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
512
|
+
if (available.value <= 0) return { opacity: 0 };
|
|
513
|
+
const seam = (available.value * layoutOffset(layout.value, boundary)) / 100;
|
|
514
|
+
const shift = seam - HANDLE_THICKNESS / 2;
|
|
515
|
+
return {
|
|
516
|
+
opacity: 1,
|
|
517
|
+
transform: horizontal ? [{ translateX: shift * sign }] : [{ translateY: shift }],
|
|
518
|
+
};
|
|
519
|
+
}, [boundary, horizontal, sign]);
|
|
520
|
+
|
|
521
|
+
// A step moves the seam along the layout, not along the screen, so it is the
|
|
522
|
+
// one piece of this that the reading direction does not touch: incrementing
|
|
523
|
+
// grows the pane that comes first either way.
|
|
524
|
+
const step = useCallback(
|
|
525
|
+
(direction: 1 | -1) => {
|
|
526
|
+
const next = resizeLayout(
|
|
527
|
+
layout.value.slice(),
|
|
528
|
+
boundary,
|
|
529
|
+
direction * context.step,
|
|
530
|
+
constraints
|
|
531
|
+
);
|
|
532
|
+
layout.value = next;
|
|
533
|
+
commit(next);
|
|
534
|
+
},
|
|
535
|
+
[boundary, commit, constraints, context.step, layout]
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
const onAccessibilityAction = useCallback(
|
|
539
|
+
(event: AccessibilityActionEvent) => {
|
|
540
|
+
if (event.nativeEvent.actionName === 'increment') step(1);
|
|
541
|
+
else if (event.nativeEvent.actionName === 'decrement') step(-1);
|
|
542
|
+
},
|
|
543
|
+
[step]
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
const now = Math.round(layoutOffset(context.settled, boundary));
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
<GestureDetector gesture={gesture}>
|
|
550
|
+
<Animated.View
|
|
551
|
+
accessible
|
|
552
|
+
accessibilityRole="adjustable"
|
|
553
|
+
accessibilityLabel={accessibilityLabel}
|
|
554
|
+
accessibilityState={{ disabled: frozen }}
|
|
555
|
+
accessibilityValue={{ min: 0, max: 100, now }}
|
|
556
|
+
accessibilityActions={frozen ? undefined : [{ name: 'increment' }, { name: 'decrement' }]}
|
|
557
|
+
onAccessibilityAction={frozen ? undefined : onAccessibilityAction}
|
|
558
|
+
className={handle({ className })}
|
|
559
|
+
style={animatedStyle}
|
|
560
|
+
{...props}
|
|
561
|
+
>
|
|
562
|
+
<View className={line()} />
|
|
563
|
+
{withGrip ? <View className={grip()} /> : null}
|
|
564
|
+
</Animated.View>
|
|
565
|
+
</GestureDetector>
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
SplitterRoot.displayName = 'Splitter';
|
|
570
|
+
SplitterPanel.displayName = 'Splitter.Panel';
|
|
571
|
+
SplitterHandle.displayName = 'Splitter.Handle';
|
|
572
|
+
|
|
573
|
+
export const Splitter = Object.assign(SplitterRoot, {
|
|
574
|
+
Panel: SplitterPanel,
|
|
575
|
+
Handle: SplitterHandle,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
export {
|
|
579
|
+
DEFAULT_MAX_SIZE,
|
|
580
|
+
DEFAULT_MIN_SIZE,
|
|
581
|
+
isCollapsed,
|
|
582
|
+
layoutOffset,
|
|
583
|
+
normalizeConstraint,
|
|
584
|
+
panelFloor,
|
|
585
|
+
resizeLayout,
|
|
586
|
+
resolveLayout,
|
|
587
|
+
type SplitterConstraint,
|
|
588
|
+
type SplitterConstraintInput,
|
|
589
|
+
} from './splitter-math';
|