@kanso-labs/react-native-kanso-ui 0.8.0 → 0.10.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 +4 -3
- package/dist/autocomplete/index.js +15 -1
- package/dist/autocomplete/index.js.map +1 -1
- package/dist/badge/index.js +9 -70
- package/dist/badge/index.js.map +1 -1
- package/dist/button/group.d.ts +5 -0
- package/dist/button/group.js +115 -0
- package/dist/button/group.js.map +1 -0
- package/dist/button/index.d.ts +4 -1
- package/dist/button/index.js +45 -23
- package/dist/button/index.js.map +1 -1
- package/dist/button/palettes.d.ts +2 -0
- package/dist/button/slots.js +2 -1
- package/dist/button/slots.js.map +1 -1
- package/dist/calendar/index.js +351 -182
- package/dist/calendar/index.js.map +1 -1
- package/dist/calendar/styles.js +33 -2
- package/dist/calendar/styles.js.map +1 -1
- package/dist/carousel/arrangement.d.ts +5 -0
- package/dist/carousel/arrangement.js +123 -0
- package/dist/carousel/arrangement.js.map +1 -0
- package/dist/chequer/index.js +95 -0
- package/dist/chequer/index.js.map +1 -0
- package/dist/collection/index.d.ts +157 -0
- package/dist/collection/index.js +1331 -0
- package/dist/collection/index.js.map +1 -0
- package/dist/color-thumb/index.js +56 -0
- package/dist/color-thumb/index.js.map +1 -0
- package/dist/components/badge/index.d.ts +48 -0
- package/dist/components/badge/index.js +122 -0
- package/dist/components/badge/index.js.map +1 -0
- package/dist/{badge → components/badge}/styles.js +10 -6
- package/dist/components/badge/styles.js.map +1 -0
- package/dist/components/breadcrumbs/index.d.ts +66 -0
- package/dist/components/breadcrumbs/index.js +285 -0
- package/dist/components/breadcrumbs/index.js.map +1 -0
- package/dist/components/button/index.d.ts +4 -2
- package/dist/components/button/index.js +227 -188
- package/dist/components/button/index.js.map +1 -1
- package/dist/components/button-group/index.d.ts +87 -0
- package/dist/components/button-group/index.js +275 -0
- package/dist/components/button-group/index.js.map +1 -0
- package/dist/components/calendar/index.d.ts +11 -3
- package/dist/components/calendar/index.js +60 -45
- package/dist/components/calendar/index.js.map +1 -1
- package/dist/components/carousel/index.d.ts +72 -0
- package/dist/components/carousel/index.js +496 -0
- package/dist/components/carousel/index.js.map +1 -0
- package/dist/components/checkbox/index.d.ts +7 -1
- package/dist/components/checkbox/index.js +183 -138
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/checkbox-group/index.d.ts +92 -0
- package/dist/components/checkbox-group/index.js +306 -0
- package/dist/components/checkbox-group/index.js.map +1 -0
- package/dist/components/code/index.d.ts +25 -0
- package/dist/components/code/index.js +87 -0
- package/dist/components/code/index.js.map +1 -0
- package/dist/components/color-area/index.d.ts +85 -0
- package/dist/components/color-area/index.js +630 -0
- package/dist/components/color-area/index.js.map +1 -0
- package/dist/components/color-field/index.d.ts +144 -0
- package/dist/components/color-field/index.js +516 -0
- package/dist/components/color-field/index.js.map +1 -0
- package/dist/components/color-picker/index.d.ts +74 -0
- package/dist/components/color-picker/index.js +317 -0
- package/dist/components/color-picker/index.js.map +1 -0
- package/dist/components/color-slider/index.d.ts +82 -0
- package/dist/components/color-slider/index.js +456 -0
- package/dist/components/color-slider/index.js.map +1 -0
- package/dist/components/color-swatch/index.d.ts +46 -0
- package/dist/components/color-swatch/index.js +127 -0
- package/dist/components/color-swatch/index.js.map +1 -0
- package/dist/components/color-swatch-picker/index.d.ts +81 -0
- package/dist/components/color-swatch-picker/index.js +287 -0
- package/dist/components/color-swatch-picker/index.js.map +1 -0
- package/dist/components/color-wheel/index.d.ts +71 -0
- package/dist/components/color-wheel/index.js +407 -0
- package/dist/components/color-wheel/index.js.map +1 -0
- package/dist/components/combo-box/index.d.ts +193 -0
- package/dist/components/combo-box/index.js +616 -0
- package/dist/components/combo-box/index.js.map +1 -0
- package/dist/components/container/index.d.ts +47 -0
- package/dist/components/container/index.js +85 -0
- package/dist/components/container/index.js.map +1 -0
- package/dist/components/date-field/index.d.ts +120 -0
- package/dist/components/date-field/index.js +111 -0
- package/dist/components/date-field/index.js.map +1 -0
- package/dist/components/date-picker/index.d.ts +130 -0
- package/dist/components/date-picker/index.js +310 -0
- package/dist/components/date-picker/index.js.map +1 -0
- package/dist/components/date-range-picker/index.d.ts +139 -0
- package/dist/components/date-range-picker/index.js +421 -0
- package/dist/components/date-range-picker/index.js.map +1 -0
- package/dist/components/disclosure/index.d.ts +94 -0
- package/dist/components/disclosure/index.js +365 -0
- package/dist/components/disclosure/index.js.map +1 -0
- package/dist/components/disclosure-group/index.d.ts +61 -0
- package/dist/components/disclosure-group/index.js +100 -0
- package/dist/components/disclosure-group/index.js.map +1 -0
- package/dist/components/drop-zone/index.d.ts +113 -0
- package/dist/components/drop-zone/index.js +304 -0
- package/dist/components/drop-zone/index.js.map +1 -0
- package/dist/components/fab/index.d.ts +80 -0
- package/dist/components/fab/index.js +246 -0
- package/dist/components/fab/index.js.map +1 -0
- package/dist/components/fab-menu/index.d.ts +120 -0
- package/dist/components/fab-menu/index.js +640 -0
- package/dist/components/fab-menu/index.js.map +1 -0
- package/dist/components/feed/index.d.ts +57 -0
- package/dist/components/feed/index.js +167 -0
- package/dist/components/feed/index.js.map +1 -0
- package/dist/components/form/index.d.ts +3 -2
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/icon-button/index.d.ts +4 -2
- package/dist/components/icon-button/index.js +204 -171
- package/dist/components/icon-button/index.js.map +1 -1
- package/dist/components/keycap/index.d.ts +27 -0
- package/dist/components/keycap/index.js +136 -0
- package/dist/components/keycap/index.js.map +1 -0
- package/dist/components/list/index.d.ts +15 -158
- package/dist/components/list/index.js +21 -1047
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/list-box/index.d.ts +73 -0
- package/dist/components/list-box/index.js +82 -0
- package/dist/components/list-box/index.js.map +1 -0
- package/dist/components/loading-indicator/index.d.ts +32 -0
- package/dist/components/loading-indicator/index.js +174 -0
- package/dist/components/loading-indicator/index.js.map +1 -0
- package/dist/components/menu/index.js +1 -1
- package/dist/components/navigation-tree/index.d.ts +117 -0
- package/dist/components/navigation-tree/index.js +135 -0
- package/dist/components/navigation-tree/index.js.map +1 -0
- package/dist/components/number-field/index.js +132 -143
- package/dist/components/number-field/index.js.map +1 -1
- package/dist/components/range-calendar/index.d.ts +69 -0
- package/dist/components/range-calendar/index.js +144 -0
- package/dist/components/range-calendar/index.js.map +1 -0
- package/dist/components/search-view/index.d.ts +78 -0
- package/dist/components/search-view/index.js +570 -0
- package/dist/components/search-view/index.js.map +1 -0
- package/dist/components/select/index.d.ts +152 -0
- package/dist/components/select/index.js +419 -0
- package/dist/components/select/index.js.map +1 -0
- package/dist/components/separator/index.d.ts +3 -2
- package/dist/components/separator/index.js +29 -26
- package/dist/components/separator/index.js.map +1 -1
- package/dist/components/slider/index.d.ts +111 -0
- package/dist/components/slider/index.js +673 -0
- package/dist/components/slider/index.js.map +1 -0
- package/dist/components/split-button/index.d.ts +77 -0
- package/dist/components/split-button/index.js +354 -0
- package/dist/components/split-button/index.js.map +1 -0
- package/dist/components/supporting-pane/index.d.ts +44 -0
- package/dist/components/supporting-pane/index.js +109 -0
- package/dist/components/supporting-pane/index.js.map +1 -0
- package/dist/components/tabs/index.d.ts +196 -0
- package/dist/components/tabs/index.js +1137 -0
- package/dist/components/tabs/index.js.map +1 -0
- package/dist/components/text/index.d.ts +1 -1
- package/dist/components/text/index.js +49 -21
- package/dist/components/text/index.js.map +1 -1
- package/dist/components/text-area/index.d.ts +145 -0
- package/dist/components/text-area/index.js +231 -0
- package/dist/components/text-area/index.js.map +1 -0
- package/dist/components/time-field/index.d.ts +43 -0
- package/dist/components/time-field/index.js +107 -0
- package/dist/components/time-field/index.js.map +1 -0
- package/dist/components/time-picker/index.d.ts +65 -0
- package/dist/components/time-picker/index.js +1168 -0
- package/dist/components/time-picker/index.js.map +1 -0
- package/dist/components/toolbar/index.d.ts +53 -0
- package/dist/components/toolbar/index.js +185 -0
- package/dist/components/toolbar/index.js.map +1 -0
- package/dist/components/tooltip/index.d.ts +101 -0
- package/dist/components/tooltip/index.js +223 -0
- package/dist/components/tooltip/index.js.map +1 -0
- package/dist/components/tree/index.d.ts +112 -0
- package/dist/components/tree/index.js +157 -0
- package/dist/components/tree/index.js.map +1 -0
- package/dist/control/group.js +23 -0
- package/dist/control/group.js.map +1 -0
- package/dist/disclosure/chevron.js +75 -0
- package/dist/disclosure/chevron.js.map +1 -0
- package/dist/disclosure/index.js +7 -0
- package/dist/disclosure/index.js.map +1 -0
- package/dist/drop/index.d.ts +54 -0
- package/dist/drop/index.js +161 -0
- package/dist/drop/index.js.map +1 -0
- package/dist/fab/index.d.ts +19 -0
- package/dist/fab/index.js +147 -0
- package/dist/fab/index.js.map +1 -0
- package/dist/field/chevron.js +74 -0
- package/dist/field/chevron.js.map +1 -0
- package/dist/field/index.d.ts +2 -1
- package/dist/field/index.js +350 -153
- package/dist/field/index.js.map +1 -1
- package/dist/field/styles.js +27 -0
- package/dist/field/styles.js.map +1 -1
- package/dist/files/index.d.ts +18 -0
- package/dist/files/index.js +107 -0
- package/dist/files/index.js.map +1 -0
- package/dist/form/index.d.ts +6 -4
- package/dist/form/index.js +64 -85
- package/dist/form/index.js.map +1 -1
- package/dist/glyphs/index.js +92 -1
- package/dist/glyphs/index.js.map +1 -1
- package/dist/i18n/index.js +20 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/messages.js +1394 -34
- package/dist/i18n/messages.js.map +1 -1
- package/dist/index.d.ts +51 -6
- package/dist/index.js +47 -4
- package/dist/indicator/paths.js +23 -1
- package/dist/indicator/paths.js.map +1 -1
- package/dist/modal/index.js +96 -68
- package/dist/modal/index.js.map +1 -1
- package/dist/navigation/index.js +8 -5
- package/dist/navigation/index.js.map +1 -1
- package/dist/number/keyboard.js +16 -0
- package/dist/number/keyboard.js.map +1 -0
- package/dist/overlay/anchored.js +68 -38
- package/dist/overlay/anchored.js.map +1 -1
- package/dist/overlay/keyboard.js +57 -0
- package/dist/overlay/keyboard.js.map +1 -0
- package/dist/overlay/panel.js +173 -19
- package/dist/overlay/panel.js.map +1 -1
- package/dist/overlay/placement.js +16 -2
- package/dist/overlay/placement.js.map +1 -1
- package/dist/picker/index.js +322 -0
- package/dist/picker/index.js.map +1 -0
- package/dist/press/index.d.ts +2 -0
- package/dist/press/index.js +102 -100
- package/dist/press/index.js.map +1 -1
- package/dist/row/index.js +77 -69
- package/dist/row/index.js.map +1 -1
- package/dist/row/styles.js +13 -2
- package/dist/row/styles.js.map +1 -1
- package/dist/segments/field.js +277 -0
- package/dist/segments/field.js.map +1 -0
- package/dist/segments/index.js +574 -0
- package/dist/segments/index.js.map +1 -0
- package/dist/segments/styles.js +24 -0
- package/dist/segments/styles.js.map +1 -0
- package/dist/separator/index.js +7 -0
- package/dist/separator/index.js.map +1 -0
- package/dist/slider/index.js +145 -0
- package/dist/slider/index.js.map +1 -0
- package/dist/swatch/index.js +56 -0
- package/dist/swatch/index.js.map +1 -0
- package/dist/typography/size.js +14 -0
- package/dist/typography/size.js.map +1 -0
- package/package.json +11 -2
- package/dist/badge/styles.js.map +0 -1
package/dist/overlay/panel.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useTheme } from "../theme/scope.js";
|
|
2
2
|
import { c } from "react/compiler-runtime";
|
|
3
|
-
import { AccessibilityInfo, BackHandler, Platform, Pressable, StyleSheet } from "react-native";
|
|
3
|
+
import { AccessibilityInfo, BackHandler, I18nManager, Platform, Pressable, StyleSheet, View } from "react-native";
|
|
4
4
|
import { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
|
|
5
5
|
import { useEffect, useRef } from "react";
|
|
6
|
-
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/overlay/panel.tsx
|
|
8
8
|
const SCALE_FROM = .95;
|
|
9
9
|
/** The edge a panel grows from, the one facing its anchor. */
|
|
@@ -13,39 +13,181 @@ const PANEL_ORIGINS = {
|
|
|
13
13
|
right: "left",
|
|
14
14
|
top: "bottom"
|
|
15
15
|
};
|
|
16
|
-
const styles = StyleSheet.create({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const styles = StyleSheet.create({
|
|
17
|
+
layer: {
|
|
18
|
+
bottom: 0,
|
|
19
|
+
left: 0,
|
|
20
|
+
position: "absolute",
|
|
21
|
+
right: 0,
|
|
22
|
+
top: 0
|
|
23
|
+
},
|
|
24
|
+
layerAround: {
|
|
25
|
+
bottom: 0,
|
|
26
|
+
left: 0,
|
|
27
|
+
pointerEvents: "box-none",
|
|
28
|
+
position: "absolute",
|
|
29
|
+
right: 0,
|
|
30
|
+
top: 0
|
|
31
|
+
}
|
|
32
|
+
});
|
|
23
33
|
/**
|
|
24
34
|
* The layer a press outside a non-modal panel lands on, drawn just before the
|
|
25
35
|
* panel so it sits under it. The press goes no further, so a press on the
|
|
26
|
-
* trigger closes the panel rather than opening it again, as on web.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
36
|
+
* trigger closes the panel rather than opening it again, as on web.
|
|
37
|
+
*
|
|
38
|
+
* Given a `hole`, the layer is drawn around that frame rather than over it,
|
|
39
|
+
* so a press there reaches the screen under it and leaves the panel open: a
|
|
40
|
+
* combo box's box, where a tap places the caret in the text being typed, as
|
|
41
|
+
* a click in a combo box's input leaves its list open on web.
|
|
29
42
|
*/
|
|
30
43
|
function DismissLayer(t0) {
|
|
31
|
-
const $ = c(
|
|
32
|
-
const { onPress } = t0;
|
|
44
|
+
const $ = c(21);
|
|
45
|
+
const { hole, onPress } = t0;
|
|
46
|
+
if (hole === void 0) {
|
|
47
|
+
let t1;
|
|
48
|
+
if ($[0] !== onPress) {
|
|
49
|
+
t1 = /* @__PURE__ */ jsx(LayerPart, {
|
|
50
|
+
onPress,
|
|
51
|
+
style: styles.layer
|
|
52
|
+
});
|
|
53
|
+
$[0] = onPress;
|
|
54
|
+
$[1] = t1;
|
|
55
|
+
} else t1 = $[1];
|
|
56
|
+
return t1;
|
|
57
|
+
}
|
|
58
|
+
let t1;
|
|
59
|
+
if ($[2] !== hole) {
|
|
60
|
+
t1 = partsAround(hole);
|
|
61
|
+
$[2] = hole;
|
|
62
|
+
$[3] = t1;
|
|
63
|
+
} else t1 = $[3];
|
|
64
|
+
const parts = t1;
|
|
65
|
+
let t2;
|
|
66
|
+
if ($[4] !== onPress || $[5] !== parts.above) {
|
|
67
|
+
t2 = /* @__PURE__ */ jsx(LayerPart, {
|
|
68
|
+
onPress,
|
|
69
|
+
style: parts.above
|
|
70
|
+
});
|
|
71
|
+
$[4] = onPress;
|
|
72
|
+
$[5] = parts.above;
|
|
73
|
+
$[6] = t2;
|
|
74
|
+
} else t2 = $[6];
|
|
75
|
+
let t3;
|
|
76
|
+
if ($[7] !== onPress || $[8] !== parts.below) {
|
|
77
|
+
t3 = /* @__PURE__ */ jsx(LayerPart, {
|
|
78
|
+
onPress,
|
|
79
|
+
style: parts.below
|
|
80
|
+
});
|
|
81
|
+
$[7] = onPress;
|
|
82
|
+
$[8] = parts.below;
|
|
83
|
+
$[9] = t3;
|
|
84
|
+
} else t3 = $[9];
|
|
85
|
+
let t4;
|
|
86
|
+
if ($[10] !== onPress || $[11] !== parts.left) {
|
|
87
|
+
t4 = /* @__PURE__ */ jsx(LayerPart, {
|
|
88
|
+
onPress,
|
|
89
|
+
style: parts.left
|
|
90
|
+
});
|
|
91
|
+
$[10] = onPress;
|
|
92
|
+
$[11] = parts.left;
|
|
93
|
+
$[12] = t4;
|
|
94
|
+
} else t4 = $[12];
|
|
95
|
+
let t5;
|
|
96
|
+
if ($[13] !== onPress || $[14] !== parts.right) {
|
|
97
|
+
t5 = /* @__PURE__ */ jsx(LayerPart, {
|
|
98
|
+
onPress,
|
|
99
|
+
style: parts.right
|
|
100
|
+
});
|
|
101
|
+
$[13] = onPress;
|
|
102
|
+
$[14] = parts.right;
|
|
103
|
+
$[15] = t5;
|
|
104
|
+
} else t5 = $[15];
|
|
105
|
+
let t6;
|
|
106
|
+
if ($[16] !== t2 || $[17] !== t3 || $[18] !== t4 || $[19] !== t5) {
|
|
107
|
+
t6 = /* @__PURE__ */ jsxs(View, {
|
|
108
|
+
style: styles.layerAround,
|
|
109
|
+
children: [
|
|
110
|
+
t2,
|
|
111
|
+
t3,
|
|
112
|
+
t4,
|
|
113
|
+
t5
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
$[16] = t2;
|
|
117
|
+
$[17] = t3;
|
|
118
|
+
$[18] = t4;
|
|
119
|
+
$[19] = t5;
|
|
120
|
+
$[20] = t6;
|
|
121
|
+
} else t6 = $[20];
|
|
122
|
+
return t6;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* One piece of the layer. Nothing but a press reaches it: React Native Web's
|
|
126
|
+
* Pressable is a tab stop unless told otherwise, whatever `focusable` says.
|
|
127
|
+
*/
|
|
128
|
+
function LayerPart(t0) {
|
|
129
|
+
const $ = c(3);
|
|
130
|
+
const { onPress, style } = t0;
|
|
33
131
|
let t1;
|
|
34
|
-
if ($[0] !== onPress) {
|
|
132
|
+
if ($[0] !== onPress || $[1] !== style) {
|
|
35
133
|
t1 = /* @__PURE__ */ jsx(Pressable, {
|
|
36
134
|
accessible: false,
|
|
37
135
|
"aria-hidden": true,
|
|
38
136
|
focusable: false,
|
|
39
137
|
onPress,
|
|
40
|
-
style
|
|
138
|
+
style,
|
|
41
139
|
tabIndex: -1
|
|
42
140
|
});
|
|
43
141
|
$[0] = onPress;
|
|
44
|
-
$[1] =
|
|
45
|
-
|
|
142
|
+
$[1] = style;
|
|
143
|
+
$[2] = t1;
|
|
144
|
+
} else t1 = $[2];
|
|
46
145
|
return t1;
|
|
47
146
|
}
|
|
48
147
|
/**
|
|
148
|
+
* The four pieces of a layer drawn around a frame: the bands above and below
|
|
149
|
+
* it, across the whole layer, and the two beside it, as tall as it. The frame
|
|
150
|
+
* is in physical coordinates, as `measureInWindow` reports it, and React
|
|
151
|
+
* Native swaps `left` and `right` in a right-to-left layout unless an app
|
|
152
|
+
* turns that off, so there each side is set through the other's name.
|
|
153
|
+
*/
|
|
154
|
+
function partsAround({ height, width, x, y }) {
|
|
155
|
+
const swapped = I18nManager.isRTL && I18nManager.doLeftAndRightSwapInRTL;
|
|
156
|
+
const left = swapped ? "right" : "left";
|
|
157
|
+
const right = swapped ? "left" : "right";
|
|
158
|
+
const beside = {
|
|
159
|
+
height,
|
|
160
|
+
position: "absolute",
|
|
161
|
+
top: y
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
above: {
|
|
165
|
+
height: Math.max(0, y),
|
|
166
|
+
left: 0,
|
|
167
|
+
position: "absolute",
|
|
168
|
+
right: 0,
|
|
169
|
+
top: 0
|
|
170
|
+
},
|
|
171
|
+
below: {
|
|
172
|
+
bottom: 0,
|
|
173
|
+
left: 0,
|
|
174
|
+
position: "absolute",
|
|
175
|
+
right: 0,
|
|
176
|
+
top: y + height
|
|
177
|
+
},
|
|
178
|
+
left: {
|
|
179
|
+
...beside,
|
|
180
|
+
[left]: 0,
|
|
181
|
+
width: Math.max(0, x)
|
|
182
|
+
},
|
|
183
|
+
right: {
|
|
184
|
+
...beside,
|
|
185
|
+
[left]: x + width,
|
|
186
|
+
[right]: 0
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
49
191
|
* Closes a non-modal panel from Android's back gesture, as a `Modal` closes a
|
|
50
192
|
* modal one. React Native calls the latest listener first, so a panel opened
|
|
51
193
|
* from another closes before the one it came from. React Native Web has no
|
|
@@ -111,6 +253,14 @@ function useFocusOnPlaced(ready, target) {
|
|
|
111
253
|
* until then. Under reduced motion it arrives and leaves at once.
|
|
112
254
|
*/
|
|
113
255
|
function usePanelMotion(isOpen, placed, setPresent) {
|
|
256
|
+
return usePanelStyle(usePanelShown(isOpen, placed, setPresent));
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* How far a panel has arrived, from 0 to 1, on `usePanelMotion`'s timing,
|
|
260
|
+
* for a panel that moves something besides itself with it, as a picker's
|
|
261
|
+
* scrim fades with the surface it is cast by.
|
|
262
|
+
*/
|
|
263
|
+
function usePanelShown(isOpen, placed, setPresent) {
|
|
114
264
|
const $ = c(8);
|
|
115
265
|
const { motion } = useTheme();
|
|
116
266
|
const reduceMotion = useReducedMotion();
|
|
@@ -164,6 +314,10 @@ function usePanelMotion(isOpen, placed, setPresent) {
|
|
|
164
314
|
t1 = $[7];
|
|
165
315
|
}
|
|
166
316
|
useEffect(t0, t1);
|
|
317
|
+
return shown;
|
|
318
|
+
}
|
|
319
|
+
/** The panel's fade and scale, for how far it has arrived. */
|
|
320
|
+
function usePanelStyle(shown) {
|
|
167
321
|
return useAnimatedStyle(() => {
|
|
168
322
|
"worklet";
|
|
169
323
|
const progress = shown.get();
|
|
@@ -210,6 +364,6 @@ function useReturnFocus(present, quiet, trigger) {
|
|
|
210
364
|
useEffect(t0, t1);
|
|
211
365
|
}
|
|
212
366
|
//#endregion
|
|
213
|
-
export { DismissLayer, PANEL_ORIGINS, useBackToClose, useFocusOnPlaced, usePanelMotion, useReturnFocus };
|
|
367
|
+
export { DismissLayer, PANEL_ORIGINS, useBackToClose, useFocusOnPlaced, usePanelMotion, usePanelShown, usePanelStyle, useReturnFocus };
|
|
214
368
|
|
|
215
369
|
//# sourceMappingURL=panel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel.js","names":["RefObject","HostInstance","useEffect","useRef","AccessibilityInfo","BackHandler","Platform","Pressable","StyleSheet","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","OverlaySide","useTheme","SCALE_FROM","PANEL_ORIGINS","bottom","left","right","top","const","Record","styles","create","layer","position","DismissLayer","t0","$","_c","onPress","t1","useBackToClose","close","active","OS","subscription","addEventListener","remove","useFocusOnPlaced","ready","target","sendAccessibilityEvent","usePanelMotion","isOpen","placed","setPresent","motion","reduceMotion","shown","set","duration","durationShort3","easing","bezier","easingEmphasizedDecelerate","durationShort2","easingEmphasizedAccelerate","timer","setTimeout","clearTimeout","progress","get","opacity","transform","scale","useReturnFocus","present","quiet","trigger","current","gone","_c"],"sources":["../../src/overlay/panel.tsx"],"sourcesContent":["import type { RefObject } from 'react'\nimport type { HostInstance } from 'react-native'\n\nimport { useEffect, useRef } from 'react'\nimport {\n AccessibilityInfo,\n BackHandler,\n Platform,\n Pressable,\n StyleSheet,\n} from 'react-native'\nimport {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { OverlaySide } from './placement'\n\nimport { useTheme } from '../theme/scope'\n\n// What a non-modal panel anchored beside its trigger does besides being\n// placed, shared by Popover and Menu so the two cannot drift: the layer a\n// press outside lands on, Android's back gesture, the motion in and out, and\n// where a screen reader is sent as it comes and goes.\n\n// What the panel scales up from as it arrives, web's keyframes'.\nconst SCALE_FROM = 0.95\n\n/** The edge a panel grows from, the one facing its anchor. */\nconst PANEL_ORIGINS = {\n bottom: 'top',\n left: 'right',\n right: 'left',\n top: 'bottom',\n} as const satisfies Record<OverlaySide, string>\n\nconst styles = StyleSheet.create({\n // Over the whole screen and under the panel.\n layer: {\n bottom: 0,\n left: 0,\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n/**\n * The layer a press outside a non-modal panel lands on, drawn just before the\n * panel so it sits under it. The press goes no further, so a press on the\n * trigger closes the panel rather than opening it again, as on web. Nothing\n * but that press reaches it: React Native Web's Pressable is a tab stop\n * unless told otherwise, whatever `focusable` says.\n */\nfunction DismissLayer({ onPress }: { readonly onPress: () => void }) {\n return (\n <Pressable\n accessible={false}\n aria-hidden\n focusable={false}\n onPress={onPress}\n style={styles.layer}\n tabIndex={-1}\n />\n )\n}\n\n/**\n * Closes a non-modal panel from Android's back gesture, as a `Modal` closes a\n * modal one. React Native calls the latest listener first, so a panel opened\n * from another closes before the one it came from. React Native Web has no\n * back gesture, and its BackHandler reports an error when one is listened for,\n * so nothing listens there.\n */\nfunction useBackToClose(close: () => void, active: boolean) {\n useEffect(() => {\n if (!active || Platform.OS === 'web') {\n return undefined\n }\n\n const subscription = BackHandler.addEventListener(\n 'hardwareBackPress',\n () => {\n close()\n return true\n },\n )\n\n return () => {\n subscription.remove()\n }\n }, [active, close])\n}\n\n/**\n * Sends the screen reader to `target` once `ready`, which a panel passes once\n * it has been placed, and so can be seen. React Native Web has no screen\n * reader of its own to send.\n */\nfunction useFocusOnPlaced(ready: boolean, target: HostInstance | null) {\n useEffect(() => {\n if (ready && target !== null && Platform.OS !== 'web') {\n AccessibilityInfo.sendAccessibilityEvent(target, 'focus')\n }\n }, [ready, target])\n}\n\n/**\n * A panel's motion, and the timer that unmounts it once its exit has run: it\n * fades and scales up from 95% over the short3 duration on the emphasised\n * decelerating curve, and back over short2 on the accelerating one. The\n * entrance waits for the panel to be placed, so it is not spent while the\n * panel is still hidden being measured, and the panel stays transparent\n * until then. Under reduced motion it arrives and leaves at once.\n */\nfunction usePanelMotion(\n isOpen: boolean,\n placed: boolean,\n setPresent: (present: boolean) => void,\n) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const shown = useSharedValue(0)\n\n useEffect(() => {\n if (isOpen && !placed) {\n return undefined\n }\n\n if (isOpen) {\n shown.set(\n reduceMotion\n ? 1\n : withTiming(1, {\n duration: motion.durationShort3,\n easing: Easing.bezier(...motion.easingEmphasizedDecelerate),\n }),\n )\n return undefined\n }\n\n if (reduceMotion) {\n shown.set(0)\n setPresent(false)\n return undefined\n }\n\n shown.set(\n withTiming(0, {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasizedAccelerate),\n }),\n )\n const timer = setTimeout(() => {\n setPresent(false)\n }, motion.durationShort2)\n\n return () => {\n clearTimeout(timer)\n }\n }, [isOpen, motion, placed, reduceMotion, setPresent, shown])\n\n return useAnimatedStyle(() => {\n 'worklet'\n const progress = shown.get()\n\n return {\n opacity: progress,\n transform: [{ scale: SCALE_FROM + (1 - SCALE_FROM) * progress }],\n }\n })\n}\n\n/**\n * Sends the screen reader back to the trigger once a panel has gone, unless\n * `quiet` says nothing moved it there, as a pointer resting on a hover\n * trigger does not. React Native Web's `Modal` restores focus itself, and has\n * no screen reader to send otherwise.\n */\nfunction useReturnFocus(\n present: boolean,\n quiet: boolean,\n trigger: RefObject<HostInstance | null>,\n) {\n const shown = useRef(present)\n\n useEffect(() => {\n const gone = shown.current && !present\n const target = trigger.current\n\n shown.current = present\n if (gone && !quiet && target !== null && Platform.OS !== 'web') {\n AccessibilityInfo.sendAccessibilityEvent(target, 'focus')\n }\n }, [quiet, present, trigger])\n}\n\nexport {\n DismissLayer,\n PANEL_ORIGINS,\n useBackToClose,\n useFocusOnPlaced,\n usePanelMotion,\n useReturnFocus,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"panel.js","names":["RefObject","HostInstance","ViewStyle","SharedValue","useEffect","useRef","AccessibilityInfo","BackHandler","I18nManager","Platform","Pressable","StyleSheet","View","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","OverlaySide","Rect","useTheme","SCALE_FROM","PANEL_ORIGINS","bottom","left","right","top","const","Record","styles","create","layer","position","layerAround","pointerEvents","DismissLayer","t0","$","_c","hole","onPress","undefined","t1","partsAround","parts","t2","above","t3","below","t4","t5","t6","LayerPart","style","height","width","x","y","swapped","isRTL","doLeftAndRightSwapInRTL","beside","Math","max","useBackToClose","close","active","OS","subscription","addEventListener","remove","useFocusOnPlaced","ready","target","sendAccessibilityEvent","usePanelMotion","isOpen","placed","setPresent","usePanelStyle","usePanelShown","motion","reduceMotion","shown","set","duration","durationShort3","easing","bezier","easingEmphasizedDecelerate","durationShort2","easingEmphasizedAccelerate","timer","setTimeout","clearTimeout","progress","get","opacity","transform","scale","useReturnFocus","present","quiet","trigger","current","gone","_c"],"sources":["../../src/overlay/panel.tsx"],"sourcesContent":["import type { RefObject } from 'react'\nimport type { HostInstance, ViewStyle } from 'react-native'\nimport type { SharedValue } from 'react-native-reanimated'\n\nimport { useEffect, useRef } from 'react'\nimport {\n AccessibilityInfo,\n BackHandler,\n I18nManager,\n Platform,\n Pressable,\n StyleSheet,\n View,\n} from 'react-native'\nimport {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { OverlaySide, Rect } from './placement'\n\nimport { useTheme } from '../theme/scope'\n\n// What a non-modal panel anchored beside its trigger does besides being\n// placed, shared by Popover and Menu so the two cannot drift: the layer a\n// press outside lands on, Android's back gesture, the motion in and out, and\n// where a screen reader is sent as it comes and goes.\n\n// What the panel scales up from as it arrives, web's keyframes'.\nconst SCALE_FROM = 0.95\n\n/** The edge a panel grows from, the one facing its anchor. */\nconst PANEL_ORIGINS = {\n bottom: 'top',\n left: 'right',\n right: 'left',\n top: 'bottom',\n} as const satisfies Record<OverlaySide, string>\n\nconst styles = StyleSheet.create({\n // Over the whole screen and under the panel.\n layer: {\n bottom: 0,\n left: 0,\n position: 'absolute',\n right: 0,\n top: 0,\n },\n // The same, passing a press through wherever no part of it is drawn.\n layerAround: {\n bottom: 0,\n left: 0,\n pointerEvents: 'box-none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n/**\n * The layer a press outside a non-modal panel lands on, drawn just before the\n * panel so it sits under it. The press goes no further, so a press on the\n * trigger closes the panel rather than opening it again, as on web.\n *\n * Given a `hole`, the layer is drawn around that frame rather than over it,\n * so a press there reaches the screen under it and leaves the panel open: a\n * combo box's box, where a tap places the caret in the text being typed, as\n * a click in a combo box's input leaves its list open on web.\n */\nfunction DismissLayer({\n hole,\n onPress,\n}: {\n readonly hole?: Rect | undefined\n readonly onPress: () => void\n}) {\n if (hole === undefined) {\n return <LayerPart onPress={onPress} style={styles.layer} />\n }\n\n const parts = partsAround(hole)\n\n return (\n <View style={styles.layerAround}>\n <LayerPart onPress={onPress} style={parts.above} />\n <LayerPart onPress={onPress} style={parts.below} />\n <LayerPart onPress={onPress} style={parts.left} />\n <LayerPart onPress={onPress} style={parts.right} />\n </View>\n )\n}\n\n/**\n * One piece of the layer. Nothing but a press reaches it: React Native Web's\n * Pressable is a tab stop unless told otherwise, whatever `focusable` says.\n */\nfunction LayerPart({\n onPress,\n style,\n}: {\n readonly onPress: () => void\n readonly style: ViewStyle\n}) {\n return (\n <Pressable\n accessible={false}\n aria-hidden\n focusable={false}\n onPress={onPress}\n style={style}\n tabIndex={-1}\n />\n )\n}\n\n/**\n * The four pieces of a layer drawn around a frame: the bands above and below\n * it, across the whole layer, and the two beside it, as tall as it. The frame\n * is in physical coordinates, as `measureInWindow` reports it, and React\n * Native swaps `left` and `right` in a right-to-left layout unless an app\n * turns that off, so there each side is set through the other's name.\n */\nfunction partsAround({ height, width, x, y }: Rect) {\n const swapped = I18nManager.isRTL && I18nManager.doLeftAndRightSwapInRTL\n const left = swapped ? 'right' : 'left'\n const right = swapped ? 'left' : 'right'\n const beside: ViewStyle = {\n height,\n position: 'absolute',\n top: y,\n }\n\n return {\n above: {\n height: Math.max(0, y),\n left: 0,\n position: 'absolute',\n right: 0,\n top: 0,\n },\n below: {\n bottom: 0,\n left: 0,\n position: 'absolute',\n right: 0,\n top: y + height,\n },\n left: { ...beside, [left]: 0, width: Math.max(0, x) },\n right: { ...beside, [left]: x + width, [right]: 0 },\n } satisfies Record<string, ViewStyle>\n}\n\n/**\n * Closes a non-modal panel from Android's back gesture, as a `Modal` closes a\n * modal one. React Native calls the latest listener first, so a panel opened\n * from another closes before the one it came from. React Native Web has no\n * back gesture, and its BackHandler reports an error when one is listened for,\n * so nothing listens there.\n */\nfunction useBackToClose(close: () => void, active: boolean) {\n useEffect(() => {\n if (!active || Platform.OS === 'web') {\n return undefined\n }\n\n const subscription = BackHandler.addEventListener(\n 'hardwareBackPress',\n () => {\n close()\n return true\n },\n )\n\n return () => {\n subscription.remove()\n }\n }, [active, close])\n}\n\n/**\n * Sends the screen reader to `target` once `ready`, which a panel passes once\n * it has been placed, and so can be seen. React Native Web has no screen\n * reader of its own to send.\n */\nfunction useFocusOnPlaced(ready: boolean, target: HostInstance | null) {\n useEffect(() => {\n if (ready && target !== null && Platform.OS !== 'web') {\n AccessibilityInfo.sendAccessibilityEvent(target, 'focus')\n }\n }, [ready, target])\n}\n\n/**\n * A panel's motion, and the timer that unmounts it once its exit has run: it\n * fades and scales up from 95% over the short3 duration on the emphasised\n * decelerating curve, and back over short2 on the accelerating one. The\n * entrance waits for the panel to be placed, so it is not spent while the\n * panel is still hidden being measured, and the panel stays transparent\n * until then. Under reduced motion it arrives and leaves at once.\n */\nfunction usePanelMotion(\n isOpen: boolean,\n placed: boolean,\n setPresent: (present: boolean) => void,\n) {\n return usePanelStyle(usePanelShown(isOpen, placed, setPresent))\n}\n\n/**\n * How far a panel has arrived, from 0 to 1, on `usePanelMotion`'s timing,\n * for a panel that moves something besides itself with it, as a picker's\n * scrim fades with the surface it is cast by.\n */\nfunction usePanelShown(\n isOpen: boolean,\n placed: boolean,\n setPresent: (present: boolean) => void,\n) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const shown = useSharedValue(0)\n\n useEffect(() => {\n if (isOpen && !placed) {\n return undefined\n }\n\n if (isOpen) {\n shown.set(\n reduceMotion\n ? 1\n : withTiming(1, {\n duration: motion.durationShort3,\n easing: Easing.bezier(...motion.easingEmphasizedDecelerate),\n }),\n )\n return undefined\n }\n\n if (reduceMotion) {\n shown.set(0)\n setPresent(false)\n return undefined\n }\n\n shown.set(\n withTiming(0, {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasizedAccelerate),\n }),\n )\n const timer = setTimeout(() => {\n setPresent(false)\n }, motion.durationShort2)\n\n return () => {\n clearTimeout(timer)\n }\n }, [isOpen, motion, placed, reduceMotion, setPresent, shown])\n\n return shown\n}\n\n/** The panel's fade and scale, for how far it has arrived. */\nfunction usePanelStyle(shown: SharedValue<number>) {\n return useAnimatedStyle(() => {\n 'worklet'\n const progress = shown.get()\n\n return {\n opacity: progress,\n transform: [{ scale: SCALE_FROM + (1 - SCALE_FROM) * progress }],\n }\n })\n}\n\n/**\n * Sends the screen reader back to the trigger once a panel has gone, unless\n * `quiet` says nothing moved it there, as a pointer resting on a hover\n * trigger does not. React Native Web's `Modal` restores focus itself, and has\n * no screen reader to send otherwise.\n */\nfunction useReturnFocus(\n present: boolean,\n quiet: boolean,\n trigger: RefObject<HostInstance | null>,\n) {\n const shown = useRef(present)\n\n useEffect(() => {\n const gone = shown.current && !present\n const target = trigger.current\n\n shown.current = present\n if (gone && !quiet && target !== null && Platform.OS !== 'web') {\n AccessibilityInfo.sendAccessibilityEvent(target, 'focus')\n }\n }, [quiet, present, trigger])\n}\n\nexport {\n DismissLayer,\n PANEL_ORIGINS,\n useBackToClose,\n useFocusOnPlaced,\n usePanelMotion,\n usePanelShown,\n usePanelStyle,\n useReturnFocus,\n}\n"],"mappings":"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"}
|
|
@@ -6,6 +6,18 @@ const OPPOSITE = {
|
|
|
6
6
|
top: "bottom"
|
|
7
7
|
};
|
|
8
8
|
/**
|
|
9
|
+
* Bounds that end above the software keyboard, with the same padding above
|
|
10
|
+
* it, where one is shown: its top edge in the window, or null.
|
|
11
|
+
*/
|
|
12
|
+
function aboveKeyboard(bounds, keyboardTop, padding) {
|
|
13
|
+
if (keyboardTop === null) return bounds;
|
|
14
|
+
const bottom = Math.min(bounds.y + bounds.height, keyboardTop - padding);
|
|
15
|
+
return {
|
|
16
|
+
...bounds,
|
|
17
|
+
height: Math.max(0, bottom - bounds.y)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
9
21
|
* Where a surface of a given length starts along the anchor, for an edge,
|
|
10
22
|
* moved by the offset. However far the offset moves it, it keeps overlapping
|
|
11
23
|
* the anchor, as React Aria's placement does on web.
|
|
@@ -28,7 +40,9 @@ function boundsOf(window, insets, padding) {
|
|
|
28
40
|
}
|
|
29
41
|
function edgeOf(align, inline, rtl) {
|
|
30
42
|
if (align === "center") return "center";
|
|
31
|
-
|
|
43
|
+
const fromRight = inline && rtl;
|
|
44
|
+
if (align === "start") return fromRight ? "far" : "near";
|
|
45
|
+
return fromRight ? "near" : "far";
|
|
32
46
|
}
|
|
33
47
|
function isVertical(side) {
|
|
34
48
|
return side === "bottom" || side === "top";
|
|
@@ -82,6 +96,6 @@ function within(position, start, length, size) {
|
|
|
82
96
|
return position;
|
|
83
97
|
}
|
|
84
98
|
//#endregion
|
|
85
|
-
export { boundsOf, place };
|
|
99
|
+
export { aboveKeyboard, boundsOf, place };
|
|
86
100
|
|
|
87
101
|
//# sourceMappingURL=placement.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"placement.js","names":["Edge","OverlayAlign","OverlaySide","Placement","maxHeight","side","x","y","PlacementInput","align","alignOffset","anchor","Rect","bounds","rtl","sideOffset","surface","Size","height","width","OPPOSITE","bottom","left","right","top","const","Record","along","start","length","size","edge","offset","lined","Math","min","max","boundsOf","window","insets","padding","edgeOf","inline","isVertical","place","preferred","room","on","spaceOn","opposite","within","position"],"sources":["../../src/overlay/placement.ts"],"sourcesContent":["// Which end of the anchor the surface lines up with, along the side.\ntype Edge = 'center' | 'far' | 'near'\n\n/** Where along that side it lines up with the anchor. */\ntype OverlayAlign = 'center' | 'end' | 'start'\n\n/** Which side of the anchor a surface opens on. */\ntype OverlaySide = 'bottom' | 'left' | 'right' | 'top'\n\n/** Where an anchored surface sits, in the coordinates its inputs were in. */\ntype Placement = {\n /**\n * The height the surface may take before it would pass the edge of the\n * bounds on the side it opened on. A surface taller than this scrolls.\n */\n readonly maxHeight: number\n /** The side it opened on, which is the opposite one after a flip. */\n readonly side: OverlaySide\n readonly x: number\n readonly y: number\n}\n\ntype PlacementInput = {\n readonly align: OverlayAlign\n /** Moves the surface along the side, in dp: right or down when positive. */\n readonly alignOffset: number\n readonly anchor: Rect\n /** The area the surface stays inside, padding already taken off. */\n readonly bounds: Rect\n /** Whether `start` and `end` read from the right, on `top` and `bottom`. */\n readonly rtl: boolean\n readonly side: OverlaySide\n /** The gap between the anchor and the surface, in dp. */\n readonly sideOffset: number\n readonly surface: Size\n}\n\ntype Rect = Size & { readonly x: number; readonly y: number }\n\ntype Size = { readonly height: number; readonly width: number }\n\nconst OPPOSITE = {\n bottom: 'top',\n left: 'right',\n right: 'left',\n top: 'bottom',\n} as const satisfies Record<OverlaySide, OverlaySide>\n\n/**\n * Where a surface of a given length starts along the anchor, for an edge,\n * moved by the offset. However far the offset moves it, it keeps overlapping\n * the anchor, as React Aria's placement does on web.\n */\nfunction along(\n start: number,\n length: number,\n size: number,\n edge: Edge,\n offset: number,\n) {\n const lined =\n edge === 'near'\n ? start\n : edge === 'far'\n ? start + length - size\n : start + (length - size) / 2\n\n return Math.min(Math.max(lined + offset, start - size), start + length)\n}\n\n/**\n * Bounds a surface stays inside: the window, less the safe area's insets and\n * a padding on every side.\n */\nfunction boundsOf(\n window: Size,\n insets: { bottom: number; left: number; right: number; top: number },\n padding: number,\n): Rect {\n return {\n height: window.height - insets.top - insets.bottom - padding * 2,\n width: window.width - insets.left - insets.right - padding * 2,\n x: insets.left + padding,\n y: insets.top + padding,\n }\n}\n\n// Which end of the anchor an alignment names. On `top` and `bottom` the\n// surface lines up along the inline axis, so `start` is the left edge, or the\n// right one in a right-to-left layout; on `left` and `right` it lines up along\n// the block axis, where `start` is the top edge in both.\nfunction edgeOf(align: OverlayAlign, inline: boolean, rtl: boolean): Edge {\n if (align === 'center') {\n return 'center'\n }\n\n return (align === 'start') === (inline && rtl) ? 'far' : 'near'\n}\n\nfunction isVertical(side: OverlaySide) {\n return side === 'bottom' || side === 'top'\n}\n\n/**\n * Places a surface beside its anchor, as React Aria places a popover on web.\n *\n * It opens on `side`, lined up along it by `align`, and flips to the opposite\n * side when it does not fit and that side has more room. Along the side it is\n * shifted to stay inside the bounds; across it, on `top` and `bottom`, it\n * stays inside them through `maxHeight`, and on `left` and `right`, where a\n * surface wider than both sides has nowhere to go, it is shifted over the\n * anchor rather than past the edge.\n */\nfunction place({\n align,\n alignOffset,\n anchor,\n bounds,\n rtl,\n side: preferred,\n sideOffset,\n surface,\n}: PlacementInput): Placement {\n const room = (on: OverlaySide) => spaceOn(on, anchor, bounds, sideOffset)\n const opposite = OPPOSITE[preferred]\n const length = isVertical(preferred) ? surface.height : surface.width\n const side =\n length > room(preferred) && room(opposite) > room(preferred)\n ? opposite\n : preferred\n\n if (isVertical(side)) {\n const maxHeight = Math.max(0, room(side))\n const height = Math.min(surface.height, maxHeight)\n const x = along(\n anchor.x,\n anchor.width,\n surface.width,\n edgeOf(align, true, rtl),\n alignOffset,\n )\n\n return {\n maxHeight,\n side,\n x: within(x, bounds.x, bounds.width, surface.width),\n y:\n side === 'bottom'\n ? anchor.y + anchor.height + sideOffset\n : anchor.y - sideOffset - height,\n }\n }\n\n const maxHeight = Math.max(0, bounds.height)\n const height = Math.min(surface.height, maxHeight)\n const x =\n side === 'right'\n ? anchor.x + anchor.width + sideOffset\n : anchor.x - sideOffset - surface.width\n const y = along(\n anchor.y,\n anchor.height,\n height,\n edgeOf(align, false, rtl),\n alignOffset,\n )\n\n return {\n maxHeight,\n side,\n x: within(x, bounds.x, bounds.width, surface.width),\n y: within(y, bounds.y, bounds.height, height),\n }\n}\n\n// The room between the anchor and the edge of the bounds on a side, less the\n// gap the surface keeps from the anchor.\nfunction spaceOn(\n side: OverlaySide,\n anchor: Rect,\n bounds: Rect,\n sideOffset: number,\n) {\n const room = {\n bottom: bounds.y + bounds.height - anchor.y - anchor.height,\n left: anchor.x - bounds.x,\n right: bounds.x + bounds.width - anchor.x - anchor.width,\n top: anchor.y - bounds.y,\n }\n\n return room[side] - sideOffset\n}\n\n// Shifts a surface along an axis until it is inside the bounds, or, when it is\n// longer than they are, until it starts where they start.\nfunction within(position: number, start: number, length: number, size: number) {\n if (position < start) {\n return start\n }\n\n if (position + size > start + length) {\n return Math.max(start + length - size, start)\n }\n\n return position\n}\n\nexport type { OverlayAlign, OverlaySide, Placement, PlacementInput, Rect, Size }\n\nexport { boundsOf, place }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"placement.js","names":["Edge","OverlayAlign","OverlaySide","Placement","maxHeight","side","x","y","PlacementInput","align","alignOffset","anchor","Rect","bounds","rtl","sideOffset","surface","Size","height","width","OPPOSITE","bottom","left","right","top","const","Record","aboveKeyboard","keyboardTop","padding","Math","min","max","along","start","length","size","edge","offset","lined","boundsOf","window","insets","edgeOf","inline","fromRight","isVertical","place","preferred","room","on","spaceOn","opposite","within","position"],"sources":["../../src/overlay/placement.ts"],"sourcesContent":["// Which end of the anchor the surface lines up with, along the side.\ntype Edge = 'center' | 'far' | 'near'\n\n/** Where along that side it lines up with the anchor. */\ntype OverlayAlign = 'center' | 'end' | 'start'\n\n/** Which side of the anchor a surface opens on. */\ntype OverlaySide = 'bottom' | 'left' | 'right' | 'top'\n\n/** Where an anchored surface sits, in the coordinates its inputs were in. */\ntype Placement = {\n /**\n * The height the surface may take before it would pass the edge of the\n * bounds on the side it opened on. A surface taller than this scrolls.\n */\n readonly maxHeight: number\n /** The side it opened on, which is the opposite one after a flip. */\n readonly side: OverlaySide\n readonly x: number\n readonly y: number\n}\n\ntype PlacementInput = {\n readonly align: OverlayAlign\n /** Moves the surface along the side, in dp: right or down when positive. */\n readonly alignOffset: number\n readonly anchor: Rect\n /** The area the surface stays inside, padding already taken off. */\n readonly bounds: Rect\n /** Whether `start` and `end` read from the right, on `top` and `bottom`. */\n readonly rtl: boolean\n readonly side: OverlaySide\n /** The gap between the anchor and the surface, in dp. */\n readonly sideOffset: number\n readonly surface: Size\n}\n\ntype Rect = Size & { readonly x: number; readonly y: number }\n\ntype Size = { readonly height: number; readonly width: number }\n\nconst OPPOSITE = {\n bottom: 'top',\n left: 'right',\n right: 'left',\n top: 'bottom',\n} as const satisfies Record<OverlaySide, OverlaySide>\n\n/**\n * Bounds that end above the software keyboard, with the same padding above\n * it, where one is shown: its top edge in the window, or null.\n */\nfunction aboveKeyboard(\n bounds: Rect,\n keyboardTop: null | number,\n padding: number,\n): Rect {\n if (keyboardTop === null) {\n return bounds\n }\n\n const bottom = Math.min(bounds.y + bounds.height, keyboardTop - padding)\n\n return { ...bounds, height: Math.max(0, bottom - bounds.y) }\n}\n\n/**\n * Where a surface of a given length starts along the anchor, for an edge,\n * moved by the offset. However far the offset moves it, it keeps overlapping\n * the anchor, as React Aria's placement does on web.\n */\nfunction along(\n start: number,\n length: number,\n size: number,\n edge: Edge,\n offset: number,\n) {\n const lined =\n edge === 'near'\n ? start\n : edge === 'far'\n ? start + length - size\n : start + (length - size) / 2\n\n return Math.min(Math.max(lined + offset, start - size), start + length)\n}\n\n/**\n * Bounds a surface stays inside: the window, less the safe area's insets and\n * a padding on every side.\n */\nfunction boundsOf(\n window: Size,\n insets: { bottom: number; left: number; right: number; top: number },\n padding: number,\n): Rect {\n return {\n height: window.height - insets.top - insets.bottom - padding * 2,\n width: window.width - insets.left - insets.right - padding * 2,\n x: insets.left + padding,\n y: insets.top + padding,\n }\n}\n\n// Which end of the anchor an alignment names. On `top` and `bottom` the\n// surface lines up along the inline axis, so `start` is the left edge, or the\n// right one in a right-to-left layout; on `left` and `right` it lines up along\n// the block axis, where `start` is the top edge in both.\n//\n// `rtl` is read for its truth rather than compared with a boolean: it comes\n// from `I18nManager.isRTL`, which React Native Web leaves undefined, and an\n// equality with undefined put every `end` at the start there.\nfunction edgeOf(align: OverlayAlign, inline: boolean, rtl: boolean): Edge {\n if (align === 'center') {\n return 'center'\n }\n\n const fromRight = inline && rtl\n\n if (align === 'start') {\n return fromRight ? 'far' : 'near'\n }\n\n return fromRight ? 'near' : 'far'\n}\n\nfunction isVertical(side: OverlaySide) {\n return side === 'bottom' || side === 'top'\n}\n\n/**\n * Places a surface beside its anchor, as React Aria places a popover on web.\n *\n * It opens on `side`, lined up along it by `align`, and flips to the opposite\n * side when it does not fit and that side has more room. Along the side it is\n * shifted to stay inside the bounds; across it, on `top` and `bottom`, it\n * stays inside them through `maxHeight`, and on `left` and `right`, where a\n * surface wider than both sides has nowhere to go, it is shifted over the\n * anchor rather than past the edge.\n */\nfunction place({\n align,\n alignOffset,\n anchor,\n bounds,\n rtl,\n side: preferred,\n sideOffset,\n surface,\n}: PlacementInput): Placement {\n const room = (on: OverlaySide) => spaceOn(on, anchor, bounds, sideOffset)\n const opposite = OPPOSITE[preferred]\n const length = isVertical(preferred) ? surface.height : surface.width\n const side =\n length > room(preferred) && room(opposite) > room(preferred)\n ? opposite\n : preferred\n\n if (isVertical(side)) {\n const maxHeight = Math.max(0, room(side))\n const height = Math.min(surface.height, maxHeight)\n const x = along(\n anchor.x,\n anchor.width,\n surface.width,\n edgeOf(align, true, rtl),\n alignOffset,\n )\n\n return {\n maxHeight,\n side,\n x: within(x, bounds.x, bounds.width, surface.width),\n y:\n side === 'bottom'\n ? anchor.y + anchor.height + sideOffset\n : anchor.y - sideOffset - height,\n }\n }\n\n const maxHeight = Math.max(0, bounds.height)\n const height = Math.min(surface.height, maxHeight)\n const x =\n side === 'right'\n ? anchor.x + anchor.width + sideOffset\n : anchor.x - sideOffset - surface.width\n const y = along(\n anchor.y,\n anchor.height,\n height,\n edgeOf(align, false, rtl),\n alignOffset,\n )\n\n return {\n maxHeight,\n side,\n x: within(x, bounds.x, bounds.width, surface.width),\n y: within(y, bounds.y, bounds.height, height),\n }\n}\n\n// The room between the anchor and the edge of the bounds on a side, less the\n// gap the surface keeps from the anchor.\nfunction spaceOn(\n side: OverlaySide,\n anchor: Rect,\n bounds: Rect,\n sideOffset: number,\n) {\n const room = {\n bottom: bounds.y + bounds.height - anchor.y - anchor.height,\n left: anchor.x - bounds.x,\n right: bounds.x + bounds.width - anchor.x - anchor.width,\n top: anchor.y - bounds.y,\n }\n\n return room[side] - sideOffset\n}\n\n// Shifts a surface along an axis until it is inside the bounds, or, when it is\n// longer than they are, until it starts where they start.\nfunction within(position: number, start: number, length: number, size: number) {\n if (position < start) {\n return start\n }\n\n if (position + size > start + length) {\n return Math.max(start + length - size, start)\n }\n\n return position\n}\n\nexport type { OverlayAlign, OverlaySide, Placement, PlacementInput, Rect, Size }\n\nexport { aboveKeyboard, boundsOf, place }\n"],"mappings":"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"}
|