@sincpro/mobile-ui 2.0.1 → 3.0.1

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 (198) hide show
  1. package/README.md +7 -2
  2. package/dist/Actions/Actions.Button.d.ts +2 -1
  3. package/dist/Actions/Actions.Button.js +31 -37
  4. package/dist/Actions/Actions.Dock.d.ts +6 -6
  5. package/dist/Actions/Actions.Dock.js +14 -72
  6. package/dist/Actions/Actions.FAB.d.ts +2 -1
  7. package/dist/Actions/Actions.FilterButton.js +1 -1
  8. package/dist/Actions/Actions.IconButton.d.ts +3 -4
  9. package/dist/Actions/Actions.IconButton.js +2 -2
  10. package/dist/Dialog/Confirmation.context.d.ts +5 -14
  11. package/dist/Dialog/Confirmation.context.js +11 -51
  12. package/dist/Dialog/Dialog.ActionSheet.d.ts +8 -7
  13. package/dist/Dialog/Dialog.ActionSheet.js +45 -19
  14. package/dist/Dialog/Dialog.Confirmation.d.ts +14 -11
  15. package/dist/Dialog/Dialog.Confirmation.js +38 -14
  16. package/dist/Dialog/Dialog.EditValue.d.ts +21 -16
  17. package/dist/Dialog/Dialog.EditValue.js +73 -14
  18. package/dist/Dialog/Dialog.Sheet.d.ts +8 -24
  19. package/dist/Dialog/Dialog.Sheet.js +15 -60
  20. package/dist/Dialog/index.d.ts +14 -14
  21. package/dist/Dialog/index.js +5 -3
  22. package/dist/Dialog/requestConfirmation.d.ts +3 -0
  23. package/dist/Dialog/requestConfirmation.js +13 -0
  24. package/dist/Display/Display.Accordion.js +1 -1
  25. package/dist/Display/Display.Avatar.d.ts +6 -6
  26. package/dist/Display/Display.Avatar.js +2 -2
  27. package/dist/Display/Display.Badge.js +4 -4
  28. package/dist/Display/Display.Card.js +5 -2
  29. package/dist/Display/Display.Carousel.js +1 -1
  30. package/dist/Display/Display.CircleButton.d.ts +3 -8
  31. package/dist/Display/Display.CircleButton.js +2 -2
  32. package/dist/Display/Display.CopyableText.js +1 -5
  33. package/dist/Display/Display.Date.d.ts +3 -2
  34. package/dist/Display/Display.Date.js +3 -3
  35. package/dist/Display/Display.GradientSurface.d.ts +52 -9
  36. package/dist/Display/Display.GradientSurface.js +9 -14
  37. package/dist/Display/Display.Icon.d.ts +7 -11
  38. package/dist/Display/Display.Icon.js +15 -30
  39. package/dist/Display/Display.Image.d.ts +2 -1
  40. package/dist/Display/Display.Image.js +1 -1
  41. package/dist/Display/Display.KeyValue.js +1 -1
  42. package/dist/Display/Display.ListItem.js +1 -1
  43. package/dist/Display/Display.MenuButton.js +4 -4
  44. package/dist/Display/Display.MenuCard.d.ts +3 -8
  45. package/dist/Display/Display.MenuCard.js +2 -2
  46. package/dist/Display/Display.MenuGrid.d.ts +2 -7
  47. package/dist/Display/Display.MenuGrid.js +1 -1
  48. package/dist/Display/Display.Monetary.d.ts +2 -6
  49. package/dist/Display/Display.Monetary.js +2 -2
  50. package/dist/Display/Display.PriceTag.js +1 -1
  51. package/dist/Display/Display.Rating.js +1 -1
  52. package/dist/Display/Display.SearchHistory.d.ts +2 -1
  53. package/dist/Display/Display.SearchHistory.js +1 -1
  54. package/dist/Display/Display.StatCard.d.ts +2 -1
  55. package/dist/Display/Display.StatCard.js +1 -1
  56. package/dist/Display/Display.StoryViewer.js +10 -6
  57. package/dist/Display/Display.SwipeableRow.d.ts +2 -1
  58. package/dist/Display/Display.Timeline.js +1 -1
  59. package/dist/Display/icon-map.d.ts +344 -0
  60. package/dist/Display/icon-map.js +86 -0
  61. package/dist/Display/index.d.ts +2 -0
  62. package/dist/Display/index.js +1 -0
  63. package/dist/Feedback/Feedback.Banner.js +13 -12
  64. package/dist/Feedback/Feedback.Empty.js +1 -1
  65. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
  66. package/dist/Feedback/Feedback.EmptyState.js +1 -1
  67. package/dist/Feedback/Feedback.Error.js +1 -1
  68. package/dist/Feedback/Feedback.PermissionCard.d.ts +2 -1
  69. package/dist/Feedback/Feedback.PermissionCard.js +2 -2
  70. package/dist/Feedback/Toast.context.js +3 -3
  71. package/dist/Form/Form.ControlledDropdown.d.ts +5 -11
  72. package/dist/Form/Form.ControlledDropdown.js +5 -2
  73. package/dist/Form/Form.DatePicker.d.ts +5 -9
  74. package/dist/Form/Form.DatePicker.js +116 -21
  75. package/dist/Form/Form.Dropdown.d.ts +11 -16
  76. package/dist/Form/Form.Dropdown.js +54 -90
  77. package/dist/Form/Form.FractionalQuantityInput.js +4 -11
  78. package/dist/Form/Form.Input.d.ts +3 -3
  79. package/dist/Form/Form.KeyboardForm.d.ts +2 -2
  80. package/dist/Form/Form.KeyboardForm.js +2 -2
  81. package/dist/Form/Form.KeyboardToolbar.d.ts +2 -1
  82. package/dist/Form/Form.KeyboardToolbar.js +3 -7
  83. package/dist/Form/Form.PasswordInput.js +1 -1
  84. package/dist/Form/Form.QuantitySelector.js +1 -1
  85. package/dist/Form/Form.Select.d.ts +12 -8
  86. package/dist/Form/Form.Select.js +64 -31
  87. package/dist/Form/Form.Slider.d.ts +4 -7
  88. package/dist/Form/Form.Slider.js +11 -10
  89. package/dist/Form/Form.Switch.js +2 -1
  90. package/dist/Form/index.d.ts +6 -14
  91. package/dist/Form/index.js +0 -11
  92. package/dist/Navigation/Navigation.AppBar.d.ts +2 -1
  93. package/dist/Navigation/Navigation.AppBar.js +2 -2
  94. package/dist/Navigation/Navigation.BottomNav.d.ts +4 -8
  95. package/dist/Navigation/Navigation.BottomNav.js +2 -2
  96. package/dist/Navigation/Navigation.CollapsingHeader.js +3 -2
  97. package/dist/Navigation/Navigation.Menu.d.ts +16 -11
  98. package/dist/Navigation/Navigation.Menu.js +97 -42
  99. package/dist/Navigation/Navigation.Search.js +2 -2
  100. package/dist/Navigation/Navigation.SearchBar.js +3 -4
  101. package/dist/Navigation/Navigation.SegmentedControl.d.ts +7 -25
  102. package/dist/Navigation/Navigation.SegmentedControl.js +18 -39
  103. package/dist/Navigation/Navigation.Steps.js +1 -1
  104. package/dist/Navigation/Navigation.Tabs.d.ts +2 -1
  105. package/dist/Navigation/index.d.ts +0 -6
  106. package/dist/Navigation/index.js +0 -8
  107. package/dist/Typography/Typography.SelectableText.js +3 -7
  108. package/dist/Typography/Typography.Text.d.ts +1 -6
  109. package/dist/Typography/Typography.Text.js +3 -6
  110. package/dist/index.d.ts +1 -4
  111. package/dist/index.js +1 -4
  112. package/dist/layouts/AuthContainer.d.ts +4 -2
  113. package/dist/layouts/AuthContainer.js +3 -4
  114. package/dist/layouts/Container.d.ts +0 -4
  115. package/dist/layouts/Container.js +3 -4
  116. package/dist/layouts/ScrollContainer.d.ts +7 -3
  117. package/dist/layouts/ScrollContainer.js +5 -4
  118. package/dist/primitives/BaseButton.d.ts +2 -1
  119. package/dist/theme/build.js +0 -4
  120. package/dist/theme/css-vars.js +0 -4
  121. package/dist/theme/dark.js +2 -6
  122. package/dist/theme/default.js +1 -5
  123. package/dist/theme/index.d.ts +1 -1
  124. package/dist/theme/index.js +1 -1
  125. package/dist/theme/types.d.ts +0 -11
  126. package/dist/theme/typography.d.ts +8 -7
  127. package/dist/theme/typography.js +25 -4
  128. package/dist/utils/timezone.d.ts +0 -5
  129. package/dist/views/AuthFormView.d.ts +3 -1
  130. package/dist/views/AuthFormView.js +2 -2
  131. package/dist/views/FormViewV2.d.ts +6 -13
  132. package/dist/views/FormViewV2.js +29 -25
  133. package/dist/views/ListViewV2.context.d.ts +5 -5
  134. package/dist/views/ListViewV2.d.ts +23 -18
  135. package/dist/views/ListViewV2.js +45 -48
  136. package/dist/views/types/IListView.d.ts +5 -4
  137. package/dist/views/types/IListView.js +18 -15
  138. package/dist/widgets/CollectionOptions.d.ts +2 -2
  139. package/dist/widgets/CollectionOptions.js +92 -65
  140. package/dist/widgets/FilterBar.js +1 -1
  141. package/dist/widgets/PromoModal.js +10 -4
  142. package/dist/widgets/QuickActions.d.ts +2 -7
  143. package/dist/widgets/QuickActions.js +1 -1
  144. package/dist/widgets/ReorderableList.js +3 -3
  145. package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -6
  146. package/dist/widgets/TimeZoneSelectorModal.js +17 -12
  147. package/dist/widgets/index.d.ts +0 -11
  148. package/dist/widgets/index.js +0 -7
  149. package/package.json +35 -38
  150. package/tailwind.preset.js +0 -4
  151. package/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +0 -324
  152. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +0 -373
  153. package/dist/Dialog/BottomSheet/index.d.ts +0 -1
  154. package/dist/Dialog/BottomSheet/index.js +0 -1
  155. package/dist/Dialog/Dialog.NativeSheet.d.ts +0 -10
  156. package/dist/Dialog/Dialog.NativeSheet.js +0 -21
  157. package/dist/Form/Form.Button.d.ts +0 -3
  158. package/dist/Form/Form.Button.js +0 -1
  159. package/dist/Form/Form.FAB.d.ts +0 -3
  160. package/dist/Form/Form.FAB.js +0 -1
  161. package/dist/Form/Form.FilterButton.d.ts +0 -3
  162. package/dist/Form/Form.FilterButton.js +0 -1
  163. package/dist/Form/Form.IconButton.d.ts +0 -3
  164. package/dist/Form/Form.IconButton.js +0 -1
  165. package/dist/Form/Form.SearchBar.d.ts +0 -13
  166. package/dist/Form/Form.SearchBar.js +0 -26
  167. package/dist/Navigation/Navigation.Header.d.ts +0 -16
  168. package/dist/Navigation/Navigation.Header.js +0 -17
  169. package/dist/Navigation/Navigation.HeaderToolbar.d.ts +0 -15
  170. package/dist/Navigation/Navigation.HeaderToolbar.js +0 -18
  171. package/dist/Navigation/Navigation.TabIcon.d.ts +0 -18
  172. package/dist/Navigation/Navigation.TabIcon.js +0 -12
  173. package/dist/Navigation/Navigation.TabItem.d.ts +0 -19
  174. package/dist/Navigation/Navigation.TabItem.js +0 -13
  175. package/dist/icons/OdooIcon.d.ts +0 -11
  176. package/dist/icons/OdooIcon.js +0 -11
  177. package/dist/icons/PrinterIcon.d.ts +0 -9
  178. package/dist/icons/PrinterIcon.js +0 -9
  179. package/dist/layouts/TabNavigatorLayout.d.ts +0 -26
  180. package/dist/layouts/TabNavigatorLayout.js +0 -60
  181. package/dist/utils/icon.d.ts +0 -1
  182. package/dist/utils/icon.js +0 -1
  183. package/dist/widgets/CircleButton.d.ts +0 -3
  184. package/dist/widgets/CircleButton.js +0 -1
  185. package/dist/widgets/GeoPermissionCard.d.ts +0 -13
  186. package/dist/widgets/GeoPermissionCard.js +0 -24
  187. package/dist/widgets/HomeHeader.d.ts +0 -15
  188. package/dist/widgets/HomeHeader.js +0 -13
  189. package/dist/widgets/MenuCard.d.ts +0 -3
  190. package/dist/widgets/MenuCard.js +0 -1
  191. package/dist/widgets/MenuGrid.d.ts +0 -5
  192. package/dist/widgets/MenuGrid.js +0 -1
  193. package/dist/widgets/MenuSection.d.ts +0 -3
  194. package/dist/widgets/MenuSection.js +0 -1
  195. package/dist/widgets/ScreenHeader.d.ts +0 -25
  196. package/dist/widgets/ScreenHeader.js +0 -32
  197. package/dist/widgets/TimeZoneSelector.d.ts +0 -12
  198. package/dist/widgets/TimeZoneSelector.js +0 -24
@@ -1,11 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import DateTimePicker from "@react-native-community/datetimepicker";
2
+ import DateTimePicker from "@expo/ui/community/datetime-picker";
3
+ import { Actions } from "../Actions/index.js";
4
+ import Sheet from "../Dialog/Dialog.Sheet.js";
3
5
  import Icon from "../Display/Display.Icon.js";
4
6
  import Pressable from "../primitives/Pressable.js";
5
- import { useTheme } from "../theme/index.js";
7
+ import { relativeLuminance, useTheme } from "../theme/index.js";
6
8
  import { cn } from "../theme/tw.js";
7
9
  import { Typography } from "../Typography/index.js";
8
- import { useState } from "react";
10
+ import { useLayoutEffect, useRef, useState } from "react";
9
11
  import { Platform, View } from "react-native";
10
12
  function defaultFormat(value, mode) {
11
13
  if (mode === "time")
@@ -14,26 +16,119 @@ function defaultFormat(value, mode) {
14
16
  return value.toLocaleString("es-BO");
15
17
  return value.toLocaleDateString("es-BO");
16
18
  }
17
- const iconByMode = {
18
- date: "calendar-outline",
19
- time: "time-outline",
20
- datetime: "calendar-outline",
21
- };
22
- /**
23
- * **Hybrid** date/time field: tokenized trigger (NativeWind) + **native picker**
24
- * (`@react-native-community/datetimepicker`) — a case where native wins (expected UX).
25
- * Subpath `Form/DatePicker`; **optional** peer dep.
26
- */
27
- function DatePicker({ value, onChange, mode = "date", label, placeholder = "Seleccionar…", format, minimumDate, maximumDate, disabled = false, className, testID, }) {
28
- const [open, setOpen] = useState(false);
19
+ function comparableDate(value, mode) {
20
+ return mode === "date"
21
+ ? new Date(value.getFullYear(), value.getMonth(), value.getDate()).getTime()
22
+ : value.getTime();
23
+ }
24
+ function withCalendarDay(base, day) {
25
+ const next = new Date(base);
26
+ next.setFullYear(day.getFullYear(), day.getMonth(), day.getDate());
27
+ return next;
28
+ }
29
+ function withClockTime(base, time) {
30
+ const next = new Date(base);
31
+ next.setHours(time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds());
32
+ return next;
33
+ }
34
+ function DatePicker({ value, onChange, mode = "date", label, placeholder = "Seleccionar…", format, minimumDate, maximumDate, disabled = false, readOnly = false, className, testID, }) {
29
35
  const theme = useTheme();
30
- const fmt = format ?? ((d) => defaultFormat(d, mode));
31
- const handleValueChange = (_event, date) => {
32
- if (Platform.OS === "android")
36
+ const appearance = relativeLuminance(theme.bg.card) < 0.179 ? "dark" : "light";
37
+ const saving = useRef(false);
38
+ const dialogSession = useRef(0);
39
+ const visibleOwner = useRef(0);
40
+ const [open, setOpen] = useState(false);
41
+ const [phase, setPhase] = useState("date");
42
+ const [draft, setDraft] = useState(() => value ?? new Date());
43
+ const [pending, setPending] = useState(false);
44
+ const [error, setError] = useState();
45
+ const blocked = disabled || readOnly || pending;
46
+ const android = Platform.OS === "android";
47
+ useLayoutEffect(() => {
48
+ if (!android || !open)
49
+ return;
50
+ visibleOwner.current = dialogSession.current;
51
+ }, [android, open, phase]);
52
+ const fmt = format ?? ((date) => defaultFormat(date, mode));
53
+ const close = () => {
54
+ if (!saving.current)
55
+ setOpen(false);
56
+ };
57
+ const outOfRange = (next) => (minimumDate !== undefined &&
58
+ comparableDate(next, mode) < comparableDate(minimumDate, mode)) ||
59
+ (maximumDate !== undefined &&
60
+ comparableDate(next, mode) > comparableDate(maximumDate, mode));
61
+ const apply = async (next) => {
62
+ if (android)
63
+ setOpen(false);
64
+ if (blocked || saving.current)
65
+ return;
66
+ if (outOfRange(next)) {
67
+ setError("Selecciona una fecha y hora dentro del rango permitido.");
68
+ setDraft(next);
69
+ return;
70
+ }
71
+ saving.current = true;
72
+ setPending(true);
73
+ setError(undefined);
74
+ try {
75
+ await onChange(next);
33
76
  setOpen(false);
34
- if (date)
35
- onChange(date);
77
+ }
78
+ catch (cause) {
79
+ setDraft(next);
80
+ setError(cause instanceof Error && cause.message.trim()
81
+ ? cause.message
82
+ : "No se pudo aplicar la fecha. Vuelve a intentarlo.");
83
+ }
84
+ finally {
85
+ saving.current = false;
86
+ setPending(false);
87
+ }
88
+ };
89
+ const update = (date) => {
90
+ if (!date || blocked || saving.current)
91
+ return;
92
+ setDraft(date);
93
+ setError(undefined);
94
+ };
95
+ const confirmAndroid = (date) => {
96
+ if (!date || blocked || saving.current)
97
+ return;
98
+ if (mode === "datetime" && phase === "date") {
99
+ dialogSession.current += 1;
100
+ setDraft((current) => withCalendarDay(current, date));
101
+ setPhase("time");
102
+ setError(undefined);
103
+ return;
104
+ }
105
+ const next = mode === "datetime" ? withClockTime(draft, date) : date;
106
+ void apply(next);
107
+ };
108
+ const dismissAndroid = () => {
109
+ if (dialogSession.current !== visibleOwner.current)
110
+ return;
111
+ setOpen(false);
112
+ };
113
+ const openPicker = () => {
114
+ const initial = error ? draft : (value ?? new Date());
115
+ const clamped = minimumDate && comparableDate(initial, mode) < comparableDate(minimumDate, mode)
116
+ ? minimumDate
117
+ : maximumDate && comparableDate(initial, mode) > comparableDate(maximumDate, mode)
118
+ ? maximumDate
119
+ : initial;
120
+ dialogSession.current += 1;
121
+ visibleOwner.current = dialogSession.current;
122
+ setDraft(clamped);
123
+ setPhase("date");
124
+ setError(undefined);
125
+ setOpen(true);
36
126
  };
37
- return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "font-medium text-text-primary", variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre el selector de fecha", accessibilityLabel: label ?? placeholder, accessibilityRole: "button", accessibilityState: { disabled }, accessibilityValue: { text: value ? fmt(value) : placeholder }, className: "flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3 min-h-[48px]", disabled: disabled, onPress: () => setOpen(true), style: { minHeight: 48, minWidth: 48 }, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", value ? "text-text-primary" : "text-text-tertiary"), style: { paddingVertical: 12 }, children: value ? fmt(value) : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: iconByMode[mode], size: 18 })] }), open ? (_jsx(DateTimePicker, { maximumDate: maximumDate, minimumDate: minimumDate, mode: mode === "datetime" ? "date" : mode, onDismiss: () => setOpen(false), onValueChange: handleValueChange, value: value ?? new Date() })) : null] }));
127
+ const androidMode = mode === "datetime" ? phase : mode;
128
+ return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "text-text-primary", semibold: true, variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre el selector de fecha y hora", accessibilityLabel: label ?? placeholder, accessibilityRole: "button", accessibilityState: { disabled: blocked, expanded: open }, accessibilityValue: { text: value ? fmt(value) : placeholder }, className: "flex-row items-center gap-3 rounded-lg border border-input bg-bg-card px-3", disabled: blocked, onPress: openPicker, style: { minHeight: 48, minWidth: 48 }, children: [_jsx(Typography.Text, { className: cn("flex-1", value ? "text-text-primary" : "text-text-tertiary"), style: { paddingVertical: 12 }, children: value ? fmt(value) : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: mode === "time" ? "time" : "calendar", size: 18 })] }), android && error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", accessibilityRole: "alert", className: "text-danger", selectable: true, children: error })) : null, android && open ? (_jsx(DateTimePicker, { accentColor: theme.primary, display: "default", maximumDate: maximumDate, minimumDate: minimumDate, mode: androidMode, negativeButton: { label: "Cancelar" }, onDismiss: dismissAndroid, onValueChange: (_event, date) => confirmAndroid(date), positiveButton: {
129
+ label: mode === "datetime" && phase === "date" ? "Continuar" : "Aplicar",
130
+ }, presentation: "dialog", testID: testID
131
+ ? `${testID}-${androidMode === "time" ? "time" : "date"}-picker`
132
+ : undefined, themeVariant: appearance, value: draft }, androidMode)) : null, android ? null : (_jsxs(Sheet, { dismissible: !pending, footer: _jsx(Actions.Button, { disabled: blocked, loading: pending, onPress: () => void apply(draft), title: "Aplicar" }), onClose: close, title: label ?? (mode === "time" ? "Seleccionar hora" : "Seleccionar fecha"), visible: open, children: [open ? (_jsx(View, { accessibilityElementsHidden: blocked, importantForAccessibility: blocked ? "no-hide-descendants" : "auto", pointerEvents: blocked ? "none" : "auto", children: _jsx(DateTimePicker, { accentColor: theme.primary, disabled: blocked, display: mode === "time" || mode === "datetime" ? "spinner" : "inline", maximumDate: maximumDate, minimumDate: minimumDate, mode: mode, onValueChange: (_event, date) => update(date), presentation: "inline", testID: testID ? `${testID}-date-picker` : undefined, themeVariant: appearance, value: draft }) })) : null, error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", accessibilityRole: "alert", className: "pt-3 text-danger", selectable: true, children: error })) : null] }))] }));
38
133
  }
39
134
  export default DatePicker;
@@ -1,26 +1,21 @@
1
- import { Typography } from "../Typography/index.js";
2
- import { type ComponentProps, type ReactNode } from "react";
3
- import { type TouchableOpacityProps, type ViewProps } from "react-native";
4
- export interface DropdownOption {
1
+ import { type PressableProps } from "../primitives/Pressable.js";
2
+ import { type ReactNode } from "react";
3
+ export type DropdownOption = {
5
4
  id: string;
6
5
  title: string;
7
- onPress: () => void;
6
+ onPress: () => void | Promise<void>;
8
7
  disabled?: boolean;
9
- }
10
- export interface DropdownButtonProps {
8
+ };
9
+ export type DropdownButtonProps = {
11
10
  title: string;
12
11
  options: readonly DropdownOption[];
13
12
  variant?: "primary" | "secondary" | "outline";
14
13
  size?: "xs" | "small" | "medium" | "large";
15
14
  disabled?: boolean;
16
- closeAccessibilityLabel?: string;
17
- buttonProps?: Omit<TouchableOpacityProps, "children" | "onPress" | "disabled">;
18
- contentProps?: Omit<ViewProps, "children">;
19
- optionProps?: Omit<TouchableOpacityProps, "children" | "onPress" | "disabled">;
20
- titleTextProps?: ComponentProps<typeof Typography.Text>;
21
- optionTextProps?: ComponentProps<typeof Typography.Text>;
15
+ readOnly?: boolean;
16
+ buttonProps?: Omit<PressableProps, "children" | "onPress" | "disabled">;
22
17
  renderOption?: (option: DropdownOption) => ReactNode;
23
18
  testID?: string;
24
- }
25
- declare function DropdownButton({ title, options, variant, size, disabled, closeAccessibilityLabel, buttonProps, contentProps, optionProps, titleTextProps, optionTextProps, renderOption, testID, }: DropdownButtonProps): import("react").JSX.Element;
26
- export default DropdownButton;
19
+ };
20
+ declare function Dropdown({ title, options, variant, size, disabled, readOnly, buttonProps, renderOption, testID, }: DropdownButtonProps): import("react").JSX.Element;
21
+ export default Dropdown;
@@ -1,100 +1,64 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
2
- import IconButton from "../Actions/Actions.IconButton.js";
2
+ import Sheet from "../Dialog/Dialog.Sheet.js";
3
3
  import Icon from "../Display/Display.Icon.js";
4
+ import Pressable from "../primitives/Pressable.js";
4
5
  import { useTheme } from "../theme/index.js";
5
6
  import { cn } from "../theme/tw.js";
6
7
  import { Typography } from "../Typography/index.js";
7
- import { useReducedMotion } from "../utils/useReducedMotion.js";
8
- import { useState } from "react";
9
- import { Modal, ScrollView, TouchableOpacity, TouchableWithoutFeedback, View, } from "react-native";
10
- import { tv } from "tailwind-variants";
11
- const dropdown = tv({
12
- slots: {
13
- button: "flex-row justify-between items-center rounded-lg px-3",
14
- overlay: "flex-1 bg-black/40 justify-center items-center",
15
- dropdownContainer: "rounded-xl mx-5 max-w-[320px] w-[90%] max-h-[70%]",
16
- header: "flex-row justify-between items-center px-4 py-3 border-b border-border-default",
17
- optionsList: "p-1",
18
- option: "py-3 px-3 rounded-lg my-0.5",
19
- separator: "h-[1px] bg-border-light mx-3",
20
- },
21
- variants: {
22
- variant: {
23
- primary: {
24
- button: "bg-primary",
25
- },
26
- secondary: {
27
- button: "bg-bg-card border border-primary",
28
- },
29
- outline: {
30
- button: "bg-transparent border border-primary",
31
- },
32
- },
33
- size: {
34
- xs: {
35
- button: "py-1.5 min-h-[32px]",
36
- },
37
- small: {
38
- button: "py-2 min-h-[36px]",
39
- },
40
- medium: {
41
- button: "py-2.5 min-h-[40px]",
42
- },
43
- large: {
44
- button: "py-3 min-h-[44px]",
45
- },
46
- },
47
- disabled: {
48
- true: {
49
- option: "opacity-50",
50
- },
51
- },
52
- },
53
- defaultVariants: {
54
- variant: "secondary",
55
- size: "small",
56
- },
57
- });
58
- function DropdownButton({ title, options, variant = "secondary", size = "small", disabled = false, closeAccessibilityLabel = "Cerrar opciones", buttonProps, contentProps, optionProps, titleTextProps, optionTextProps, renderOption, testID, }) {
8
+ import { useRef, useState } from "react";
9
+ import { ActivityIndicator, View } from "react-native";
10
+ function Dropdown({ title, options, variant = "secondary", size = "small", disabled = false, readOnly = false, buttonProps, renderOption, testID, }) {
59
11
  const theme = useTheme();
60
- const reducedMotion = useReducedMotion();
61
- const [isOpen, setIsOpen] = useState(false);
62
- const handleOptionPress = (option) => {
63
- if (!option.disabled) {
64
- option.onPress();
65
- setIsOpen(false);
12
+ const saving = useRef(false);
13
+ const [open, setOpen] = useState(false);
14
+ const [pending, setPending] = useState();
15
+ const [error, setError] = useState();
16
+ const blocked = disabled || readOnly || pending !== undefined;
17
+ const close = () => {
18
+ if (!saving.current)
19
+ setOpen(false);
20
+ };
21
+ const choose = async (option) => {
22
+ if (blocked || option.disabled || saving.current)
23
+ return;
24
+ saving.current = true;
25
+ setPending(option.id);
26
+ setError(undefined);
27
+ try {
28
+ await option.onPress();
29
+ setOpen(false);
30
+ }
31
+ catch (cause) {
32
+ setError(cause instanceof Error && cause.message.trim()
33
+ ? cause.message
34
+ : "No se pudo completar la acción. Vuelve a intentarlo.");
35
+ }
36
+ finally {
37
+ saving.current = false;
38
+ setPending(undefined);
66
39
  }
67
40
  };
68
- const styles = dropdown({ variant, size });
69
- const textColorClass = {
70
- primary: "text-text-on-primary",
71
- outline: "text-text-accent",
72
- secondary: "text-text-accent",
73
- }[variant];
74
- const iconColor = {
75
- primary: theme.text.onPrimary,
76
- outline: theme.text.accent,
77
- secondary: theme.text.accent,
78
- }[variant];
79
- const textVariant = {
80
- large: "body",
81
- medium: "body",
82
- xs: "bodySmall",
83
- small: "bodySmall",
84
- }[size];
85
- return (_jsxs(_Fragment, { children: [_jsxs(TouchableOpacity, { ...buttonProps, accessibilityLabel: buttonProps?.accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
41
+ return (_jsxs(_Fragment, { children: [_jsxs(Pressable, { ...buttonProps, accessibilityLabel: buttonProps?.accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
86
42
  ...buttonProps?.accessibilityState,
87
- disabled,
88
- expanded: isOpen,
89
- }, activeOpacity: 0.7, className: cn(styles.button(), buttonProps?.className), disabled: disabled, onPress: () => setIsOpen(true), style: [{ minHeight: 48, minWidth: 48 }, buttonProps?.style], testID: testID, children: [_jsx(Typography.Text, { className: textColorClass, variant: textVariant, children: title }), _jsx(Icon, { color: iconColor, name: "chevron-down", size: 16, type: "feather" })] }), _jsx(Modal, { animationType: reducedMotion ? "none" : "fade", onRequestClose: () => setIsOpen(false), presentationStyle: "overFullScreen", transparent: true, visible: isOpen, children: _jsxs(View, { className: styles.overlay(), children: [_jsx(TouchableWithoutFeedback, { accessible: false, onPress: () => setIsOpen(false), children: _jsx(View, { className: "absolute inset-0" }) }), _jsxs(View, { ...contentProps, accessibilityViewIsModal: true, className: cn(styles.dropdownContainer(), contentProps?.className), onAccessibilityEscape: () => setIsOpen(false), style: [
90
- {
91
- backgroundColor: theme.bg.popover,
92
- ...theme.shadow.lg,
93
- },
94
- contentProps?.style,
95
- ], children: [_jsxs(View, { className: styles.header(), children: [_jsx(Typography.Text, { ...titleTextProps, accessibilityRole: "header", className: cn("text-text-primary", titleTextProps?.className), semibold: titleTextProps?.semibold ?? true, variant: titleTextProps?.variant ?? "bodyLarge", children: title }), _jsx(IconButton, { accessibilityLabel: closeAccessibilityLabel, icon: "close", onPress: () => setIsOpen(false) })] }), _jsx(ScrollView, { contentContainerClassName: styles.optionsList(), keyboardShouldPersistTaps: "handled", style: { flexShrink: 1 }, children: options.map((option, index) => (_jsxs(View, { children: [_jsx(TouchableOpacity, { ...optionProps, accessibilityLabel: option.title, accessibilityRole: "button", accessibilityState: {
96
- ...optionProps?.accessibilityState,
97
- disabled: option.disabled,
98
- }, activeOpacity: 0.7, className: cn(dropdown({ disabled: option.disabled }).option(), optionProps?.className), disabled: option.disabled, onPress: () => handleOptionPress(option), style: [{ minHeight: 48, minWidth: 48 }, optionProps?.style], children: renderOption ? (renderOption(option)) : (_jsx(Typography.Text, { ...optionTextProps, className: cn(option.disabled ? "text-text-disabled" : "text-text-primary", optionTextProps?.className), variant: optionTextProps?.variant ?? "body", children: option.title })) }), index < options.length - 1 && _jsx(View, { className: styles.separator() })] }, option.id))) })] })] }) })] }));
43
+ disabled: blocked,
44
+ expanded: open,
45
+ }, className: cn("flex-row items-center justify-between gap-3 rounded-lg px-3", variant === "primary"
46
+ ? "bg-primary"
47
+ : variant === "secondary"
48
+ ? "bg-bg-card border border-primary"
49
+ : "bg-transparent border border-primary", buttonProps?.className), disabled: blocked, onPress: () => {
50
+ setError(undefined);
51
+ setOpen(true);
52
+ }, style: [
53
+ {
54
+ minHeight: 48,
55
+ minWidth: 48,
56
+ paddingVertical: { xs: 6, small: 8, medium: 10, large: 12 }[size],
57
+ },
58
+ buttonProps?.style,
59
+ ], testID: testID, children: [_jsx(Typography.Text, { className: variant === "primary" ? "text-text-on-primary" : "text-text-accent", variant: size === "large" || size === "medium" ? "body" : "bodySmall", children: title }), _jsx(Icon, { color: variant === "primary" ? theme.text.onPrimary : theme.text.accent, name: "expand", size: 16 })] }), _jsxs(Sheet, { dismissible: pending === undefined, onClose: close, title: title, visible: open, children: [options.map((option) => (_jsxs(Pressable, { accessibilityLabel: option.title, accessibilityRole: "button", accessibilityState: {
60
+ disabled: blocked || !!option.disabled,
61
+ busy: pending === option.id,
62
+ }, className: "flex-row items-center gap-3 border-b border-border-light py-3", disabled: blocked || option.disabled, onPress: () => void choose(option), style: { minHeight: 48, minWidth: 48 }, children: [_jsx(View, { className: "flex-1", children: renderOption ? (renderOption(option)) : (_jsx(Typography.Text, { className: option.disabled ? "text-text-disabled" : "text-text-primary", children: option.title })) }), pending === option.id ? _jsx(ActivityIndicator, { color: theme.primary }) : null] }, option.id))), !options.length ? (_jsx(Typography.Text, { children: "No hay opciones disponibles." })) : null, error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", accessibilityRole: "alert", className: "pt-3 text-danger", selectable: true, children: error })) : null] })] }));
99
63
  }
100
- export default DropdownButton;
64
+ export default Dropdown;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import { Actions } from "../Actions/index.js";
2
3
  import { Dialog } from "../Dialog/index.js";
3
4
  import { Display } from "../Display/index.js";
4
5
  import { Typography } from "../Typography/index.js";
@@ -91,8 +92,8 @@ function FractionalQuantityInput({ value, onChange, precision = 2, minIncrement,
91
92
  setDraft(String(value));
92
93
  setModalVisible(true);
93
94
  }
94
- function confirmDraft() {
95
- let parsed = parseLocalizedNumber(draft);
95
+ function confirmDraft(nextDraft) {
96
+ let parsed = parseLocalizedNumber(nextDraft);
96
97
  parsed = roundTo(parsed, precision);
97
98
  if (parsed < 0)
98
99
  parsed = 0;
@@ -102,17 +103,9 @@ function FractionalQuantityInput({ value, onChange, precision = 2, minIncrement,
102
103
  setModalVisible(false);
103
104
  }
104
105
  const formatted = useMemo(() => value.toFixed(precision), [value, precision]);
105
- const modalChips = chips.map((item) => ({
106
- label: `+${item}`,
107
- onPress: () => {
108
- const base = parseLocalizedNumber(draft) || 0;
109
- const next = roundTo(base + item, precision);
110
- setDraft(String(next));
111
- },
112
- }));
113
106
  const decrementStyles = fractionalQuantityInput({ disabled: disabled || value <= 0 });
114
107
  const incrementStyles = fractionalQuantityInput({ disabled });
115
108
  const containerStyles = fractionalQuantityInput();
116
- return (_jsxs(View, { accessibilityLabel: `Cantidad ${formatted}`, className: containerStyles.container(), style: style, children: [_jsx(TouchableOpacity, { className: decrementStyles.button(), disabled: disabled || value <= 0, onPress: handleDecrement, children: _jsx(Display.Icon, { name: "minus", size: 16, type: "feather" }) }), _jsx(TouchableOpacity, { className: containerStyles.valueContainer(), disabled: disabled, onPress: openModal, children: _jsx(Typography.Text, { semibold: true, variant: "body", children: formatted }) }), _jsx(TouchableOpacity, { className: incrementStyles.button(), disabled: disabled, onPress: handleIncrement, children: _jsx(Display.Icon, { name: "plus", size: 16, type: "feather" }) }), _jsx(Dialog.EditValue, { chips: modalChips, keyboardType: "decimal-pad", onCancel: () => setModalVisible(false), onChangeValue: setDraft, onConfirm: confirmDraft, placeholder: `0${precision > 0 ? ",".padEnd(precision + 1, "0") : ""}`, rightAdornment: _jsxs(Typography.Text, { className: "ml-1", variant: "body", children: ["(m\u00EDn ", effectiveIncrement, ")"] }), title: "Editar cantidad", value: draft, visible: modalVisible })] }));
109
+ return (_jsxs(View, { accessibilityLabel: `Cantidad ${formatted}`, className: containerStyles.container(), style: style, children: [_jsx(TouchableOpacity, { className: decrementStyles.button(), disabled: disabled || value <= 0, onPress: handleDecrement, children: _jsx(Display.Icon, { name: "remove", size: 16 }) }), _jsx(TouchableOpacity, { className: containerStyles.valueContainer(), disabled: disabled, onPress: openModal, children: _jsx(Typography.Text, { semibold: true, variant: "body", children: formatted }) }), _jsx(TouchableOpacity, { className: incrementStyles.button(), disabled: disabled, onPress: handleIncrement, children: _jsx(Display.Icon, { name: "add", size: 16 }) }), _jsx(Dialog.EditValue, { footerAdornment: _jsx(View, { className: "flex-row flex-wrap gap-2", children: chips.map((item, index) => (_jsx(Actions.Button, { onPress: () => setDraft((current) => String(roundTo(parseLocalizedNumber(current) + item, precision))), size: "small", title: `+${item}`, variant: "secondary" }, `${item}-${index}`))) }), keyboardType: "decimal-pad", onChange: setDraft, onClose: () => setModalVisible(false), onConfirm: confirmDraft, placeholder: `0${precision > 0 ? ",".padEnd(precision + 1, "0") : ""}`, rightAdornment: _jsxs(Typography.Text, { className: "ml-1", variant: "body", children: ["(m\u00EDn ", effectiveIncrement, ")"] }), title: "Editar cantidad", value: draft, visible: modalVisible })] }));
117
110
  }
118
111
  export default FractionalQuantityInput;
@@ -1,7 +1,7 @@
1
- import type { ComponentRef, Ref } from "react";
2
- import { TextInput, type TextInputProps } from "react-native";
1
+ import type { Ref } from "react";
2
+ import { type TextInputInstance, type TextInputProps } from "react-native";
3
3
  interface FormInputProps extends TextInputProps {
4
- ref?: Ref<ComponentRef<typeof TextInput>>;
4
+ ref?: Ref<TextInputInstance>;
5
5
  label: string;
6
6
  error?: string;
7
7
  tooltip?: string;
@@ -5,7 +5,7 @@ export interface KeyboardFormProps extends KeyboardAwareScrollViewProps {
5
5
  children: ReactNode;
6
6
  ref?: Ref<KeyboardAwareScrollViewRef>;
7
7
  toolbar?: boolean;
8
+ stickyFooterHeight?: number;
8
9
  toolbarProps?: Omit<KeyboardToolbarProps, "onHeightChange">;
9
- toolbarLabels?: Pick<KeyboardToolbarProps, "previousLabel" | "nextLabel" | "doneLabel" | "showDoneLabel">;
10
10
  }
11
- export default function KeyboardForm({ children, toolbar, toolbarLabels, toolbarProps, bottomOffset, style, ...props }: KeyboardFormProps): import("react").JSX.Element;
11
+ export default function KeyboardForm({ children, toolbar, stickyFooterHeight, toolbarProps, bottomOffset, style, ...props }: KeyboardFormProps): import("react").JSX.Element;
@@ -3,7 +3,7 @@ import KeyboardToolbar from "./Form.KeyboardToolbar.js";
3
3
  import { useState } from "react";
4
4
  import { View } from "react-native";
5
5
  import { KeyboardAwareScrollView, KeyboardToolbar as NativeToolbar, } from "react-native-keyboard-controller";
6
- export default function KeyboardForm({ children, toolbar = true, toolbarLabels, toolbarProps, bottomOffset, style, ...props }) {
6
+ export default function KeyboardForm({ children, toolbar = true, stickyFooterHeight = 0, toolbarProps, bottomOffset, style, ...props }) {
7
7
  const [toolbarHeight, setToolbarHeight] = useState(48);
8
- return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(NativeToolbar.Group, { style: { flex: 1 }, children: _jsx(KeyboardAwareScrollView, { bottomOffset: bottomOffset ?? (toolbar ? toolbarHeight + 24 : 20), keyboardShouldPersistTaps: "handled", style: [{ flex: 1 }, style], ...props, children: children }) }), toolbar ? (_jsx(KeyboardToolbar, { ...toolbarLabels, ...toolbarProps, onHeightChange: setToolbarHeight })) : null] }));
8
+ return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(NativeToolbar.Group, { style: { flex: 1 }, children: _jsx(KeyboardAwareScrollView, { bottomOffset: bottomOffset ?? stickyFooterHeight + (toolbar ? toolbarHeight + 24 : 24), keyboardShouldPersistTaps: "handled", style: [{ flex: 1 }, style], ...props, children: children }) }), toolbar ? (_jsx(KeyboardToolbar, { ...toolbarProps, bottomGapOffset: -stickyFooterHeight, onHeightChange: setToolbarHeight })) : null] }));
9
9
  }
@@ -12,10 +12,11 @@ export interface KeyboardToolbarProps {
12
12
  actionStyle?: StyleProp<ViewStyle>;
13
13
  labelStyle?: StyleProp<TextStyle>;
14
14
  iconSize?: number;
15
+ bottomGapOffset?: number;
15
16
  testID?: string;
16
17
  renderActionContent?: (action: KeyboardToolbarAction, state: {
17
18
  label: string;
18
19
  disabled: boolean;
19
20
  }) => ReactNode;
20
21
  }
21
- export default function KeyboardToolbar({ previousLabel, nextLabel, doneLabel, showDoneLabel, onHeightChange, className, style, actionStyle, labelStyle, iconSize, testID, renderActionContent, }: KeyboardToolbarProps): import("react").JSX.Element;
22
+ export default function KeyboardToolbar({ previousLabel, nextLabel, doneLabel, showDoneLabel, onHeightChange, className, style, actionStyle, labelStyle, iconSize, bottomGapOffset, testID, renderActionContent, }: KeyboardToolbarProps): import("react").JSX.Element;
@@ -6,7 +6,7 @@ import { Typography } from "../Typography/index.js";
6
6
  import { useEffect, useRef, useState } from "react";
7
7
  import { StyleSheet, useWindowDimensions, View, } from "react-native";
8
8
  import { FocusedInputEvents, KeyboardController, KeyboardStickyView, useKeyboardState, } from "react-native-keyboard-controller";
9
- export default function KeyboardToolbar({ previousLabel = "Campo anterior", nextLabel = "Campo siguiente", doneLabel = "Ocultar teclado", showDoneLabel = false, onHeightChange, className, style, actionStyle, labelStyle, iconSize = 20, testID = "keyboard-toolbar", renderActionContent, }) {
9
+ export default function KeyboardToolbar({ previousLabel = "Campo anterior", nextLabel = "Campo siguiente", doneLabel = "Ocultar teclado", showDoneLabel = false, onHeightChange, className, style, actionStyle, labelStyle, iconSize = 20, bottomGapOffset = 0, testID = "keyboard-toolbar", renderActionContent, }) {
10
10
  const theme = useTheme();
11
11
  const frame = useRef(null);
12
12
  const { height: windowHeight } = useWindowDimensions();
@@ -25,15 +25,11 @@ export default function KeyboardToolbar({ previousLabel = "Campo anterior", next
25
25
  return renderActionContent(action, { label, disabled });
26
26
  if (action === "done" && showDoneLabel)
27
27
  return (_jsx(Typography.Text, { className: "text-text-accent", style: labelStyle, variant: "bodySmall", children: label }));
28
- return (_jsx(Icon, { color: disabled ? theme.text.tertiary : theme.text.accent, name: action === "previous"
29
- ? "chevron-up"
30
- : action === "next"
31
- ? "chevron-down"
32
- : "checkmark", size: iconSize }));
28
+ return (_jsx(Icon, { color: disabled ? theme.text.tertiary : theme.text.accent, name: action === "previous" ? "collapse" : action === "next" ? "expand" : "check", size: iconSize }));
33
29
  };
34
30
  return (_jsx(View, { accessibilityElementsHidden: !isVisible, importantForAccessibility: isVisible ? "auto" : "no-hide-descendants", onLayout: () => {
35
31
  frame.current?.measureInWindow((_x, y, _width, frameHeight) => setBottomGap(Math.max(0, windowHeight - y - frameHeight)));
36
- }, pointerEvents: "box-none", ref: frame, style: StyleSheet.absoluteFill, children: _jsx(KeyboardStickyView, { offset: { closed: height, opened: bottomGap - 8 }, style: { position: "absolute", bottom: 0, left: 0, right: 0 }, children: _jsxs(View, { className: className, onLayout: (event) => {
32
+ }, pointerEvents: "box-none", ref: frame, style: StyleSheet.absoluteFill, children: _jsx(KeyboardStickyView, { offset: { closed: height, opened: bottomGap + bottomGapOffset - 8 }, style: { position: "absolute", bottom: 0, left: 0, right: 0 }, children: _jsxs(View, { className: className, onLayout: (event) => {
37
33
  const nextHeight = event.nativeEvent.layout.height;
38
34
  setHeight(nextHeight);
39
35
  onHeightChange?.(nextHeight);
@@ -12,6 +12,6 @@ const PasswordField = ({ label, error, inputProps }) => {
12
12
  secureTextEntry: !showPassword,
13
13
  autoComplete: "off",
14
14
  importantForAutofill: "no",
15
- }, label: label }), _jsx(TouchableOpacity, { className: "absolute right-3 bottom-[25px]", hitSlop: 8, onPress: () => setShowPassword(!showPassword), children: _jsx(Display.Icon, { color: theme.text.primary, name: showPassword ? "eye-off-outline" : "eye-outline", size: 22, type: "ionicons" }) })] }));
15
+ }, label: label }), _jsx(TouchableOpacity, { className: "absolute right-3 bottom-[25px]", hitSlop: 8, onPress: () => setShowPassword(!showPassword), children: _jsx(Display.Icon, { color: theme.text.primary, name: showPassword ? "hidden" : "visibility", size: 22 }) })] }));
16
16
  };
17
17
  export default PasswordField;
@@ -31,6 +31,6 @@ function QuantitySelector({ value, onChange, min = 1, max = 999, style, }) {
31
31
  const decrementStyles = quantitySelector({ disabled: value <= min });
32
32
  const incrementStyles = quantitySelector({ disabled: value >= max });
33
33
  const containerStyles = quantitySelector();
34
- return (_jsxs(View, { className: containerStyles.container(), style: style, children: [_jsx(TouchableOpacity, { className: decrementStyles.button(), disabled: value <= min, onPress: handleDecrement, children: _jsx(Display.Icon, { name: "minus", size: 16, type: "feather" }) }), _jsx(View, { className: containerStyles.valueContainer(), children: _jsx(Typography.Text, { semibold: true, variant: "body", children: value }) }), _jsx(TouchableOpacity, { className: incrementStyles.button(), disabled: value >= max, onPress: handleIncrement, children: _jsx(Display.Icon, { name: "plus", size: 16, type: "feather" }) })] }));
34
+ return (_jsxs(View, { className: containerStyles.container(), style: style, children: [_jsx(TouchableOpacity, { className: decrementStyles.button(), disabled: value <= min, onPress: handleDecrement, children: _jsx(Display.Icon, { name: "remove", size: 16 }) }), _jsx(View, { className: containerStyles.valueContainer(), children: _jsx(Typography.Text, { semibold: true, variant: "body", children: value }) }), _jsx(TouchableOpacity, { className: incrementStyles.button(), disabled: value >= max, onPress: handleIncrement, children: _jsx(Display.Icon, { name: "add", size: 16 }) })] }));
35
35
  }
36
36
  export default QuantitySelector;
@@ -1,18 +1,22 @@
1
- export interface SelectOption<T extends string | number> {
1
+ export type SelectOption<T extends string | number> = {
2
2
  label: string;
3
3
  value: T;
4
- }
5
- export interface SelectProps<T extends string | number> {
6
- options: SelectOption<T>[];
4
+ };
5
+ export type SelectProps<T extends string | number> = {
6
+ options: readonly SelectOption<T>[];
7
7
  value: T | null;
8
- onChange: (value: T) => void;
8
+ onChange: (value: T) => void | Promise<void>;
9
+ onBlur?: () => void;
9
10
  label?: string;
10
11
  placeholder?: string;
11
12
  title?: string;
13
+ confirmLabel?: string;
12
14
  disabled?: boolean;
15
+ readOnly?: boolean;
16
+ loading?: boolean;
17
+ error?: string;
13
18
  className?: string;
14
19
  testID?: string;
15
- showCloseButton?: boolean;
16
- }
17
- declare function Select<T extends string | number>({ options, value, onChange, label, placeholder, title, disabled, className, testID, showCloseButton, }: SelectProps<T>): import("react").JSX.Element;
20
+ };
21
+ declare function Select<T extends string | number>({ options, value, onChange, onBlur, label, placeholder, title, confirmLabel, disabled, readOnly, loading, error, className, testID, }: SelectProps<T>): import("react").JSX.Element;
18
22
  export default Select;