@lmb-it/kitsconcerto-native 0.1.264 → 0.1.265
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/apps/mobile/src/Core/Dropdown/index.cjs +1 -1
- package/apps/mobile/src/Core/Dropdown/index.js +1 -1
- package/form/selects/helpers/SelectPopupOverlay.cjs +20 -8
- package/form/selects/helpers/SelectPopupOverlay.cjs.map +1 -1
- package/form/selects/helpers/SelectPopupOverlay.js +20 -8
- package/form/selects/helpers/SelectPopupOverlay.js.map +1 -1
- package/package.json +1 -1
|
@@ -66,6 +66,7 @@ require('../../../../../packages/types/src/Theme/defaultTheme.cjs');
|
|
|
66
66
|
require('../../../../../packages/types/src/Theme/alphaColors.cjs');
|
|
67
67
|
require('../../Factory/helpers/cssVarMap.cjs');
|
|
68
68
|
var useKeyboardHeight = require('../../Factory/helpers/useKeyboardHeight.cjs');
|
|
69
|
+
var readerEdge = require('../../Factory/helpers/readerEdge.cjs');
|
|
69
70
|
require('i18next');
|
|
70
71
|
require('react-i18next');
|
|
71
72
|
require('../AutoComplete/index.cjs');
|
|
@@ -85,7 +86,6 @@ require('../RadioButton/index.cjs');
|
|
|
85
86
|
require('../Input/index.cjs');
|
|
86
87
|
require('../Input/Field.cjs');
|
|
87
88
|
require('../Input/Slot.cjs');
|
|
88
|
-
var readerEdge = require('../../Factory/helpers/readerEdge.cjs');
|
|
89
89
|
var panelGeometry = require('./panelGeometry.cjs');
|
|
90
90
|
|
|
91
91
|
const getLabel = (item, optionLabel) => {
|
|
@@ -62,6 +62,7 @@ import '../../../../../packages/types/src/Theme/defaultTheme.js';
|
|
|
62
62
|
import '../../../../../packages/types/src/Theme/alphaColors.js';
|
|
63
63
|
import '../../Factory/helpers/cssVarMap.js';
|
|
64
64
|
import { useKeyboardHeight } from '../../Factory/helpers/useKeyboardHeight.js';
|
|
65
|
+
import { readerEdge } from '../../Factory/helpers/readerEdge.js';
|
|
65
66
|
import 'i18next';
|
|
66
67
|
import 'react-i18next';
|
|
67
68
|
import '../AutoComplete/index.js';
|
|
@@ -81,7 +82,6 @@ import '../RadioButton/index.js';
|
|
|
81
82
|
import '../Input/index.js';
|
|
82
83
|
import '../Input/Field.js';
|
|
83
84
|
import '../Input/Slot.js';
|
|
84
|
-
import { readerEdge } from '../../Factory/helpers/readerEdge.js';
|
|
85
85
|
import { computePanelGeometry } from './panelGeometry.js';
|
|
86
86
|
|
|
87
87
|
const getLabel = (item, optionLabel) => {
|
|
@@ -65,6 +65,7 @@ require('../../../packages/types/src/Theme/alphaColors.cjs');
|
|
|
65
65
|
require('../../../apps/mobile/src/Factory/helpers/cssVarMap.cjs');
|
|
66
66
|
require('../../../apps/mobile/src/Factory/DimensionsContext.cjs');
|
|
67
67
|
var useKeyboardHeight = require('../../../apps/mobile/src/Factory/helpers/useKeyboardHeight.cjs');
|
|
68
|
+
var readerEdge = require('../../../apps/mobile/src/Factory/helpers/readerEdge.cjs');
|
|
68
69
|
require('i18next');
|
|
69
70
|
require('react-i18next');
|
|
70
71
|
require('../../../apps/mobile/src/Core/AutoComplete/index.cjs');
|
|
@@ -96,6 +97,7 @@ const SHEET_MIN_HEIGHT = 240;
|
|
|
96
97
|
const SelectPopupOverlay = ({ visible, onClose, title, children }) => {
|
|
97
98
|
const { resolveToken } = KitsThemeProvider_native.useKitsTheme();
|
|
98
99
|
const { isRTL } = locale.useLanguage();
|
|
100
|
+
const edge = readerEdge.readerEdge(isRTL);
|
|
99
101
|
const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
|
|
100
102
|
const { height: windowHeight } = reactNative.useWindowDimensions();
|
|
101
103
|
const headingFont = useThemedFont.default("heading");
|
|
@@ -135,9 +137,17 @@ const SelectPopupOverlay = ({ visible, onClose, title, children }) => {
|
|
|
135
137
|
style: [
|
|
136
138
|
styles.header,
|
|
137
139
|
{ borderBottomColor: borderColor },
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
/* ⛔ THE PHYSICAL EDGE IS COMPUTED, NEVER NAMED.
|
|
141
|
+
React Native mirrors `flexDirection: 'row'`
|
|
142
|
+
and `textAlign` when the process is RTL — the
|
|
143
|
+
App calls `I18nManager.forceRTL` — so naming
|
|
144
|
+
the RTL edge for an RTL reader asks for it
|
|
145
|
+
twice and lands on the LTR one. That is why
|
|
146
|
+
this header drew its title LEFT and its ✕
|
|
147
|
+
RIGHT in Arabic while the option rows below
|
|
148
|
+
it, which already went through `readerEdge`,
|
|
149
|
+
were correct. See `readerEdge`. */
|
|
150
|
+
{ flexDirection: edge.flexDirection }
|
|
141
151
|
],
|
|
142
152
|
children: [
|
|
143
153
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -147,7 +157,13 @@ const SelectPopupOverlay = ({ visible, onClose, title, children }) => {
|
|
|
147
157
|
style: [
|
|
148
158
|
styles.title,
|
|
149
159
|
{ color: textColor, fontFamily: headingFont },
|
|
150
|
-
|
|
160
|
+
{
|
|
161
|
+
textAlign: edge.textAlign,
|
|
162
|
+
/* `writingDirection` is NOT mirrored — it
|
|
163
|
+
orders glyphs inside the line, so it
|
|
164
|
+
takes the reader's own direction. */
|
|
165
|
+
writingDirection: isRTL ? "rtl" : "ltr"
|
|
166
|
+
}
|
|
151
167
|
],
|
|
152
168
|
children: title
|
|
153
169
|
}
|
|
@@ -182,15 +198,11 @@ const styles = reactNative.StyleSheet.create({
|
|
|
182
198
|
borderBottomWidth: 1,
|
|
183
199
|
gap: 12
|
|
184
200
|
},
|
|
185
|
-
headerLtr: { flexDirection: "row" },
|
|
186
|
-
headerRtl: { flexDirection: "row-reverse" },
|
|
187
201
|
title: {
|
|
188
202
|
flex: 1,
|
|
189
203
|
fontSize: 17,
|
|
190
204
|
fontWeight: "700"
|
|
191
205
|
},
|
|
192
|
-
titleLtr: { textAlign: "left", writingDirection: "ltr" },
|
|
193
|
-
titleRtl: { textAlign: "right", writingDirection: "rtl" },
|
|
194
206
|
close: {
|
|
195
207
|
fontSize: 18
|
|
196
208
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectPopupOverlay.cjs","sources":["../../../../../src/form/selects/helpers/SelectPopupOverlay.native.tsx"],"sourcesContent":["import React from 'react';\nimport {Pressable, StyleSheet, Text, View, useWindowDimensions} from 'react-native';\nimport {useSafeAreaInsets} from 'react-native-safe-area-context';\nimport {useKeyboardHeight} from '@lmb-it/kitsconcertomobile';\nimport KitsDialogControlled from '../../../components/overlay/Dialog';\nimport {useKitsTheme} from '../../../contexts/Theme';\nimport {useLanguage} from '../../../hooks';\nimport useThemedFont from '../../../hooks/useThemedFont';\n\n/**\n * The container a select's options live in when `popup` is on.\n *\n * ⛔ THIS IS NOT A SECOND OVERLAY MECHANISM. It is the library's own\n * `KitsDialogControlled` in bottom-sheet position, so the scrim, the slide\n * animation, the safe-area insets, `dismissableMask` and Android's hardware\n * back all come from the one place that already owns them. The only thing\n * this file adds is the header row and the sizing a picker needs.\n *\n * ⛔ IT USES `content` MODE ON PURPOSE. The Dialog's default template puts its\n * children inside a ScrollView with `maxHeight: 400`. A FlatList inside a\n * ScrollView scrolls badly and warns, and a 400pt cap is the very thing the\n * popup exists to escape. `content` replaces the whole header/body/footer\n * template, so the caller's search field can be pinned and its list can own\n * the remaining height.\n *\n * WHY A FIXED FRACTION OF THE SCREEN. The popup's whole purpose is that its\n * height does not depend on where the trigger happens to sit or on whether the\n * keyboard is up. It takes a fixed share of the window, the search field sits\n * at the top of it, and the list takes the rest.\n */\n\nexport interface ISelectPopupOverlayProps {\n /** Open state. */\n visible: boolean;\n /** Called for every dismissal route — the ✕, the scrim, Android back. */\n onClose: () => void;\n /** Title shown beside the close control. Falls back to the field's label. */\n title?: React.ReactNode;\n /** The search field and the list. */\n children: React.ReactNode;\n}\n\n/** Share of the window height the sheet occupies when the keyboard is down. */\nconst SHEET_HEIGHT_RATIO = 0.75;\n/** Never shorter than this, even on a small screen with the keyboard up. */\nconst SHEET_MIN_HEIGHT = 240;\n\nconst SelectPopupOverlay = ({visible, onClose, title, children}: ISelectPopupOverlayProps) => {\n const {resolveToken} = useKitsTheme();\n const {isRTL} = useLanguage();\n const insets = useSafeAreaInsets();\n const {height: windowHeight} = useWindowDimensions();\n /* Raw RN <Text> does not inherit `fontFamily` through View parents, so a\n consumer that swaps the theme font would see it everywhere except here.\n See useThemedFont's own note. */\n const headingFont = useThemedFont('heading');\n const keyboardHeight = useKeyboardHeight();\n\n /*\n * ⛔ \"OWNS THE SCREEN\" MEANS ABOVE THE KEYBOARD, NOT BEHIND IT. A sheet\n * pinned to the bottom at a fixed 75% still has its lower half under the\n * keyboard the moment the search field takes focus, and the list is back to\n * competing for the gap — the exact fault the popup exists to escape, one\n * layer along. The sheet is capped to what is actually visible.\n *\n * The keyboard replaces the bottom inset rather than adding to it: it\n * covers the home indicator.\n */\n const visibleHeight = windowHeight - Math.max(insets.bottom, keyboardHeight) - insets.top;\n const sheetHeight = Math.max(\n Math.min(SHEET_MIN_HEIGHT, visibleHeight),\n Math.min(Math.round(windowHeight * SHEET_HEIGHT_RATIO), visibleHeight),\n );\n\n const textColor = resolveToken('text');\n const borderColor = resolveToken('border');\n const grabberColor = resolveToken('gray.300');\n const subtextColor = resolveToken('gray.500');\n\n return (\n <KitsDialogControlled\n onHide={onClose}\n state={{\n visible,\n position: 'bottom',\n // Tap-outside dismiss. `onRequestClose` inside the Dialog gives\n // Android's back button the same route.\n dismissableMask: true,\n onClose,\n style: {\n height: sheetHeight,\n // The keyboard already clears the home indicator, so the\n // inset is only owed when the keyboard is down.\n paddingBottom: keyboardHeight > 0 ? 0 : insets.bottom,\n marginBottom: keyboardHeight,\n },\n content: ({hide}: {hide: () => void}) => (\n <View style={styles.root}>\n <View style={[styles.grabber, {backgroundColor: grabberColor}]} />\n <View\n style={[\n styles.header,\n {borderBottomColor: borderColor},\n // The header mirrors with the reader, like every\n // other row this library draws.\n isRTL ? styles.headerRtl : styles.headerLtr,\n ]}>\n <Text\n numberOfLines={1}\n style={[\n styles.title,\n {color: textColor, fontFamily: headingFont},\n isRTL ? styles.titleRtl : styles.titleLtr,\n ]}>\n {title}\n </Text>\n <Pressable onPress={hide} hitSlop={12} accessibilityRole=\"button\">\n <Text style={[styles.close, {color: subtextColor}]}>✕</Text>\n </Pressable>\n </View>\n {children}\n </View>\n ),\n }}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n minHeight: 0,\n },\n grabber: {\n width: 40,\n height: 4,\n borderRadius: 2,\n alignSelf: 'center',\n marginTop: 8,\n marginBottom: 4,\n },\n header: {\n alignItems: 'center',\n paddingHorizontal: 16,\n paddingVertical: 12,\n borderBottomWidth: 1,\n gap: 12,\n },\n headerLtr: {flexDirection: 'row'},\n headerRtl: {flexDirection: 'row-reverse'},\n title: {\n flex: 1,\n fontSize: 17,\n fontWeight: '700',\n },\n titleLtr: {textAlign: 'left', writingDirection: 'ltr'},\n titleRtl: {textAlign: 'right', writingDirection: 'rtl'},\n close: {\n fontSize: 18,\n },\n});\n\nexport default SelectPopupOverlay;\n"],"names":["useKitsTheme","useLanguage","useSafeAreaInsets","useWindowDimensions","useThemedFont","useKeyboardHeight","jsx","KitsDialogControlled","jsxs","View","Text","Pressable","StyleSheet"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SelectPopupOverlay.cjs","sources":["../../../../../src/form/selects/helpers/SelectPopupOverlay.native.tsx"],"sourcesContent":["import React from 'react';\nimport {Pressable, StyleSheet, Text, View, useWindowDimensions} from 'react-native';\nimport {useSafeAreaInsets} from 'react-native-safe-area-context';\nimport {readerEdge, useKeyboardHeight} from '@lmb-it/kitsconcertomobile';\nimport KitsDialogControlled from '../../../components/overlay/Dialog';\nimport {useKitsTheme} from '../../../contexts/Theme';\nimport {useLanguage} from '../../../hooks';\nimport useThemedFont from '../../../hooks/useThemedFont';\n\n/**\n * The container a select's options live in when `popup` is on.\n *\n * ⛔ THIS IS NOT A SECOND OVERLAY MECHANISM. It is the library's own\n * `KitsDialogControlled` in bottom-sheet position, so the scrim, the slide\n * animation, the safe-area insets, `dismissableMask` and Android's hardware\n * back all come from the one place that already owns them. The only thing\n * this file adds is the header row and the sizing a picker needs.\n *\n * ⛔ IT USES `content` MODE ON PURPOSE. The Dialog's default template puts its\n * children inside a ScrollView with `maxHeight: 400`. A FlatList inside a\n * ScrollView scrolls badly and warns, and a 400pt cap is the very thing the\n * popup exists to escape. `content` replaces the whole header/body/footer\n * template, so the caller's search field can be pinned and its list can own\n * the remaining height.\n *\n * WHY A FIXED FRACTION OF THE SCREEN. The popup's whole purpose is that its\n * height does not depend on where the trigger happens to sit or on whether the\n * keyboard is up. It takes a fixed share of the window, the search field sits\n * at the top of it, and the list takes the rest.\n */\n\nexport interface ISelectPopupOverlayProps {\n /** Open state. */\n visible: boolean;\n /** Called for every dismissal route — the ✕, the scrim, Android back. */\n onClose: () => void;\n /** Title shown beside the close control. Falls back to the field's label. */\n title?: React.ReactNode;\n /** The search field and the list. */\n children: React.ReactNode;\n}\n\n/** Share of the window height the sheet occupies when the keyboard is down. */\nconst SHEET_HEIGHT_RATIO = 0.75;\n/** Never shorter than this, even on a small screen with the keyboard up. */\nconst SHEET_MIN_HEIGHT = 240;\n\nconst SelectPopupOverlay = ({visible, onClose, title, children}: ISelectPopupOverlayProps) => {\n const {resolveToken} = useKitsTheme();\n const {isRTL} = useLanguage();\n /* Physical edges to write so the header lands where the reader starts. */\n const edge = readerEdge(isRTL);\n const insets = useSafeAreaInsets();\n const {height: windowHeight} = useWindowDimensions();\n /* Raw RN <Text> does not inherit `fontFamily` through View parents, so a\n consumer that swaps the theme font would see it everywhere except here.\n See useThemedFont's own note. */\n const headingFont = useThemedFont('heading');\n const keyboardHeight = useKeyboardHeight();\n\n /*\n * ⛔ \"OWNS THE SCREEN\" MEANS ABOVE THE KEYBOARD, NOT BEHIND IT. A sheet\n * pinned to the bottom at a fixed 75% still has its lower half under the\n * keyboard the moment the search field takes focus, and the list is back to\n * competing for the gap — the exact fault the popup exists to escape, one\n * layer along. The sheet is capped to what is actually visible.\n *\n * The keyboard replaces the bottom inset rather than adding to it: it\n * covers the home indicator.\n */\n const visibleHeight = windowHeight - Math.max(insets.bottom, keyboardHeight) - insets.top;\n const sheetHeight = Math.max(\n Math.min(SHEET_MIN_HEIGHT, visibleHeight),\n Math.min(Math.round(windowHeight * SHEET_HEIGHT_RATIO), visibleHeight),\n );\n\n const textColor = resolveToken('text');\n const borderColor = resolveToken('border');\n const grabberColor = resolveToken('gray.300');\n const subtextColor = resolveToken('gray.500');\n\n return (\n <KitsDialogControlled\n onHide={onClose}\n state={{\n visible,\n position: 'bottom',\n // Tap-outside dismiss. `onRequestClose` inside the Dialog gives\n // Android's back button the same route.\n dismissableMask: true,\n onClose,\n style: {\n height: sheetHeight,\n // The keyboard already clears the home indicator, so the\n // inset is only owed when the keyboard is down.\n paddingBottom: keyboardHeight > 0 ? 0 : insets.bottom,\n marginBottom: keyboardHeight,\n },\n content: ({hide}: {hide: () => void}) => (\n <View style={styles.root}>\n <View style={[styles.grabber, {backgroundColor: grabberColor}]} />\n <View\n style={[\n styles.header,\n {borderBottomColor: borderColor},\n /* ⛔ THE PHYSICAL EDGE IS COMPUTED, NEVER NAMED.\n React Native mirrors `flexDirection: 'row'`\n and `textAlign` when the process is RTL — the\n App calls `I18nManager.forceRTL` — so naming\n the RTL edge for an RTL reader asks for it\n twice and lands on the LTR one. That is why\n this header drew its title LEFT and its ✕\n RIGHT in Arabic while the option rows below\n it, which already went through `readerEdge`,\n were correct. See `readerEdge`. */\n {flexDirection: edge.flexDirection},\n ]}>\n <Text\n numberOfLines={1}\n style={[\n styles.title,\n {color: textColor, fontFamily: headingFont},\n {\n textAlign: edge.textAlign,\n /* `writingDirection` is NOT mirrored — it\n orders glyphs inside the line, so it\n takes the reader's own direction. */\n writingDirection: isRTL ? ('rtl' as const) : ('ltr' as const),\n },\n ]}>\n {title}\n </Text>\n <Pressable onPress={hide} hitSlop={12} accessibilityRole=\"button\">\n <Text style={[styles.close, {color: subtextColor}]}>✕</Text>\n </Pressable>\n </View>\n {children}\n </View>\n ),\n }}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n minHeight: 0,\n },\n grabber: {\n width: 40,\n height: 4,\n borderRadius: 2,\n alignSelf: 'center',\n marginTop: 8,\n marginBottom: 4,\n },\n header: {\n alignItems: 'center',\n paddingHorizontal: 16,\n paddingVertical: 12,\n borderBottomWidth: 1,\n gap: 12,\n },\n title: {\n flex: 1,\n fontSize: 17,\n fontWeight: '700',\n },\n close: {\n fontSize: 18,\n },\n});\n\nexport default SelectPopupOverlay;\n"],"names":["useKitsTheme","useLanguage","readerEdge","useSafeAreaInsets","useWindowDimensions","useThemedFont","useKeyboardHeight","jsx","KitsDialogControlled","jsxs","View","Text","Pressable","StyleSheet"],"mappings":"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"}
|
|
@@ -61,6 +61,7 @@ import '../../../packages/types/src/Theme/alphaColors.js';
|
|
|
61
61
|
import '../../../apps/mobile/src/Factory/helpers/cssVarMap.js';
|
|
62
62
|
import '../../../apps/mobile/src/Factory/DimensionsContext.js';
|
|
63
63
|
import { useKeyboardHeight } from '../../../apps/mobile/src/Factory/helpers/useKeyboardHeight.js';
|
|
64
|
+
import { readerEdge } from '../../../apps/mobile/src/Factory/helpers/readerEdge.js';
|
|
64
65
|
import 'i18next';
|
|
65
66
|
import 'react-i18next';
|
|
66
67
|
import '../../../apps/mobile/src/Core/AutoComplete/index.js';
|
|
@@ -92,6 +93,7 @@ const SHEET_MIN_HEIGHT = 240;
|
|
|
92
93
|
const SelectPopupOverlay = ({ visible, onClose, title, children }) => {
|
|
93
94
|
const { resolveToken } = useKitsTheme();
|
|
94
95
|
const { isRTL } = useLanguage();
|
|
96
|
+
const edge = readerEdge(isRTL);
|
|
95
97
|
const insets = useSafeAreaInsets();
|
|
96
98
|
const { height: windowHeight } = useWindowDimensions();
|
|
97
99
|
const headingFont = useThemedFont("heading");
|
|
@@ -131,9 +133,17 @@ const SelectPopupOverlay = ({ visible, onClose, title, children }) => {
|
|
|
131
133
|
style: [
|
|
132
134
|
styles.header,
|
|
133
135
|
{ borderBottomColor: borderColor },
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
/* ⛔ THE PHYSICAL EDGE IS COMPUTED, NEVER NAMED.
|
|
137
|
+
React Native mirrors `flexDirection: 'row'`
|
|
138
|
+
and `textAlign` when the process is RTL — the
|
|
139
|
+
App calls `I18nManager.forceRTL` — so naming
|
|
140
|
+
the RTL edge for an RTL reader asks for it
|
|
141
|
+
twice and lands on the LTR one. That is why
|
|
142
|
+
this header drew its title LEFT and its ✕
|
|
143
|
+
RIGHT in Arabic while the option rows below
|
|
144
|
+
it, which already went through `readerEdge`,
|
|
145
|
+
were correct. See `readerEdge`. */
|
|
146
|
+
{ flexDirection: edge.flexDirection }
|
|
137
147
|
],
|
|
138
148
|
children: [
|
|
139
149
|
/* @__PURE__ */ jsx(
|
|
@@ -143,7 +153,13 @@ const SelectPopupOverlay = ({ visible, onClose, title, children }) => {
|
|
|
143
153
|
style: [
|
|
144
154
|
styles.title,
|
|
145
155
|
{ color: textColor, fontFamily: headingFont },
|
|
146
|
-
|
|
156
|
+
{
|
|
157
|
+
textAlign: edge.textAlign,
|
|
158
|
+
/* `writingDirection` is NOT mirrored — it
|
|
159
|
+
orders glyphs inside the line, so it
|
|
160
|
+
takes the reader's own direction. */
|
|
161
|
+
writingDirection: isRTL ? "rtl" : "ltr"
|
|
162
|
+
}
|
|
147
163
|
],
|
|
148
164
|
children: title
|
|
149
165
|
}
|
|
@@ -178,15 +194,11 @@ const styles = StyleSheet.create({
|
|
|
178
194
|
borderBottomWidth: 1,
|
|
179
195
|
gap: 12
|
|
180
196
|
},
|
|
181
|
-
headerLtr: { flexDirection: "row" },
|
|
182
|
-
headerRtl: { flexDirection: "row-reverse" },
|
|
183
197
|
title: {
|
|
184
198
|
flex: 1,
|
|
185
199
|
fontSize: 17,
|
|
186
200
|
fontWeight: "700"
|
|
187
201
|
},
|
|
188
|
-
titleLtr: { textAlign: "left", writingDirection: "ltr" },
|
|
189
|
-
titleRtl: { textAlign: "right", writingDirection: "rtl" },
|
|
190
202
|
close: {
|
|
191
203
|
fontSize: 18
|
|
192
204
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectPopupOverlay.js","sources":["../../../../../src/form/selects/helpers/SelectPopupOverlay.native.tsx"],"sourcesContent":["import React from 'react';\nimport {Pressable, StyleSheet, Text, View, useWindowDimensions} from 'react-native';\nimport {useSafeAreaInsets} from 'react-native-safe-area-context';\nimport {useKeyboardHeight} from '@lmb-it/kitsconcertomobile';\nimport KitsDialogControlled from '../../../components/overlay/Dialog';\nimport {useKitsTheme} from '../../../contexts/Theme';\nimport {useLanguage} from '../../../hooks';\nimport useThemedFont from '../../../hooks/useThemedFont';\n\n/**\n * The container a select's options live in when `popup` is on.\n *\n * ⛔ THIS IS NOT A SECOND OVERLAY MECHANISM. It is the library's own\n * `KitsDialogControlled` in bottom-sheet position, so the scrim, the slide\n * animation, the safe-area insets, `dismissableMask` and Android's hardware\n * back all come from the one place that already owns them. The only thing\n * this file adds is the header row and the sizing a picker needs.\n *\n * ⛔ IT USES `content` MODE ON PURPOSE. The Dialog's default template puts its\n * children inside a ScrollView with `maxHeight: 400`. A FlatList inside a\n * ScrollView scrolls badly and warns, and a 400pt cap is the very thing the\n * popup exists to escape. `content` replaces the whole header/body/footer\n * template, so the caller's search field can be pinned and its list can own\n * the remaining height.\n *\n * WHY A FIXED FRACTION OF THE SCREEN. The popup's whole purpose is that its\n * height does not depend on where the trigger happens to sit or on whether the\n * keyboard is up. It takes a fixed share of the window, the search field sits\n * at the top of it, and the list takes the rest.\n */\n\nexport interface ISelectPopupOverlayProps {\n /** Open state. */\n visible: boolean;\n /** Called for every dismissal route — the ✕, the scrim, Android back. */\n onClose: () => void;\n /** Title shown beside the close control. Falls back to the field's label. */\n title?: React.ReactNode;\n /** The search field and the list. */\n children: React.ReactNode;\n}\n\n/** Share of the window height the sheet occupies when the keyboard is down. */\nconst SHEET_HEIGHT_RATIO = 0.75;\n/** Never shorter than this, even on a small screen with the keyboard up. */\nconst SHEET_MIN_HEIGHT = 240;\n\nconst SelectPopupOverlay = ({visible, onClose, title, children}: ISelectPopupOverlayProps) => {\n const {resolveToken} = useKitsTheme();\n const {isRTL} = useLanguage();\n const insets = useSafeAreaInsets();\n const {height: windowHeight} = useWindowDimensions();\n /* Raw RN <Text> does not inherit `fontFamily` through View parents, so a\n consumer that swaps the theme font would see it everywhere except here.\n See useThemedFont's own note. */\n const headingFont = useThemedFont('heading');\n const keyboardHeight = useKeyboardHeight();\n\n /*\n * ⛔ \"OWNS THE SCREEN\" MEANS ABOVE THE KEYBOARD, NOT BEHIND IT. A sheet\n * pinned to the bottom at a fixed 75% still has its lower half under the\n * keyboard the moment the search field takes focus, and the list is back to\n * competing for the gap — the exact fault the popup exists to escape, one\n * layer along. The sheet is capped to what is actually visible.\n *\n * The keyboard replaces the bottom inset rather than adding to it: it\n * covers the home indicator.\n */\n const visibleHeight = windowHeight - Math.max(insets.bottom, keyboardHeight) - insets.top;\n const sheetHeight = Math.max(\n Math.min(SHEET_MIN_HEIGHT, visibleHeight),\n Math.min(Math.round(windowHeight * SHEET_HEIGHT_RATIO), visibleHeight),\n );\n\n const textColor = resolveToken('text');\n const borderColor = resolveToken('border');\n const grabberColor = resolveToken('gray.300');\n const subtextColor = resolveToken('gray.500');\n\n return (\n <KitsDialogControlled\n onHide={onClose}\n state={{\n visible,\n position: 'bottom',\n // Tap-outside dismiss. `onRequestClose` inside the Dialog gives\n // Android's back button the same route.\n dismissableMask: true,\n onClose,\n style: {\n height: sheetHeight,\n // The keyboard already clears the home indicator, so the\n // inset is only owed when the keyboard is down.\n paddingBottom: keyboardHeight > 0 ? 0 : insets.bottom,\n marginBottom: keyboardHeight,\n },\n content: ({hide}: {hide: () => void}) => (\n <View style={styles.root}>\n <View style={[styles.grabber, {backgroundColor: grabberColor}]} />\n <View\n style={[\n styles.header,\n {borderBottomColor: borderColor},\n // The header mirrors with the reader, like every\n // other row this library draws.\n isRTL ? styles.headerRtl : styles.headerLtr,\n ]}>\n <Text\n numberOfLines={1}\n style={[\n styles.title,\n {color: textColor, fontFamily: headingFont},\n isRTL ? styles.titleRtl : styles.titleLtr,\n ]}>\n {title}\n </Text>\n <Pressable onPress={hide} hitSlop={12} accessibilityRole=\"button\">\n <Text style={[styles.close, {color: subtextColor}]}>✕</Text>\n </Pressable>\n </View>\n {children}\n </View>\n ),\n }}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n minHeight: 0,\n },\n grabber: {\n width: 40,\n height: 4,\n borderRadius: 2,\n alignSelf: 'center',\n marginTop: 8,\n marginBottom: 4,\n },\n header: {\n alignItems: 'center',\n paddingHorizontal: 16,\n paddingVertical: 12,\n borderBottomWidth: 1,\n gap: 12,\n },\n headerLtr: {flexDirection: 'row'},\n headerRtl: {flexDirection: 'row-reverse'},\n title: {\n flex: 1,\n fontSize: 17,\n fontWeight: '700',\n },\n titleLtr: {textAlign: 'left', writingDirection: 'ltr'},\n titleRtl: {textAlign: 'right', writingDirection: 'rtl'},\n close: {\n fontSize: 18,\n },\n});\n\nexport default SelectPopupOverlay;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SelectPopupOverlay.js","sources":["../../../../../src/form/selects/helpers/SelectPopupOverlay.native.tsx"],"sourcesContent":["import React from 'react';\nimport {Pressable, StyleSheet, Text, View, useWindowDimensions} from 'react-native';\nimport {useSafeAreaInsets} from 'react-native-safe-area-context';\nimport {readerEdge, useKeyboardHeight} from '@lmb-it/kitsconcertomobile';\nimport KitsDialogControlled from '../../../components/overlay/Dialog';\nimport {useKitsTheme} from '../../../contexts/Theme';\nimport {useLanguage} from '../../../hooks';\nimport useThemedFont from '../../../hooks/useThemedFont';\n\n/**\n * The container a select's options live in when `popup` is on.\n *\n * ⛔ THIS IS NOT A SECOND OVERLAY MECHANISM. It is the library's own\n * `KitsDialogControlled` in bottom-sheet position, so the scrim, the slide\n * animation, the safe-area insets, `dismissableMask` and Android's hardware\n * back all come from the one place that already owns them. The only thing\n * this file adds is the header row and the sizing a picker needs.\n *\n * ⛔ IT USES `content` MODE ON PURPOSE. The Dialog's default template puts its\n * children inside a ScrollView with `maxHeight: 400`. A FlatList inside a\n * ScrollView scrolls badly and warns, and a 400pt cap is the very thing the\n * popup exists to escape. `content` replaces the whole header/body/footer\n * template, so the caller's search field can be pinned and its list can own\n * the remaining height.\n *\n * WHY A FIXED FRACTION OF THE SCREEN. The popup's whole purpose is that its\n * height does not depend on where the trigger happens to sit or on whether the\n * keyboard is up. It takes a fixed share of the window, the search field sits\n * at the top of it, and the list takes the rest.\n */\n\nexport interface ISelectPopupOverlayProps {\n /** Open state. */\n visible: boolean;\n /** Called for every dismissal route — the ✕, the scrim, Android back. */\n onClose: () => void;\n /** Title shown beside the close control. Falls back to the field's label. */\n title?: React.ReactNode;\n /** The search field and the list. */\n children: React.ReactNode;\n}\n\n/** Share of the window height the sheet occupies when the keyboard is down. */\nconst SHEET_HEIGHT_RATIO = 0.75;\n/** Never shorter than this, even on a small screen with the keyboard up. */\nconst SHEET_MIN_HEIGHT = 240;\n\nconst SelectPopupOverlay = ({visible, onClose, title, children}: ISelectPopupOverlayProps) => {\n const {resolveToken} = useKitsTheme();\n const {isRTL} = useLanguage();\n /* Physical edges to write so the header lands where the reader starts. */\n const edge = readerEdge(isRTL);\n const insets = useSafeAreaInsets();\n const {height: windowHeight} = useWindowDimensions();\n /* Raw RN <Text> does not inherit `fontFamily` through View parents, so a\n consumer that swaps the theme font would see it everywhere except here.\n See useThemedFont's own note. */\n const headingFont = useThemedFont('heading');\n const keyboardHeight = useKeyboardHeight();\n\n /*\n * ⛔ \"OWNS THE SCREEN\" MEANS ABOVE THE KEYBOARD, NOT BEHIND IT. A sheet\n * pinned to the bottom at a fixed 75% still has its lower half under the\n * keyboard the moment the search field takes focus, and the list is back to\n * competing for the gap — the exact fault the popup exists to escape, one\n * layer along. The sheet is capped to what is actually visible.\n *\n * The keyboard replaces the bottom inset rather than adding to it: it\n * covers the home indicator.\n */\n const visibleHeight = windowHeight - Math.max(insets.bottom, keyboardHeight) - insets.top;\n const sheetHeight = Math.max(\n Math.min(SHEET_MIN_HEIGHT, visibleHeight),\n Math.min(Math.round(windowHeight * SHEET_HEIGHT_RATIO), visibleHeight),\n );\n\n const textColor = resolveToken('text');\n const borderColor = resolveToken('border');\n const grabberColor = resolveToken('gray.300');\n const subtextColor = resolveToken('gray.500');\n\n return (\n <KitsDialogControlled\n onHide={onClose}\n state={{\n visible,\n position: 'bottom',\n // Tap-outside dismiss. `onRequestClose` inside the Dialog gives\n // Android's back button the same route.\n dismissableMask: true,\n onClose,\n style: {\n height: sheetHeight,\n // The keyboard already clears the home indicator, so the\n // inset is only owed when the keyboard is down.\n paddingBottom: keyboardHeight > 0 ? 0 : insets.bottom,\n marginBottom: keyboardHeight,\n },\n content: ({hide}: {hide: () => void}) => (\n <View style={styles.root}>\n <View style={[styles.grabber, {backgroundColor: grabberColor}]} />\n <View\n style={[\n styles.header,\n {borderBottomColor: borderColor},\n /* ⛔ THE PHYSICAL EDGE IS COMPUTED, NEVER NAMED.\n React Native mirrors `flexDirection: 'row'`\n and `textAlign` when the process is RTL — the\n App calls `I18nManager.forceRTL` — so naming\n the RTL edge for an RTL reader asks for it\n twice and lands on the LTR one. That is why\n this header drew its title LEFT and its ✕\n RIGHT in Arabic while the option rows below\n it, which already went through `readerEdge`,\n were correct. See `readerEdge`. */\n {flexDirection: edge.flexDirection},\n ]}>\n <Text\n numberOfLines={1}\n style={[\n styles.title,\n {color: textColor, fontFamily: headingFont},\n {\n textAlign: edge.textAlign,\n /* `writingDirection` is NOT mirrored — it\n orders glyphs inside the line, so it\n takes the reader's own direction. */\n writingDirection: isRTL ? ('rtl' as const) : ('ltr' as const),\n },\n ]}>\n {title}\n </Text>\n <Pressable onPress={hide} hitSlop={12} accessibilityRole=\"button\">\n <Text style={[styles.close, {color: subtextColor}]}>✕</Text>\n </Pressable>\n </View>\n {children}\n </View>\n ),\n }}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n minHeight: 0,\n },\n grabber: {\n width: 40,\n height: 4,\n borderRadius: 2,\n alignSelf: 'center',\n marginTop: 8,\n marginBottom: 4,\n },\n header: {\n alignItems: 'center',\n paddingHorizontal: 16,\n paddingVertical: 12,\n borderBottomWidth: 1,\n gap: 12,\n },\n title: {\n flex: 1,\n fontSize: 17,\n fontWeight: '700',\n },\n close: {\n fontSize: 18,\n },\n});\n\nexport default SelectPopupOverlay;\n"],"names":[],"mappings":"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"}
|