@sincpro/mobile-ui 1.1.0 → 2.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 (123) hide show
  1. package/README.md +156 -29
  2. package/dist/Actions/Actions.Button.d.ts +10 -12
  3. package/dist/Actions/Actions.Button.js +9 -11
  4. package/dist/Actions/Actions.Dock.d.ts +20 -0
  5. package/dist/Actions/Actions.Dock.js +89 -0
  6. package/dist/Actions/Actions.FAB.d.ts +1 -2
  7. package/dist/Actions/Actions.FAB.js +5 -7
  8. package/dist/Actions/Actions.IconButton.d.ts +1 -2
  9. package/dist/Actions/Actions.IconButton.js +2 -2
  10. package/dist/Actions/index.d.ts +4 -0
  11. package/dist/Actions/index.js +3 -0
  12. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
  13. package/dist/Dialog/Dialog.ActionSheet.js +2 -16
  14. package/dist/Dialog/Dialog.Confirmation.js +7 -5
  15. package/dist/Dialog/Dialog.EditValue.js +3 -1
  16. package/dist/Dialog/Dialog.Sheet.d.ts +3 -3
  17. package/dist/Dialog/Dialog.Sheet.js +18 -25
  18. package/dist/Display/Display.Accordion.js +7 -3
  19. package/dist/Display/Display.AmountSummary.d.ts +28 -0
  20. package/dist/Display/Display.AmountSummary.js +25 -0
  21. package/dist/Display/Display.Carousel.d.ts +27 -4
  22. package/dist/Display/Display.Carousel.js +112 -12
  23. package/dist/Display/Display.ExpoImage.d.ts +8 -0
  24. package/dist/Display/Display.ExpoImage.js +15 -0
  25. package/dist/Display/Display.Image.d.ts +10 -6
  26. package/dist/Display/Display.Image.js +39 -6
  27. package/dist/Display/Display.ListItem.d.ts +1 -2
  28. package/dist/Display/Display.ListItem.js +3 -3
  29. package/dist/Display/Display.Logo.d.ts +2 -2
  30. package/dist/Display/Display.Logo.js +3 -2
  31. package/dist/Display/Display.MediaCard.d.ts +2 -2
  32. package/dist/Display/Display.MediaEmbed.d.ts +2 -2
  33. package/dist/Display/Display.ProductCard.d.ts +2 -2
  34. package/dist/Display/Display.Story.d.ts +3 -3
  35. package/dist/Display/Display.Story.js +5 -4
  36. package/dist/Display/Display.StoryTray.d.ts +4 -4
  37. package/dist/Display/Display.StoryTray.js +3 -2
  38. package/dist/Display/Display.StoryViewer.d.ts +3 -3
  39. package/dist/Display/Display.StoryViewer.js +6 -3
  40. package/dist/Display/index.d.ts +9 -0
  41. package/dist/Display/index.js +4 -0
  42. package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
  43. package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
  44. package/dist/Feedback/Feedback.EmptyState.d.ts +3 -4
  45. package/dist/Feedback/Feedback.EmptyState.js +4 -3
  46. package/dist/Feedback/Feedback.Error.d.ts +1 -2
  47. package/dist/Feedback/Feedback.Error.js +2 -2
  48. package/dist/Feedback/Feedback.Loading.d.ts +6 -2
  49. package/dist/Feedback/Feedback.Loading.js +2 -2
  50. package/dist/Feedback/Feedback.Progress.d.ts +6 -3
  51. package/dist/Feedback/Feedback.Progress.js +29 -19
  52. package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
  53. package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
  54. package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
  55. package/dist/Feedback/Feedback.Skeleton.js +15 -3
  56. package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
  57. package/dist/Feedback/Feedback.Spinner.js +40 -9
  58. package/dist/Feedback/Feedback.Splash.d.ts +2 -2
  59. package/dist/Feedback/Feedback.Splash.js +5 -3
  60. package/dist/Feedback/Toast.context.d.ts +64 -3
  61. package/dist/Feedback/Toast.context.js +112 -31
  62. package/dist/Feedback/index.d.ts +15 -1
  63. package/dist/Feedback/index.js +4 -1
  64. package/dist/Form/Form.Checkbox.d.ts +1 -2
  65. package/dist/Form/Form.Checkbox.js +2 -2
  66. package/dist/Form/Form.DatePicker.d.ts +1 -2
  67. package/dist/Form/Form.DatePicker.js +2 -2
  68. package/dist/Form/Form.Dropdown.d.ts +15 -3
  69. package/dist/Form/Form.Dropdown.js +21 -6
  70. package/dist/Form/Form.ErrorSummary.d.ts +23 -0
  71. package/dist/Form/Form.ErrorSummary.js +53 -0
  72. package/dist/Form/Form.Input.d.ts +1 -2
  73. package/dist/Form/Form.Input.js +2 -2
  74. package/dist/Form/Form.Select.d.ts +1 -2
  75. package/dist/Form/Form.Select.js +6 -19
  76. package/dist/Form/index.d.ts +3 -0
  77. package/dist/Form/index.js +2 -0
  78. package/dist/Navigation/Navigation.AppBar.d.ts +2 -6
  79. package/dist/Navigation/Navigation.AppBar.js +9 -7
  80. package/dist/Navigation/Navigation.BottomNav.d.ts +1 -3
  81. package/dist/Navigation/Navigation.BottomNav.js +7 -6
  82. package/dist/Navigation/Navigation.Header.d.ts +2 -2
  83. package/dist/Navigation/Navigation.Menu.js +10 -3
  84. package/dist/Navigation/Navigation.Search.d.ts +41 -0
  85. package/dist/Navigation/Navigation.Search.js +47 -0
  86. package/dist/Navigation/Navigation.SearchBar.d.ts +1 -2
  87. package/dist/Navigation/Navigation.SearchBar.js +16 -19
  88. package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -3
  89. package/dist/Navigation/Navigation.SegmentedControl.js +29 -16
  90. package/dist/Navigation/index.d.ts +11 -2
  91. package/dist/Navigation/index.js +2 -0
  92. package/dist/branding.d.ts +3 -2
  93. package/dist/primitives/BaseButton.d.ts +10 -12
  94. package/dist/primitives/BaseButton.js +9 -11
  95. package/dist/theme/typography.d.ts +1 -1
  96. package/dist/utils/image.d.ts +3 -0
  97. package/dist/utils/image.js +13 -0
  98. package/dist/utils/keys.d.ts +1 -0
  99. package/dist/utils/keys.js +7 -0
  100. package/dist/utils/progress.d.ts +1 -0
  101. package/dist/utils/progress.js +5 -0
  102. package/dist/utils/reorder.js +2 -5
  103. package/dist/utils/useReducedMotion.d.ts +1 -0
  104. package/dist/utils/useReducedMotion.js +43 -0
  105. package/dist/utils/useSheetTranslation.d.ts +5 -0
  106. package/dist/utils/useSheetTranslation.js +23 -0
  107. package/dist/views/AppSplashView.d.ts +3 -2
  108. package/dist/views/AppSplashView.js +3 -2
  109. package/dist/views/AuthFormView.d.ts +2 -2
  110. package/dist/views/FormViewV2.d.ts +2 -2
  111. package/dist/views/ListViewV2.context.d.ts +3 -1
  112. package/dist/views/ListViewV2.context.js +41 -21
  113. package/dist/views/ListViewV2.d.ts +15 -7
  114. package/dist/views/ListViewV2.js +44 -23
  115. package/dist/views/types/IListView.js +6 -5
  116. package/dist/widgets/CollectionOptions.js +9 -7
  117. package/dist/widgets/FilterBar.d.ts +1 -1
  118. package/dist/widgets/FilterBar.js +1 -1
  119. package/dist/widgets/FilterBuilder.js +7 -7
  120. package/dist/widgets/HomeHeader.d.ts +2 -2
  121. package/dist/widgets/PromoModal.js +3 -1
  122. package/dist/widgets/ScreenHeader.d.ts +2 -2
  123. package/package.json +4 -1
@@ -7,6 +7,7 @@ import Pressable from "../primitives/Pressable.js";
7
7
  import { themeToVars, useTheme } from "../theme/index.js";
8
8
  import { Typography } from "../Typography/index.js";
9
9
  import { describeCriteria, moveCollectionEntry, validateCriteria, } from "../utils/criteria.js";
10
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
10
11
  import FilterBuilder from "./FilterBuilder.js";
11
12
  import QueryChoice from "./QueryChoice.js";
12
13
  import { vars } from "nativewind";
@@ -24,6 +25,7 @@ const sections = [
24
25
  ];
25
26
  function OptionsEditor({ value, fields, sortFields = [], groupFields = [], savedQueries = [], onApply, onClose, onSave, title = "Configurar colección", testID, }) {
26
27
  const theme = useTheme();
28
+ const reducedMotion = useReducedMotion();
27
29
  const insets = useSafeAreaInsets();
28
30
  const [draft, setDraft] = useState(value);
29
31
  const [section, setSection] = useState("filter");
@@ -56,7 +58,7 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
56
58
  }),
57
59
  }
58
60
  : { ...current, groupBy: next });
59
- return (_jsx(Modal, { animationType: "slide", onRequestClose: close, presentationStyle: "fullScreen", visible: true, children: _jsxs(View, { accessibilityViewIsModal: true, onAccessibilityEscape: close, style: [
61
+ return (_jsx(Modal, { animationType: reducedMotion ? "none" : "slide", onRequestClose: close, presentationStyle: "fullScreen", visible: true, children: _jsxs(View, { accessibilityViewIsModal: true, onAccessibilityEscape: close, style: [
60
62
  vars(themeToVars(theme)),
61
63
  {
62
64
  flex: 1,
@@ -64,7 +66,7 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
64
66
  paddingTop: insets.top,
65
67
  paddingBottom: insets.bottom,
66
68
  },
67
- ], 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", accessibleSizing: true, 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: {
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: {
68
70
  selected: item.id === section,
69
71
  disabled: editing || saving,
70
72
  }, disabled: editing || saving, onPress: () => {
@@ -75,11 +77,11 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
75
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) => {
76
78
  setError(undefined);
77
79
  setDraft({ ...draft, filter });
78
- }, 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, { accessibleSizing: true, disabled: saving, onPress: () => {
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: () => {
79
81
  setDraft(saved.query);
80
82
  setError(undefined);
81
83
  setStatus(`Configuración ${saved.label} cargada. Pulsa Aplicar configuración.`);
82
- }, title: `Cargar ${saved.label}`, variant: "secondary" }, saved.id)))), onSave ? (_jsxs(_Fragment, { children: [_jsx(Input, { accessibleSizing: true, editable: !saving, label: "Nombre de la configuraci\u00F3n", onChangeText: setLabel, testID: "collection-query-name", value: label }), _jsx(Button, { accessibleSizing: true, loading: saving, onPress: () => {
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: () => {
83
85
  const invalid = problem();
84
86
  if (invalid || !label.trim()) {
85
87
  setError(invalid ?? "Escribe un nombre para guardar.");
@@ -102,7 +104,7 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
102
104
  : "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) => {
103
105
  const field = available.find((item) => item.id === id);
104
106
  const sort = draft.sort.find((entry) => entry.field === id);
105
- 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, { accessibleSizing: true, onPress: () => setDraft({
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({
106
108
  ...draft,
107
109
  sort: draft.sort.map((entry) => entry.field === id
108
110
  ? {
@@ -110,12 +112,12 @@ function OptionsEditor({ value, fields, sortFields = [], groupFields = [], saved
110
112
  direction: entry.direction === "asc" ? "desc" : "asc",
111
113
  }
112
114
  : entry),
113
- }), 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}`, accessibleSizing: true, disabled: index === 0, onPress: () => updateOrder(moveCollectionEntry(ordered, index, -1)), title: "Subir", variant: "link" }), _jsx(Button, { accessibilityLabel: `Bajar ${field?.label ?? id}`, accessibleSizing: true, disabled: index === ordered.length - 1, onPress: () => updateOrder(moveCollectionEntry(ordered, index, 1)), title: "Bajar", variant: "link" }), _jsx(Button, { accessibilityLabel: `Quitar ${field?.label ?? id}`, accessibleSizing: true, onPress: () => updateOrder(ordered.filter((key) => key !== id)), title: "Quitar", variant: "link" })] })] }, id));
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));
114
116
  }), available.some((field) => !ordered.includes(field.id)) ? (_jsx(QueryChoice, { label: section === "sort"
115
117
  ? "Añadir criterio de orden"
116
118
  : "Añadir nivel de agrupación", onChange: (id) => updateOrder([...ordered, id]), options: available
117
119
  .filter((field) => !ordered.includes(field.id))
118
- .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, { accessibleSizing: true, disabled: editing || saving, onPress: () => {
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: () => {
119
121
  const invalid = problem();
120
122
  if (invalid) {
121
123
  setError(invalid);
@@ -8,7 +8,7 @@ export interface FilterBarOption {
8
8
  export interface FilterBarProps {
9
9
  filters: readonly FilterBarOption[];
10
10
  onFilterPress: (id: string) => void;
11
- search?: Omit<SearchBarProps, "accessibleSizing">;
11
+ search?: SearchBarProps;
12
12
  onOpenFilters?: () => void;
13
13
  filtersLabel?: string;
14
14
  onClearFilters?: () => void;
@@ -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, accessibleSizing: true }) })) : null, onOpenFilters ? (_jsx(IconButton, { accessibilityLabel: filtersLabel, accessibleSizing: true, 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: "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] }));
14
14
  }
@@ -21,7 +21,7 @@ export default function FilterBuilder({ fields, value, onChange, onEditingChange
21
21
  current = current?.kind === "group" ? current.children[index] : undefined;
22
22
  }
23
23
  if (current?.kind !== "group")
24
- return (_jsxs(View, { className: className, testID: testID, children: [_jsx(Typography.Text, { accessibilityLiveRegion: "polite", children: "Este grupo ya no est\u00E1 disponible. La configuraci\u00F3n cambi\u00F3." }), _jsx(Button, { accessibleSizing: true, onPress: () => {
24
+ return (_jsxs(View, { className: className, testID: testID, children: [_jsx(Typography.Text, { accessibilityLiveRegion: "polite", children: "Este grupo ya no est\u00E1 disponible. La configuraci\u00F3n cambi\u00F3." }), _jsx(Button, { onPress: () => {
25
25
  setPath([]);
26
26
  setEditing(null);
27
27
  }, title: "Volver a condiciones", variant: "secondary" })] }));
@@ -71,11 +71,11 @@ export default function FilterBuilder({ fields, value, onChange, onEditingChange
71
71
  values[index] = next;
72
72
  changeCondition({ ...condition, values });
73
73
  };
74
- return field?.type === "choice" ? (_jsx(QueryChoice, { label: label, onChange: update, options: field.options ?? [], value: condition.values[index] }, index)) : field?.type === "date" ? (_jsx(DatePicker, { accessibleSizing: true, label: label, onChange: (date) => update(`${String(date.getFullYear()).padStart(4, "0")}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`), value: parseCriteriaDate(condition.values[index]) }, index)) : (_jsx(Input, { accessibleSizing: true, keyboardType: field?.type === "number" ? "numbers-and-punctuation" : "default", label: label, onChangeText: update, testID: `criteria-value-${index}`, value: condition.values[index] === undefined ||
74
+ return field?.type === "choice" ? (_jsx(QueryChoice, { label: label, onChange: update, options: field.options ?? [], value: condition.values[index] }, index)) : field?.type === "date" ? (_jsx(DatePicker, { label: label, onChange: (date) => update(`${String(date.getFullYear()).padStart(4, "0")}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`), value: parseCriteriaDate(condition.values[index]) }, index)) : (_jsx(Input, { keyboardType: field?.type === "number" ? "numbers-and-punctuation" : "default", label: label, onChangeText: update, testID: `criteria-value-${index}`, value: condition.values[index] === undefined ||
75
75
  Number.isNaN(condition.values[index])
76
76
  ? ""
77
77
  : String(condition.values[index]) }, index));
78
- }), error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-danger", children: error })) : null, _jsx(Button, { accessibleSizing: true, onPress: () => {
78
+ }), error ? (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "text-danger", children: error })) : null, _jsx(Button, { onPress: () => {
79
79
  const next = {
80
80
  ...condition,
81
81
  values: field?.type === "number"
@@ -94,18 +94,18 @@ export default function FilterBuilder({ fields, value, onChange, onEditingChange
94
94
  children[editing.index] = next;
95
95
  changeGroup({ ...group, children });
96
96
  setEditing(null);
97
- }, title: "Guardar condici\u00F3n" }), _jsx(Button, { accessibleSizing: true, onPress: () => setEditing(null), title: "Cancelar condici\u00F3n", variant: "secondary" })] })) : (_jsxs(_Fragment, { children: [path.length ? (_jsx(Button, { accessibleSizing: true, onPress: () => setPath(path.slice(0, -1)), title: "Volver al grupo anterior", variant: "link" })) : null, _jsx(QueryChoice, { label: "Coinciden", onChange: (join) => changeGroup({ ...group, join }), options: [
97
+ }, title: "Guardar condici\u00F3n" }), _jsx(Button, { onPress: () => setEditing(null), title: "Cancelar condici\u00F3n", variant: "secondary" })] })) : (_jsxs(_Fragment, { children: [path.length ? (_jsx(Button, { onPress: () => setPath(path.slice(0, -1)), title: "Volver al grupo anterior", variant: "link" })) : null, _jsx(QueryChoice, { label: "Coinciden", onChange: (join) => changeGroup({ ...group, join }), options: [
98
98
  { label: "Todas las condiciones", value: "all" },
99
99
  { label: "Cualquiera de las condiciones", value: "any" },
100
100
  { label: "Ninguna de las condiciones", value: "none" },
101
- ], value: group.join }), !group.children.length ? (_jsx(Typography.Text, { className: "text-text-secondary", children: "Sin condiciones en este grupo." })) : null, group.children.map((child, index) => (_jsxs(View, { className: "border-b border-border-light pb-3 gap-2", children: [_jsx(Typography.Text, { variant: "bodyReadable", children: describeCriteria(child, fields) }), _jsxs(View, { className: "flex-row flex-wrap gap-2", children: [_jsx(Button, { accessibleSizing: true, onPress: () => child.kind === "group"
101
+ ], value: group.join }), !group.children.length ? (_jsx(Typography.Text, { className: "text-text-secondary", children: "Sin condiciones en este grupo." })) : null, group.children.map((child, index) => (_jsxs(View, { className: "border-b border-border-light pb-3 gap-2", children: [_jsx(Typography.Text, { variant: "bodyReadable", children: describeCriteria(child, fields) }), _jsxs(View, { className: "flex-row flex-wrap gap-2", children: [_jsx(Button, { onPress: () => child.kind === "group"
102
102
  ? setPath([...path, index])
103
103
  : (setError(undefined), setEditing({ index, condition: child })), title: child.kind === "group"
104
104
  ? `Abrir grupo ${index + 1}`
105
- : `Editar condición ${index + 1}`, variant: "secondary" }), _jsx(Button, { accessibleSizing: true, onPress: () => changeGroup({
105
+ : `Editar condición ${index + 1}`, variant: "secondary" }), _jsx(Button, { onPress: () => changeGroup({
106
106
  ...group,
107
107
  children: group.children.filter((_, position) => position !== index),
108
- }), title: `Eliminar ${child.kind === "group" ? "grupo" : "condición"} ${index + 1}`, variant: "link" })] })] }, index))), _jsx(Button, { accessibleSizing: true, disabled: !fields.some((item) => item.operators.length), onPress: addCondition, title: "A\u00F1adir condici\u00F3n", variant: "secondary" }), _jsx(Button, { accessibleSizing: true, onPress: () => {
108
+ }), title: `Eliminar ${child.kind === "group" ? "grupo" : "condición"} ${index + 1}`, variant: "link" })] })] }, index))), _jsx(Button, { disabled: !fields.some((item) => item.operators.length), onPress: addCondition, title: "A\u00F1adir condici\u00F3n", variant: "secondary" }), _jsx(Button, { onPress: () => {
109
109
  changeGroup({
110
110
  ...group,
111
111
  children: [...group.children, { kind: "group", join: "any", children: [] }],
@@ -1,9 +1,9 @@
1
+ import type { ImageSource } from "../utils/image.js";
1
2
  import React, { ReactNode } from "react";
2
- import { type ImageSourcePropType } from "react-native";
3
3
  interface HomeHeaderProps {
4
4
  readonly title?: string;
5
5
  readonly logo?: ReactNode;
6
- readonly logoSource?: ImageSourcePropType;
6
+ readonly logoSource?: ImageSource;
7
7
  readonly backButton?: boolean;
8
8
  readonly onBack?: () => void;
9
9
  }
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import GradientSurface from "../Display/Display.GradientSurface.js";
3
3
  import { useTheme } from "../theme/index.js";
4
4
  import { Typography } from "../Typography/index.js";
5
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
5
6
  import { Modal, TouchableOpacity, TouchableWithoutFeedback, View } from "react-native";
6
7
  import { useSafeAreaInsets } from "react-native-safe-area-context";
7
8
  /**
@@ -23,9 +24,10 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
23
24
  */
24
25
  export function PromoModal({ visible, onDismiss, title, body, icon: Icon, headerGradient, ctaLabel, onPressCta, dismissLabel = "Ahora no", badge, accentColor, }) {
25
26
  const theme = useTheme();
27
+ const reducedMotion = useReducedMotion();
26
28
  const insets = useSafeAreaInsets();
27
29
  const accent = accentColor ?? theme.accent;
28
- return (_jsxs(Modal, { animationType: "fade", onRequestClose: onDismiss, presentationStyle: "overFullScreen", statusBarTranslucent: true, transparent: true, visible: visible, children: [_jsx(TouchableWithoutFeedback, { onPress: onDismiss, children: _jsx(View, { style: {
30
+ return (_jsxs(Modal, { animationType: reducedMotion ? "none" : "fade", onRequestClose: onDismiss, presentationStyle: "overFullScreen", statusBarTranslucent: true, transparent: true, visible: visible, children: [_jsx(TouchableWithoutFeedback, { onPress: onDismiss, children: _jsx(View, { style: {
29
31
  flex: 1,
30
32
  backgroundColor: "rgba(0,0,0,0.55)",
31
33
  alignItems: "center",
@@ -1,4 +1,4 @@
1
- import { type ImageSourcePropType } from "react-native";
1
+ import type { ImageSource } from "../utils/image.js";
2
2
  export declare enum EVariantScreenHeader {
3
3
  FLAT_HEADER = "FLAT_HEADER",
4
4
  ROUNDED_HEADER = "ROUNDED_HEADER",
@@ -11,7 +11,7 @@ interface ScreenHeaderProps {
11
11
  centerTitle?: boolean;
12
12
  subtitle?: string;
13
13
  logo?: boolean;
14
- logoSource?: ImageSourcePropType;
14
+ logoSource?: ImageSource;
15
15
  rightComponent?: React.ReactNode;
16
16
  children?: React.ReactNode;
17
17
  withGradientContainer?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sincpro/mobile-ui",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Sincpro Mobile design system: generic, presentational React Native components (no domain logic)",
5
5
  "main": "expo/AppEntry.js",
6
6
  "types": "./dist/index.d.ts",
@@ -22,6 +22,7 @@
22
22
  "./Dialog/NativeSheet": "./dist/Dialog/Dialog.NativeSheet.js",
23
23
  "./Actions": "./dist/Actions/index.js",
24
24
  "./Display": "./dist/Display/index.js",
25
+ "./Display/ExpoImage": "./dist/Display/Display.ExpoImage.js",
25
26
  "./Feedback": "./dist/Feedback/index.js",
26
27
  "./Form": "./dist/Form/index.js",
27
28
  "./Navigation": "./dist/Navigation/index.js",
@@ -63,6 +64,7 @@
63
64
  "expo-checkbox": ">=58.0.0",
64
65
  "expo-clipboard": ">=58.0.0",
65
66
  "expo-linear-gradient": ">=58.0.0",
67
+ "expo-image": ">=58.0.0",
66
68
  "expo-localization": ">=58.0.0",
67
69
  "nativewind": ">=4.2.7",
68
70
  "react": ">=19.3",
@@ -124,6 +126,7 @@
124
126
  "expo-checkbox": "~58.0.2",
125
127
  "expo-clipboard": "~58.0.4",
126
128
  "expo-font": "~58.0.11",
129
+ "expo-image": "~58.0.18",
127
130
  "expo-linear-gradient": "~58.0.3",
128
131
  "expo-localization": "~58.0.5",
129
132
  "expo-status-bar": "~58.0.4",