@sincpro/mobile-ui 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/README.md +7 -2
  2. package/assets/brand-mesh.png +0 -0
  3. package/assets/fonts/FFL.txt +57 -0
  4. package/assets/fonts/Satoshi-Black.otf +0 -0
  5. package/assets/fonts/Satoshi-Bold.otf +0 -0
  6. package/assets/fonts/Satoshi-Medium.otf +0 -0
  7. package/assets/fonts/Satoshi-Regular.otf +0 -0
  8. package/dist/Actions/Actions.Button.d.ts +2 -1
  9. package/dist/Actions/Actions.Button.js +31 -37
  10. package/dist/Actions/Actions.Dock.d.ts +6 -6
  11. package/dist/Actions/Actions.Dock.js +14 -72
  12. package/dist/Actions/Actions.FAB.d.ts +2 -1
  13. package/dist/Actions/Actions.FilterButton.js +1 -1
  14. package/dist/Actions/Actions.IconButton.d.ts +3 -4
  15. package/dist/Actions/Actions.IconButton.js +2 -2
  16. package/dist/Dialog/Confirmation.context.d.ts +5 -14
  17. package/dist/Dialog/Confirmation.context.js +11 -51
  18. package/dist/Dialog/Dialog.ActionSheet.d.ts +8 -7
  19. package/dist/Dialog/Dialog.ActionSheet.js +45 -19
  20. package/dist/Dialog/Dialog.Confirmation.d.ts +14 -11
  21. package/dist/Dialog/Dialog.Confirmation.js +38 -14
  22. package/dist/Dialog/Dialog.EditValue.d.ts +21 -16
  23. package/dist/Dialog/Dialog.EditValue.js +73 -14
  24. package/dist/Dialog/Dialog.Sheet.d.ts +8 -24
  25. package/dist/Dialog/Dialog.Sheet.js +15 -60
  26. package/dist/Dialog/index.d.ts +14 -14
  27. package/dist/Dialog/index.js +5 -3
  28. package/dist/Dialog/requestConfirmation.d.ts +3 -0
  29. package/dist/Dialog/requestConfirmation.js +13 -0
  30. package/dist/Display/Display.Accordion.js +1 -1
  31. package/dist/Display/Display.Avatar.d.ts +6 -6
  32. package/dist/Display/Display.Avatar.js +2 -2
  33. package/dist/Display/Display.Badge.js +4 -4
  34. package/dist/Display/Display.Card.js +5 -2
  35. package/dist/Display/Display.Carousel.js +1 -1
  36. package/dist/Display/Display.CircleButton.d.ts +3 -8
  37. package/dist/Display/Display.CircleButton.js +2 -2
  38. package/dist/Display/Display.CopyableText.js +1 -5
  39. package/dist/Display/Display.Date.d.ts +3 -2
  40. package/dist/Display/Display.Date.js +3 -3
  41. package/dist/Display/Display.GradientSurface.d.ts +52 -9
  42. package/dist/Display/Display.GradientSurface.js +9 -14
  43. package/dist/Display/Display.Icon.d.ts +7 -11
  44. package/dist/Display/Display.Icon.js +15 -30
  45. package/dist/Display/Display.Image.d.ts +2 -1
  46. package/dist/Display/Display.Image.js +1 -1
  47. package/dist/Display/Display.KeyValue.js +1 -1
  48. package/dist/Display/Display.ListItem.js +1 -1
  49. package/dist/Display/Display.MenuButton.js +4 -4
  50. package/dist/Display/Display.MenuCard.d.ts +3 -8
  51. package/dist/Display/Display.MenuCard.js +2 -2
  52. package/dist/Display/Display.MenuGrid.d.ts +2 -7
  53. package/dist/Display/Display.MenuGrid.js +1 -1
  54. package/dist/Display/Display.Monetary.d.ts +2 -6
  55. package/dist/Display/Display.Monetary.js +2 -2
  56. package/dist/Display/Display.PriceTag.js +1 -1
  57. package/dist/Display/Display.Rating.js +1 -1
  58. package/dist/Display/Display.SearchHistory.d.ts +2 -1
  59. package/dist/Display/Display.SearchHistory.js +1 -1
  60. package/dist/Display/Display.StatCard.d.ts +2 -1
  61. package/dist/Display/Display.StatCard.js +1 -1
  62. package/dist/Display/Display.StoryViewer.js +10 -6
  63. package/dist/Display/Display.SwipeableRow.d.ts +2 -1
  64. package/dist/Display/Display.Timeline.js +1 -1
  65. package/dist/Display/icon-map.d.ts +344 -0
  66. package/dist/Display/icon-map.js +86 -0
  67. package/dist/Display/index.d.ts +2 -0
  68. package/dist/Display/index.js +1 -0
  69. package/dist/Feedback/Feedback.Banner.js +13 -12
  70. package/dist/Feedback/Feedback.Empty.js +1 -1
  71. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
  72. package/dist/Feedback/Feedback.EmptyState.js +1 -1
  73. package/dist/Feedback/Feedback.Error.js +1 -1
  74. package/dist/Feedback/Feedback.PermissionCard.d.ts +2 -1
  75. package/dist/Feedback/Feedback.PermissionCard.js +2 -2
  76. package/dist/Feedback/Toast.context.js +3 -3
  77. package/dist/Form/Form.ControlledDropdown.d.ts +5 -11
  78. package/dist/Form/Form.ControlledDropdown.js +5 -2
  79. package/dist/Form/Form.DatePicker.d.ts +5 -9
  80. package/dist/Form/Form.DatePicker.js +116 -21
  81. package/dist/Form/Form.Dropdown.d.ts +11 -16
  82. package/dist/Form/Form.Dropdown.js +54 -90
  83. package/dist/Form/Form.FractionalQuantityInput.js +4 -11
  84. package/dist/Form/Form.Input.d.ts +3 -3
  85. package/dist/Form/Form.KeyboardForm.d.ts +2 -2
  86. package/dist/Form/Form.KeyboardForm.js +2 -2
  87. package/dist/Form/Form.KeyboardToolbar.d.ts +2 -1
  88. package/dist/Form/Form.KeyboardToolbar.js +3 -7
  89. package/dist/Form/Form.PasswordInput.js +1 -1
  90. package/dist/Form/Form.QuantitySelector.js +1 -1
  91. package/dist/Form/Form.Select.d.ts +12 -8
  92. package/dist/Form/Form.Select.js +64 -31
  93. package/dist/Form/Form.Slider.d.ts +4 -7
  94. package/dist/Form/Form.Slider.js +11 -10
  95. package/dist/Form/Form.Switch.js +2 -1
  96. package/dist/Form/index.d.ts +6 -14
  97. package/dist/Form/index.js +0 -11
  98. package/dist/Navigation/Navigation.AppBar.d.ts +2 -1
  99. package/dist/Navigation/Navigation.AppBar.js +2 -2
  100. package/dist/Navigation/Navigation.BottomNav.d.ts +4 -8
  101. package/dist/Navigation/Navigation.BottomNav.js +2 -2
  102. package/dist/Navigation/Navigation.CollapsingHeader.js +3 -2
  103. package/dist/Navigation/Navigation.Menu.d.ts +16 -11
  104. package/dist/Navigation/Navigation.Menu.js +97 -42
  105. package/dist/Navigation/Navigation.Search.js +2 -2
  106. package/dist/Navigation/Navigation.SearchBar.js +3 -4
  107. package/dist/Navigation/Navigation.SegmentedControl.d.ts +7 -25
  108. package/dist/Navigation/Navigation.SegmentedControl.js +18 -39
  109. package/dist/Navigation/Navigation.Steps.js +1 -1
  110. package/dist/Navigation/Navigation.Tabs.d.ts +2 -1
  111. package/dist/Navigation/index.d.ts +0 -6
  112. package/dist/Navigation/index.js +0 -8
  113. package/dist/Typography/Typography.SelectableText.js +3 -7
  114. package/dist/Typography/Typography.Text.d.ts +1 -6
  115. package/dist/Typography/Typography.Text.js +3 -6
  116. package/dist/index.d.ts +1 -4
  117. package/dist/index.js +1 -4
  118. package/dist/layouts/AuthContainer.d.ts +4 -2
  119. package/dist/layouts/AuthContainer.js +3 -4
  120. package/dist/layouts/Container.d.ts +0 -4
  121. package/dist/layouts/Container.js +3 -4
  122. package/dist/layouts/ScrollContainer.d.ts +7 -3
  123. package/dist/layouts/ScrollContainer.js +5 -4
  124. package/dist/primitives/BaseButton.d.ts +2 -1
  125. package/dist/theme/build.js +0 -4
  126. package/dist/theme/css-vars.js +0 -4
  127. package/dist/theme/dark.js +2 -6
  128. package/dist/theme/default.js +1 -5
  129. package/dist/theme/index.d.ts +1 -1
  130. package/dist/theme/index.js +1 -1
  131. package/dist/theme/types.d.ts +0 -11
  132. package/dist/theme/typography.d.ts +8 -7
  133. package/dist/theme/typography.js +25 -4
  134. package/dist/utils/timezone.d.ts +0 -5
  135. package/dist/views/AuthFormView.d.ts +3 -1
  136. package/dist/views/AuthFormView.js +2 -2
  137. package/dist/views/FormViewV2.d.ts +6 -13
  138. package/dist/views/FormViewV2.js +29 -25
  139. package/dist/views/ListViewV2.context.d.ts +5 -5
  140. package/dist/views/ListViewV2.d.ts +23 -18
  141. package/dist/views/ListViewV2.js +45 -48
  142. package/dist/views/types/IListView.d.ts +5 -4
  143. package/dist/views/types/IListView.js +18 -15
  144. package/dist/widgets/CollectionOptions.d.ts +2 -2
  145. package/dist/widgets/CollectionOptions.js +92 -65
  146. package/dist/widgets/FilterBar.js +1 -1
  147. package/dist/widgets/PromoModal.js +10 -4
  148. package/dist/widgets/QuickActions.d.ts +2 -7
  149. package/dist/widgets/QuickActions.js +1 -1
  150. package/dist/widgets/ReorderableList.js +3 -3
  151. package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -6
  152. package/dist/widgets/TimeZoneSelectorModal.js +17 -12
  153. package/dist/widgets/index.d.ts +0 -11
  154. package/dist/widgets/index.js +0 -7
  155. package/package.json +36 -38
  156. package/tailwind.preset.js +0 -4
  157. package/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +0 -324
  158. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +0 -373
  159. package/dist/Dialog/BottomSheet/index.d.ts +0 -1
  160. package/dist/Dialog/BottomSheet/index.js +0 -1
  161. package/dist/Dialog/Dialog.NativeSheet.d.ts +0 -10
  162. package/dist/Dialog/Dialog.NativeSheet.js +0 -21
  163. package/dist/Form/Form.Button.d.ts +0 -3
  164. package/dist/Form/Form.Button.js +0 -1
  165. package/dist/Form/Form.FAB.d.ts +0 -3
  166. package/dist/Form/Form.FAB.js +0 -1
  167. package/dist/Form/Form.FilterButton.d.ts +0 -3
  168. package/dist/Form/Form.FilterButton.js +0 -1
  169. package/dist/Form/Form.IconButton.d.ts +0 -3
  170. package/dist/Form/Form.IconButton.js +0 -1
  171. package/dist/Form/Form.SearchBar.d.ts +0 -13
  172. package/dist/Form/Form.SearchBar.js +0 -26
  173. package/dist/Navigation/Navigation.Header.d.ts +0 -16
  174. package/dist/Navigation/Navigation.Header.js +0 -17
  175. package/dist/Navigation/Navigation.HeaderToolbar.d.ts +0 -15
  176. package/dist/Navigation/Navigation.HeaderToolbar.js +0 -18
  177. package/dist/Navigation/Navigation.TabIcon.d.ts +0 -18
  178. package/dist/Navigation/Navigation.TabIcon.js +0 -12
  179. package/dist/Navigation/Navigation.TabItem.d.ts +0 -19
  180. package/dist/Navigation/Navigation.TabItem.js +0 -13
  181. package/dist/icons/OdooIcon.d.ts +0 -11
  182. package/dist/icons/OdooIcon.js +0 -11
  183. package/dist/icons/PrinterIcon.d.ts +0 -9
  184. package/dist/icons/PrinterIcon.js +0 -9
  185. package/dist/layouts/TabNavigatorLayout.d.ts +0 -26
  186. package/dist/layouts/TabNavigatorLayout.js +0 -60
  187. package/dist/utils/icon.d.ts +0 -1
  188. package/dist/utils/icon.js +0 -1
  189. package/dist/widgets/CircleButton.d.ts +0 -3
  190. package/dist/widgets/CircleButton.js +0 -1
  191. package/dist/widgets/GeoPermissionCard.d.ts +0 -13
  192. package/dist/widgets/GeoPermissionCard.js +0 -24
  193. package/dist/widgets/HomeHeader.d.ts +0 -15
  194. package/dist/widgets/HomeHeader.js +0 -13
  195. package/dist/widgets/MenuCard.d.ts +0 -3
  196. package/dist/widgets/MenuCard.js +0 -1
  197. package/dist/widgets/MenuGrid.d.ts +0 -5
  198. package/dist/widgets/MenuGrid.js +0 -1
  199. package/dist/widgets/MenuSection.d.ts +0 -3
  200. package/dist/widgets/MenuSection.js +0 -1
  201. package/dist/widgets/ScreenHeader.d.ts +0 -25
  202. package/dist/widgets/ScreenHeader.js +0 -32
  203. package/dist/widgets/TimeZoneSelector.d.ts +0 -12
  204. package/dist/widgets/TimeZoneSelector.js +0 -24
@@ -5,15 +5,10 @@ const defaultRowKeys = {
5
5
  readOnly: "readonly",
6
6
  };
7
7
  export function toRowItem(item, index, mapKeys) {
8
- let mapper = {};
9
- if (!mapKeys) {
10
- mapper = defaultRowKeys;
11
- }
12
- else {
13
- mapper = { ...defaultRowKeys, ...mapKeys };
14
- }
8
+ const mapper = { ...defaultRowKeys, ...mapKeys };
15
9
  if (typeof item === "string" || typeof item === "number") {
16
10
  return {
11
+ item,
17
12
  id: item,
18
13
  index,
19
14
  rowTitle: String(item),
@@ -21,15 +16,23 @@ export function toRowItem(item, index, mapKeys) {
21
16
  readonly: false,
22
17
  };
23
18
  }
24
- const rowItem = Object.create(Object.getPrototypeOf(item));
25
- Object.assign(rowItem, item, {
26
- id: item[mapper.id] ?? index,
19
+ if (typeof item !== "object" || item === null) {
20
+ throw new TypeError("List row items must be strings, numbers or objects");
21
+ }
22
+ const id = Reflect.get(item, mapper.id ?? defaultRowKeys.id) ?? index;
23
+ if (typeof id !== "string" && typeof id !== "number") {
24
+ throw new TypeError("List row IDs must be strings or numbers");
25
+ }
26
+ const title = Reflect.get(item, mapper.rowTitle ?? defaultRowKeys.rowTitle);
27
+ const subtitle = Reflect.get(item, mapper.rowSubtitle ?? defaultRowKeys.rowSubtitle);
28
+ return {
29
+ item,
30
+ id,
27
31
  index,
28
- rowTitle: item[mapper.rowTitle] ?? "N/A",
29
- rowSubtitle: item[mapper.rowSubtitle] ?? undefined,
30
- readonly: item[mapper.readOnly] ?? false,
31
- });
32
- return rowItem;
32
+ rowTitle: String(title ?? "N/A"),
33
+ rowSubtitle: subtitle == null ? undefined : String(subtitle),
34
+ readonly: Boolean(Reflect.get(item, mapper.readOnly ?? defaultRowKeys.readOnly)),
35
+ };
33
36
  }
34
37
  export function toRowItems(items, mapKeys) {
35
38
  return items.map((item, index) => toRowItem(item, index, mapKeys));
@@ -6,10 +6,10 @@ export interface CollectionOptionsProps {
6
6
  sortFields?: readonly CollectionField[];
7
7
  groupFields?: readonly CollectionField[];
8
8
  savedQueries?: readonly SavedCollectionQuery[];
9
- onApply: (query: CollectionQuery) => void;
9
+ onApply: (query: CollectionQuery) => void | Promise<void>;
10
10
  onClose: () => void;
11
11
  onSave?: (label: string, query: CollectionQuery) => void | Promise<void>;
12
12
  title?: string;
13
13
  testID?: string;
14
14
  }
15
- export default function CollectionOptions(props: CollectionOptionsProps): import("react").JSX.Element | null;
15
+ export default function CollectionOptions(props: CollectionOptionsProps): import("react").JSX.Element;
@@ -1,32 +1,23 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
2
- import Button from "../Actions/Actions.Button.js";
3
- import IconButton from "../Actions/Actions.IconButton.js";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { Actions } from "../Actions/index.js";
3
+ import Sheet from "../Dialog/Dialog.Sheet.js";
4
4
  import Input from "../Form/Form.Input.js";
5
5
  import KeyboardForm from "../Form/Form.KeyboardForm.js";
6
6
  import Pressable from "../primitives/Pressable.js";
7
- import { themeToVars, useTheme } from "../theme/index.js";
8
7
  import { Typography } from "../Typography/index.js";
9
8
  import { describeCriteria, moveCollectionEntry, validateCriteria, } from "../utils/criteria.js";
10
- import { useReducedMotion } from "../utils/useReducedMotion.js";
11
9
  import FilterBuilder from "./FilterBuilder.js";
12
10
  import QueryChoice from "./QueryChoice.js";
13
- import { vars } from "nativewind";
14
- import { useState } from "react";
15
- import { Keyboard, Modal, View } from "react-native";
16
- import { useSafeAreaInsets } from "react-native-safe-area-context";
17
- export default function CollectionOptions(props) {
18
- return props.visible ? _jsx(OptionsEditor, { ...props }) : null;
19
- }
11
+ import { useRef, useState } from "react";
12
+ import { Keyboard, View } from "react-native";
20
13
  const sections = [
21
14
  { id: "filter", label: "Filtros" },
22
15
  { id: "sort", label: "Ordenar" },
23
16
  { id: "group", label: "Agrupar" },
24
17
  { id: "saved", label: "Guardados" },
25
18
  ];
26
- function OptionsEditor({ value, fields, sortFields = [], groupFields = [], savedQueries = [], onApply, onClose, onSave, title = "Configurar colección", testID, }) {
27
- const theme = useTheme();
28
- const reducedMotion = useReducedMotion();
29
- const insets = useSafeAreaInsets();
19
+ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], savedQueries = [], onApply, onClose, onSave, onPendingChange, }) {
20
+ const pending = useRef(false);
30
21
  const [draft, setDraft] = useState(value);
31
22
  const [section, setSection] = useState("filter");
32
23
  const [editing, setEditing] = useState(false);
@@ -34,6 +25,7 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
34
25
  const [error, setError] = useState();
35
26
  const [status, setStatus] = useState();
36
27
  const [saving, setSaving] = useState(false);
28
+ const [applying, setApplying] = useState(false);
37
29
  const problem = () => validateCriteria(draft.filter, fields) ??
38
30
  (draft.sort.some((sort) => !sortFields.some((field) => field.id === sort.field))
39
31
  ? "El orden contiene un campo no disponible."
@@ -42,7 +34,7 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
42
34
  ? "El agrupado contiene un campo no disponible."
43
35
  : undefined);
44
36
  const close = () => {
45
- if (!saving) {
37
+ if (!pending.current) {
46
38
  Keyboard.dismiss();
47
39
  onClose();
48
40
  }
@@ -58,53 +50,83 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
58
50
  }),
59
51
  }
60
52
  : { ...current, groupBy: next });
61
- return (_jsx(Modal, { animationType: reducedMotion ? "none" : "slide", onRequestClose: close, presentationStyle: "fullScreen", visible: true, children: _jsxs(View, { accessibilityViewIsModal: true, onAccessibilityEscape: close, style: [
62
- vars(themeToVars(theme)),
63
- {
64
- flex: 1,
65
- backgroundColor: theme.bg.page,
66
- paddingTop: insets.top,
67
- paddingBottom: insets.bottom,
68
- },
69
- ], testID: testID, children: [_jsxs(View, { className: "flex-row items-center justify-between gap-3 px-4", children: [_jsx(Typography.Text, { accessibilityRole: "header", semibold: true, style: { flex: 1, minWidth: 0 }, variant: "bodyReadable", children: title }), _jsx(IconButton, { accessibilityLabel: "Cancelar configuraci\u00F3n", disabled: saving, icon: "close", onPress: close })] }), _jsx(View, { accessibilityRole: "tablist", className: "flex-row flex-wrap px-4 gap-2", children: sections.map((item) => (_jsx(Pressable, { accessibilityLabel: item.label, accessibilityRole: "tab", accessibilityState: {
70
- selected: item.id === section,
71
- disabled: editing || saving,
72
- }, disabled: editing || saving, onPress: () => {
73
- Keyboard.dismiss();
74
- setSection(item.id);
75
- setError(undefined);
76
- setStatus(undefined);
77
- }, style: { minHeight: 48, justifyContent: "center", paddingHorizontal: 8 }, children: _jsx(Typography.Text, { className: section === item.id ? "text-text-accent" : "text-text-secondary", semibold: section === item.id, variant: "bodyLarge", children: item.label }) }, item.id))) }), _jsxs(KeyboardForm, { contentContainerStyle: { padding: 16, gap: 16 }, children: [!editing ? (_jsx(Typography.Text, { className: "text-text-secondary", variant: "bodyLarge", children: describeCriteria(draft.filter, fields) })) : null, section === "filter" ? (_jsx(FilterBuilder, { fields: fields, onChange: (filter) => {
53
+ const saveConfiguration = async () => {
54
+ if (!onSave || pending.current)
55
+ return;
56
+ const invalid = problem();
57
+ if (invalid || !label.trim()) {
58
+ setError(invalid ?? "Escribe un nombre para guardar.");
59
+ return;
60
+ }
61
+ pending.current = true;
62
+ setSaving(true);
63
+ onPendingChange(true);
64
+ setError(undefined);
65
+ setStatus(undefined);
66
+ try {
67
+ await onSave(label.trim(), draft);
68
+ setStatus("Configuración guardada. Pulsa Aplicar configuración para usarla.");
69
+ }
70
+ catch (cause) {
71
+ console.error("[CollectionOptions] Save failed", cause);
72
+ setError("No se pudo guardar. Conservamos la configuración para reintentar.");
73
+ }
74
+ finally {
75
+ pending.current = false;
76
+ setSaving(false);
77
+ onPendingChange(false);
78
+ }
79
+ };
80
+ const applyConfiguration = async () => {
81
+ if (editing || pending.current)
82
+ return;
83
+ const invalid = problem();
84
+ if (invalid) {
85
+ setError(invalid);
86
+ return;
87
+ }
88
+ pending.current = true;
89
+ setSaving(true);
90
+ setApplying(true);
91
+ onPendingChange(true);
92
+ setError(undefined);
93
+ try {
94
+ await onApply(draft);
95
+ Keyboard.dismiss();
96
+ onClose();
97
+ }
98
+ catch (cause) {
99
+ console.error("[CollectionOptions] Apply failed", cause);
100
+ setError("No se pudo aplicar. Conservamos la configuración para reintentar.");
101
+ }
102
+ finally {
103
+ pending.current = false;
104
+ setSaving(false);
105
+ setApplying(false);
106
+ onPendingChange(false);
107
+ }
108
+ };
109
+ return (_jsxs(View, { style: { flex: 1, minHeight: 0 }, children: [_jsx(View, { accessibilityRole: "tablist", className: "flex-row flex-wrap px-4 gap-2", children: sections.map((item) => (_jsx(Pressable, { accessibilityLabel: item.label, accessibilityRole: "tab", accessibilityState: {
110
+ selected: item.id === section,
111
+ disabled: editing || saving,
112
+ }, disabled: editing || saving, onPress: () => {
113
+ Keyboard.dismiss();
114
+ setSection(item.id);
115
+ setError(undefined);
116
+ setStatus(undefined);
117
+ }, style: { minHeight: 48, justifyContent: "center", paddingHorizontal: 8 }, children: _jsx(Typography.Text, { className: section === item.id ? "text-text-accent" : "text-text-secondary", semibold: section === item.id, variant: "bodyLarge", children: item.label }) }, item.id))) }), _jsx(View, { importantForAccessibility: saving ? "no-hide-descendants" : "auto", pointerEvents: saving ? "none" : "auto", style: { flex: 1, minHeight: 0 }, children: _jsxs(KeyboardForm, { contentContainerStyle: { paddingVertical: 16, gap: 16 }, children: [!editing ? (_jsx(Typography.Text, { className: "text-text-secondary", variant: "bodyLarge", children: describeCriteria(draft.filter, fields) })) : null, section === "filter" ? (_jsx(FilterBuilder, { fields: fields, onChange: (filter) => {
78
118
  setError(undefined);
79
119
  setDraft({ ...draft, filter });
80
- }, onEditingChange: setEditing, value: draft.filter })) : section === "saved" ? (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { variant: "bodyReadable", children: "Una configuraci\u00F3n incluye b\u00FAsqueda, condiciones, orden y niveles de agrupaci\u00F3n. Cargarla todav\u00EDa no modifica la colecci\u00F3n." }), !savedQueries.length ? (_jsx(Typography.Text, { children: "No hay configuraciones guardadas." })) : (savedQueries.map((saved) => (_jsx(Button, { disabled: saving, onPress: () => {
120
+ }, onEditingChange: setEditing, value: draft.filter })) : section === "saved" ? (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { variant: "bodyReadable", children: "Una configuraci\u00F3n incluye b\u00FAsqueda, condiciones, orden y niveles de agrupaci\u00F3n. Cargarla todav\u00EDa no modifica la colecci\u00F3n." }), !savedQueries.length ? (_jsx(Typography.Text, { children: "No hay configuraciones guardadas." })) : (savedQueries.map((saved) => (_jsx(Actions.Button, { disabled: saving, onPress: () => {
81
121
  setDraft(saved.query);
82
122
  setError(undefined);
83
123
  setStatus(`Configuración ${saved.label} cargada. Pulsa Aplicar configuración.`);
84
- }, title: `Cargar ${saved.label}`, variant: "secondary" }, saved.id)))), onSave ? (_jsxs(_Fragment, { children: [_jsx(Input, { editable: !saving, label: "Nombre de la configuraci\u00F3n", onChangeText: setLabel, testID: "collection-query-name", value: label }), _jsx(Button, { loading: saving, onPress: () => {
85
- const invalid = problem();
86
- if (invalid || !label.trim()) {
87
- setError(invalid ?? "Escribe un nombre para guardar.");
88
- return;
89
- }
90
- setError(undefined);
91
- setStatus(undefined);
92
- setSaving(true);
93
- Promise.resolve()
94
- .then(() => onSave(label.trim(), draft))
95
- .then(() => {
96
- setStatus("Configuración guardada. Pulsa Aplicar configuración para usarla.");
97
- }, (cause) => {
98
- console.error("[CollectionOptions] Save failed", cause);
99
- setError("No se pudo guardar. Conservamos la configuración para reintentar.");
100
- })
101
- .finally(() => setSaving(false));
102
- }, title: "Guardar configuraci\u00F3n" })] })) : null] })) : (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { variant: "bodyReadable", children: section === "sort"
124
+ }, title: `Cargar ${saved.label}`, variant: "secondary" }, saved.id)))), onSave ? (_jsxs(_Fragment, { children: [_jsx(Input, { editable: !saving, label: "Nombre de la configuraci\u00F3n", onChangeText: setLabel, testID: "collection-query-name", value: label }), _jsx(Actions.Button, { loading: saving, onPress: () => void saveConfiguration(), title: "Guardar configuraci\u00F3n" })] })) : null] })) : (_jsxs(_Fragment, { children: [_jsx(Typography.Text, { variant: "bodyReadable", children: section === "sort"
103
125
  ? "El primer campo tiene prioridad; los siguientes resuelven empates."
104
126
  : "Los grupos se anidan en este orden. La app aplica los niveles al conjunto completo." }), !ordered.length ? (_jsx(Typography.Text, { children: section === "sort" ? "Sin orden configurado." : "Sin agrupación." })) : null, ordered.map((id, index) => {
105
127
  const field = available.find((item) => item.id === id);
106
128
  const sort = draft.sort.find((entry) => entry.field === id);
107
- return (_jsxs(View, { className: "gap-2 border-b border-border-light pb-3", children: [_jsxs(Typography.Text, { semibold: true, variant: "bodyReadable", children: [index + 1, ". ", field?.label ?? `${id} (no disponible)`] }), section === "sort" ? (_jsx(Button, { onPress: () => setDraft({
129
+ return (_jsxs(View, { className: "gap-2 border-b border-border-light pb-3", children: [_jsxs(Typography.Text, { semibold: true, variant: "bodyReadable", children: [index + 1, ". ", field?.label ?? `${id} (no disponible)`] }), section === "sort" ? (_jsx(Actions.Button, { onPress: () => setDraft({
108
130
  ...draft,
109
131
  sort: draft.sort.map((entry) => entry.field === id
110
132
  ? {
@@ -112,18 +134,23 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
112
134
  direction: entry.direction === "asc" ? "desc" : "asc",
113
135
  }
114
136
  : entry),
115
- }), title: `${field?.label ?? id}: ${sort?.direction === "asc" ? (field?.ascLabel ?? "Ascendente") : (field?.descLabel ?? "Descendente")}`, variant: "secondary" })) : null, _jsxs(View, { className: "flex-row flex-wrap gap-2", children: [_jsx(Button, { accessibilityLabel: `Subir ${field?.label ?? id}`, disabled: index === 0, onPress: () => updateOrder(moveCollectionEntry(ordered, index, -1)), title: "Subir", variant: "link" }), _jsx(Button, { accessibilityLabel: `Bajar ${field?.label ?? id}`, disabled: index === ordered.length - 1, onPress: () => updateOrder(moveCollectionEntry(ordered, index, 1)), title: "Bajar", variant: "link" }), _jsx(Button, { accessibilityLabel: `Quitar ${field?.label ?? id}`, onPress: () => updateOrder(ordered.filter((key) => key !== id)), title: "Quitar", variant: "link" })] })] }, id));
137
+ }), title: `${field?.label ?? id}: ${sort?.direction === "asc" ? (field?.ascLabel ?? "Ascendente") : (field?.descLabel ?? "Descendente")}`, variant: "secondary" })) : null, _jsxs(View, { className: "flex-row flex-wrap gap-2", children: [_jsx(Actions.Button, { accessibilityLabel: `Subir ${field?.label ?? id}`, disabled: index === 0, onPress: () => updateOrder(moveCollectionEntry(ordered, index, -1)), title: "Subir", variant: "link" }), _jsx(Actions.Button, { accessibilityLabel: `Bajar ${field?.label ?? id}`, disabled: index === ordered.length - 1, onPress: () => updateOrder(moveCollectionEntry(ordered, index, 1)), title: "Bajar", variant: "link" }), _jsx(Actions.Button, { accessibilityLabel: `Quitar ${field?.label ?? id}`, onPress: () => updateOrder(ordered.filter((key) => key !== id)), title: "Quitar", variant: "link" })] })] }, id));
116
138
  }), available.some((field) => !ordered.includes(field.id)) ? (_jsx(QueryChoice, { label: section === "sort"
117
139
  ? "Añadir criterio de orden"
118
140
  : "Añadir nivel de agrupación", onChange: (id) => updateOrder([...ordered, id]), options: available
119
141
  .filter((field) => !ordered.includes(field.id))
120
- .map((field) => ({ label: field.label, value: field.id })), value: undefined })) : null] })), status ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", children: status })) : null, error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-danger", children: error })) : null] }), _jsxs(View, { className: "px-4 py-3 border-t border-border-light gap-2", children: [editing ? (_jsx(Typography.Text, { children: "Guarda o cancela la condici\u00F3n antes de aplicar." })) : null, _jsx(Button, { disabled: editing || saving, onPress: () => {
121
- const invalid = problem();
122
- if (invalid) {
123
- setError(invalid);
124
- return;
125
- }
126
- Keyboard.dismiss();
127
- onApply(draft);
128
- }, title: "Aplicar configuraci\u00F3n" })] })] }) }));
142
+ .map((field) => ({ label: field.label, value: field.id })), value: undefined })) : null] })), status ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", children: status })) : null] }) }), error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", accessibilityRole: "alert", className: "pb-3 text-danger", selectable: true, children: error })) : null, _jsxs(View, { className: "py-3 border-t border-border-light gap-2", children: [editing ? (_jsx(Typography.Text, { children: "Guarda o cancela la condici\u00F3n antes de aplicar." })) : null, _jsx(Actions.Button, { accessibilityLabel: "Cancelar configuraci\u00F3n", disabled: saving, onPress: close, title: "Cancelar", variant: "secondary" }), _jsx(Actions.Button, { disabled: editing || saving, loading: applying, onPress: () => void applyConfiguration(), title: "Aplicar configuraci\u00F3n" })] })] }));
143
+ }
144
+ export default function CollectionOptions(props) {
145
+ const busy = useRef(false);
146
+ const [pending, setPending] = useState(false);
147
+ return (_jsx(Sheet, { dismissible: !pending, onClose: () => {
148
+ if (!busy.current) {
149
+ Keyboard.dismiss();
150
+ props.onClose();
151
+ }
152
+ }, scrollable: false, testID: props.testID, title: props.title ?? "Configurar colección", visible: props.visible, children: props.visible ? (_jsx(OptionsEditor, { ...props, onPendingChange: (next) => {
153
+ busy.current = next;
154
+ setPending(next);
155
+ } })) : null }));
129
156
  }
@@ -10,5 +10,5 @@ export function FilterBar({ filters, onFilterPress, search, onOpenFilters, filte
10
10
  const { fontScale } = useWindowDimensions();
11
11
  const selectedCount = filters.filter((filter) => filter.selected).length;
12
12
  const filterLayout = layout === "auto" ? (fontScale > 1.3 ? "wrap" : "scroll") : layout;
13
- return (_jsxs(View, { className: cn("gap-2", className), testID: testID, children: [search || onOpenFilters ? (_jsxs(View, { className: "flex-row items-center gap-2", children: [search ? (_jsx(View, { className: "flex-1 min-w-0", children: _jsx(SearchBar, { ...search }) })) : null, onOpenFilters ? (_jsx(IconButton, { accessibilityLabel: filtersLabel, icon: "sliders", onPress: onOpenFilters })) : null] })) : null, _jsx(FilterButtonGroup, { className: "p-0", layout: filterLayout, testID: testID ? `${testID}-options` : undefined, children: filters.map((filter) => (_jsx(FilterButton, { active: filter.selected, disabled: filter.disabled, onPress: () => onFilterPress(filter.id), testID: testID ? `${testID}-${filter.id}` : undefined, title: filter.label, variant: "mobile" }, filter.id))) }), resultsLabel || (selectedCount > 0 && onClearFilters) ? (_jsxs(View, { className: "flex-row flex-wrap items-center justify-between gap-x-3", children: [resultsLabel ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-text-secondary", variant: "bodyLarge", children: resultsLabel })) : null, selectedCount > 0 && onClearFilters ? (_jsx(Pressable, { accessibilityLabel: clearLabel, accessibilityRole: "button", onPress: onClearFilters, style: { minHeight: 48, minWidth: 48, justifyContent: "center" }, children: _jsx(Typography.Text, { className: "text-text-accent", variant: "bodyLarge", children: clearLabel }) })) : null] })) : null] }));
13
+ return (_jsxs(View, { className: cn("gap-2", className), testID: testID, children: [search || onOpenFilters ? (_jsxs(View, { className: "flex-row items-center gap-2", children: [search ? (_jsx(View, { className: "flex-1 min-w-0", children: _jsx(SearchBar, { ...search }) })) : null, onOpenFilters ? (_jsx(IconButton, { accessibilityLabel: filtersLabel, icon: "filter", onPress: onOpenFilters })) : null] })) : null, _jsx(FilterButtonGroup, { className: "p-0", layout: filterLayout, testID: testID ? `${testID}-options` : undefined, children: filters.map((filter) => (_jsx(FilterButton, { active: filter.selected, disabled: filter.disabled, onPress: () => onFilterPress(filter.id), testID: testID ? `${testID}-${filter.id}` : undefined, title: filter.label, variant: "mobile" }, filter.id))) }), resultsLabel || (selectedCount > 0 && onClearFilters) ? (_jsxs(View, { className: "flex-row flex-wrap items-center justify-between gap-x-3", children: [resultsLabel ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-text-secondary", variant: "bodyLarge", children: resultsLabel })) : null, selectedCount > 0 && onClearFilters ? (_jsx(Pressable, { accessibilityLabel: clearLabel, accessibilityRole: "button", onPress: onClearFilters, style: { minHeight: 48, minWidth: 48, justifyContent: "center" }, children: _jsx(Typography.Text, { className: "text-text-accent", variant: "bodyLarge", children: clearLabel }) })) : null] })) : null] }));
14
14
  }
@@ -4,6 +4,7 @@ import { useTheme } from "../theme/index.js";
4
4
  import { Typography } from "../Typography/index.js";
5
5
  import { useReducedMotion } from "../utils/useReducedMotion.js";
6
6
  import { Modal, TouchableOpacity, TouchableWithoutFeedback, View } from "react-native";
7
+ import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
7
8
  import { useSafeAreaInsets } from "react-native-safe-area-context";
8
9
  /**
9
10
  * Bottom-anchored modal for news, promotions, or announcements.
@@ -34,15 +35,20 @@ export function PromoModal({ visible, onDismiss, title, body, icon: Icon, header
34
35
  justifyContent: "center",
35
36
  paddingHorizontal: 24,
36
37
  paddingVertical: insets.top + 16,
37
- } }) }), _jsx(View, { pointerEvents: "box-none", style: {
38
+ } }) }), _jsx(KeyboardAwareScrollView, { bottomOffset: 24, contentContainerStyle: {
39
+ flexGrow: 1,
40
+ alignItems: "center",
41
+ justifyContent: "center",
42
+ paddingLeft: insets.left + 24,
43
+ paddingRight: insets.right + 24,
44
+ paddingTop: insets.top + 16,
45
+ paddingBottom: insets.bottom + 16,
46
+ }, keyboardShouldPersistTaps: "handled", pointerEvents: "box-none", style: {
38
47
  position: "absolute",
39
48
  top: 0,
40
49
  left: 0,
41
50
  right: 0,
42
51
  bottom: 0,
43
- alignItems: "center",
44
- justifyContent: "center",
45
- paddingHorizontal: 24,
46
52
  }, children: _jsxs(View, { style: {
47
53
  backgroundColor: theme.bg.card,
48
54
  borderRadius: 28,
@@ -1,13 +1,8 @@
1
- import { ComponentType } from "react";
1
+ import type { IconName } from "../Display/index.js";
2
2
  export interface QuickActionItem {
3
3
  id: string;
4
4
  label: string;
5
- /** Ionicon name — use this OR `customIcon`. */
6
- icon?: string;
7
- customIcon?: ComponentType<{
8
- size?: number;
9
- color?: string;
10
- }>;
5
+ icon: IconName;
11
6
  badge?: string | number;
12
7
  onPress?: () => void;
13
8
  /** Circle background color override. Defaults to `theme.bg.muted`. */
@@ -19,7 +19,7 @@ export function QuickActions({ items, title }) {
19
19
  justifyContent: "center",
20
20
  borderWidth: 1,
21
21
  borderColor: theme.border.default,
22
- }, children: _jsx(Icon, { color: theme.text.primary, customIcon: item.customIcon, name: item.icon, size: 24, type: item.customIcon ? "custom" : "ionicons" }) }), item.badge != null ? (_jsx(View, { style: {
22
+ }, children: _jsx(Icon, { color: theme.text.primary, name: item.icon, size: 24 }) }), item.badge != null ? (_jsx(View, { style: {
23
23
  position: "absolute",
24
24
  top: 2,
25
25
  right: 4,
@@ -105,7 +105,7 @@ export default function ReorderableList({ items, keyExtractor, getItemLabel, onC
105
105
  justifyContent: "center",
106
106
  },
107
107
  handleStyle,
108
- ], testID: testID ? `${testID}-handle-${key}` : undefined, children: renderHandleContent ? (renderHandleContent(context)) : (_jsx(Icon, { name: "reorder-two-outline", size: 20 })) }) }));
108
+ ], testID: testID ? `${testID}-handle-${key}` : undefined, children: renderHandleContent ? (renderHandleContent(context)) : (_jsx(Icon, { name: "reorder", size: 20 })) }) }));
109
109
  return (_jsxs(View, { style: [
110
110
  {
111
111
  width: "100%",
@@ -125,7 +125,7 @@ export default function ReorderableList({ items, keyExtractor, getItemLabel, onC
125
125
  justifyContent: "center",
126
126
  },
127
127
  actionStyle,
128
- ], children: _jsx(Icon, { name: "arrow-up-outline", size: 18 }) }), _jsx(Pressable, { accessibilityLabel: labels?.down?.(label) ?? `Bajar ${label}`, disabled: !context.canMoveDown, onPress: context.moveDown, style: [
128
+ ], children: _jsx(Icon, { name: "up", size: 18 }) }), _jsx(Pressable, { accessibilityLabel: labels?.down?.(label) ?? `Bajar ${label}`, disabled: !context.canMoveDown, onPress: context.moveDown, style: [
129
129
  {
130
130
  minWidth: 48,
131
131
  minHeight: 48,
@@ -133,6 +133,6 @@ export default function ReorderableList({ items, keyExtractor, getItemLabel, onC
133
133
  justifyContent: "center",
134
134
  },
135
135
  actionStyle,
136
- ], children: _jsx(Icon, { name: "arrow-down-outline", size: 18 }) })] })) })] }, key));
136
+ ], children: _jsx(Icon, { name: "down", size: 18 }) })] })) })] }, key));
137
137
  }, sortEnabled: !disabled, strategy: "insert" }, revision), error ? (_jsx(Typography.Text, { accessibilityRole: "alert", className: "text-danger", children: error })) : null] }));
138
138
  }
@@ -1,14 +1,9 @@
1
- import { TimezoneLocale } from "../utils/timezone.js";
1
+ import type { TimezoneLocale } from "../utils/timezone.js";
2
2
  interface TimeZoneSelectorModalProps {
3
3
  visible: boolean;
4
4
  onClose: () => void;
5
5
  onSelect: (tz: TimezoneLocale) => void;
6
6
  currentTimezone?: string | null;
7
7
  }
8
- /**
9
- * @deprecated Domain/region-coupled (hardcoded LatAm timezones + Spanish copy); relocated
10
- * to the core. Import from `@sincpro/mobile/ui/components/molecules` instead. Kept here for
11
- * backwards compatibility and removed in the next major.
12
- */
13
8
  declare function TimeZoneSelectorModal({ visible, onClose, onSelect, currentTimezone, }: TimeZoneSelectorModalProps): import("react").JSX.Element;
14
9
  export { TimeZoneSelectorModal };
@@ -1,10 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { BottomSheet } from "../Dialog/BottomSheet/index.js";
3
- import { Display, Form } from "../index.js";
4
- import { theme } from "../theme/index.js";
2
+ import { FlashList } from "@shopify/flash-list";
3
+ import Button from "../Actions/Actions.Button.js";
4
+ import Sheet from "../Dialog/Dialog.Sheet.js";
5
+ import Icon from "../Display/Display.Icon.js";
6
+ import Pressable from "../primitives/Pressable.js";
7
+ import { useTheme } from "../theme/index.js";
5
8
  import { Typography } from "../Typography/index.js";
6
9
  import { useMemo, useState } from "react";
7
- import { FlatList, TextInput, TouchableOpacity, View } from "react-native";
10
+ import { TextInput, View } from "react-native";
8
11
  const TIMEZONES_LOCALES = {
9
12
  bolivia: { timezone: "America/La_Paz", locale: "es-BO" },
10
13
  costaRica: { timezone: "America/Costa_Rica", locale: "es-CR" },
@@ -15,17 +18,18 @@ const TIMEZONES_LOCALES = {
15
18
  japon: { timezone: "Asia/Tokyo", locale: "ja-JP" },
16
19
  };
17
20
  function TimezoneRow({ item, isSelected, onSelect, }) {
18
- return (_jsxs(TouchableOpacity, { activeOpacity: 0.7, className: `flex-row items-center p-4 border-b border-border-light ${isSelected ? "bg-info-light" : "bg-white"}`, onPress: onSelect, children: [_jsx(Display.Icon, { color: isSelected ? theme.info : theme.text.secondary, name: "schedule", size: 24, type: "material" }), _jsxs(View, { className: "ml-3 flex-1", children: [_jsx(Typography.Text, { semibold: true, children: item.label }), _jsx(Typography.Text, { className: "text-text-tertiary text-xs", children: item.locale })] }), isSelected && (_jsx(View, { className: "w-6 h-6 rounded-full bg-info items-center justify-center", children: _jsx(Display.Icon, { color: theme.text.inverse, name: "check", size: 16, type: "material" }) }))] }));
21
+ const theme = useTheme();
22
+ return (_jsxs(Pressable, { accessibilityHint: item.locale, accessibilityLabel: item.label, accessibilityRole: "radio", accessibilityState: { checked: isSelected, selected: isSelected }, className: "flex-row items-center p-4 border-b border-border-light", onPress: onSelect, style: {
23
+ minHeight: 64,
24
+ backgroundColor: isSelected ? theme.container.info : theme.bg.card,
25
+ }, testID: `timezone-${item.timezone}`, children: [_jsx(Icon, { color: isSelected ? theme.info : theme.text.secondary, name: "time", size: 24 }), _jsxs(View, { className: "ml-3 flex-1", children: [_jsx(Typography.Text, { semibold: true, children: item.label }), _jsx(Typography.Text, { className: "text-text-tertiary text-xs", children: item.locale })] }), isSelected && (_jsx(View, { className: "w-6 h-6 rounded-full bg-info items-center justify-center", children: _jsx(Icon, { color: theme.text.inverse, name: "check", size: 16 }) }))] }));
19
26
  }
20
27
  function EmptyState() {
21
- return (_jsxs(View, { className: "p-8 items-center justify-center", style: { minHeight: 200 }, children: [_jsx(Display.Icon, { color: theme.text.tertiary, name: "search-off", size: 48, type: "material" }), _jsx(Typography.Text, { className: "text-text-secondary text-center mt-4", children: "No se encontraron zonas horarias" })] }));
28
+ const theme = useTheme();
29
+ return (_jsxs(View, { className: "p-8 items-center justify-center", style: { minHeight: 200 }, children: [_jsx(Icon, { color: theme.text.tertiary, name: "search-empty", size: 48 }), _jsx(Typography.Text, { className: "text-text-secondary text-center mt-4", children: "No se encontraron zonas horarias" })] }));
22
30
  }
23
- /**
24
- * @deprecated Domain/region-coupled (hardcoded LatAm timezones + Spanish copy); relocated
25
- * to the core. Import from `@sincpro/mobile/ui/components/molecules` instead. Kept here for
26
- * backwards compatibility and removed in the next major.
27
- */
28
31
  function TimeZoneSelectorModal({ visible, onClose, onSelect, currentTimezone, }) {
32
+ const theme = useTheme();
29
33
  const [query, setQuery] = useState("");
30
34
  const [selectedTimezone, setSelectedTimezone] = useState(currentTimezone || null);
31
35
  const timezoneList = useMemo(() => {
@@ -38,6 +42,7 @@ function TimeZoneSelectorModal({ visible, onClose, onSelect, currentTimezone, })
38
42
  if (!query.trim())
39
43
  return timezoneList;
40
44
  const normalizedQuery = query
45
+ .trim()
41
46
  .toLowerCase()
42
47
  .normalize("NFD")
43
48
  .replace(/[\u0300-\u036f]/g, "");
@@ -57,6 +62,6 @@ function TimeZoneSelectorModal({ visible, onClose, onSelect, currentTimezone, })
57
62
  onClose();
58
63
  }
59
64
  }
60
- return (_jsxs(BottomSheet.Root, { onClose: onClose, size: "large", visible: visible, children: [_jsxs(BottomSheet.Header, { children: [_jsxs(View, { className: "px-5 pb-3", children: [_jsx(Typography.Text, { bold: true, variant: "subtitle", children: "Zona Horaria" }), _jsx(Typography.Text, { className: "text-text-secondary text-xs", children: "Selecciona tu zona horaria" })] }), _jsx(View, { className: "px-5 pb-3", children: _jsx(TextInput, { className: "bg-bg-muted px-4 py-3 rounded-xl", onChangeText: setQuery, placeholder: "Buscar zona horaria...", placeholderTextColor: theme.text.tertiary, selectionColor: theme.primary, underlineColorAndroid: "transparent", value: query }) })] }), _jsx(BottomSheet.Content, { scrollable: true, children: filteredTimezones.length === 0 ? (_jsx(EmptyState, {})) : (_jsx(FlatList, { data: filteredTimezones, keyExtractor: (item) => item.timezone, renderItem: ({ item }) => (_jsx(TimezoneRow, { isSelected: selectedTimezone === item.timezone, item: item, onSelect: () => setSelectedTimezone(item.timezone) })), scrollEnabled: false })) }), _jsxs(BottomSheet.Actions, { layout: "horizontal", children: [_jsx(View, { className: "flex-1", children: _jsx(Form.Button, { onPress: onClose, title: "Cancelar", variant: "outline" }) }), selectedTimezone && (_jsx(View, { className: "flex-1", children: _jsx(Form.Button, { onPress: handleConfirm, title: "Confirmar", variant: "primary" }) }))] })] }));
65
+ return (_jsx(Sheet, { footer: _jsxs(View, { className: "flex-row gap-3", children: [_jsx(View, { className: "flex-1", children: _jsx(Button, { onPress: onClose, title: "Cancelar", variant: "outline" }) }), selectedTimezone && (_jsx(View, { className: "flex-1", children: _jsx(Button, { onPress: handleConfirm, title: "Confirmar", variant: "primary" }) }))] }), onClose: onClose, scrollable: false, snapPoints: ["90%"], testID: "timezone-sheet", title: "Zona Horaria", visible: visible, children: _jsxs(View, { className: "flex-1", children: [_jsxs(View, { className: "px-5 pb-3", children: [_jsx(Typography.Text, { className: "text-text-secondary text-xs mb-3", children: "Selecciona tu zona horaria" }), _jsx(TextInput, { accessibilityLabel: "Buscar zona horaria", className: "bg-bg-muted px-4 py-3 rounded-xl", onChangeText: setQuery, placeholder: "Buscar zona horaria...", placeholderTextColor: theme.text.tertiary, selectionColor: theme.primary, style: { color: theme.text.primary }, testID: "timezone-search", underlineColorAndroid: "transparent", value: query })] }), _jsx(FlashList, { contentContainerStyle: { flexGrow: 1 }, data: filteredTimezones, extraData: selectedTimezone, keyboardDismissMode: "on-drag", keyboardShouldPersistTaps: "handled", keyExtractor: (item) => item.timezone, ListEmptyComponent: EmptyState, maintainVisibleContentPosition: { disabled: true }, renderItem: ({ item }) => (_jsx(TimezoneRow, { isSelected: selectedTimezone === item.timezone, item: item, onSelect: () => setSelectedTimezone(item.timezone) })), style: { flex: 1 }, testID: "timezone-list" })] }) }));
61
66
  }
62
67
  export { TimeZoneSelectorModal };
@@ -1,21 +1,10 @@
1
1
  export type { CollectionField, CollectionQuery, CollectionSort, CriteriaCondition, CriteriaField, CriteriaGroup, CriteriaNode, CriteriaOperator, CriteriaValue, SavedCollectionQuery, } from "../utils/criteria.js";
2
- export { CircleButton } from "./CircleButton.js";
3
2
  export { default as CollectionOptions, type CollectionOptionsProps, } from "./CollectionOptions.js";
4
3
  export { FilterBar, type FilterBarOption, type FilterBarProps, } from "./FilterBar.js";
5
4
  export { default as FilterBuilder, type FilterBuilderProps, } from "./FilterBuilder.js";
6
- export { default as GeoPermissionCard } from "./GeoPermissionCard.js";
7
- export { HomeHeader } from "./HomeHeader.js";
8
- export { MenuCard } from "./MenuCard.js";
9
- export type { MenuGridItem, MenuGridProps } from "./MenuGrid.js";
10
- /** @deprecated Renamed to `MenuGridItem`. */
11
- export type { MenuGridItem as MenuItem } from "./MenuGrid.js";
12
- export { MenuGrid } from "./MenuGrid.js";
13
- export type { MenuSectionProps } from "./MenuSection.js";
14
- export { MenuSection } from "./MenuSection.js";
15
5
  export type { PromoBannerItem, PromoBannerProps, } from "./PromoBanner.js";
16
6
  export { PromoBanner } from "./PromoBanner.js";
17
7
  export type { PromoModalProps } from "./PromoModal.js";
18
8
  export { PromoModal } from "./PromoModal.js";
19
9
  export type { QuickActionItem, QuickActionsProps, } from "./QuickActions.js";
20
10
  export { QuickActions } from "./QuickActions.js";
21
- export { TimeZoneSelector } from "./TimeZoneSelector.js";
@@ -1,13 +1,6 @@
1
- export { CircleButton } from "./CircleButton.js";
2
1
  export { default as CollectionOptions, } from "./CollectionOptions.js";
3
2
  export { FilterBar, } from "./FilterBar.js";
4
3
  export { default as FilterBuilder, } from "./FilterBuilder.js";
5
- export { default as GeoPermissionCard } from "./GeoPermissionCard.js";
6
- export { HomeHeader } from "./HomeHeader.js";
7
- export { MenuCard } from "./MenuCard.js";
8
- export { MenuGrid } from "./MenuGrid.js";
9
- export { MenuSection } from "./MenuSection.js";
10
4
  export { PromoBanner } from "./PromoBanner.js";
11
5
  export { PromoModal } from "./PromoModal.js";
12
6
  export { QuickActions } from "./QuickActions.js";
13
- export { TimeZoneSelector } from "./TimeZoneSelector.js";