@lmb-it/kitsconcerto-native 0.1.257 → 0.1.259
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 +96 -76
- package/apps/mobile/src/Core/Dropdown/index.cjs.map +1 -1
- package/apps/mobile/src/Core/Dropdown/index.js +98 -78
- package/apps/mobile/src/Core/Dropdown/index.js.map +1 -1
- package/apps/mobile/src/Core/Dropdown/panelGeometry.cjs +43 -0
- package/apps/mobile/src/Core/Dropdown/panelGeometry.cjs.map +1 -0
- package/apps/mobile/src/Core/Dropdown/panelGeometry.js +38 -0
- package/apps/mobile/src/Core/Dropdown/panelGeometry.js.map +1 -0
- package/apps/mobile/src/Core/MultiSelect/index.cjs +34 -5
- package/apps/mobile/src/Core/MultiSelect/index.cjs.map +1 -1
- package/apps/mobile/src/Core/MultiSelect/index.js +34 -5
- package/apps/mobile/src/Core/MultiSelect/index.js.map +1 -1
- package/apps/mobile/src/Factory/helpers/useKeyboardHeight.cjs +24 -0
- package/apps/mobile/src/Factory/helpers/useKeyboardHeight.cjs.map +1 -0
- package/apps/mobile/src/Factory/helpers/useKeyboardHeight.js +22 -0
- package/apps/mobile/src/Factory/helpers/useKeyboardHeight.js.map +1 -0
- package/form/selects/Dropdown/index.cjs +11 -2
- package/form/selects/Dropdown/index.cjs.map +1 -1
- package/form/selects/Dropdown/index.js +11 -2
- package/form/selects/Dropdown/index.js.map +1 -1
- package/form/selects/ListBox/index.cjs +6 -2
- package/form/selects/ListBox/index.cjs.map +1 -1
- package/form/selects/ListBox/index.js +6 -2
- package/form/selects/ListBox/index.js.map +1 -1
- package/form/selects/MultiSelect/index.cjs +11 -2
- package/form/selects/MultiSelect/index.cjs.map +1 -1
- package/form/selects/MultiSelect/index.js +11 -2
- package/form/selects/MultiSelect/index.js.map +1 -1
- package/form/selects/helpers/SelectPopupOverlay.cjs +200 -0
- package/form/selects/helpers/SelectPopupOverlay.cjs.map +1 -0
- package/form/selects/helpers/SelectPopupOverlay.js +196 -0
- package/form/selects/helpers/SelectPopupOverlay.js.map +1 -0
- package/form-renderer/components/fields/Select/index.cjs +2 -0
- package/form-renderer/components/fields/Select/index.cjs.map +1 -1
- package/form-renderer/components/fields/Select/index.js +2 -0
- package/form-renderer/components/fields/Select/index.js.map +1 -1
- package/index.d.ts +15 -0
- package/locale/ar.json.cjs +3 -0
- package/locale/ar.json.cjs.map +1 -1
- package/locale/ar.json.js +3 -1
- package/locale/ar.json.js.map +1 -1
- package/locale/en.json.cjs +3 -0
- package/locale/en.json.cjs.map +1 -1
- package/locale/en.json.js +3 -1
- package/locale/en.json.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import React, { useRef, useState, useMemo, useCallback } from 'react';
|
|
3
|
-
import { Text, Pressable, View, Modal, Dimensions, TextInput, FlatList,
|
|
3
|
+
import { Text, Pressable, View, Modal, Dimensions, Keyboard, TextInput, FlatList, StyleSheet } from 'react-native';
|
|
4
4
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
5
|
import '../../ui/accordion/index.js';
|
|
6
6
|
import '../../ui/actionsheet/index.js';
|
|
@@ -61,6 +61,7 @@ import '../../../../../packages/types/src/Theme/shadows.js';
|
|
|
61
61
|
import '../../../../../packages/types/src/Theme/defaultTheme.js';
|
|
62
62
|
import '../../../../../packages/types/src/Theme/alphaColors.js';
|
|
63
63
|
import '../../Factory/helpers/cssVarMap.js';
|
|
64
|
+
import { useKeyboardHeight } from '../../Factory/helpers/useKeyboardHeight.js';
|
|
64
65
|
import 'i18next';
|
|
65
66
|
import 'react-i18next';
|
|
66
67
|
import '../AutoComplete/index.js';
|
|
@@ -80,6 +81,7 @@ import '../RadioButton/index.js';
|
|
|
80
81
|
import '../Input/index.js';
|
|
81
82
|
import '../Input/Field.js';
|
|
82
83
|
import '../Input/Slot.js';
|
|
84
|
+
import { computePanelGeometry } from './panelGeometry.js';
|
|
83
85
|
|
|
84
86
|
const getLabel = (item, optionLabel) => {
|
|
85
87
|
if (!item) return "";
|
|
@@ -131,7 +133,18 @@ function Dropdown({
|
|
|
131
133
|
highlightOnSelect = true,
|
|
132
134
|
filter,
|
|
133
135
|
filterBy,
|
|
136
|
+
// ⚠️ THESE TWO ENGLISH LITERALS ARE A LAST RESORT, NOT A
|
|
137
|
+
// TRANSLATION. The KitsConcerto wrapper always passes the
|
|
138
|
+
// reader's own sentence; these only apply when this component
|
|
139
|
+
// is used directly from `apps/mobile`, and they exist so a
|
|
140
|
+
// panel can never render a blank search box or an empty
|
|
141
|
+
// "no results" line. Recorded as English fallbacks.
|
|
134
142
|
emptyFilterMessage = "No results found",
|
|
143
|
+
filterPlaceholder = "Search...",
|
|
144
|
+
isRTL = false,
|
|
145
|
+
popup = false,
|
|
146
|
+
renderPopup,
|
|
147
|
+
popupTitle,
|
|
135
148
|
itemTemplate,
|
|
136
149
|
valueTemplate,
|
|
137
150
|
panelFooterTemplate,
|
|
@@ -150,6 +163,7 @@ function Dropdown({
|
|
|
150
163
|
const [filterQuery, setFilterQuery] = useState("");
|
|
151
164
|
const [triggerLayout, setTriggerLayout] = useState(null);
|
|
152
165
|
const insets = useSafeAreaInsets();
|
|
166
|
+
const keyboardHeight = useKeyboardHeight();
|
|
153
167
|
const { viewStyle: externalViewStyle, textStyle: externalTextStyle } = splitStyle(externalStyle);
|
|
154
168
|
const colors = {
|
|
155
169
|
border: t("border") || t("gray.300"),
|
|
@@ -270,6 +284,46 @@ function Dropdown({
|
|
|
270
284
|
});
|
|
271
285
|
}, [flatData, filter, filterQuery, filterBy, optionLabel]);
|
|
272
286
|
const displayValue = valueTemplate ? valueTemplate(selectedItem) : selectedItem ? getLabel(selectedItem, optionLabel) : "";
|
|
287
|
+
const panelBody = (fill, autoFocusFilter) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
288
|
+
filter && /* @__PURE__ */ jsx(View, { style: [styles.filterContainer, { borderBottomColor: colors.filterBorder }], children: /* @__PURE__ */ jsx(
|
|
289
|
+
TextInput,
|
|
290
|
+
{
|
|
291
|
+
value: filterQuery,
|
|
292
|
+
onChangeText: setFilterQuery,
|
|
293
|
+
placeholder: filterPlaceholder,
|
|
294
|
+
placeholderTextColor: colors.placeholder,
|
|
295
|
+
style: [
|
|
296
|
+
styles.filterInput,
|
|
297
|
+
{
|
|
298
|
+
borderColor: colors.filterBorder,
|
|
299
|
+
backgroundColor: colors.filterBg,
|
|
300
|
+
color: colors.filterText
|
|
301
|
+
},
|
|
302
|
+
// Every other input in this library names its own edge and
|
|
303
|
+
// direction from the reader's language. Leaving these unset let the
|
|
304
|
+
// platform decide, so the field followed the process layout
|
|
305
|
+
// direction instead of the person reading it.
|
|
306
|
+
isRTL ? styles.filterRtl : styles.filterLtr
|
|
307
|
+
],
|
|
308
|
+
autoFocus: autoFocusFilter
|
|
309
|
+
}
|
|
310
|
+
) }),
|
|
311
|
+
/* @__PURE__ */ jsx(
|
|
312
|
+
FlatList,
|
|
313
|
+
{
|
|
314
|
+
style: fill ? styles.listFill : styles.listShrink,
|
|
315
|
+
contentContainerStyle: styles.listContent,
|
|
316
|
+
data: filteredData,
|
|
317
|
+
keyExtractor: (item, i) => item?.__group ? `group-${i}` : `item-${i}`,
|
|
318
|
+
renderItem: ({ item, index }) => renderItem(item, index),
|
|
319
|
+
keyboardShouldPersistTaps: "handled",
|
|
320
|
+
showsHorizontalScrollIndicator: false,
|
|
321
|
+
ListEmptyComponent: filterQuery ? /* @__PURE__ */ jsx(View, { style: styles.emptyContainer, children: /* @__PURE__ */ jsx(Text, { style: [styles.emptyText, { color: colors.emptyText }], children: emptyFilterMessage }) }) : null
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
panelFooterTemplate?.()
|
|
325
|
+
] });
|
|
326
|
+
const usePopup = popup && typeof renderPopup === "function";
|
|
273
327
|
return /* @__PURE__ */ jsxs(View, { style: styles.container, testID, children: [
|
|
274
328
|
/* @__PURE__ */ jsxs(
|
|
275
329
|
Pressable,
|
|
@@ -313,7 +367,7 @@ function Dropdown({
|
|
|
313
367
|
]
|
|
314
368
|
}
|
|
315
369
|
),
|
|
316
|
-
!isModal && /* @__PURE__ */ jsx(
|
|
370
|
+
!usePopup && !isModal && /* @__PURE__ */ jsx(
|
|
317
371
|
Modal,
|
|
318
372
|
{
|
|
319
373
|
visible: open && !!triggerLayout,
|
|
@@ -322,17 +376,15 @@ function Dropdown({
|
|
|
322
376
|
onRequestClose: close,
|
|
323
377
|
children: /* @__PURE__ */ jsx(Pressable, { style: styles.popoverOverlay, onPress: close, children: triggerLayout && (() => {
|
|
324
378
|
const screen = Dimensions.get("window");
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const top = openUp ? triggerLayout.y - maxHeight - GAP : triggerLayout.y + triggerLayout.height + GAP;
|
|
335
|
-
return /* @__PURE__ */ jsxs(
|
|
379
|
+
const { top, maxHeight } = computePanelGeometry({
|
|
380
|
+
screenHeight: screen.height,
|
|
381
|
+
insetTop: insets.top,
|
|
382
|
+
insetBottom: insets.bottom,
|
|
383
|
+
keyboardHeight,
|
|
384
|
+
triggerY: triggerLayout.y,
|
|
385
|
+
triggerHeight: triggerLayout.height
|
|
386
|
+
});
|
|
387
|
+
return /* @__PURE__ */ jsx(
|
|
336
388
|
View,
|
|
337
389
|
{
|
|
338
390
|
style: [
|
|
@@ -347,80 +399,26 @@ function Dropdown({
|
|
|
347
399
|
}
|
|
348
400
|
],
|
|
349
401
|
onStartShouldSetResponder: () => true,
|
|
350
|
-
children:
|
|
351
|
-
filter && /* @__PURE__ */ jsx(View, { style: [styles.filterContainer, { borderBottomColor: colors.filterBorder }], children: /* @__PURE__ */ jsx(
|
|
352
|
-
TextInput,
|
|
353
|
-
{
|
|
354
|
-
value: filterQuery,
|
|
355
|
-
onChangeText: setFilterQuery,
|
|
356
|
-
placeholder: "Search...",
|
|
357
|
-
placeholderTextColor: colors.placeholder,
|
|
358
|
-
style: [styles.filterInput, {
|
|
359
|
-
borderColor: colors.filterBorder,
|
|
360
|
-
backgroundColor: colors.filterBg,
|
|
361
|
-
color: colors.filterText
|
|
362
|
-
}],
|
|
363
|
-
autoFocus: true
|
|
364
|
-
}
|
|
365
|
-
) }),
|
|
366
|
-
/* @__PURE__ */ jsx(
|
|
367
|
-
FlatList,
|
|
368
|
-
{
|
|
369
|
-
style: { flex: 1 },
|
|
370
|
-
contentContainerStyle: { paddingBottom: 12 },
|
|
371
|
-
data: filteredData,
|
|
372
|
-
keyExtractor: (item, i) => item?.__group ? `group-${i}` : `item-${i}`,
|
|
373
|
-
renderItem: ({ item, index }) => renderItem(item, index),
|
|
374
|
-
keyboardShouldPersistTaps: "handled",
|
|
375
|
-
showsHorizontalScrollIndicator: false,
|
|
376
|
-
ListEmptyComponent: filterQuery ? /* @__PURE__ */ jsx(View, { style: styles.emptyContainer, children: /* @__PURE__ */ jsx(Text, { style: [styles.emptyText, { color: colors.emptyText }], children: emptyFilterMessage }) }) : null
|
|
377
|
-
}
|
|
378
|
-
),
|
|
379
|
-
panelFooterTemplate?.()
|
|
380
|
-
]
|
|
402
|
+
children: panelBody(false, true)
|
|
381
403
|
}
|
|
382
404
|
);
|
|
383
405
|
})() })
|
|
384
406
|
}
|
|
385
407
|
),
|
|
386
|
-
isModal && /* @__PURE__ */ jsx(Modal, { visible: open, transparent: true, animationType: "fade", children: /* @__PURE__ */ jsx(Pressable, { style: styles.modalOverlay, onPress: close, children: /* @__PURE__ */
|
|
408
|
+
!usePopup && isModal && /* @__PURE__ */ jsx(Modal, { visible: open, transparent: true, animationType: "fade", children: /* @__PURE__ */ jsx(Pressable, { style: styles.modalOverlay, onPress: close, children: /* @__PURE__ */ jsx(
|
|
387
409
|
View,
|
|
388
410
|
{
|
|
389
411
|
style: [styles.modalPanel, { backgroundColor: colors.bg }],
|
|
390
412
|
onStartShouldSetResponder: () => true,
|
|
391
|
-
children:
|
|
392
|
-
filter && /* @__PURE__ */ jsx(View, { style: [styles.filterContainer, { borderBottomColor: colors.filterBorder }], children: /* @__PURE__ */ jsx(
|
|
393
|
-
TextInput,
|
|
394
|
-
{
|
|
395
|
-
value: filterQuery,
|
|
396
|
-
onChangeText: setFilterQuery,
|
|
397
|
-
placeholder: "Search...",
|
|
398
|
-
placeholderTextColor: colors.placeholder,
|
|
399
|
-
style: [styles.filterInput, {
|
|
400
|
-
borderColor: colors.filterBorder,
|
|
401
|
-
backgroundColor: colors.filterBg,
|
|
402
|
-
color: colors.filterText
|
|
403
|
-
}],
|
|
404
|
-
autoFocus: true
|
|
405
|
-
}
|
|
406
|
-
) }),
|
|
407
|
-
/* @__PURE__ */ jsx(
|
|
408
|
-
FlatList,
|
|
409
|
-
{
|
|
410
|
-
data: filteredData,
|
|
411
|
-
keyExtractor: (item, i) => item?.__group ? `group-${i}` : `item-${i}`,
|
|
412
|
-
renderItem: ({ item, index }) => renderItem(item, index),
|
|
413
|
-
nestedScrollEnabled: true,
|
|
414
|
-
keyboardShouldPersistTaps: "handled",
|
|
415
|
-
style: { flex: 1 },
|
|
416
|
-
contentContainerStyle: { paddingBottom: 12 },
|
|
417
|
-
ListEmptyComponent: filterQuery ? /* @__PURE__ */ jsx(View, { style: styles.emptyContainer, children: /* @__PURE__ */ jsx(Text, { style: [styles.emptyText, { color: colors.emptyText }], children: emptyFilterMessage }) }) : null
|
|
418
|
-
}
|
|
419
|
-
),
|
|
420
|
-
panelFooterTemplate?.()
|
|
421
|
-
]
|
|
413
|
+
children: panelBody(true, true)
|
|
422
414
|
}
|
|
423
|
-
) }) })
|
|
415
|
+
) }) }),
|
|
416
|
+
usePopup && renderPopup({
|
|
417
|
+
visible: open,
|
|
418
|
+
onClose: close,
|
|
419
|
+
title: popupTitle,
|
|
420
|
+
children: panelBody(true, false)
|
|
421
|
+
})
|
|
424
422
|
] });
|
|
425
423
|
}
|
|
426
424
|
const styles = StyleSheet.create({
|
|
@@ -472,6 +470,20 @@ const styles = StyleSheet.create({
|
|
|
472
470
|
paddingVertical: 6,
|
|
473
471
|
borderBottomWidth: 1
|
|
474
472
|
},
|
|
473
|
+
listFill: {
|
|
474
|
+
flex: 1
|
|
475
|
+
},
|
|
476
|
+
// The anchored panel is sized by `maxHeight` with no height, so the list has
|
|
477
|
+
// to grow with its content up to that ceiling and then scroll inside it.
|
|
478
|
+
// `flex: 1` sets flexBasis 0, which contributes nothing to a shrink-wrapping
|
|
479
|
+
// parent.
|
|
480
|
+
listShrink: {
|
|
481
|
+
flexGrow: 0,
|
|
482
|
+
flexShrink: 1
|
|
483
|
+
},
|
|
484
|
+
listContent: {
|
|
485
|
+
paddingBottom: 12
|
|
486
|
+
},
|
|
475
487
|
filterInput: {
|
|
476
488
|
fontSize: 14,
|
|
477
489
|
paddingHorizontal: 8,
|
|
@@ -479,6 +491,14 @@ const styles = StyleSheet.create({
|
|
|
479
491
|
borderWidth: 1,
|
|
480
492
|
borderRadius: 4
|
|
481
493
|
},
|
|
494
|
+
filterLtr: {
|
|
495
|
+
textAlign: "left",
|
|
496
|
+
writingDirection: "ltr"
|
|
497
|
+
},
|
|
498
|
+
filterRtl: {
|
|
499
|
+
textAlign: "right",
|
|
500
|
+
writingDirection: "rtl"
|
|
501
|
+
},
|
|
482
502
|
emptyContainer: {
|
|
483
503
|
padding: 16,
|
|
484
504
|
alignItems: "center"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../../../../../../apps/mobile/src/Core/Dropdown/index.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useRef, useState, type ChangeEvent } from \"react\";\nimport {\n Dimensions,\n FlatList,\n Keyboard,\n Modal,\n Pressable,\n ScrollView,\n TouchableOpacity,\n View,\n StyleSheet, Text, TextInput\n} from \"react-native\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\nimport { Input, InputField, type VirtualScrollerOptions } from \"../../\";\nimport { resolveThemeTokenForNative } from '../../Factory/helpers/style';\n\n/* ---------------------------------------------\n Types (PrimeReact-compatible)\n---------------------------------------------- */\n\nexport interface DropdownChangeEvent<T = any> {\n originalEvent?: any;\n value: T | null;\n}\n\n\nexport interface DropdownProps<T = any> {\n value?: T | null;\n options?: T[];\n\n onChange?: (e: ChangeEvent<T>, item:any) => void;\n\n optionLabel?: string;\n optionValue?: string;\n\n placeholder?: string;\n disabled?: boolean;\n editable?: boolean;\n loading?: boolean;\n showClear?: boolean;\n isModal?: boolean;\n\n checkmark?: boolean;\n highlightOnSelect?: boolean;\n\n filter?: boolean;\n filterBy?: string;\n emptyFilterMessage?: string;\n\n itemTemplate?: (item: T) => React.ReactNode;\n valueTemplate?: (value: T | null) => React.ReactNode;\n panelFooterTemplate?: () => React.ReactNode;\n\n optionGroupLabel?: string;\n optionGroupChildren?: string;\n optionGroupTemplate?: (group: any) => React.ReactNode;\n\n virtualScrollerOptions?: VirtualScrollerOptions;\n\n invalid?: boolean;\n testID?: string;\n style?: import('react-native').ViewStyle;\n}\n\n/* ---------------------------------------------\n Helpers\n---------------------------------------------- */\n\nconst getLabel = (item: any, optionLabel?: string) => {\n if (!item) return \"\";\n if (!optionLabel) return String(item);\n return String(item[optionLabel]);\n};\n\nconst getValue = (item: any, optionValue?: string) => {\n if (!item) return null;\n if (!optionValue) return item;\n return item[optionValue];\n};\n\nconst t = resolveThemeTokenForNative;\n\nconst TEXT_STYLE_KEYS = new Set([\n 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle',\n 'color', 'letterSpacing', 'lineHeight', 'textAlign',\n 'textDecorationLine', 'textTransform',\n]);\n\nfunction splitStyle(combined?: any): { viewStyle?: any; textStyle?: any } {\n if (!combined) return {};\n const view: Record<string, any> = {};\n const text: Record<string, any> = {};\n for (const [k, v] of Object.entries(combined)) {\n if (TEXT_STYLE_KEYS.has(k)) text[k] = v;\n else view[k] = v;\n }\n return {\n viewStyle: Object.keys(view).length ? view : undefined,\n textStyle: Object.keys(text).length ? text : undefined,\n };\n}\n\n/* ---------------------------------------------\n Component\n---------------------------------------------- */\n\nexport default function Dropdown<T = any>({\n value,\n options = [],\n onChange,\n\n optionLabel,\n optionValue,\n\n placeholder,\n disabled,\n editable,\n isModal = false,\n\n checkmark,\n highlightOnSelect = true,\n\n filter,\n filterBy,\n emptyFilterMessage = 'No results found',\n\n itemTemplate,\n valueTemplate,\n panelFooterTemplate,\n\n optionGroupLabel,\n optionGroupChildren,\n optionGroupTemplate,\n\n virtualScrollerOptions,\n\n invalid,\n testID,\n style: externalStyle,\n }: DropdownProps<T>) {\n const inputRef = useRef<any>(null);\n const triggerRef = useRef<View>(null);\n const [open, setOpen] = useState(false);\n const [inputText, setInputText] = useState(\"\");\n const [filterQuery, setFilterQuery] = useState(\"\");\n const [triggerLayout, setTriggerLayout] = useState<{\n x: number; y: number; width: number; height: number;\n } | null>(null);\n const insets = useSafeAreaInsets();\n\n const { viewStyle: externalViewStyle, textStyle: externalTextStyle } = splitStyle(externalStyle);\n\n const colors = {\n border: t('border') || t('gray.300'),\n borderInvalid: t('danger') || t('red.500'),\n bg: t('surface-card') || '#fff',\n text: t('text') || t('gray.800'),\n placeholder: t('text-secondary') || t('gray.400'),\n optionSelected: t('highlight-bg') || t('gray.100'),\n groupBg: t('surface-ground') || t('gray.50'),\n filterBorder: t('border') || t('gray.200'),\n filterBg: t('surface-ground') || t('gray.50'),\n filterText: t('text') || t('gray.800'),\n emptyText: t('text-secondary') || t('gray.400'),\n primary: t('primary'),\n };\n\n /* ---------------------------------------------\n Derived\n ---------------------------------------------- */\n\n const selectedItem = useMemo(() => {\n if (value == null) return null;\n // Search through group children when grouped\n if (optionGroupChildren) {\n for (const group of options as any[]) {\n const children = group[optionGroupChildren];\n if (Array.isArray(children)) {\n const found = children.find((c: any) => getValue(c, optionValue) === value);\n if (found) return found;\n }\n }\n return null;\n }\n return options.find(\n (o) => getValue(o, optionValue) === value\n ) ?? null;\n }, [value, options, optionValue, optionGroupChildren]);\n\n /* ---------------------------------------------\n Open / Close\n ---------------------------------------------- */\n\n const toggle = () => {\n if (disabled) return;\n Keyboard.dismiss();\n if (open) {\n setOpen(false);\n return;\n }\n setFilterQuery(\"\");\n // Measure trigger position in window so the popover can be rendered\n // inside a Modal (escaping any parent ScrollView) at the correct anchor.\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerLayout({ x, y, width, height });\n setOpen(true);\n });\n };\n\n const close = () => setOpen(false);\n\n /* ---------------------------------------------\n Selection\n ---------------------------------------------- */\n\n const selectItem = (item: T) => {\n const newValue = getValue(item, optionValue);\n\n if (!onChange) return;\n\n const syntheticEvent = {\n target: {\n value: newValue,\n },\n } as React.ChangeEvent<any>;\n\n onChange(syntheticEvent, item);\n\n setOpen(false);\n };\n\n /* ---------------------------------------------\n Render item\n ---------------------------------------------- */\n\n const renderItem = useCallback(\n (item: any, index: number) => {\n if (!item) {\n return null;\n }\n\n // Group header\n if (item.__group) {\n return (\n <Text key={`group-${index}`} style={[styles.groupLabel, { backgroundColor: colors.groupBg }]}>\n {optionGroupLabel ? item.group?.[optionGroupLabel] : \"\"}\n </Text>\n );\n }\n\n const selected =\n value != null &&\n getValue(item, optionValue) === value;\n\n let content: React.ReactElement;\n\n if (itemTemplate) {\n const rendered = itemTemplate(item);\n\n content = React.isValidElement(rendered) ? (\n rendered\n ) : (\n <Text>{String(rendered ?? \"\")}</Text>\n );\n } else {\n content = (\n <Text style={[{ color: colors.text }, externalTextStyle && { fontSize: externalTextStyle.fontSize }]}>\n {getLabel(item, optionLabel)}\n </Text>\n );\n }\n\n return (\n <Pressable\n key={`item-${index}`}\n onPress={() => selectItem(item)}\n style={[\n styles.option,\n selected && highlightOnSelect && { backgroundColor: colors.optionSelected },\n ]}\n >\n {content}\n {checkmark && selected && <Text>✓</Text>}\n </Pressable>\n );\n },\n [\n value,\n optionValue,\n optionLabel,\n itemTemplate,\n optionGroupLabel,\n checkmark,\n highlightOnSelect,\n ]\n );\n\n\n /* ---------------------------------------------\n Group flattening\n ---------------------------------------------- */\n\n const flatData = useMemo(() => {\n if (!optionGroupLabel || !optionGroupChildren) {\n return options;\n }\n\n const out: any[] = [];\n options.forEach((g: any) => {\n out.push({ __group: true, group: g });\n g[optionGroupChildren]?.forEach((c: any) => out.push(c));\n });\n return out;\n }, [options]);\n\n /* ---------------------------------------------\n Filter\n ---------------------------------------------- */\n\n const filteredData = useMemo(() => {\n if (!filter || !filterQuery) return flatData;\n const q = filterQuery.toLowerCase();\n const key = filterBy || optionLabel;\n\n return flatData.filter((entry: any) => {\n // Keep group headers\n if (entry.__group) return true;\n const label = getLabel(entry, key);\n return label.toLowerCase().includes(q);\n });\n }, [flatData, filter, filterQuery, filterBy, optionLabel]);\n\n /* ---------------------------------------------\n Display value\n ---------------------------------------------- */\n\n const displayValue = valueTemplate\n ? valueTemplate(selectedItem)\n : selectedItem\n ? getLabel(selectedItem, optionLabel)\n : \"\";\n\n /* ---------------------------------------------\n Render\n ---------------------------------------------- */\n\n return (\n <View style={styles.container} testID={testID}>\n <Pressable\n ref={triggerRef as any}\n style={[\n styles.control,\n { borderColor: colors.border, backgroundColor: colors.bg },\n invalid && { borderColor: colors.borderInvalid },\n disabled && styles.disabled,\n externalViewStyle,\n ]}\n onPress={toggle}\n >\n {editable ? (\n <Input style={styles.input}>\n <InputField\n ref={inputRef}\n value={inputText}\n onChangeText={setInputText}\n placeholder={placeholder}\n editable={!disabled}\n />\n </Input>\n ) : (\n <Text\n numberOfLines={1}\n style={[\n // Fill the trigger row and left-align the value/placeholder so it\n // doesn't read as centred (the row is justify=space-between, so the\n // arrow stays pinned right).\n { flex: 1, textAlign: \"left\" as const },\n displayValue ? { color: colors.text } : { color: colors.placeholder },\n externalTextStyle,\n !displayValue && { color: colors.placeholder },\n ]}>\n {displayValue || placeholder}\n </Text>\n )}\n\n <Text style={[{ color: colors.text }, externalTextStyle]}>▾</Text>\n </Pressable>\n\n {!isModal && (\n <Modal\n visible={open && !!triggerLayout}\n transparent\n animationType=\"none\"\n onRequestClose={close}\n >\n <Pressable style={styles.popoverOverlay} onPress={close}>\n {triggerLayout && (() => {\n const screen = Dimensions.get('window');\n const PANEL_MAX_HEIGHT = 260;\n const GAP = 4;\n const spaceBelow =\n screen.height - (triggerLayout.y + triggerLayout.height) - insets.bottom;\n const spaceAbove = triggerLayout.y - insets.top;\n const openUp = spaceBelow < PANEL_MAX_HEIGHT && spaceAbove > spaceBelow;\n const maxHeight = Math.min(\n PANEL_MAX_HEIGHT,\n openUp ? spaceAbove - GAP : spaceBelow - GAP,\n );\n const top = openUp\n ? triggerLayout.y - maxHeight - GAP\n : triggerLayout.y + triggerLayout.height + GAP;\n\n return (\n <View\n style={[\n styles.popoverPanel,\n {\n top,\n left: triggerLayout.x,\n width: triggerLayout.width,\n maxHeight,\n borderColor: colors.border,\n backgroundColor: colors.bg,\n },\n ]}\n onStartShouldSetResponder={() => true}\n >\n {filter && (\n <View style={[styles.filterContainer, { borderBottomColor: colors.filterBorder }]}>\n <TextInput\n value={filterQuery}\n onChangeText={setFilterQuery}\n placeholder=\"Search...\"\n placeholderTextColor={colors.placeholder}\n style={[styles.filterInput, {\n borderColor: colors.filterBorder,\n backgroundColor: colors.filterBg,\n color: colors.filterText,\n }]}\n autoFocus\n />\n </View>\n )}\n <FlatList\n style={{ flex: 1 }}\n contentContainerStyle={{ paddingBottom: 12 }}\n data={filteredData}\n keyExtractor={(item: any, i: number) =>\n item?.__group ? `group-${i}` : `item-${i}`\n }\n renderItem={({ item, index }) => renderItem(item, index)}\n keyboardShouldPersistTaps=\"handled\"\n showsHorizontalScrollIndicator={false}\n ListEmptyComponent={\n filterQuery ? (\n <View style={styles.emptyContainer}>\n <Text style={[styles.emptyText, { color: colors.emptyText }]}>{emptyFilterMessage}</Text>\n </View>\n ) : null\n }\n />\n {panelFooterTemplate?.()}\n </View>\n );\n })()}\n </Pressable>\n </Modal>\n )}\n\n {isModal && (\n <Modal visible={open} transparent animationType=\"fade\">\n <Pressable style={styles.modalOverlay} onPress={close}>\n <View\n style={[styles.modalPanel, { backgroundColor: colors.bg }]}\n onStartShouldSetResponder={() => true}\n >\n {filter && (\n <View style={[styles.filterContainer, { borderBottomColor: colors.filterBorder }]}>\n <TextInput\n value={filterQuery}\n onChangeText={setFilterQuery}\n placeholder=\"Search...\"\n placeholderTextColor={colors.placeholder}\n style={[styles.filterInput, {\n borderColor: colors.filterBorder,\n backgroundColor: colors.filterBg,\n color: colors.filterText,\n }]}\n autoFocus\n />\n </View>\n )}\n <FlatList\n data={filteredData}\n keyExtractor={(item: any, i: number) =>\n item?.__group ? `group-${i}` : `item-${i}`\n }\n renderItem={({ item, index }) => renderItem(item, index)}\n nestedScrollEnabled\n keyboardShouldPersistTaps=\"handled\"\n style={{ flex: 1 }}\n contentContainerStyle={{ paddingBottom: 12 }}\n ListEmptyComponent={\n filterQuery ? (\n <View style={styles.emptyContainer}>\n <Text style={[styles.emptyText, { color: colors.emptyText }]}>{emptyFilterMessage}</Text>\n </View>\n ) : null\n }\n />\n {panelFooterTemplate?.()}\n </View>\n </Pressable>\n </Modal>\n )}\n </View>\n );\n}\n\n/* ---------------------------------------------\n Styles (layout only — colors applied inline via theme tokens)\n---------------------------------------------- */\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n position: \"relative\" as const,\n },\n control: {\n flexDirection: \"row\" as const,\n alignItems: \"center\" as const,\n justifyContent: \"space-between\" as const,\n borderWidth: 1,\n borderRadius: 6,\n paddingHorizontal: 10,\n paddingVertical: 10,\n },\n disabled: {\n opacity: 0.5,\n },\n option: {\n paddingHorizontal: 12,\n paddingVertical: 10,\n flexDirection: \"row\" as const,\n justifyContent: \"space-between\" as const,\n alignItems: \"center\" as const,\n },\n groupLabel: {\n paddingHorizontal: 12,\n paddingVertical: 8,\n fontWeight: \"600\" as const,\n },\n input: {\n flex: 1,\n },\n panel: {\n position: \"absolute\" as const,\n top: \"100%\",\n left: 0,\n right: 0,\n marginTop: 4,\n borderWidth: 1,\n borderRadius: 6,\n maxHeight: 260,\n overflow: \"hidden\" as const,\n zIndex: 9999,\n },\n filterContainer: {\n paddingHorizontal: 8,\n paddingVertical: 6,\n borderBottomWidth: 1,\n },\n filterInput: {\n fontSize: 14,\n paddingHorizontal: 8,\n paddingVertical: 6,\n borderWidth: 1,\n borderRadius: 4,\n },\n emptyContainer: {\n padding: 16,\n alignItems: 'center' as const,\n },\n emptyText: {\n fontSize: 14,\n },\n popoverOverlay: {\n flex: 1,\n backgroundColor: 'transparent',\n },\n popoverPanel: {\n position: 'absolute' as const,\n borderWidth: 1,\n borderRadius: 6,\n overflow: 'hidden' as const,\n elevation: 8,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n shadowOpacity: 0.15,\n shadowRadius: 8,\n },\n modalOverlay: {\n flex: 1,\n backgroundColor: 'rgba(0,0,0,0.4)',\n justifyContent: 'center' as const,\n },\n modalPanel: {\n margin: 24,\n maxHeight: '70%',\n borderRadius: 12,\n overflow: 'hidden' as const,\n },\n});\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../../../../apps/mobile/src/Core/Dropdown/index.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useRef, useState, type ChangeEvent } from \"react\";\nimport {\n Dimensions,\n FlatList,\n Keyboard,\n Modal,\n Pressable,\n ScrollView,\n TouchableOpacity,\n View,\n StyleSheet, Text, TextInput\n} from \"react-native\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\nimport { Input, InputField, type VirtualScrollerOptions } from \"../../\";\nimport { resolveThemeTokenForNative } from '../../Factory/helpers/style';\nimport { useKeyboardHeight } from '../../Factory/helpers/useKeyboardHeight';\nimport { computePanelGeometry } from './panelGeometry';\n\n/* ---------------------------------------------\n Types (PrimeReact-compatible)\n---------------------------------------------- */\n\nexport interface DropdownChangeEvent<T = any> {\n originalEvent?: any;\n value: T | null;\n}\n\n\nexport interface DropdownProps<T = any> {\n value?: T | null;\n options?: T[];\n\n onChange?: (e: ChangeEvent<T>, item:any) => void;\n\n optionLabel?: string;\n optionValue?: string;\n\n placeholder?: string;\n disabled?: boolean;\n editable?: boolean;\n loading?: boolean;\n showClear?: boolean;\n isModal?: boolean;\n\n checkmark?: boolean;\n highlightOnSelect?: boolean;\n\n filter?: boolean;\n filterBy?: string;\n emptyFilterMessage?: string;\n /**\n * Placeholder for the panel's filter field.\n *\n * ⛔ THIS COMPONENT CANNOT TRANSLATE. It lives in `apps/mobile`, which\n * `packages/ui` imports — the reverse import would be a cycle between the two\n * workspaces. So the sentence is resolved by the KitsConcerto wrapper, which\n * has `useLanguage()`, and arrives here already written, exactly as\n * `emptyFilterMessage` does.\n */\n filterPlaceholder?: string;\n /**\n * Open the options in a popup that owns the screen instead of a panel\n * anchored to the trigger.\n *\n * ⛔ THE ANCHOR IS THE PROBLEM THIS ESCAPES. An anchored panel can never be\n * taller than the gap between the trigger and the keyboard, and a field in\n * the middle of a form has ~190pt of gap on a 874pt phone — a search box and\n * four rows. Sizing that gap correctly does not make it big. The popup takes\n * a fixed share of the window instead, so where the field sits stops\n * mattering.\n *\n * Default false: every existing consumer keeps the anchored panel.\n */\n popup?: boolean;\n /**\n * Supplies the popup's container. Injected by the KitsConcerto wrapper\n * because the container is this library's `KitsDialogControlled`, which\n * lives in `packages/ui` — and `packages/ui` imports THIS file, so importing\n * it back would be a cycle between the two workspaces.\n *\n * Without it `popup` falls back to the anchored panel rather than rendering\n * nothing.\n */\n renderPopup?: (args: {\n visible: boolean;\n onClose: () => void;\n title?: React.ReactNode;\n children: React.ReactNode;\n }) => React.ReactNode;\n /** Title for the popup header. The wrapper passes the field's label. */\n popupTitle?: React.ReactNode;\n /**\n * Which way the reader reads, from the wrapper's `useLanguage().isRTL`.\n *\n * ⛔ NOT `I18nManager.isRTL`. That is the PROCESS layout direction, which a\n * consumer can force at boot and which does not change when the reader\n * switches language. The filter field follows the interface language, like\n * every other input in this library.\n */\n isRTL?: boolean;\n\n itemTemplate?: (item: T) => React.ReactNode;\n valueTemplate?: (value: T | null) => React.ReactNode;\n panelFooterTemplate?: () => React.ReactNode;\n\n optionGroupLabel?: string;\n optionGroupChildren?: string;\n optionGroupTemplate?: (group: any) => React.ReactNode;\n\n virtualScrollerOptions?: VirtualScrollerOptions;\n\n invalid?: boolean;\n testID?: string;\n style?: import('react-native').ViewStyle;\n}\n\n/* ---------------------------------------------\n Helpers\n---------------------------------------------- */\n\nconst getLabel = (item: any, optionLabel?: string) => {\n if (!item) return \"\";\n if (!optionLabel) return String(item);\n return String(item[optionLabel]);\n};\n\nconst getValue = (item: any, optionValue?: string) => {\n if (!item) return null;\n if (!optionValue) return item;\n return item[optionValue];\n};\n\nconst t = resolveThemeTokenForNative;\n\nconst TEXT_STYLE_KEYS = new Set([\n 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle',\n 'color', 'letterSpacing', 'lineHeight', 'textAlign',\n 'textDecorationLine', 'textTransform',\n]);\n\nfunction splitStyle(combined?: any): { viewStyle?: any; textStyle?: any } {\n if (!combined) return {};\n const view: Record<string, any> = {};\n const text: Record<string, any> = {};\n for (const [k, v] of Object.entries(combined)) {\n if (TEXT_STYLE_KEYS.has(k)) text[k] = v;\n else view[k] = v;\n }\n return {\n viewStyle: Object.keys(view).length ? view : undefined,\n textStyle: Object.keys(text).length ? text : undefined,\n };\n}\n\n/* ---------------------------------------------\n Component\n---------------------------------------------- */\n\nexport default function Dropdown<T = any>({\n value,\n options = [],\n onChange,\n\n optionLabel,\n optionValue,\n\n placeholder,\n disabled,\n editable,\n isModal = false,\n\n checkmark,\n highlightOnSelect = true,\n\n filter,\n filterBy,\n // ⚠️ THESE TWO ENGLISH LITERALS ARE A LAST RESORT, NOT A\n // TRANSLATION. The KitsConcerto wrapper always passes the\n // reader's own sentence; these only apply when this component\n // is used directly from `apps/mobile`, and they exist so a\n // panel can never render a blank search box or an empty\n // \"no results\" line. Recorded as English fallbacks.\n emptyFilterMessage = 'No results found',\n filterPlaceholder = 'Search...',\n isRTL = false,\n popup = false,\n renderPopup,\n popupTitle,\n\n itemTemplate,\n valueTemplate,\n panelFooterTemplate,\n\n optionGroupLabel,\n optionGroupChildren,\n optionGroupTemplate,\n\n virtualScrollerOptions,\n\n invalid,\n testID,\n style: externalStyle,\n }: DropdownProps<T>) {\n const inputRef = useRef<any>(null);\n const triggerRef = useRef<View>(null);\n const [open, setOpen] = useState(false);\n const [inputText, setInputText] = useState(\"\");\n const [filterQuery, setFilterQuery] = useState(\"\");\n const [triggerLayout, setTriggerLayout] = useState<{\n x: number; y: number; width: number; height: number;\n } | null>(null);\n const insets = useSafeAreaInsets();\n const keyboardHeight = useKeyboardHeight();\n\n const { viewStyle: externalViewStyle, textStyle: externalTextStyle } = splitStyle(externalStyle);\n\n const colors = {\n border: t('border') || t('gray.300'),\n borderInvalid: t('danger') || t('red.500'),\n bg: t('surface-card') || '#fff',\n text: t('text') || t('gray.800'),\n placeholder: t('text-secondary') || t('gray.400'),\n optionSelected: t('highlight-bg') || t('gray.100'),\n groupBg: t('surface-ground') || t('gray.50'),\n filterBorder: t('border') || t('gray.200'),\n filterBg: t('surface-ground') || t('gray.50'),\n filterText: t('text') || t('gray.800'),\n emptyText: t('text-secondary') || t('gray.400'),\n primary: t('primary'),\n };\n\n /* ---------------------------------------------\n Derived\n ---------------------------------------------- */\n\n const selectedItem = useMemo(() => {\n if (value == null) return null;\n // Search through group children when grouped\n if (optionGroupChildren) {\n for (const group of options as any[]) {\n const children = group[optionGroupChildren];\n if (Array.isArray(children)) {\n const found = children.find((c: any) => getValue(c, optionValue) === value);\n if (found) return found;\n }\n }\n return null;\n }\n return options.find(\n (o) => getValue(o, optionValue) === value\n ) ?? null;\n }, [value, options, optionValue, optionGroupChildren]);\n\n /* ---------------------------------------------\n Open / Close\n ---------------------------------------------- */\n\n const toggle = () => {\n if (disabled) return;\n Keyboard.dismiss();\n if (open) {\n setOpen(false);\n return;\n }\n setFilterQuery(\"\");\n // Measure trigger position in window so the popover can be rendered\n // inside a Modal (escaping any parent ScrollView) at the correct anchor.\n triggerRef.current?.measureInWindow((x, y, width, height) => {\n setTriggerLayout({ x, y, width, height });\n setOpen(true);\n });\n };\n\n const close = () => setOpen(false);\n\n /* ---------------------------------------------\n Selection\n ---------------------------------------------- */\n\n const selectItem = (item: T) => {\n const newValue = getValue(item, optionValue);\n\n if (!onChange) return;\n\n const syntheticEvent = {\n target: {\n value: newValue,\n },\n } as React.ChangeEvent<any>;\n\n onChange(syntheticEvent, item);\n\n setOpen(false);\n };\n\n /* ---------------------------------------------\n Render item\n ---------------------------------------------- */\n\n const renderItem = useCallback(\n (item: any, index: number) => {\n if (!item) {\n return null;\n }\n\n // Group header\n if (item.__group) {\n return (\n <Text key={`group-${index}`} style={[styles.groupLabel, { backgroundColor: colors.groupBg }]}>\n {optionGroupLabel ? item.group?.[optionGroupLabel] : \"\"}\n </Text>\n );\n }\n\n const selected =\n value != null &&\n getValue(item, optionValue) === value;\n\n let content: React.ReactElement;\n\n if (itemTemplate) {\n const rendered = itemTemplate(item);\n\n content = React.isValidElement(rendered) ? (\n rendered\n ) : (\n <Text>{String(rendered ?? \"\")}</Text>\n );\n } else {\n content = (\n <Text style={[{ color: colors.text }, externalTextStyle && { fontSize: externalTextStyle.fontSize }]}>\n {getLabel(item, optionLabel)}\n </Text>\n );\n }\n\n return (\n <Pressable\n key={`item-${index}`}\n onPress={() => selectItem(item)}\n style={[\n styles.option,\n selected && highlightOnSelect && { backgroundColor: colors.optionSelected },\n ]}\n >\n {content}\n {checkmark && selected && <Text>✓</Text>}\n </Pressable>\n );\n },\n [\n value,\n optionValue,\n optionLabel,\n itemTemplate,\n optionGroupLabel,\n checkmark,\n highlightOnSelect,\n ]\n );\n\n\n /* ---------------------------------------------\n Group flattening\n ---------------------------------------------- */\n\n const flatData = useMemo(() => {\n if (!optionGroupLabel || !optionGroupChildren) {\n return options;\n }\n\n const out: any[] = [];\n options.forEach((g: any) => {\n out.push({ __group: true, group: g });\n g[optionGroupChildren]?.forEach((c: any) => out.push(c));\n });\n return out;\n }, [options]);\n\n /* ---------------------------------------------\n Filter\n ---------------------------------------------- */\n\n const filteredData = useMemo(() => {\n if (!filter || !filterQuery) return flatData;\n const q = filterQuery.toLowerCase();\n const key = filterBy || optionLabel;\n\n return flatData.filter((entry: any) => {\n // Keep group headers\n if (entry.__group) return true;\n const label = getLabel(entry, key);\n return label.toLowerCase().includes(q);\n });\n }, [flatData, filter, filterQuery, filterBy, optionLabel]);\n\n /* ---------------------------------------------\n Display value\n ---------------------------------------------- */\n\n const displayValue = valueTemplate\n ? valueTemplate(selectedItem)\n : selectedItem\n ? getLabel(selectedItem, optionLabel)\n : \"\";\n\n /* ---------------------------------------------\n Panel body — the search field and the list\n ---------------------------------------------- */\n\n /*\n * ⛔ ONE BODY, THREE CONTAINERS. The anchored panel and the centred modal\n * each carried their own copy of this markup, which is why the hardcoded\n * \"Search...\" had to be found and fixed twice. The popup would have made it\n * three. The containers differ; what is inside them does not.\n *\n * `fill` is the one real difference. In the anchored panel the container has\n * a `maxHeight` and no height, so the list must shrink-wrap its content up\n * to that ceiling (`flexShrink`). In the popup and the centred modal the\n * container has a real height, so the list takes what is left (`flex: 1`).\n */\n const panelBody = (fill: boolean, autoFocusFilter: boolean) => (\n <>\n {filter && (\n <View style={[styles.filterContainer, { borderBottomColor: colors.filterBorder }]}>\n <TextInput\n value={filterQuery}\n onChangeText={setFilterQuery}\n placeholder={filterPlaceholder}\n placeholderTextColor={colors.placeholder}\n style={[styles.filterInput, {\n borderColor: colors.filterBorder,\n backgroundColor: colors.filterBg,\n color: colors.filterText,\n },\n // Every other input in this library names its own edge and\n // direction from the reader's language. Leaving these unset let the\n // platform decide, so the field followed the process layout\n // direction instead of the person reading it.\n isRTL ? styles.filterRtl : styles.filterLtr]}\n /* ⛔ THE ANCHORED PANEL KEEPS ITS AUTOFOCUS — changing it would\n change behaviour for every consumer that has not opted in. In\n the popup the list is already the whole point and is fully\n visible, so the keyboard waits to be asked for: one tap on the\n field raises it, and until then the list has the whole sheet. */\n autoFocus={autoFocusFilter}\n />\n </View>\n )}\n <FlatList\n style={fill ? styles.listFill : styles.listShrink}\n contentContainerStyle={styles.listContent}\n data={filteredData}\n keyExtractor={(item: any, i: number) =>\n item?.__group ? `group-${i}` : `item-${i}`\n }\n renderItem={({ item, index }) => renderItem(item, index)}\n keyboardShouldPersistTaps=\"handled\"\n showsHorizontalScrollIndicator={false}\n ListEmptyComponent={\n filterQuery ? (\n <View style={styles.emptyContainer}>\n <Text style={[styles.emptyText, { color: colors.emptyText }]}>{emptyFilterMessage}</Text>\n </View>\n ) : null\n }\n />\n {panelFooterTemplate?.()}\n </>\n );\n\n /* ---------------------------------------------\n Render\n ---------------------------------------------- */\n\n const usePopup = popup && typeof renderPopup === 'function';\n\n return (\n <View style={styles.container} testID={testID}>\n <Pressable\n ref={triggerRef as any}\n style={[\n styles.control,\n { borderColor: colors.border, backgroundColor: colors.bg },\n invalid && { borderColor: colors.borderInvalid },\n disabled && styles.disabled,\n externalViewStyle,\n ]}\n onPress={toggle}\n >\n {editable ? (\n <Input style={styles.input}>\n <InputField\n ref={inputRef}\n value={inputText}\n onChangeText={setInputText}\n placeholder={placeholder}\n editable={!disabled}\n />\n </Input>\n ) : (\n <Text\n numberOfLines={1}\n style={[\n // Fill the trigger row and left-align the value/placeholder so it\n // doesn't read as centred (the row is justify=space-between, so the\n // arrow stays pinned right).\n { flex: 1, textAlign: \"left\" as const },\n displayValue ? { color: colors.text } : { color: colors.placeholder },\n externalTextStyle,\n !displayValue && { color: colors.placeholder },\n ]}>\n {displayValue || placeholder}\n </Text>\n )}\n\n <Text style={[{ color: colors.text }, externalTextStyle]}>▾</Text>\n </Pressable>\n\n {!usePopup && !isModal && (\n <Modal\n visible={open && !!triggerLayout}\n transparent\n animationType=\"none\"\n onRequestClose={close}\n >\n <Pressable style={styles.popoverOverlay} onPress={close}>\n {triggerLayout && (() => {\n const screen = Dimensions.get('window');\n const { top, maxHeight } = computePanelGeometry({\n screenHeight: screen.height,\n insetTop: insets.top,\n insetBottom: insets.bottom,\n keyboardHeight,\n triggerY: triggerLayout.y,\n triggerHeight: triggerLayout.height,\n });\n\n return (\n <View\n style={[\n styles.popoverPanel,\n {\n top,\n left: triggerLayout.x,\n width: triggerLayout.width,\n maxHeight,\n borderColor: colors.border,\n backgroundColor: colors.bg,\n },\n ]}\n onStartShouldSetResponder={() => true}\n >\n {panelBody(false, true)}\n </View>\n );\n })()}\n </Pressable>\n </Modal>\n )}\n\n {!usePopup && isModal && (\n <Modal visible={open} transparent animationType=\"fade\">\n <Pressable style={styles.modalOverlay} onPress={close}>\n <View\n style={[styles.modalPanel, { backgroundColor: colors.bg }]}\n onStartShouldSetResponder={() => true}\n >\n {panelBody(true, true)}\n </View>\n </Pressable>\n </Modal>\n )}\n\n {/* Popup — the container comes from the wrapper (this library's own\n Dialog in sheet position); everything inside it is the same body the\n other two containers render. */}\n {usePopup && renderPopup!({\n visible: open,\n onClose: close,\n title: popupTitle,\n children: panelBody(true, false),\n })}\n\n </View>\n );\n}\n\n/* ---------------------------------------------\n Styles (layout only — colors applied inline via theme tokens)\n---------------------------------------------- */\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n position: \"relative\" as const,\n },\n control: {\n flexDirection: \"row\" as const,\n alignItems: \"center\" as const,\n justifyContent: \"space-between\" as const,\n borderWidth: 1,\n borderRadius: 6,\n paddingHorizontal: 10,\n paddingVertical: 10,\n },\n disabled: {\n opacity: 0.5,\n },\n option: {\n paddingHorizontal: 12,\n paddingVertical: 10,\n flexDirection: \"row\" as const,\n justifyContent: \"space-between\" as const,\n alignItems: \"center\" as const,\n },\n groupLabel: {\n paddingHorizontal: 12,\n paddingVertical: 8,\n fontWeight: \"600\" as const,\n },\n input: {\n flex: 1,\n },\n panel: {\n position: \"absolute\" as const,\n top: \"100%\",\n left: 0,\n right: 0,\n marginTop: 4,\n borderWidth: 1,\n borderRadius: 6,\n maxHeight: 260,\n overflow: \"hidden\" as const,\n zIndex: 9999,\n },\n filterContainer: {\n paddingHorizontal: 8,\n paddingVertical: 6,\n borderBottomWidth: 1,\n },\n listFill: {\n flex: 1,\n },\n // The anchored panel is sized by `maxHeight` with no height, so the list has\n // to grow with its content up to that ceiling and then scroll inside it.\n // `flex: 1` sets flexBasis 0, which contributes nothing to a shrink-wrapping\n // parent.\n listShrink: {\n flexGrow: 0,\n flexShrink: 1,\n },\n listContent: {\n paddingBottom: 12,\n },\n filterInput: {\n fontSize: 14,\n paddingHorizontal: 8,\n paddingVertical: 6,\n borderWidth: 1,\n borderRadius: 4,\n },\n filterLtr: {\n textAlign: 'left' as const,\n writingDirection: 'ltr' as const,\n },\n filterRtl: {\n textAlign: 'right' as const,\n writingDirection: 'rtl' as const,\n },\n emptyContainer: {\n padding: 16,\n alignItems: 'center' as const,\n },\n emptyText: {\n fontSize: 14,\n },\n popoverOverlay: {\n flex: 1,\n backgroundColor: 'transparent',\n },\n popoverPanel: {\n position: 'absolute' as const,\n borderWidth: 1,\n borderRadius: 6,\n overflow: 'hidden' as const,\n elevation: 8,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n shadowOpacity: 0.15,\n shadowRadius: 8,\n },\n modalOverlay: {\n flex: 1,\n backgroundColor: 'rgba(0,0,0,0.4)',\n justifyContent: 'center' as const,\n },\n modalPanel: {\n margin: 24,\n maxHeight: '70%',\n borderRadius: 12,\n overflow: 'hidden' as const,\n },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PANEL_MAX_HEIGHT = 260;
|
|
4
|
+
const PANEL_MIN_HEIGHT = 140;
|
|
5
|
+
const PANEL_GAP = 4;
|
|
6
|
+
const clamp = (value, low, high) => Math.min(Math.max(value, low), Math.max(low, high));
|
|
7
|
+
function computePanelGeometry({
|
|
8
|
+
screenHeight,
|
|
9
|
+
insetTop,
|
|
10
|
+
insetBottom,
|
|
11
|
+
keyboardHeight,
|
|
12
|
+
triggerY,
|
|
13
|
+
triggerHeight,
|
|
14
|
+
maxPanelHeight = PANEL_MAX_HEIGHT,
|
|
15
|
+
minPanelHeight = PANEL_MIN_HEIGHT,
|
|
16
|
+
gap = PANEL_GAP
|
|
17
|
+
}) {
|
|
18
|
+
const occludedBottom = Math.max(insetBottom, keyboardHeight);
|
|
19
|
+
const viewportTop = insetTop;
|
|
20
|
+
const viewportBottom = Math.max(viewportTop, screenHeight - occludedBottom);
|
|
21
|
+
const viewportHeight = viewportBottom - viewportTop;
|
|
22
|
+
const triggerBottom = triggerY + triggerHeight;
|
|
23
|
+
const spaceBelow = Math.max(0, viewportBottom - triggerBottom - gap);
|
|
24
|
+
const spaceAbove = Math.max(0, triggerY - viewportTop - gap);
|
|
25
|
+
const wanted = Math.min(maxPanelHeight, viewportHeight);
|
|
26
|
+
const usable = Math.min(wanted, minPanelHeight);
|
|
27
|
+
const openUp = spaceBelow < usable && spaceAbove > spaceBelow;
|
|
28
|
+
const anchoredSpace = openUp ? spaceAbove : spaceBelow;
|
|
29
|
+
if (anchoredSpace >= usable) {
|
|
30
|
+
const maxHeight2 = Math.min(wanted, anchoredSpace);
|
|
31
|
+
const top2 = openUp ? triggerY - gap - maxHeight2 : triggerBottom + gap;
|
|
32
|
+
return { top: clamp(top2, viewportTop, viewportBottom - maxHeight2), maxHeight: maxHeight2, openUp, anchored: true };
|
|
33
|
+
}
|
|
34
|
+
const maxHeight = Math.min(wanted, viewportHeight);
|
|
35
|
+
const top = openUp ? viewportTop : viewportBottom - maxHeight;
|
|
36
|
+
return { top: clamp(top, viewportTop, viewportBottom - maxHeight), maxHeight, openUp, anchored: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.PANEL_GAP = PANEL_GAP;
|
|
40
|
+
exports.PANEL_MAX_HEIGHT = PANEL_MAX_HEIGHT;
|
|
41
|
+
exports.PANEL_MIN_HEIGHT = PANEL_MIN_HEIGHT;
|
|
42
|
+
exports.computePanelGeometry = computePanelGeometry;
|
|
43
|
+
//# sourceMappingURL=panelGeometry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panelGeometry.cjs","sources":["../../../../../../../../../apps/mobile/src/Core/Dropdown/panelGeometry.ts"],"sourcesContent":["/**\n * Where the Dropdown's option panel goes, and how tall it is.\n *\n * Kept as a pure function — no react-native import — so the arithmetic can be\n * exercised by the Jest suite instead of only by eye on a simulator. The\n * component supplies the measurements; this file decides nothing about styling.\n *\n * ⛔ THE KEYBOARD IS PART OF THE VIEWPORT, NOT AN AFTERTHOUGHT. The filter\n * field inside the panel carries `autoFocus`, so on every filtered dropdown the\n * keyboard is already rising while this runs. Sizing the panel against the raw\n * screen height hands it space that the keyboard is about to take, and the\n * panel then renders with room for its search box and a sliver of one result.\n *\n * ⛔ AND A PANEL THAT DOES NOT FIT ANYWHERE STILL HAS TO BE READABLE. When\n * neither side of the trigger can hold a usable panel, this stops anchoring to\n * the trigger and places the panel inside the visible viewport instead —\n * overlapping the trigger if it must. Opening a dropdown must never require\n * scrolling the page behind it.\n */\n\nexport interface IPanelGeometryInput {\n /** Full window height in points. */\n screenHeight: number;\n /** Safe-area inset at the top (status bar / notch). */\n insetTop: number;\n /** Safe-area inset at the bottom (home indicator). */\n insetBottom: number;\n /**\n * Height of the on-screen keyboard, 0 when it is closed. This is the\n * measurement the old code had no way to see.\n */\n keyboardHeight: number;\n /** Trigger's Y in window coordinates. */\n triggerY: number;\n /** Trigger's height. */\n triggerHeight: number;\n /** Tallest the panel may ever be. */\n maxPanelHeight?: number;\n /**\n * Shortest panel still worth anchoring to the trigger. Below this the panel\n * shows its search box and almost no results, which is the filmed fault —\n * so at that point the panel gives up the anchor and takes the viewport.\n */\n minPanelHeight?: number;\n /** Space between trigger and panel. */\n gap?: number;\n}\n\nexport interface IPanelGeometry {\n /** Panel's Y in window coordinates. */\n top: number;\n /** Space the panel may occupy. The panel shrinks to its content below this. */\n maxHeight: number;\n /** True when the panel was placed above the trigger. */\n openUp: boolean;\n /** False when the panel had to leave the trigger to stay on screen. */\n anchored: boolean;\n}\n\nexport const PANEL_MAX_HEIGHT = 260;\nexport const PANEL_MIN_HEIGHT = 140;\nexport const PANEL_GAP = 4;\n\nconst clamp = (value: number, low: number, high: number) =>\n Math.min(Math.max(value, low), Math.max(low, high));\n\nexport function computePanelGeometry({\n screenHeight,\n insetTop,\n insetBottom,\n keyboardHeight,\n triggerY,\n triggerHeight,\n maxPanelHeight = PANEL_MAX_HEIGHT,\n minPanelHeight = PANEL_MIN_HEIGHT,\n gap = PANEL_GAP,\n}: IPanelGeometryInput): IPanelGeometry {\n // The keyboard sits over the home indicator, so it replaces the bottom\n // inset rather than adding to it.\n const occludedBottom = Math.max(insetBottom, keyboardHeight);\n\n const viewportTop = insetTop;\n const viewportBottom = Math.max(viewportTop, screenHeight - occludedBottom);\n const viewportHeight = viewportBottom - viewportTop;\n\n const triggerBottom = triggerY + triggerHeight;\n const spaceBelow = Math.max(0, viewportBottom - triggerBottom - gap);\n const spaceAbove = Math.max(0, triggerY - viewportTop - gap);\n\n const wanted = Math.min(maxPanelHeight, viewportHeight);\n // Never demand more than the viewport itself can give, or a short screen\n // would always fall through to the unanchored branch.\n const usable = Math.min(wanted, minPanelHeight);\n\n const openUp = spaceBelow < usable && spaceAbove > spaceBelow;\n const anchoredSpace = openUp ? spaceAbove : spaceBelow;\n\n if (anchoredSpace >= usable) {\n const maxHeight = Math.min(wanted, anchoredSpace);\n const top = openUp ? triggerY - gap - maxHeight : triggerBottom + gap;\n return {top: clamp(top, viewportTop, viewportBottom - maxHeight), maxHeight, openUp, anchored: true};\n }\n\n // Neither side can hold a usable panel — the trigger is boxed in by the\n // keyboard. Drop the anchor and take the visible viewport.\n const maxHeight = Math.min(wanted, viewportHeight);\n const top = openUp ? viewportTop : viewportBottom - maxHeight;\n\n return {top: clamp(top, viewportTop, viewportBottom - maxHeight), maxHeight, openUp, anchored: false};\n}\n"],"names":["maxHeight","top"],"mappings":";;AA2DO,MAAM,gBAAA,GAAmB;AACzB,MAAM,gBAAA,GAAmB;AACzB,MAAM,SAAA,GAAY;AAEzB,MAAM,QAAQ,CAAC,KAAA,EAAe,GAAA,EAAa,IAAA,KACvC,KAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,GAAG,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAI,CAAC,CAAA;AAE/C,SAAS,oBAAA,CAAqB;AAAA,EACjC,YAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA,GAAiB,gBAAA;AAAA,EACjB,cAAA,GAAiB,gBAAA;AAAA,EACjB,GAAA,GAAM;AACV,CAAA,EAAwC;AAGpC,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,cAAc,CAAA;AAE3D,EAAA,MAAM,WAAA,GAAc,QAAA;AACpB,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,eAAe,cAAc,CAAA;AAC1E,EAAA,MAAM,iBAAiB,cAAA,GAAiB,WAAA;AAExC,EAAA,MAAM,gBAAgB,QAAA,GAAW,aAAA;AACjC,EAAA,MAAM,aAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,cAAA,GAAiB,gBAAgB,GAAG,CAAA;AACnE,EAAA,MAAM,aAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAA,GAAW,cAAc,GAAG,CAAA;AAE3D,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,cAAc,CAAA;AAGtD,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,cAAc,CAAA;AAE9C,EAAA,MAAM,MAAA,GAAS,UAAA,GAAa,MAAA,IAAU,UAAA,GAAa,UAAA;AACnD,EAAA,MAAM,aAAA,GAAgB,SAAS,UAAA,GAAa,UAAA;AAE5C,EAAA,IAAI,iBAAiB,MAAA,EAAQ;AACzB,IAAA,MAAMA,UAAAA,GAAY,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,aAAa,CAAA;AAChD,IAAA,MAAMC,IAAAA,GAAM,MAAA,GAAS,QAAA,GAAW,GAAA,GAAMD,aAAY,aAAA,GAAgB,GAAA;AAClE,IAAA,OAAO,EAAC,GAAA,EAAK,KAAA,CAAMC,IAAAA,EAAK,WAAA,EAAa,cAAA,GAAiBD,UAAS,CAAA,EAAG,SAAA,EAAAA,UAAAA,EAAW,MAAA,EAAQ,QAAA,EAAU,IAAA,EAAI;AAAA,EACvG;AAIA,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,cAAc,CAAA;AACjD,EAAA,MAAM,GAAA,GAAM,MAAA,GAAS,WAAA,GAAc,cAAA,GAAiB,SAAA;AAEpD,EAAA,OAAO,EAAC,GAAA,EAAK,KAAA,CAAM,GAAA,EAAK,WAAA,EAAa,cAAA,GAAiB,SAAS,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAK;AACxG;;;;;;;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const PANEL_MAX_HEIGHT = 260;
|
|
2
|
+
const PANEL_MIN_HEIGHT = 140;
|
|
3
|
+
const PANEL_GAP = 4;
|
|
4
|
+
const clamp = (value, low, high) => Math.min(Math.max(value, low), Math.max(low, high));
|
|
5
|
+
function computePanelGeometry({
|
|
6
|
+
screenHeight,
|
|
7
|
+
insetTop,
|
|
8
|
+
insetBottom,
|
|
9
|
+
keyboardHeight,
|
|
10
|
+
triggerY,
|
|
11
|
+
triggerHeight,
|
|
12
|
+
maxPanelHeight = PANEL_MAX_HEIGHT,
|
|
13
|
+
minPanelHeight = PANEL_MIN_HEIGHT,
|
|
14
|
+
gap = PANEL_GAP
|
|
15
|
+
}) {
|
|
16
|
+
const occludedBottom = Math.max(insetBottom, keyboardHeight);
|
|
17
|
+
const viewportTop = insetTop;
|
|
18
|
+
const viewportBottom = Math.max(viewportTop, screenHeight - occludedBottom);
|
|
19
|
+
const viewportHeight = viewportBottom - viewportTop;
|
|
20
|
+
const triggerBottom = triggerY + triggerHeight;
|
|
21
|
+
const spaceBelow = Math.max(0, viewportBottom - triggerBottom - gap);
|
|
22
|
+
const spaceAbove = Math.max(0, triggerY - viewportTop - gap);
|
|
23
|
+
const wanted = Math.min(maxPanelHeight, viewportHeight);
|
|
24
|
+
const usable = Math.min(wanted, minPanelHeight);
|
|
25
|
+
const openUp = spaceBelow < usable && spaceAbove > spaceBelow;
|
|
26
|
+
const anchoredSpace = openUp ? spaceAbove : spaceBelow;
|
|
27
|
+
if (anchoredSpace >= usable) {
|
|
28
|
+
const maxHeight2 = Math.min(wanted, anchoredSpace);
|
|
29
|
+
const top2 = openUp ? triggerY - gap - maxHeight2 : triggerBottom + gap;
|
|
30
|
+
return { top: clamp(top2, viewportTop, viewportBottom - maxHeight2), maxHeight: maxHeight2, openUp, anchored: true };
|
|
31
|
+
}
|
|
32
|
+
const maxHeight = Math.min(wanted, viewportHeight);
|
|
33
|
+
const top = openUp ? viewportTop : viewportBottom - maxHeight;
|
|
34
|
+
return { top: clamp(top, viewportTop, viewportBottom - maxHeight), maxHeight, openUp, anchored: false };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { PANEL_GAP, PANEL_MAX_HEIGHT, PANEL_MIN_HEIGHT, computePanelGeometry };
|
|
38
|
+
//# sourceMappingURL=panelGeometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panelGeometry.js","sources":["../../../../../../../../../apps/mobile/src/Core/Dropdown/panelGeometry.ts"],"sourcesContent":["/**\n * Where the Dropdown's option panel goes, and how tall it is.\n *\n * Kept as a pure function — no react-native import — so the arithmetic can be\n * exercised by the Jest suite instead of only by eye on a simulator. The\n * component supplies the measurements; this file decides nothing about styling.\n *\n * ⛔ THE KEYBOARD IS PART OF THE VIEWPORT, NOT AN AFTERTHOUGHT. The filter\n * field inside the panel carries `autoFocus`, so on every filtered dropdown the\n * keyboard is already rising while this runs. Sizing the panel against the raw\n * screen height hands it space that the keyboard is about to take, and the\n * panel then renders with room for its search box and a sliver of one result.\n *\n * ⛔ AND A PANEL THAT DOES NOT FIT ANYWHERE STILL HAS TO BE READABLE. When\n * neither side of the trigger can hold a usable panel, this stops anchoring to\n * the trigger and places the panel inside the visible viewport instead —\n * overlapping the trigger if it must. Opening a dropdown must never require\n * scrolling the page behind it.\n */\n\nexport interface IPanelGeometryInput {\n /** Full window height in points. */\n screenHeight: number;\n /** Safe-area inset at the top (status bar / notch). */\n insetTop: number;\n /** Safe-area inset at the bottom (home indicator). */\n insetBottom: number;\n /**\n * Height of the on-screen keyboard, 0 when it is closed. This is the\n * measurement the old code had no way to see.\n */\n keyboardHeight: number;\n /** Trigger's Y in window coordinates. */\n triggerY: number;\n /** Trigger's height. */\n triggerHeight: number;\n /** Tallest the panel may ever be. */\n maxPanelHeight?: number;\n /**\n * Shortest panel still worth anchoring to the trigger. Below this the panel\n * shows its search box and almost no results, which is the filmed fault —\n * so at that point the panel gives up the anchor and takes the viewport.\n */\n minPanelHeight?: number;\n /** Space between trigger and panel. */\n gap?: number;\n}\n\nexport interface IPanelGeometry {\n /** Panel's Y in window coordinates. */\n top: number;\n /** Space the panel may occupy. The panel shrinks to its content below this. */\n maxHeight: number;\n /** True when the panel was placed above the trigger. */\n openUp: boolean;\n /** False when the panel had to leave the trigger to stay on screen. */\n anchored: boolean;\n}\n\nexport const PANEL_MAX_HEIGHT = 260;\nexport const PANEL_MIN_HEIGHT = 140;\nexport const PANEL_GAP = 4;\n\nconst clamp = (value: number, low: number, high: number) =>\n Math.min(Math.max(value, low), Math.max(low, high));\n\nexport function computePanelGeometry({\n screenHeight,\n insetTop,\n insetBottom,\n keyboardHeight,\n triggerY,\n triggerHeight,\n maxPanelHeight = PANEL_MAX_HEIGHT,\n minPanelHeight = PANEL_MIN_HEIGHT,\n gap = PANEL_GAP,\n}: IPanelGeometryInput): IPanelGeometry {\n // The keyboard sits over the home indicator, so it replaces the bottom\n // inset rather than adding to it.\n const occludedBottom = Math.max(insetBottom, keyboardHeight);\n\n const viewportTop = insetTop;\n const viewportBottom = Math.max(viewportTop, screenHeight - occludedBottom);\n const viewportHeight = viewportBottom - viewportTop;\n\n const triggerBottom = triggerY + triggerHeight;\n const spaceBelow = Math.max(0, viewportBottom - triggerBottom - gap);\n const spaceAbove = Math.max(0, triggerY - viewportTop - gap);\n\n const wanted = Math.min(maxPanelHeight, viewportHeight);\n // Never demand more than the viewport itself can give, or a short screen\n // would always fall through to the unanchored branch.\n const usable = Math.min(wanted, minPanelHeight);\n\n const openUp = spaceBelow < usable && spaceAbove > spaceBelow;\n const anchoredSpace = openUp ? spaceAbove : spaceBelow;\n\n if (anchoredSpace >= usable) {\n const maxHeight = Math.min(wanted, anchoredSpace);\n const top = openUp ? triggerY - gap - maxHeight : triggerBottom + gap;\n return {top: clamp(top, viewportTop, viewportBottom - maxHeight), maxHeight, openUp, anchored: true};\n }\n\n // Neither side can hold a usable panel — the trigger is boxed in by the\n // keyboard. Drop the anchor and take the visible viewport.\n const maxHeight = Math.min(wanted, viewportHeight);\n const top = openUp ? viewportTop : viewportBottom - maxHeight;\n\n return {top: clamp(top, viewportTop, viewportBottom - maxHeight), maxHeight, openUp, anchored: false};\n}\n"],"names":["maxHeight","top"],"mappings":"AA2DO,MAAM,gBAAA,GAAmB;AACzB,MAAM,gBAAA,GAAmB;AACzB,MAAM,SAAA,GAAY;AAEzB,MAAM,QAAQ,CAAC,KAAA,EAAe,GAAA,EAAa,IAAA,KACvC,KAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,GAAG,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAI,CAAC,CAAA;AAE/C,SAAS,oBAAA,CAAqB;AAAA,EACjC,YAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA,GAAiB,gBAAA;AAAA,EACjB,cAAA,GAAiB,gBAAA;AAAA,EACjB,GAAA,GAAM;AACV,CAAA,EAAwC;AAGpC,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,cAAc,CAAA;AAE3D,EAAA,MAAM,WAAA,GAAc,QAAA;AACpB,EAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,eAAe,cAAc,CAAA;AAC1E,EAAA,MAAM,iBAAiB,cAAA,GAAiB,WAAA;AAExC,EAAA,MAAM,gBAAgB,QAAA,GAAW,aAAA;AACjC,EAAA,MAAM,aAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,cAAA,GAAiB,gBAAgB,GAAG,CAAA;AACnE,EAAA,MAAM,aAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAA,GAAW,cAAc,GAAG,CAAA;AAE3D,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,cAAc,CAAA;AAGtD,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,cAAc,CAAA;AAE9C,EAAA,MAAM,MAAA,GAAS,UAAA,GAAa,MAAA,IAAU,UAAA,GAAa,UAAA;AACnD,EAAA,MAAM,aAAA,GAAgB,SAAS,UAAA,GAAa,UAAA;AAE5C,EAAA,IAAI,iBAAiB,MAAA,EAAQ;AACzB,IAAA,MAAMA,UAAAA,GAAY,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,aAAa,CAAA;AAChD,IAAA,MAAMC,IAAAA,GAAM,MAAA,GAAS,QAAA,GAAW,GAAA,GAAMD,aAAY,aAAA,GAAgB,GAAA;AAClE,IAAA,OAAO,EAAC,GAAA,EAAK,KAAA,CAAMC,IAAAA,EAAK,WAAA,EAAa,cAAA,GAAiBD,UAAS,CAAA,EAAG,SAAA,EAAAA,UAAAA,EAAW,MAAA,EAAQ,QAAA,EAAU,IAAA,EAAI;AAAA,EACvG;AAIA,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,cAAc,CAAA;AACjD,EAAA,MAAM,GAAA,GAAM,MAAA,GAAS,WAAA,GAAc,cAAA,GAAiB,SAAA;AAEpD,EAAA,OAAO,EAAC,GAAA,EAAK,KAAA,CAAM,GAAA,EAAK,WAAA,EAAa,cAAA,GAAiB,SAAS,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAK;AACxG;;;;"}
|
|
@@ -47,7 +47,15 @@ function MultiSelect(props) {
|
|
|
47
47
|
disabled,
|
|
48
48
|
loading,
|
|
49
49
|
filter,
|
|
50
|
+
// ⚠️ English literals of last resort, not translations — the wrapper always
|
|
51
|
+
// passes the reader's own sentence. These only apply when this component is
|
|
52
|
+
// used directly from `apps/mobile`, so a panel can never render blank.
|
|
50
53
|
emptyFilterMessage = "No results found",
|
|
54
|
+
filterPlaceholder = "Search...",
|
|
55
|
+
isRTL = false,
|
|
56
|
+
popup = false,
|
|
57
|
+
renderPopup,
|
|
58
|
+
popupTitle,
|
|
51
59
|
display = "chip",
|
|
52
60
|
maxSelectedLabels = 10,
|
|
53
61
|
itemTemplate,
|
|
@@ -224,6 +232,7 @@ function MultiSelect(props) {
|
|
|
224
232
|
}
|
|
225
233
|
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { numberOfLines: 1, style: [styles.commaText, { color: colors.text }, externalTextStyle], children: selectedValues.slice(0, maxSelectedLabels).map((v) => getLabel(v, optionLabel)).join(", ") });
|
|
226
234
|
};
|
|
235
|
+
const usePopup = popup && typeof renderPopup === "function";
|
|
227
236
|
const panelContent = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
|
|
228
237
|
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.panelHeader, { borderBottomColor: colors.headerBorder }], children: [
|
|
229
238
|
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles.panelHeaderText, { color: colors.textSecondary }], children: [
|
|
@@ -232,7 +241,7 @@ function MultiSelect(props) {
|
|
|
232
241
|
selectedValues.length !== 1 ? "s" : "",
|
|
233
242
|
" selected"
|
|
234
243
|
] }),
|
|
235
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: () => setOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.panelCloseText, { color: colors.placeholder }], children: "\u2715" }) })
|
|
244
|
+
!usePopup && /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: () => setOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.panelCloseText, { color: colors.placeholder }], children: "\u2715" }) })
|
|
236
245
|
] }),
|
|
237
246
|
filter && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.searchContainer, { borderColor: colors.border, backgroundColor: colors.searchBg }], children: [
|
|
238
247
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.searchIcon, children: "\u{1F50D}" }),
|
|
@@ -241,8 +250,14 @@ function MultiSelect(props) {
|
|
|
241
250
|
{
|
|
242
251
|
value: query,
|
|
243
252
|
onChangeText: setQuery,
|
|
244
|
-
placeholder:
|
|
245
|
-
style: [
|
|
253
|
+
placeholder: filterPlaceholder,
|
|
254
|
+
style: [
|
|
255
|
+
styles.searchInput,
|
|
256
|
+
{ color: colors.text },
|
|
257
|
+
// The filter follows the reader's language, not the platform's
|
|
258
|
+
// layout direction — same rule as the Dropdown's panel.
|
|
259
|
+
isRTL ? styles.searchRtl : styles.searchLtr
|
|
260
|
+
],
|
|
246
261
|
placeholderTextColor: colors.placeholder
|
|
247
262
|
}
|
|
248
263
|
)
|
|
@@ -270,7 +285,13 @@ function MultiSelect(props) {
|
|
|
270
285
|
]
|
|
271
286
|
}
|
|
272
287
|
),
|
|
273
|
-
|
|
288
|
+
usePopup && renderPopup({
|
|
289
|
+
visible: open,
|
|
290
|
+
onClose: () => setOpen(false),
|
|
291
|
+
title: popupTitle,
|
|
292
|
+
children: panelContent
|
|
293
|
+
}),
|
|
294
|
+
!usePopup && isModal && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Modal, { visible: open, transparent: true, animationType: "fade", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: styles.overlay, onPress: () => setOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
274
295
|
reactNative.View,
|
|
275
296
|
{
|
|
276
297
|
style: [styles.modalPanel, { backgroundColor: colors.bg }],
|
|
@@ -278,7 +299,7 @@ function MultiSelect(props) {
|
|
|
278
299
|
children: panelContent
|
|
279
300
|
}
|
|
280
301
|
) }) }),
|
|
281
|
-
!isModal && /* @__PURE__ */ jsxRuntime.jsx(
|
|
302
|
+
!usePopup && !isModal && /* @__PURE__ */ jsxRuntime.jsx(
|
|
282
303
|
reactNative.Modal,
|
|
283
304
|
{
|
|
284
305
|
visible: open && !!triggerLayout,
|
|
@@ -406,6 +427,14 @@ const styles = reactNative.StyleSheet.create({
|
|
|
406
427
|
fontSize: 14,
|
|
407
428
|
paddingVertical: 0
|
|
408
429
|
},
|
|
430
|
+
searchLtr: {
|
|
431
|
+
textAlign: "left",
|
|
432
|
+
writingDirection: "ltr"
|
|
433
|
+
},
|
|
434
|
+
searchRtl: {
|
|
435
|
+
textAlign: "right",
|
|
436
|
+
writingDirection: "rtl"
|
|
437
|
+
},
|
|
409
438
|
// Group header
|
|
410
439
|
groupHeader: {
|
|
411
440
|
paddingHorizontal: 12,
|