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,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The arithmetic a splitter runs while a seam is under a finger.
|
|
3
|
+
*
|
|
4
|
+
* Sizes are percentages of the whole splitter, never points, which is what
|
|
5
|
+
* makes a dragged layout survive a rotation or a window resize: the numbers
|
|
6
|
+
* mean the same thing at any container size, so nothing has to be re-measured
|
|
7
|
+
* and re-derived when one changes.
|
|
8
|
+
*
|
|
9
|
+
* It lives apart from the component for two reasons. It is the part with the
|
|
10
|
+
* edge cases — a pair of panels that cannot both honour their minimums, a
|
|
11
|
+
* collapse that has to decide between shut and its floor, a caller's sizes that
|
|
12
|
+
* do not add up — and it is the part that runs on the UI thread, where a wrong
|
|
13
|
+
* answer is a layout that sticks rather than an exception anybody sees.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** How small a panel may get before its neighbour stops taking room, in percent. */
|
|
17
|
+
export const DEFAULT_MIN_SIZE = 10;
|
|
18
|
+
/** How large a panel may get, in percent. */
|
|
19
|
+
export const DEFAULT_MAX_SIZE = 100;
|
|
20
|
+
|
|
21
|
+
export interface SplitterConstraint {
|
|
22
|
+
/** Smallest share the panel may hold while open, in percent. */
|
|
23
|
+
minSize: number;
|
|
24
|
+
/** Largest share the panel may hold, in percent. */
|
|
25
|
+
maxSize: number;
|
|
26
|
+
/** Whether dragging past the minimum shuts the panel instead of stopping. */
|
|
27
|
+
collapsible: boolean;
|
|
28
|
+
/** Share the panel holds while shut, in percent. */
|
|
29
|
+
collapsedSize: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SplitterConstraintInput {
|
|
33
|
+
minSize?: number;
|
|
34
|
+
maxSize?: number;
|
|
35
|
+
collapsible?: boolean;
|
|
36
|
+
collapsedSize?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function clamp(value: number, low: number, high: number): number {
|
|
40
|
+
'worklet';
|
|
41
|
+
return Math.min(Math.max(value, low), high);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function finite(value: number | undefined, fallback: number): number {
|
|
45
|
+
'worklet';
|
|
46
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The smallest share a panel can hold at all — its floor once shut, or its minimum. */
|
|
50
|
+
export function panelFloor(constraint: SplitterConstraint): number {
|
|
51
|
+
'worklet';
|
|
52
|
+
return constraint.collapsible ? constraint.collapsedSize : constraint.minSize;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Fills a panel's declared limits in, in an order that cannot contradict
|
|
57
|
+
* itself: the maximum is never below the minimum, and a collapsed size is never
|
|
58
|
+
* above the minimum it exists to sit below.
|
|
59
|
+
*/
|
|
60
|
+
export function normalizeConstraint(
|
|
61
|
+
input: SplitterConstraintInput | undefined
|
|
62
|
+
): SplitterConstraint {
|
|
63
|
+
const minSize = clamp(finite(input?.minSize, DEFAULT_MIN_SIZE), 0, 100);
|
|
64
|
+
const maxSize = clamp(finite(input?.maxSize, DEFAULT_MAX_SIZE), minSize, 100);
|
|
65
|
+
const collapsible = input?.collapsible === true;
|
|
66
|
+
const collapsedSize = clamp(finite(input?.collapsedSize, 0), 0, minSize);
|
|
67
|
+
return { minSize, maxSize, collapsible, collapsedSize };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function total(layout: number[]): number {
|
|
71
|
+
'worklet';
|
|
72
|
+
let sum = 0;
|
|
73
|
+
for (let index = 0; index < layout.length; index += 1) sum += layout[index]!;
|
|
74
|
+
return sum;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Pushes a layout onto exactly 100, moving each panel by its share of the room
|
|
79
|
+
* it has left in the direction the correction is going. Panels already against
|
|
80
|
+
* a limit therefore absorb none of it, which is what keeps a correction from
|
|
81
|
+
* quietly undoing the limit it just honoured.
|
|
82
|
+
*
|
|
83
|
+
* Four passes, because clamping after each one can hand the next a smaller
|
|
84
|
+
* remainder rather than none; it converges long before that in practice, and
|
|
85
|
+
* the bound is what stops a contradictory set of limits from spinning.
|
|
86
|
+
*/
|
|
87
|
+
function settle(layout: number[], constraints: SplitterConstraint[]): number[] {
|
|
88
|
+
'worklet';
|
|
89
|
+
const next = layout.slice();
|
|
90
|
+
|
|
91
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
92
|
+
const remainder = 100 - total(next);
|
|
93
|
+
if (Math.abs(remainder) < 1e-6) break;
|
|
94
|
+
|
|
95
|
+
let room = 0;
|
|
96
|
+
const headroom: number[] = [];
|
|
97
|
+
for (let index = 0; index < next.length; index += 1) {
|
|
98
|
+
const constraint = constraints[index]!;
|
|
99
|
+
const available =
|
|
100
|
+
remainder > 0
|
|
101
|
+
? constraint.maxSize - next[index]!
|
|
102
|
+
: next[index]! - panelFloor(constraint);
|
|
103
|
+
headroom.push(Math.max(available, 0));
|
|
104
|
+
room += Math.max(available, 0);
|
|
105
|
+
}
|
|
106
|
+
if (room <= 1e-6) break;
|
|
107
|
+
|
|
108
|
+
for (let index = 0; index < next.length; index += 1) {
|
|
109
|
+
next[index] = clamp(
|
|
110
|
+
next[index]! + (remainder * headroom[index]!) / room,
|
|
111
|
+
panelFloor(constraints[index]!),
|
|
112
|
+
constraints[index]!.maxSize
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Minimums that add up to more than the whole cannot all be honoured. Scaling
|
|
118
|
+
// the whole layout down is the failure that stays legible: every panel is
|
|
119
|
+
// smaller than it asked for, rather than the last one being pushed out of the
|
|
120
|
+
// container where nobody can see that it is there at all.
|
|
121
|
+
const sum = total(next);
|
|
122
|
+
if (sum > 100 + 1e-6) {
|
|
123
|
+
for (let index = 0; index < next.length; index += 1) {
|
|
124
|
+
next[index] = (next[index]! * 100) / sum;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return next;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Turns whatever sizes a caller gave into one layout that adds up.
|
|
133
|
+
*
|
|
134
|
+
* Panels with no size of their own split what the sized ones left over, so a
|
|
135
|
+
* three-pane splitter that only pins its sidebar gets the sensible reading of
|
|
136
|
+
* that: the sidebar keeps its number and the other two share the rest.
|
|
137
|
+
*/
|
|
138
|
+
export function resolveLayout(
|
|
139
|
+
sizes: (number | undefined)[],
|
|
140
|
+
constraints: SplitterConstraint[]
|
|
141
|
+
): number[] {
|
|
142
|
+
if (constraints.length === 0) return [];
|
|
143
|
+
|
|
144
|
+
let claimed = 0;
|
|
145
|
+
let unsized = 0;
|
|
146
|
+
for (let index = 0; index < constraints.length; index += 1) {
|
|
147
|
+
const size = sizes[index];
|
|
148
|
+
if (typeof size === 'number' && Number.isFinite(size)) {
|
|
149
|
+
claimed += clamp(size, 0, 100);
|
|
150
|
+
} else {
|
|
151
|
+
unsized += 1;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const share = unsized > 0 ? Math.max(100 - claimed, 0) / unsized : 0;
|
|
156
|
+
const layout: number[] = [];
|
|
157
|
+
for (let index = 0; index < constraints.length; index += 1) {
|
|
158
|
+
const size = sizes[index];
|
|
159
|
+
const raw =
|
|
160
|
+
typeof size === 'number' && Number.isFinite(size) ? clamp(size, 0, 100) : share;
|
|
161
|
+
layout.push(
|
|
162
|
+
clamp(raw, panelFloor(constraints[index]!), constraints[index]!.maxSize)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return settle(layout, constraints);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* A panel's size once it is below its minimum: shut if it is collapsible and
|
|
171
|
+
* the drag has gone more than halfway there, and otherwise held at the minimum.
|
|
172
|
+
*
|
|
173
|
+
* The halfway point is what makes collapsing feel like a decision rather than
|
|
174
|
+
* an accident — a finger that grazes the minimum springs back, and one that
|
|
175
|
+
* keeps going shuts the panel.
|
|
176
|
+
*/
|
|
177
|
+
function snapDown(size: number, constraint: SplitterConstraint): number {
|
|
178
|
+
'worklet';
|
|
179
|
+
if (size >= constraint.minSize) return size;
|
|
180
|
+
if (!constraint.collapsible) return constraint.minSize;
|
|
181
|
+
return size < (constraint.collapsedSize + constraint.minSize) / 2
|
|
182
|
+
? constraint.collapsedSize
|
|
183
|
+
: constraint.minSize;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Moves one seam, in percent, and hands back the whole layout.
|
|
188
|
+
*
|
|
189
|
+
* Only the two panels either side of the seam change: a drag borrows from its
|
|
190
|
+
* neighbour and nobody else, so the panels further along stay exactly where the
|
|
191
|
+
* reader left them.
|
|
192
|
+
*
|
|
193
|
+
* Collapsing is only ever offered to the panel being shrunk. Both sides could
|
|
194
|
+
* technically be past their minimum at once at the end of a long drag, and
|
|
195
|
+
* shutting the panel that is currently growing is never what the drag meant.
|
|
196
|
+
*/
|
|
197
|
+
export function resizeLayout(
|
|
198
|
+
layout: number[],
|
|
199
|
+
boundary: number,
|
|
200
|
+
delta: number,
|
|
201
|
+
constraints: SplitterConstraint[]
|
|
202
|
+
): number[] {
|
|
203
|
+
'worklet';
|
|
204
|
+
const next = layout.slice();
|
|
205
|
+
if (boundary < 0 || boundary + 1 >= next.length) return next;
|
|
206
|
+
if (!Number.isFinite(delta)) return next;
|
|
207
|
+
|
|
208
|
+
const first = constraints[boundary];
|
|
209
|
+
const second = constraints[boundary + 1];
|
|
210
|
+
if (!first || !second) return next;
|
|
211
|
+
|
|
212
|
+
const pair = next[boundary]! + next[boundary + 1]!;
|
|
213
|
+
let intended = next[boundary]! + delta;
|
|
214
|
+
|
|
215
|
+
if (delta < 0) {
|
|
216
|
+
intended = snapDown(intended, first);
|
|
217
|
+
} else if (delta > 0) {
|
|
218
|
+
intended = pair - snapDown(pair - intended, second);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const low = Math.max(panelFloor(first), pair - second.maxSize);
|
|
222
|
+
const high = Math.min(first.maxSize, pair - panelFloor(second));
|
|
223
|
+
if (low > high) return next;
|
|
224
|
+
|
|
225
|
+
next[boundary] = clamp(intended, low, high);
|
|
226
|
+
next[boundary + 1] = pair - next[boundary]!;
|
|
227
|
+
return next;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Where a seam sits, in percent, measured from the start of the splitter. */
|
|
231
|
+
export function layoutOffset(layout: number[], boundary: number): number {
|
|
232
|
+
'worklet';
|
|
233
|
+
let offset = 0;
|
|
234
|
+
for (let index = 0; index <= boundary && index < layout.length; index += 1) {
|
|
235
|
+
offset += layout[index]!;
|
|
236
|
+
}
|
|
237
|
+
return offset;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Whether a panel is currently shut rather than merely small. */
|
|
241
|
+
export function isCollapsed(size: number, constraint: SplitterConstraint): boolean {
|
|
242
|
+
'worklet';
|
|
243
|
+
return constraint.collapsible && size <= constraint.collapsedSize + 1e-6;
|
|
244
|
+
}
|
|
@@ -38,12 +38,19 @@ import {
|
|
|
38
38
|
useState,
|
|
39
39
|
type ReactNode,
|
|
40
40
|
} from 'react';
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
Pressable,
|
|
43
|
+
View,
|
|
44
|
+
type PressableProps,
|
|
45
|
+
type Text as RNText,
|
|
46
|
+
type ViewProps,
|
|
47
|
+
} from 'react-native';
|
|
42
48
|
import { tv } from 'tailwind-variants';
|
|
43
49
|
import { useCSSVariable } from 'uniwind';
|
|
44
50
|
import { CheckIcon } from '../../icons';
|
|
45
51
|
import { Text, type TextProps, textChildren } from '../../primitives/text';
|
|
46
52
|
import { Spinner } from '../spinner';
|
|
53
|
+
import { stepsTriggerDisabled } from './steps-trigger';
|
|
47
54
|
|
|
48
55
|
export type StepState = 'active' | 'completed' | 'inactive' | 'loading';
|
|
49
56
|
export type StepsOrientation = 'horizontal' | 'vertical';
|
|
@@ -293,7 +300,7 @@ const StepsItem = forwardRef<View, StepsItemProps>(
|
|
|
293
300
|
);
|
|
294
301
|
StepsItem.displayName = 'Steps.Item';
|
|
295
302
|
|
|
296
|
-
export interface StepsTriggerProps extends
|
|
303
|
+
export interface StepsTriggerProps extends PressableProps {
|
|
297
304
|
className?: string;
|
|
298
305
|
children?: ReactNode;
|
|
299
306
|
}
|
|
@@ -308,11 +315,23 @@ const STATE_WORDS: Record<StepState, string> = {
|
|
|
308
315
|
|
|
309
316
|
/** Makes its item selectable. Omit it for a read-only stepper. */
|
|
310
317
|
const StepsTrigger = forwardRef<View, StepsTriggerProps>(
|
|
311
|
-
(
|
|
318
|
+
(
|
|
319
|
+
{
|
|
320
|
+
className,
|
|
321
|
+
children,
|
|
322
|
+
disabled,
|
|
323
|
+
onPress,
|
|
324
|
+
accessibilityState,
|
|
325
|
+
accessibilityValue,
|
|
326
|
+
...props
|
|
327
|
+
},
|
|
328
|
+
ref
|
|
329
|
+
) => {
|
|
312
330
|
const { setActiveStep, orientation } = useSteps('Steps.Trigger');
|
|
313
331
|
const { step, state, isDisabled } = useStepItem('Steps.Trigger');
|
|
314
332
|
const placement = useContext(StepPositionContext);
|
|
315
|
-
const
|
|
333
|
+
const triggerDisabled = stepsTriggerDisabled(isDisabled, disabled);
|
|
334
|
+
const { trigger } = stepsVariants({ orientation, state, isDisabled: triggerDisabled });
|
|
316
335
|
|
|
317
336
|
/*
|
|
318
337
|
* Said as a value rather than a label, because the label is the step's own
|
|
@@ -327,13 +346,20 @@ const StepsTrigger = forwardRef<View, StepsTriggerProps>(
|
|
|
327
346
|
return (
|
|
328
347
|
<Pressable
|
|
329
348
|
ref={ref}
|
|
349
|
+
{...props}
|
|
330
350
|
accessibilityRole="button"
|
|
331
|
-
accessibilityState={{
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
351
|
+
accessibilityState={{
|
|
352
|
+
...accessibilityState,
|
|
353
|
+
disabled: triggerDisabled,
|
|
354
|
+
selected: state === 'active',
|
|
355
|
+
}}
|
|
356
|
+
accessibilityValue={{ ...accessibilityValue, text: position }}
|
|
357
|
+
disabled={triggerDisabled}
|
|
358
|
+
onPress={(event) => {
|
|
359
|
+
onPress?.(event);
|
|
360
|
+
setActiveStep(step);
|
|
361
|
+
}}
|
|
335
362
|
className={trigger({ className })}
|
|
336
|
-
{...props}
|
|
337
363
|
>
|
|
338
364
|
{textChildren(children)}
|
|
339
365
|
</Pressable>
|
|
@@ -414,10 +414,10 @@ const TableRow = forwardRef<View, TableRowProps>(
|
|
|
414
414
|
return (
|
|
415
415
|
<View
|
|
416
416
|
ref={ref}
|
|
417
|
+
{...(props as ViewProps)}
|
|
417
418
|
role="row"
|
|
418
419
|
accessibilityState={{ disabled: !!disabled, selected: !!selected }}
|
|
419
420
|
className={classes}
|
|
420
|
-
{...(props as ViewProps)}
|
|
421
421
|
>
|
|
422
422
|
{cells}
|
|
423
423
|
</View>
|
|
@@ -427,6 +427,7 @@ const TableRow = forwardRef<View, TableRowProps>(
|
|
|
427
427
|
return (
|
|
428
428
|
<AnimatedPressable
|
|
429
429
|
ref={ref}
|
|
430
|
+
{...props}
|
|
430
431
|
// A row you can act on is a button first: announcing it as a row would
|
|
431
432
|
// hide the fact that it does anything.
|
|
432
433
|
accessibilityRole="button"
|
|
@@ -434,7 +435,6 @@ const TableRow = forwardRef<View, TableRowProps>(
|
|
|
434
435
|
disabled={disabled}
|
|
435
436
|
onPress={onPress}
|
|
436
437
|
className={classes}
|
|
437
|
-
{...props}
|
|
438
438
|
>
|
|
439
439
|
{cells}
|
|
440
440
|
</AnimatedPressable>
|
|
@@ -64,6 +64,7 @@ import { Text } from '../../primitives/text';
|
|
|
64
64
|
import { selectionTick } from '../../utils/haptics';
|
|
65
65
|
import { Chip, type ChipSize, type ChipVariant } from '../chip';
|
|
66
66
|
import { Label } from '../label';
|
|
67
|
+
import { reconcileMarkedTag, type MarkedTag } from './tag-input-state';
|
|
67
68
|
|
|
68
69
|
/** Matches Input and Combobox, so the three read as the same control focused. */
|
|
69
70
|
const FOCUS_DURATION = 150;
|
|
@@ -298,18 +299,18 @@ export const TagInput = forwardRef<TextInput, TagInputProps>(
|
|
|
298
299
|
* itself, because with `allowDuplicates` two tags can read the same and
|
|
299
300
|
* marking "the last one" has to mean the last one.
|
|
300
301
|
*/
|
|
301
|
-
const [marked, setMarked] = useState<
|
|
302
|
+
const [marked, setMarked] = useState<MarkedTag | null>(null);
|
|
302
303
|
|
|
303
304
|
const overflowing = max !== undefined && tags.length > max;
|
|
304
305
|
const invalid = !!errorMessage || overflowing;
|
|
305
306
|
const interactive = !disabled && !readOnly;
|
|
306
307
|
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
308
|
+
// A controlled list can be replaced or reordered between the two
|
|
309
|
+
// backspaces. Keep the mark only while the exact tag it armed still owns
|
|
310
|
+
// that slot, so a key press aimed at the old value cannot delete the new one.
|
|
310
311
|
useEffect(() => {
|
|
311
|
-
|
|
312
|
-
}, [
|
|
312
|
+
setMarked((current) => reconcileMarkedTag(current, tags));
|
|
313
|
+
}, [tags]);
|
|
313
314
|
|
|
314
315
|
const setText = useCallback(
|
|
315
316
|
(next: string) => {
|
|
@@ -418,14 +419,15 @@ export const TagInput = forwardRef<TextInput, TagInputProps>(
|
|
|
418
419
|
// what it always means, and the tags are none of its business.
|
|
419
420
|
if (text.length > 0 || tags.length === 0) return;
|
|
420
421
|
|
|
421
|
-
if (marked !== null && marked
|
|
422
|
-
removeAt(marked);
|
|
422
|
+
if (marked !== null && reconcileMarkedTag(marked, tags)) {
|
|
423
|
+
removeAt(marked.index);
|
|
423
424
|
setMarked(null);
|
|
424
425
|
return;
|
|
425
426
|
}
|
|
426
|
-
|
|
427
|
+
const index = tags.length - 1;
|
|
428
|
+
setMarked({ index, tag: tags[index]! });
|
|
427
429
|
},
|
|
428
|
-
[onKeyPress, text.length, tags
|
|
430
|
+
[onKeyPress, text.length, tags, marked, removeAt]
|
|
429
431
|
);
|
|
430
432
|
|
|
431
433
|
const handleFocus = useCallback<NonNullable<TextInputProps['onFocus']>>(
|
|
@@ -541,7 +543,7 @@ export const TagInput = forwardRef<TextInput, TagInputProps>(
|
|
|
541
543
|
// The marked tag turns destructive rather than growing an
|
|
542
544
|
// outline: it is about to be deleted, and that is the one
|
|
543
545
|
// colour in the theme that already means exactly that.
|
|
544
|
-
variant={marked === index ? 'destructive' : chipVariant}
|
|
546
|
+
variant={marked?.index === index ? 'destructive' : chipVariant}
|
|
545
547
|
onClose={interactive ? () => removeAt(index) : undefined}
|
|
546
548
|
closeLabel={`Remove ${tag}`}
|
|
547
549
|
haptics={haptics}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The exact slot a first Backspace armed for removal. */
|
|
2
|
+
export interface MarkedTag {
|
|
3
|
+
index: number;
|
|
4
|
+
tag: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Keeps an armed removal only while the same tag still occupies the same slot.
|
|
9
|
+
* Controlled fields can replace or reorder their value between key presses;
|
|
10
|
+
* a mark from the old value must never authorize deleting the new one.
|
|
11
|
+
*/
|
|
12
|
+
export function reconcileMarkedTag(
|
|
13
|
+
marked: MarkedTag | null,
|
|
14
|
+
tags: readonly string[]
|
|
15
|
+
): MarkedTag | null {
|
|
16
|
+
if (marked === null) return null;
|
|
17
|
+
return tags[marked.index] === marked.tag ? marked : null;
|
|
18
|
+
}
|
|
@@ -39,6 +39,7 @@ import type { KeyboardAvoidanceMode } from '../../hooks/use-keyboard-avoidance';
|
|
|
39
39
|
import { KeyboardAvoider } from '../../primitives/keyboard-avoider';
|
|
40
40
|
import { Text } from '../../primitives/text';
|
|
41
41
|
import { Label } from '../label';
|
|
42
|
+
import { textareaIsDisabled } from './textarea-disabled';
|
|
42
43
|
|
|
43
44
|
/** Long enough to read as a transition, short enough not to lag a fast tab. */
|
|
44
45
|
const FOCUS_DURATION = 150;
|
|
@@ -183,6 +184,8 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
|
|
|
183
184
|
onFocus,
|
|
184
185
|
onBlur,
|
|
185
186
|
onContentSizeChange,
|
|
187
|
+
editable,
|
|
188
|
+
accessibilityState,
|
|
186
189
|
style,
|
|
187
190
|
...props
|
|
188
191
|
},
|
|
@@ -190,6 +193,7 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
|
|
|
190
193
|
) => {
|
|
191
194
|
const [focused, setFocused] = useState(false);
|
|
192
195
|
const invalid = !!errorMessage;
|
|
196
|
+
const isDisabled = textareaIsDisabled(disabled, editable);
|
|
193
197
|
|
|
194
198
|
const metrics = METRICS[size];
|
|
195
199
|
/* Both paddings plus the lines themselves — the box a row count asks for. */
|
|
@@ -225,7 +229,7 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
|
|
|
225
229
|
const slots = textareaVariants({
|
|
226
230
|
variant,
|
|
227
231
|
size,
|
|
228
|
-
disabled:
|
|
232
|
+
disabled: isDisabled,
|
|
229
233
|
over,
|
|
230
234
|
});
|
|
231
235
|
|
|
@@ -320,19 +324,19 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
|
|
|
320
324
|
const body = (
|
|
321
325
|
<>
|
|
322
326
|
{label ? (
|
|
323
|
-
<Label isRequired={isRequired} isInvalid={invalid} isDisabled={
|
|
327
|
+
<Label isRequired={isRequired} isInvalid={invalid} isDisabled={isDisabled}>
|
|
324
328
|
{label}
|
|
325
329
|
</Label>
|
|
326
330
|
) : null}
|
|
327
331
|
<AnimatedTextInput
|
|
328
332
|
ref={ref}
|
|
329
333
|
multiline
|
|
330
|
-
editable={!
|
|
334
|
+
editable={!isDisabled}
|
|
331
335
|
onFocus={handleFocus}
|
|
332
336
|
onBlur={handleBlur}
|
|
333
337
|
onContentSizeChange={handleContentSizeChange}
|
|
334
338
|
accessibilityLabel={label}
|
|
335
|
-
accessibilityState={{ disabled:
|
|
339
|
+
accessibilityState={{ ...accessibilityState, disabled: isDisabled }}
|
|
336
340
|
aria-required={isRequired}
|
|
337
341
|
aria-invalid={invalid}
|
|
338
342
|
className={slots.field({ className })}
|