@sincpro/mobile-ui 1.1.0 → 2.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 (129) hide show
  1. package/README.md +156 -29
  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 +10 -12
  9. package/dist/Actions/Actions.Button.js +9 -11
  10. package/dist/Actions/Actions.Dock.d.ts +20 -0
  11. package/dist/Actions/Actions.Dock.js +89 -0
  12. package/dist/Actions/Actions.FAB.d.ts +1 -2
  13. package/dist/Actions/Actions.FAB.js +5 -7
  14. package/dist/Actions/Actions.IconButton.d.ts +1 -2
  15. package/dist/Actions/Actions.IconButton.js +2 -2
  16. package/dist/Actions/index.d.ts +4 -0
  17. package/dist/Actions/index.js +3 -0
  18. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
  19. package/dist/Dialog/Dialog.ActionSheet.js +2 -16
  20. package/dist/Dialog/Dialog.Confirmation.js +7 -5
  21. package/dist/Dialog/Dialog.EditValue.js +3 -1
  22. package/dist/Dialog/Dialog.Sheet.d.ts +3 -3
  23. package/dist/Dialog/Dialog.Sheet.js +18 -25
  24. package/dist/Display/Display.Accordion.js +7 -3
  25. package/dist/Display/Display.AmountSummary.d.ts +28 -0
  26. package/dist/Display/Display.AmountSummary.js +25 -0
  27. package/dist/Display/Display.Carousel.d.ts +27 -4
  28. package/dist/Display/Display.Carousel.js +112 -12
  29. package/dist/Display/Display.ExpoImage.d.ts +8 -0
  30. package/dist/Display/Display.ExpoImage.js +15 -0
  31. package/dist/Display/Display.Image.d.ts +10 -6
  32. package/dist/Display/Display.Image.js +39 -6
  33. package/dist/Display/Display.ListItem.d.ts +1 -2
  34. package/dist/Display/Display.ListItem.js +3 -3
  35. package/dist/Display/Display.Logo.d.ts +2 -2
  36. package/dist/Display/Display.Logo.js +3 -2
  37. package/dist/Display/Display.MediaCard.d.ts +2 -2
  38. package/dist/Display/Display.MediaEmbed.d.ts +2 -2
  39. package/dist/Display/Display.ProductCard.d.ts +2 -2
  40. package/dist/Display/Display.Story.d.ts +3 -3
  41. package/dist/Display/Display.Story.js +5 -4
  42. package/dist/Display/Display.StoryTray.d.ts +4 -4
  43. package/dist/Display/Display.StoryTray.js +3 -2
  44. package/dist/Display/Display.StoryViewer.d.ts +3 -3
  45. package/dist/Display/Display.StoryViewer.js +6 -3
  46. package/dist/Display/index.d.ts +9 -0
  47. package/dist/Display/index.js +4 -0
  48. package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
  49. package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
  50. package/dist/Feedback/Feedback.EmptyState.d.ts +3 -4
  51. package/dist/Feedback/Feedback.EmptyState.js +4 -3
  52. package/dist/Feedback/Feedback.Error.d.ts +1 -2
  53. package/dist/Feedback/Feedback.Error.js +2 -2
  54. package/dist/Feedback/Feedback.Loading.d.ts +6 -2
  55. package/dist/Feedback/Feedback.Loading.js +2 -2
  56. package/dist/Feedback/Feedback.Progress.d.ts +6 -3
  57. package/dist/Feedback/Feedback.Progress.js +29 -19
  58. package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
  59. package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
  60. package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
  61. package/dist/Feedback/Feedback.Skeleton.js +15 -3
  62. package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
  63. package/dist/Feedback/Feedback.Spinner.js +40 -9
  64. package/dist/Feedback/Feedback.Splash.d.ts +2 -2
  65. package/dist/Feedback/Feedback.Splash.js +5 -3
  66. package/dist/Feedback/Toast.context.d.ts +64 -3
  67. package/dist/Feedback/Toast.context.js +112 -31
  68. package/dist/Feedback/index.d.ts +15 -1
  69. package/dist/Feedback/index.js +4 -1
  70. package/dist/Form/Form.Checkbox.d.ts +1 -2
  71. package/dist/Form/Form.Checkbox.js +2 -2
  72. package/dist/Form/Form.DatePicker.d.ts +1 -2
  73. package/dist/Form/Form.DatePicker.js +2 -2
  74. package/dist/Form/Form.Dropdown.d.ts +15 -3
  75. package/dist/Form/Form.Dropdown.js +21 -6
  76. package/dist/Form/Form.ErrorSummary.d.ts +23 -0
  77. package/dist/Form/Form.ErrorSummary.js +53 -0
  78. package/dist/Form/Form.Input.d.ts +1 -2
  79. package/dist/Form/Form.Input.js +2 -2
  80. package/dist/Form/Form.Select.d.ts +1 -2
  81. package/dist/Form/Form.Select.js +6 -19
  82. package/dist/Form/index.d.ts +3 -0
  83. package/dist/Form/index.js +2 -0
  84. package/dist/Navigation/Navigation.AppBar.d.ts +2 -6
  85. package/dist/Navigation/Navigation.AppBar.js +9 -7
  86. package/dist/Navigation/Navigation.BottomNav.d.ts +1 -3
  87. package/dist/Navigation/Navigation.BottomNav.js +7 -6
  88. package/dist/Navigation/Navigation.Header.d.ts +2 -2
  89. package/dist/Navigation/Navigation.Menu.js +10 -3
  90. package/dist/Navigation/Navigation.Search.d.ts +41 -0
  91. package/dist/Navigation/Navigation.Search.js +47 -0
  92. package/dist/Navigation/Navigation.SearchBar.d.ts +1 -2
  93. package/dist/Navigation/Navigation.SearchBar.js +16 -19
  94. package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -3
  95. package/dist/Navigation/Navigation.SegmentedControl.js +29 -16
  96. package/dist/Navigation/index.d.ts +11 -2
  97. package/dist/Navigation/index.js +2 -0
  98. package/dist/branding.d.ts +3 -2
  99. package/dist/primitives/BaseButton.d.ts +10 -12
  100. package/dist/primitives/BaseButton.js +9 -11
  101. package/dist/theme/typography.d.ts +1 -1
  102. package/dist/utils/image.d.ts +3 -0
  103. package/dist/utils/image.js +13 -0
  104. package/dist/utils/keys.d.ts +1 -0
  105. package/dist/utils/keys.js +7 -0
  106. package/dist/utils/progress.d.ts +1 -0
  107. package/dist/utils/progress.js +5 -0
  108. package/dist/utils/reorder.js +2 -5
  109. package/dist/utils/useReducedMotion.d.ts +1 -0
  110. package/dist/utils/useReducedMotion.js +43 -0
  111. package/dist/utils/useSheetTranslation.d.ts +5 -0
  112. package/dist/utils/useSheetTranslation.js +23 -0
  113. package/dist/views/AppSplashView.d.ts +3 -2
  114. package/dist/views/AppSplashView.js +3 -2
  115. package/dist/views/AuthFormView.d.ts +2 -2
  116. package/dist/views/FormViewV2.d.ts +2 -2
  117. package/dist/views/ListViewV2.context.d.ts +3 -1
  118. package/dist/views/ListViewV2.context.js +41 -21
  119. package/dist/views/ListViewV2.d.ts +15 -7
  120. package/dist/views/ListViewV2.js +44 -23
  121. package/dist/views/types/IListView.js +6 -5
  122. package/dist/widgets/CollectionOptions.js +9 -7
  123. package/dist/widgets/FilterBar.d.ts +1 -1
  124. package/dist/widgets/FilterBar.js +1 -1
  125. package/dist/widgets/FilterBuilder.js +7 -7
  126. package/dist/widgets/HomeHeader.d.ts +2 -2
  127. package/dist/widgets/PromoModal.js +3 -1
  128. package/dist/widgets/ScreenHeader.d.ts +2 -2
  129. package/package.json +5 -1
@@ -4,33 +4,20 @@ import Pressable from "../primitives/Pressable.js";
4
4
  import { themeToVars, useTheme } from "../theme/index.js";
5
5
  import { cn } from "../theme/tw.js";
6
6
  import { Typography } from "../Typography/index.js";
7
+ import { useSheetTranslation } from "../utils/useSheetTranslation.js";
7
8
  import { vars } from "nativewind";
8
- import { useEffect, useRef, useState } from "react";
9
+ import { useState } from "react";
9
10
  import { Animated, Modal, ScrollView, View } from "react-native";
10
11
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
- function Select({ options, value, onChange, label, placeholder = "Seleccionar…", title, disabled = false, accessibleSizing = false, className, testID, showCloseButton = accessibleSizing, }) {
12
+ function Select({ options, value, onChange, label, placeholder = "Seleccionar…", title, disabled = false, className, testID, showCloseButton = true, }) {
12
13
  const theme = useTheme();
13
14
  const insets = useSafeAreaInsets();
14
15
  const [open, setOpen] = useState(false);
15
16
  const selected = options.find((o) => o.value === value) ?? null;
16
- // eslint-disable-next-line react-hooks/refs
17
- const translateY = useRef(new Animated.Value(800)).current;
18
- useEffect(() => {
19
- if (open) {
20
- Animated.spring(translateY, {
21
- toValue: 0,
22
- useNativeDriver: true,
23
- damping: 20,
24
- stiffness: 300,
25
- }).start();
26
- }
27
- else {
28
- translateY.setValue(800);
29
- }
30
- }, [open, translateY]);
17
+ const { translateY } = useSheetTranslation(open);
31
18
  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 la lista de opciones", accessibilityLabel: label
32
19
  ? `${label}: ${selected?.label ?? placeholder}`
33
- : (selected?.label ?? placeholder), accessibilityRole: "button", accessibilityState: { disabled, expanded: open }, className: cn("flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3", accessibleSizing ? "min-h-[48px]" : "min-h-[44px]"), disabled: disabled, onPress: () => setOpen(true), children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", selected ? "text-text-primary" : "text-text-tertiary"), numberOfLines: accessibleSizing ? undefined : 1, style: accessibleSizing ? { paddingVertical: 12 } : undefined, children: selected ? selected.label : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: "chevron-down", size: 18 })] }), _jsx(Modal, { animationType: "none", onRequestClose: () => setOpen(false), presentationStyle: "overFullScreen", transparent: true, visible: open, children: _jsxs(View, { className: "flex-1 justify-end", children: [_jsx(View, { accessibilityElementsHidden: true, className: "absolute inset-0 bg-black/50", importantForAccessibility: "no-hide-descendants", onResponderRelease: () => setOpen(false), onStartShouldSetResponder: () => true }), _jsxs(Animated.View, { accessibilityViewIsModal: true, className: "rounded-t-3xl", importantForAccessibility: "yes", onAccessibilityEscape: () => setOpen(false), style: [
20
+ : (selected?.label ?? placeholder), accessibilityRole: "button", accessibilityState: { disabled, expanded: open }, 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", selected ? "text-text-primary" : "text-text-tertiary"), style: { paddingVertical: 12 }, children: selected ? selected.label : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: "chevron-down", size: 18 })] }), _jsx(Modal, { animationType: "none", onRequestClose: () => setOpen(false), presentationStyle: "overFullScreen", transparent: true, visible: open, children: _jsxs(View, { className: "flex-1 justify-end", children: [_jsx(View, { accessibilityElementsHidden: true, className: "absolute inset-0 bg-black/50", importantForAccessibility: "no-hide-descendants", onResponderRelease: () => setOpen(false), onStartShouldSetResponder: () => true }), _jsxs(Animated.View, { accessibilityViewIsModal: true, className: "rounded-t-3xl", importantForAccessibility: "yes", onAccessibilityEscape: () => setOpen(false), style: [
34
21
  vars(themeToVars(theme)),
35
22
  {
36
23
  transform: [{ translateY }],
@@ -48,7 +35,7 @@ function Select({ options, value, onChange, label, placeholder = "Seleccionar…
48
35
  return (_jsxs(Pressable, { accessibilityRole: "menuitem", accessibilityState: { selected: isSelected }, className: "flex-row items-center justify-between px-4 py-3 border-b border-border-light", onPress: () => {
49
36
  onChange(option.value);
50
37
  setOpen(false);
51
- }, style: accessibleSizing ? { minHeight: 48 } : undefined, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", isSelected ? "text-text-accent font-semibold" : "text-text-primary"), children: option.label }), isSelected ? (_jsx(Icon, { color: theme.text.accent, name: "checkmark", size: 18 })) : null] }, String(option.value)));
38
+ }, style: { minHeight: 48, minWidth: 48 }, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", isSelected ? "text-text-accent font-semibold" : "text-text-primary"), children: option.label }), isSelected ? (_jsx(Icon, { color: theme.text.accent, name: "checkmark", size: 18 })) : null] }, String(option.value)));
52
39
  }) })] })] }) })] }));
53
40
  }
54
41
  export default Select;
@@ -6,6 +6,7 @@ import ControlledMonetaryInput from "./Form.ControlledMonetaryInput.js";
6
6
  import ControlledRadioButton from "./Form.ControlledRadioButton.js";
7
7
  import DatePicker from "./Form.DatePicker.js";
8
8
  import Dropdown from "./Form.Dropdown.js";
9
+ import ErrorSummary from "./Form.ErrorSummary.js";
9
10
  import FAB from "./Form.FAB.js";
10
11
  import { FilterButtonGroup } from "./Form.FilterButton.js";
11
12
  import FractionalQuantityInput from "./Form.FractionalQuantityInput.js";
@@ -27,6 +28,7 @@ export declare const Form: {
27
28
  InputField: import("react").FC<import("./Form.InputField.js").InputFieldProps>;
28
29
  KeyboardForm: typeof KeyboardForm;
29
30
  KeyboardToolbar: typeof KeyboardToolbar;
31
+ ErrorSummary: typeof ErrorSummary;
30
32
  Button: import("react").FC<import("./Form.Button.js").ButtonProps> & {
31
33
  Icon: import("react").FC<import("./Form.Button.js").ButtonIconProps>;
32
34
  };
@@ -54,6 +56,7 @@ export declare const Form: {
54
56
  FilterButtonGroup: typeof FilterButtonGroup;
55
57
  };
56
58
  export { type DropdownOption };
59
+ export type { ErrorSummaryItem, ErrorSummaryProps, } from "./Form.ErrorSummary.js";
57
60
  export type { KeyboardFormProps } from "./Form.KeyboardForm.js";
58
61
  export type { KeyboardToolbarAction, KeyboardToolbarProps, } from "./Form.KeyboardToolbar.js";
59
62
  export default Form;
@@ -7,6 +7,7 @@ import ControlledMonetaryInput from "./Form.ControlledMonetaryInput.js";
7
7
  import ControlledRadioButton from "./Form.ControlledRadioButton.js";
8
8
  import DatePicker from "./Form.DatePicker.js";
9
9
  import Dropdown from "./Form.Dropdown.js";
10
+ import ErrorSummary from "./Form.ErrorSummary.js";
10
11
  import FAB from "./Form.FAB.js";
11
12
  import { FilterButton, FilterButtonGroup } from "./Form.FilterButton.js";
12
13
  import FractionalQuantityInput from "./Form.FractionalQuantityInput.js";
@@ -31,6 +32,7 @@ export const Form = {
31
32
  InputField,
32
33
  KeyboardForm,
33
34
  KeyboardToolbar,
35
+ ErrorSummary,
34
36
  Button: ButtonWithIcon,
35
37
  IconButton,
36
38
  PasswordInput,
@@ -16,8 +16,6 @@ export interface AppBarActionProps {
16
16
  onDark?: boolean;
17
17
  /** Drop the contained surface → bare icon/text (iOS-style plain back/action). */
18
18
  bare?: boolean;
19
- /** Increase the action target to 48 points. */
20
- accessibleSizing?: boolean;
21
19
  testID?: string;
22
20
  }
23
21
  /**
@@ -25,7 +23,7 @@ export interface AppBarActionProps {
25
23
  * (merge of Material 3 state-layer + iOS 26 contained button). Optional badge.
26
24
  * With `label`, renders a text pill CTA instead (for "Editar"/"Nuevo"/"Guardar").
27
25
  */
28
- declare function AppBarAction({ icon, label, onPress, badge, accessibilityLabel, tone, onDark, bare, accessibleSizing, testID, }: AppBarActionProps): import("react").JSX.Element;
26
+ declare function AppBarAction({ icon, label, onPress, badge, accessibilityLabel, tone, onDark, bare, testID, }: AppBarActionProps): import("react").JSX.Element;
29
27
  export interface AppBarProps {
30
28
  title?: string;
31
29
  subtitle?: string;
@@ -49,8 +47,6 @@ export interface AppBarProps {
49
47
  border?: boolean;
50
48
  /** Reserves the status-bar space (insets.top). `false` to embed/demo. */
51
49
  safeArea?: boolean;
52
- /** Increase the back-action target to 48 points. */
53
- accessibleSizing?: boolean;
54
50
  /**
55
51
  * [large variant] Extra top padding (px) above the large title row.
56
52
  * Default is 8. Use ~16–24 on root/home screens without a back button.
@@ -113,7 +109,7 @@ export interface AppBarBackground {
113
109
  * a `subheader` slot (for SearchBar/Segmented below the title), and a `large` variant.
114
110
  * Replaces the gradient look of `Navigation.Header`/`ScreenHeader`.
115
111
  */
116
- declare function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant, tone, accentBar, backVariant, border, safeArea, accessibleSizing, topSpacing, background, bottomDivider, className, testID, }: AppBarProps): import("react").JSX.Element;
112
+ declare function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant, tone, accentBar, backVariant, border, safeArea, topSpacing, background, bottomDivider, className, testID, }: AppBarProps): import("react").JSX.Element;
117
113
  declare const AppBarWithAction: typeof AppBar & {
118
114
  Action: typeof AppBarAction;
119
115
  };
@@ -15,7 +15,7 @@ const HERO_DARK_FALLBACK = "#0E1714";
15
15
  * (merge of Material 3 state-layer + iOS 26 contained button). Optional badge.
16
16
  * With `label`, renders a text pill CTA instead (for "Editar"/"Nuevo"/"Guardar").
17
17
  */
18
- function AppBarAction({ icon, label, onPress, badge, accessibilityLabel, tone = "plain", onDark = false, bare = false, accessibleSizing = false, testID, }) {
18
+ function AppBarAction({ icon, label, onPress, badge, accessibilityLabel, tone = "plain", onDark = false, bare = false, testID, }) {
19
19
  const theme = useTheme();
20
20
  const bg = bare
21
21
  ? "transparent"
@@ -35,16 +35,18 @@ function AppBarAction({ icon, label, onPress, badge, accessibilityLabel, tone =
35
35
  // primary/secondary header actions like "Editar", "Nuevo", "Guardar".
36
36
  if (label) {
37
37
  return (_jsxs(Pressable, { accessibilityLabel: accessibilityLabel ?? label, accessibilityRole: "button", className: "flex-row items-center justify-center gap-1.5", hitSlop: 6, onPress: onPress, style: {
38
- height: accessibleSizing ? 48 : 36,
38
+ minHeight: 48,
39
+ minWidth: 48,
39
40
  paddingHorizontal: 14,
41
+ paddingVertical: 8,
40
42
  borderRadius: 999,
41
43
  backgroundColor: bg,
42
44
  ...(showBorder ? { borderWidth: 1, borderColor: theme.border.light } : null),
43
- }, testID: testID, children: [icon ? _jsx(Icon, { color: iconColor, name: icon, size: 16 }) : null, _jsx(Typography.Text, { semibold: true, style: { color: iconColor }, variant: "bodySmall", children: label })] }));
45
+ }, testID: testID, children: [icon ? _jsx(Icon, { color: iconColor, name: icon, size: 16 }) : null, _jsx(Typography.Text, { semibold: true, style: { color: iconColor, flexShrink: 1 }, variant: "bodySmall", children: label })] }));
44
46
  }
45
47
  return (_jsxs(Pressable, { accessibilityLabel: accessibilityLabel, accessibilityRole: "button", className: "items-center justify-center", hitSlop: 6, onPress: onPress, style: {
46
- width: accessibleSizing ? 48 : 40,
47
- height: accessibleSizing ? 48 : 40,
48
+ width: 48,
49
+ height: 48,
48
50
  borderRadius: 12,
49
51
  backgroundColor: bg,
50
52
  ...(showBorder ? { borderWidth: 1, borderColor: theme.border.light } : null),
@@ -56,7 +58,7 @@ function AppBarAction({ icon, label, onPress, badge, accessibilityLabel, tone =
56
58
  * a `subheader` slot (for SearchBar/Segmented below the title), and a `large` variant.
57
59
  * Replaces the gradient look of `Navigation.Header`/`ScreenHeader`.
58
60
  */
59
- function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant = "default", tone = "default", accentBar = false, backVariant = "contained", border = true, safeArea = true, accessibleSizing = false, topSpacing, background, bottomDivider, className, testID, }) {
61
+ function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant = "default", tone = "default", accentBar = false, backVariant = "contained", border = true, safeArea = true, topSpacing, background, bottomDivider, className, testID, }) {
60
62
  const theme = useTheme();
61
63
  const insets = useSafeAreaInsets();
62
64
  const centered = variant === "center";
@@ -106,7 +108,7 @@ function AppBar({ title, subtitle, onBack, leading, actions, subheader, variant
106
108
  ? { borderBottomLeftRadius: 24, borderBottomRightRadius: 24, overflow: "hidden" }
107
109
  : null),
108
110
  ...(showShadow ? theme.shadow.md : null),
109
- }, testID: testID, children: [hasBackground ? (_jsx(GradientSurface, { colors: background.colors, end: background.end, padding: "none", preset: background.surface ?? "warm", radius: "none", start: background.start, style: StyleSheet.absoluteFill })) : gradient ? (_jsx(GradientSurface, { colors: theme.gradient.accent, padding: "none", radius: "none", style: StyleSheet.absoluteFill })) : null, background?.pattern ? (_jsx(Pattern, { color: background.patternColor ?? "#FFFFFF", kind: background.pattern, opacity: background.patternOpacity ?? 0.18 })) : null, showTopBar ? (_jsxs(View, { className: "flex-row items-center px-3 gap-2 min-h-[56px]", children: [_jsxs(View, { className: "flex-row items-center gap-2", style: { minWidth: 38 }, children: [onBack ? (_jsx(AppBarAction, { accessibilityLabel: "Volver", accessibleSizing: accessibleSizing, bare: backVariant === "plain", icon: "chevron-back", onDark: dark || gradient || hasBackground, onPress: onBack })) : null, leading] }), _jsxs(View, { className: cn("flex-1", centered && "items-center"), children: [title ? (_jsx(Typography.Text, { accessibilityRole: "header", className: colored ? "" : "text-text-primary", numberOfLines: 1, semibold: true, style: titleStyle, variant: "subtitle", children: title })) : null, subtitle ? (_jsx(Typography.Text, { className: colored ? "" : "text-text-secondary", numberOfLines: 1, style: subtitleStyle, variant: "caption", children: subtitle })) : null] }), _jsx(View, { className: "flex-row items-center justify-end gap-2", style: { minWidth: 38 }, children: actions })] })) : null, large && title ? (_jsxs(View, { className: cn("flex-row px-4 pb-3", largeHasNav ? "items-center gap-2" : ""), style: { paddingTop: topSpacing != null ? topSpacing : 8 }, children: [onBack ? (_jsx(AppBarAction, { accessibilityLabel: "Volver", accessibleSizing: accessibleSizing, bare: backVariant === "plain", icon: "chevron-back", onDark: dark || gradient || hasBackground, onPress: onBack })) : null, leading, accentBar && !largeHasNav ? (_jsx(View, { style: {
111
+ }, testID: testID, children: [hasBackground ? (_jsx(GradientSurface, { colors: background.colors, end: background.end, padding: "none", preset: background.surface ?? "warm", radius: "none", start: background.start, style: StyleSheet.absoluteFill })) : gradient ? (_jsx(GradientSurface, { colors: theme.gradient.accent, padding: "none", radius: "none", style: StyleSheet.absoluteFill })) : null, background?.pattern ? (_jsx(Pattern, { color: background.patternColor ?? "#FFFFFF", kind: background.pattern, opacity: background.patternOpacity ?? 0.18 })) : null, showTopBar ? (_jsxs(View, { className: "flex-row items-center px-3 gap-2 min-h-[56px]", children: [_jsxs(View, { className: "flex-row items-center gap-2", style: { minWidth: 38 }, children: [onBack ? (_jsx(AppBarAction, { accessibilityLabel: "Volver", bare: backVariant === "plain", icon: "chevron-back", onDark: dark || gradient || hasBackground, onPress: onBack })) : null, leading] }), _jsxs(View, { className: cn("flex-1", centered && "items-center"), children: [title ? (_jsx(Typography.Text, { accessibilityRole: "header", className: colored ? "" : "text-text-primary", semibold: true, style: titleStyle, variant: "subtitle", children: title })) : null, subtitle ? (_jsx(Typography.Text, { className: colored ? "" : "text-text-secondary", style: subtitleStyle, variant: "caption", children: subtitle })) : null] }), _jsx(View, { className: "flex-row items-center justify-end gap-2", style: { minWidth: 38 }, children: actions })] })) : null, large && title ? (_jsxs(View, { className: cn("flex-row px-4 pb-3", largeHasNav ? "items-center gap-2" : ""), style: { paddingTop: topSpacing != null ? topSpacing : 8 }, children: [onBack ? (_jsx(AppBarAction, { accessibilityLabel: "Volver", bare: backVariant === "plain", icon: "chevron-back", onDark: dark || gradient || hasBackground, onPress: onBack })) : null, leading, accentBar && !largeHasNav ? (_jsx(View, { style: {
110
112
  width: 4,
111
113
  marginRight: 10,
112
114
  marginVertical: 2,
@@ -44,8 +44,6 @@ export interface BottomNavProps {
44
44
  bordered?: boolean;
45
45
  /** Token for the active color. `primary` (default) or `accent` (e.g. green CTA brand). */
46
46
  activeColor?: "primary" | "accent";
47
- /** Increase each tab's target to at least 48 points. */
48
- accessibleSizing?: boolean;
49
47
  className?: string;
50
48
  testID?: string;
51
49
  }
@@ -54,6 +52,6 @@ export interface BottomNavProps {
54
52
  * `shape` (bar/floating), `tone` (light/dark), `indicator` (none/pill/pill-text/bar/glow),
55
53
  * `showLabels`, `centerAction` (FAB), `activeColor`. Replaces the legacy `TabNavigatorLayout`.
56
54
  */
57
- declare function BottomNav({ items, value, onChange, showLabels, centerAction, safeArea, shape, tone, indicator, bordered, activeColor, accessibleSizing, className, testID, }: BottomNavProps): import("react").JSX.Element;
55
+ declare function BottomNav({ items, value, onChange, showLabels, centerAction, safeArea, shape, tone, indicator, bordered, activeColor, className, testID, }: BottomNavProps): import("react").JSX.Element;
58
56
  export default BottomNav;
59
57
  export { BottomNav };
@@ -16,7 +16,7 @@ function resolveDarkSurface(theme) {
16
16
  * `shape` (bar/floating), `tone` (light/dark), `indicator` (none/pill/pill-text/bar/glow),
17
17
  * `showLabels`, `centerAction` (FAB), `activeColor`. Replaces the legacy `TabNavigatorLayout`.
18
18
  */
19
- function BottomNav({ items, value, onChange, showLabels = true, centerAction, safeArea = true, shape = "bar", tone = "light", indicator = "none", bordered = true, activeColor = "primary", accessibleSizing = false, className, testID, }) {
19
+ function BottomNav({ items, value, onChange, showLabels = true, centerAction, safeArea = true, shape = "bar", tone = "light", indicator = "none", bordered = true, activeColor = "primary", className, testID, }) {
20
20
  const insets = useSafeAreaInsets();
21
21
  const theme = useTheme();
22
22
  const dark = tone === "dark";
@@ -55,10 +55,11 @@ function BottomNav({ items, value, onChange, showLabels = true, centerAction, sa
55
55
  return (_jsxs(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", accessibilityState: { selected: true }, className: "flex-row items-center px-4 py-2", onPress: () => onChange(item.key), style: {
56
56
  borderRadius: 999,
57
57
  backgroundColor: activeTint,
58
- ...(accessibleSizing ? { minHeight: 48, minWidth: 48 } : null),
59
- }, children: [renderIconNode(item, true, onActive), item.label ? (_jsx(Typography.Text, { semibold: true, style: { color: onActive, fontSize: 13, marginLeft: 8 }, children: item.label })) : null] }, item.key));
58
+ minHeight: 48,
59
+ minWidth: 48,
60
+ }, children: [renderIconNode(item, true, onActive), item.label ? (_jsx(Typography.Text, { semibold: true, style: { color: onActive, fontSize: 13, marginLeft: 8, flexShrink: 1 }, children: item.label })) : null] }, item.key));
60
61
  }
61
- return (_jsx(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", className: "items-center justify-center px-3 py-2", onPress: () => onChange(item.key), style: accessibleSizing ? { minHeight: 48, minWidth: 48 } : undefined, children: renderIconNode(item, false, inactiveColor) }, item.key));
62
+ return (_jsx(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", className: "items-center justify-center px-3 py-2", onPress: () => onChange(item.key), style: { minHeight: 48, minWidth: 48 }, children: renderIconNode(item, false, inactiveColor) }, item.key));
62
63
  }
63
64
  // Variants none / pill / bar / glow — vertical icon + optional indicator + optional label.
64
65
  const inPill = indicator === "pill" && active;
@@ -74,7 +75,7 @@ function BottomNav({ items, value, onChange, showLabels = true, centerAction, sa
74
75
  labelColor = theme.text.inverse;
75
76
  else if (active)
76
77
  labelColor = brandText;
77
- return (_jsxs(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", accessibilityState: { selected: active }, className: "flex-1 items-center justify-center py-1.5", onPress: () => onChange(item.key), style: accessibleSizing ? { minHeight: 48, minWidth: 48 } : undefined, children: [indicator === "bar" && active ? (_jsx(View, { style: {
78
+ return (_jsxs(Pressable, { accessibilityLabel: item.label ?? item.key, accessibilityRole: "tab", accessibilityState: { selected: active }, className: "flex-1 items-center justify-center py-1.5", onPress: () => onChange(item.key), style: { minHeight: 48, minWidth: 48 }, children: [indicator === "bar" && active ? (_jsx(View, { style: {
78
79
  position: "absolute",
79
80
  top: 0,
80
81
  width: 26,
@@ -91,7 +92,7 @@ function BottomNav({ items, value, onChange, showLabels = true, centerAction, sa
91
92
  shadowOpacity: 0.9,
92
93
  shadowRadius: 5,
93
94
  elevation: 4,
94
- } })) : null, showLabels && item.label && indicator !== "glow" ? (_jsx(Typography.Text, { numberOfLines: 1, semibold: active, style: { color: labelColor, fontSize: 11, marginTop: 3 }, children: item.label })) : null] }, item.key));
95
+ } })) : null, showLabels && item.label && indicator !== "glow" ? (_jsx(Typography.Text, { semibold: active, style: { color: labelColor, fontSize: 11, marginTop: 3, textAlign: "center" }, children: item.label })) : null] }, item.key));
95
96
  };
96
97
  const renderCenterFab = () => (_jsx(Pressable, { accessibilityLabel: centerAction?.accessibilityLabel ?? "Acción", accessibilityRole: "button", className: "items-center justify-center", onPress: centerAction?.onPress, style: {
97
98
  width: 52,
@@ -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
  export interface HeaderProps {
4
4
  title?: string;
5
5
  logo?: ReactNode;
6
- logoSource?: ImageSourcePropType;
6
+ logoSource?: ImageSource;
7
7
  backButton?: boolean;
8
8
  handleBackButton?: () => void;
9
9
  className?: string;
@@ -4,10 +4,12 @@ import Pressable from "../primitives/Pressable.js";
4
4
  import { useTheme } from "../theme/index.js";
5
5
  import { cn } from "../theme/tw.js";
6
6
  import { Typography } from "../Typography/index.js";
7
+ import { useReducedMotion } from "../utils/useReducedMotion.js";
7
8
  import { cloneElement, useRef, useState } from "react";
8
9
  import { Dimensions, Modal, Pressable as RNPressable, View, } from "react-native";
9
10
  function Menu({ items, trigger, menuWidth = 220, testID }) {
10
11
  const theme = useTheme();
12
+ const reducedMotion = useReducedMotion();
11
13
  const triggerRef = useRef(null);
12
14
  const [anchor, setAnchor] = useState(null);
13
15
  const open = () => {
@@ -21,7 +23,12 @@ function Menu({ items, trigger, menuWidth = 220, testID }) {
21
23
  const top = anchor ? anchor.y + anchor.height + 4 : 0;
22
24
  return (_jsxs(View, { collapsable: false, ref: triggerRef, testID: testID, children: [trigger ? (
23
25
  // eslint-disable-next-line react-hooks/refs
24
- cloneElement(trigger, { onPress: open })) : (_jsx(Pressable, { accessibilityLabel: "M\u00E1s opciones", accessibilityRole: "button", onPress: open, children: _jsx(Icon, { color: theme.icon.secondary, name: "ellipsis-vertical", size: 22 }) })), _jsx(Modal, { animationType: "fade", onRequestClose: close, presentationStyle: "overFullScreen", transparent: true, visible: anchor !== null, children: _jsx(RNPressable, { onPress: close, style: { flex: 1 }, children: _jsx(View, { className: "rounded-lg py-1", style: {
26
+ cloneElement(trigger, { onPress: open })) : (_jsx(Pressable, { accessibilityLabel: "M\u00E1s opciones", accessibilityRole: "button", onPress: open, style: {
27
+ minWidth: 48,
28
+ minHeight: 48,
29
+ alignItems: "center",
30
+ justifyContent: "center",
31
+ }, children: _jsx(Icon, { color: theme.icon.secondary, name: "ellipsis-vertical", size: 22 }) })), _jsx(Modal, { animationType: reducedMotion ? "none" : "fade", onRequestClose: close, presentationStyle: "overFullScreen", transparent: true, visible: anchor !== null, children: _jsx(RNPressable, { onPress: close, style: { flex: 1 }, children: _jsx(View, { accessibilityViewIsModal: true, className: "rounded-lg py-1", onAccessibilityEscape: close, style: {
25
32
  position: "absolute",
26
33
  top,
27
34
  right,
@@ -35,10 +42,10 @@ function Menu({ items, trigger, menuWidth = 220, testID }) {
35
42
  return _jsx(View, { className: "my-1 h-px bg-border-default" }, `sep-${index}`);
36
43
  }
37
44
  const item = entry;
38
- return (_jsxs(Pressable, { accessibilityRole: "menuitem", accessibilityState: { disabled: item.disabled }, className: "flex-row items-center gap-3 px-4 py-3", disabled: item.disabled, onPress: () => {
45
+ return (_jsxs(Pressable, { accessibilityLabel: item.label, accessibilityRole: "menuitem", accessibilityState: { disabled: item.disabled }, className: "flex-row items-center gap-3 px-4 py-3", disabled: item.disabled, onPress: () => {
39
46
  close();
40
47
  item.onPress();
41
- }, children: [item.icon ? (_jsx(Icon, { color: item.destructive ? theme.danger : theme.icon.secondary, name: item.icon, size: 18 })) : null, _jsx(Typography.Text, { className: cn("flex-1 text-sm", item.destructive ? "text-danger" : "text-text-primary", item.disabled && "text-text-disabled"), children: item.label })] }, item.label));
48
+ }, style: { minHeight: 48 }, children: [item.icon ? (_jsx(Icon, { color: item.destructive ? theme.danger : theme.icon.secondary, name: item.icon, size: 18 })) : null, _jsx(Typography.Text, { className: cn("flex-1 text-sm", item.destructive ? "text-danger" : "text-text-primary", item.disabled && "text-text-disabled"), children: item.label })] }, item.label));
42
49
  }) }) }) })] }));
43
50
  }
44
51
  export default Menu;
@@ -0,0 +1,41 @@
1
+ import { type SearchHistoryProps } from "../Display/Display.SearchHistory.js";
2
+ import { type ReactNode } from "react";
3
+ import { type SearchBarProps } from "./Navigation.SearchBar";
4
+ import { type SearchSuggestionsProps } from "./Navigation.SearchSuggestions";
5
+ export interface SearchRootProps {
6
+ children: ReactNode;
7
+ className?: string;
8
+ testID?: string;
9
+ }
10
+ declare function Root({ children, className, testID }: SearchRootProps): import("react").JSX.Element;
11
+ declare function Field(props: SearchBarProps): import("react").JSX.Element;
12
+ export interface SearchFiltersProps {
13
+ children: ReactNode;
14
+ className?: string;
15
+ testID?: string;
16
+ }
17
+ declare function Filters({ children, className, testID }: SearchFiltersProps): import("react").JSX.Element;
18
+ export interface SearchFilterSheetProps {
19
+ children: ReactNode;
20
+ activeCount?: number;
21
+ title?: string;
22
+ triggerLabel?: string;
23
+ applyLabel?: string;
24
+ clearLabel?: string;
25
+ onApply: () => void;
26
+ onClear?: () => void;
27
+ testID?: string;
28
+ }
29
+ declare function FilterSheet({ children, activeCount, title, triggerLabel, applyLabel, clearLabel, onApply, onClear, testID, }: SearchFilterSheetProps): import("react").JSX.Element;
30
+ declare function History(props: SearchHistoryProps): import("react").JSX.Element;
31
+ declare function Suggestions(props: SearchSuggestionsProps): import("react").JSX.Element;
32
+ declare const Search: {
33
+ Root: typeof Root;
34
+ Field: typeof Field;
35
+ Filters: typeof Filters;
36
+ FilterSheet: typeof FilterSheet;
37
+ History: typeof History;
38
+ Suggestions: typeof Suggestions;
39
+ };
40
+ export type { SearchBarProps, SearchHistoryProps, SearchSuggestionsProps };
41
+ export default Search;
@@ -0,0 +1,47 @@
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 { FilterButtonGroup } from "../Actions/Actions.FilterButton.js";
4
+ import Sheet from "../Dialog/Dialog.Sheet.js";
5
+ import Icon from "../Display/Display.Icon.js";
6
+ import SearchHistory from "../Display/Display.SearchHistory.js";
7
+ import Pressable from "../primitives/Pressable.js";
8
+ import { useTheme } from "../theme/index.js";
9
+ import { cn } from "../theme/tw.js";
10
+ import { Typography } from "../Typography/index.js";
11
+ import { useState } from "react";
12
+ import { View } from "react-native";
13
+ import SearchBar from "./Navigation.SearchBar";
14
+ import SearchSuggestions from "./Navigation.SearchSuggestions";
15
+ function Root({ children, className, testID }) {
16
+ return (_jsx(View, { className: cn("gap-3", className), testID: testID, children: children }));
17
+ }
18
+ function Field(props) {
19
+ return _jsx(SearchBar, { ...props });
20
+ }
21
+ function Filters({ children, className, testID }) {
22
+ return (_jsx(FilterButtonGroup, { className: className, layout: "scroll", testID: testID, children: children }));
23
+ }
24
+ function FilterSheet({ children, activeCount = 0, title = "Filtros", triggerLabel = "Filtros", applyLabel = "Aplicar", clearLabel = "Limpiar", onApply, onClear, testID, }) {
25
+ const [visible, setVisible] = useState(false);
26
+ const theme = useTheme();
27
+ const countLabel = activeCount > 0 ? `, ${activeCount} activos` : "";
28
+ return (_jsxs(_Fragment, { children: [_jsxs(Pressable, { accessibilityLabel: `${triggerLabel}${countLabel}`, accessibilityRole: "button", className: "min-h-11 flex-row items-center justify-center gap-2 self-start rounded-full bg-bg-muted px-4", onPress: () => setVisible(true), style: { minHeight: 44 }, testID: testID ? `${testID}-trigger` : undefined, children: [_jsx(Icon, { color: theme.icon.secondary, name: "options-outline", size: 18 }), _jsx(Typography.Text, { className: "text-text-primary", variant: "buttonSmall", children: triggerLabel }), activeCount > 0 ? (_jsx(Typography.Text, { accessibilityLabel: `${activeCount} filtros activos`, className: "text-text-accent", variant: "caption", children: activeCount })) : null] }), _jsx(Sheet, { dismissOnBackdrop: true, footer: _jsxs(View, { className: "flex-row gap-3", children: [onClear ? (_jsx(View, { className: "flex-1", children: _jsx(Button, { onPress: onClear, title: clearLabel, variant: "secondary" }) })) : null, _jsx(View, { className: "flex-1", children: _jsx(Button, { onPress: () => {
29
+ onApply();
30
+ setVisible(false);
31
+ }, title: applyLabel }) })] }), onClose: () => setVisible(false), showCloseButton: true, testID: testID ? `${testID}-sheet` : undefined, title: title, visible: visible, children: children })] }));
32
+ }
33
+ function History(props) {
34
+ return _jsx(SearchHistory, { ...props });
35
+ }
36
+ function Suggestions(props) {
37
+ return _jsx(SearchSuggestions, { ...props });
38
+ }
39
+ const Search = {
40
+ Root,
41
+ Field,
42
+ Filters,
43
+ FilterSheet,
44
+ History,
45
+ Suggestions,
46
+ };
47
+ export default Search;
@@ -10,7 +10,6 @@ export interface SearchBarProps {
10
10
  onPress?: () => void;
11
11
  className?: string;
12
12
  testID?: string;
13
- accessibleSizing?: boolean;
14
13
  accessibilityLabel?: string;
15
14
  clearAccessibilityLabel?: string;
16
15
  suggestions?: SearchSuggestionsProps;
@@ -19,5 +18,5 @@ export interface SearchBarProps {
19
18
  * **Pill** search bar (rounded-full), 2026 style (merge of Material 3 + iOS 26).
20
19
  * Use `editable={false}` + `onPress` for a trigger that navigates to the search screen.
21
20
  */
22
- declare function SearchBar({ value, onChangeText, placeholder, onSubmit, onClear, autoFocus, editable, onPress, className, testID, accessibleSizing, accessibilityLabel, clearAccessibilityLabel, suggestions, }: SearchBarProps): import("react").JSX.Element;
21
+ declare function SearchBar({ value, onChangeText, placeholder, onSubmit, onClear, autoFocus, editable, onPress, className, testID, accessibilityLabel, clearAccessibilityLabel, suggestions, }: SearchBarProps): import("react").JSX.Element;
23
22
  export default SearchBar;
@@ -12,33 +12,30 @@ import { Keyboard, TextInput, View } from "react-native";
12
12
  * **Pill** search bar (rounded-full), 2026 style (merge of Material 3 + iOS 26).
13
13
  * Use `editable={false}` + `onPress` for a trigger that navigates to the search screen.
14
14
  */
15
- function SearchBar({ value, onChangeText, placeholder = "Buscar", onSubmit, onClear, autoFocus, editable = true, onPress, className, testID, accessibleSizing = false, accessibilityLabel, clearAccessibilityLabel = "Limpiar", suggestions, }) {
15
+ function SearchBar({ value, onChangeText, placeholder = "Buscar", onSubmit, onClear, autoFocus, editable = true, onPress, className, testID, accessibilityLabel, clearAccessibilityLabel = "Limpiar", suggestions, }) {
16
16
  const theme = useTheme();
17
17
  const [focused, setFocused] = useState(false);
18
18
  const body = (_jsxs(View, { className: cn("flex-row items-center gap-2 rounded-full px-4", focused ? "bg-bg-card" : "bg-bg-muted", className), style: {
19
- ...(accessibleSizing ? { minHeight: 48 } : { height: 44 }),
19
+ minHeight: 48,
20
+ minWidth: 48,
20
21
  borderWidth: 1,
21
22
  borderColor: focused ? theme.border.focus : "transparent",
22
- }, children: [_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: focused ? theme.border.focus : theme.icon.tertiary, name: "search", size: 18 }) }), editable ? (_jsx(TextInput, { accessibilityLabel: accessibilityLabel ?? placeholder, autoFocus: autoFocus, className: cn("flex-1 text-text-primary", !accessibleSizing && "text-sm"), onBlur: () => setFocused(false), onChangeText: onChangeText, onFocus: () => setFocused(true), onSubmitEditing: (e) => onSubmit?.(e.nativeEvent.text), placeholder: placeholder, placeholderTextColor: theme.text.tertiary, returnKeyType: "search", selectionColor: theme.primary, style: accessibleSizing
23
- ? {
24
- ...typographyVariants.bodyReadable,
25
- fontFamily: resolveTextFontFamily("bodyReadable"),
26
- minHeight: 48,
27
- paddingVertical: 8,
28
- }
29
- : undefined, testID: testID ? `${testID}-input` : undefined, underlineColorAndroid: "transparent", value: value })) : (_jsx(Typography.Text, { className: cn("flex-1 text-text-tertiary", !accessibleSizing && "text-sm"), numberOfLines: accessibleSizing ? undefined : 1, variant: accessibleSizing ? "bodyReadable" : "body", children: value || placeholder })), editable && value.length > 0 ? (_jsx(Pressable, { accessibilityLabel: clearAccessibilityLabel, hitSlop: 8, onPress: () => {
23
+ }, children: [_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: focused ? theme.border.focus : theme.icon.tertiary, name: "search", size: 18 }) }), editable ? (_jsx(TextInput, { accessibilityLabel: accessibilityLabel ?? placeholder, autoFocus: autoFocus, className: "flex-1 text-text-primary", onBlur: () => setFocused(false), onChangeText: onChangeText, onFocus: () => setFocused(true), onSubmitEditing: (e) => onSubmit?.(e.nativeEvent.text), placeholder: placeholder, placeholderTextColor: theme.text.tertiary, returnKeyType: "search", selectionColor: theme.primary, style: {
24
+ ...typographyVariants.bodyReadable,
25
+ fontFamily: resolveTextFontFamily("bodyReadable"),
26
+ minHeight: 48,
27
+ paddingVertical: 8,
28
+ }, testID: testID ? `${testID}-input` : undefined, underlineColorAndroid: "transparent", value: value })) : (_jsx(Typography.Text, { className: "flex-1 text-text-tertiary", variant: "bodyReadable", children: value || placeholder })), editable && value.length > 0 ? (_jsx(Pressable, { accessibilityLabel: clearAccessibilityLabel, hitSlop: 8, onPress: () => {
30
29
  onChangeText("");
31
30
  onClear?.();
32
- }, style: accessibleSizing
33
- ? {
34
- minHeight: 48,
35
- minWidth: 48,
36
- alignItems: "center",
37
- justifyContent: "center",
38
- }
39
- : undefined, children: _jsx(Icon, { color: theme.icon.tertiary, name: "close-circle", size: 18 }) })) : null] }));
31
+ }, style: {
32
+ minHeight: 48,
33
+ minWidth: 48,
34
+ alignItems: "center",
35
+ justifyContent: "center",
36
+ }, children: _jsx(Icon, { color: theme.icon.tertiary, name: "close-circle", size: 18 }) })) : null] }));
40
37
  if (!editable && onPress) {
41
- return (_jsx(Pressable, { accessibilityLabel: accessibilityLabel ?? placeholder, accessibilityRole: "search", onPress: onPress, testID: testID, children: body }));
38
+ return (_jsx(Pressable, { accessibilityLabel: accessibilityLabel ?? placeholder, accessibilityRole: "search", onPress: onPress, style: { minHeight: 48, minWidth: 48 }, testID: testID, children: body }));
42
39
  }
43
40
  return (_jsxs(View, { testID: testID, children: [body, focused && suggestions ? (_jsx(SearchSuggestions, { ...suggestions, onSelect: (suggestion) => {
44
41
  suggestions.onSelect(suggestion);
@@ -1,4 +1,6 @@
1
+ import { Typography } from "../Typography/index.js";
1
2
  import { ComponentType } from "react";
3
+ import { type StyleProp, type ViewStyle } from "react-native";
2
4
  export interface SegmentedOption<T extends string> {
3
5
  label: string;
4
6
  value: T;
@@ -9,6 +11,9 @@ export interface SegmentedOption<T extends string> {
9
11
  size?: number;
10
12
  color?: string;
11
13
  }>;
14
+ disabled?: boolean;
15
+ accessibilityLabel?: string;
16
+ accessibilityHint?: string;
12
17
  }
13
18
  export interface SegmentedControlProps<T extends string> {
14
19
  options: SegmentedOption<T>[];
@@ -20,11 +25,14 @@ export interface SegmentedControlProps<T extends string> {
20
25
  * - `"left"` — icon inline left of the label (compact, single-line).
21
26
  */
22
27
  iconLayout?: "top" | "left";
23
- /** Increase each option's target to at least 48 points. */
24
- accessibleSizing?: boolean;
28
+ disabled?: boolean;
29
+ accessibilityLabel?: string;
30
+ style?: StyleProp<ViewStyle>;
31
+ optionStyle?: StyleProp<ViewStyle>;
32
+ labelTextProps?: Partial<React.ComponentProps<typeof Typography.Text>>;
25
33
  className?: string;
26
34
  testID?: string;
27
35
  }
28
- declare function SegmentedControl<T extends string>({ options, value, onChange, iconLayout, accessibleSizing, className, testID, }: SegmentedControlProps<T>): import("react").JSX.Element;
36
+ declare function SegmentedControl<T extends string>({ options, value, onChange, iconLayout, disabled, accessibilityLabel, style, optionStyle, labelTextProps, className, testID, }: SegmentedControlProps<T>): import("react").JSX.Element;
29
37
  export default SegmentedControl;
30
38
  export { SegmentedControl };
@@ -4,28 +4,41 @@ import { useTheme } from "../theme/index.js";
4
4
  import { cn } from "../theme/tw.js";
5
5
  import { Typography } from "../Typography/index.js";
6
6
  import { TouchableOpacity, View } from "react-native";
7
- function SegmentedControl({ options, value, onChange, iconLayout = "top", accessibleSizing = false, className, testID, }) {
7
+ function SegmentedControl({ options, value, onChange, iconLayout = "top", disabled = false, accessibilityLabel, style, optionStyle, labelTextProps, className, testID, }) {
8
8
  const theme = useTheme();
9
9
  const hasIcons = options.some((o) => o.icon || o.customIcon);
10
- return (_jsx(View, { accessibilityRole: "tablist", className: cn("flex-row bg-bg-muted rounded-lg p-1 gap-1", className), testID: testID, children: options.map((option) => {
10
+ return (_jsx(View, { accessibilityLabel: accessibilityLabel, accessibilityRole: "tablist", className: cn("flex-row bg-bg-muted rounded-lg p-1 gap-1", className), style: style, testID: testID, children: options.map((option) => {
11
11
  const selected = option.value === value;
12
+ const isDisabled = disabled || !!option.disabled;
12
13
  const iconColor = selected ? theme.text.primary : theme.text.secondary;
13
14
  const hasIcon = !!(option.icon || option.customIcon);
14
15
  const inline = hasIcons && iconLayout === "left";
15
- return (_jsxs(TouchableOpacity, { accessibilityLabel: option.label, accessibilityRole: "tab", accessibilityState: { selected }, activeOpacity: 0.7, onPress: () => onChange(option.value), style: {
16
- flex: 1,
17
- minHeight: accessibleSizing ? 48 : undefined,
18
- flexDirection: inline ? "row" : "column",
19
- alignItems: "center",
20
- justifyContent: "center",
21
- gap: hasIcon ? 4 : 0,
22
- paddingVertical: hasIcons && !inline ? 8 : 8,
23
- paddingHorizontal: 4,
24
- borderRadius: 8,
25
- ...(selected
26
- ? { backgroundColor: theme.bg.card, ...theme.shadow.sm }
27
- : undefined),
28
- }, children: [hasIcon ? (_jsx(Icon, { color: iconColor, customIcon: option.customIcon, name: option.icon, size: 16, type: option.customIcon ? "custom" : "ionicons" })) : null, _jsx(Typography.Text, { numberOfLines: 1, semibold: selected, style: { color: iconColor, fontSize: 12 }, variant: "caption", children: option.label })] }, option.value));
16
+ const baseStyle = {
17
+ flex: 1,
18
+ minHeight: 48,
19
+ minWidth: 48,
20
+ flexDirection: inline ? "row" : "column",
21
+ alignItems: "center",
22
+ justifyContent: "center",
23
+ gap: hasIcon ? 4 : 0,
24
+ paddingVertical: 8,
25
+ paddingHorizontal: 4,
26
+ borderRadius: 8,
27
+ ...(selected ? { backgroundColor: theme.bg.card, ...theme.shadow.sm } : undefined),
28
+ opacity: isDisabled ? 0.5 : 1,
29
+ };
30
+ return (_jsxs(TouchableOpacity, { accessibilityHint: option.accessibilityHint, accessibilityLabel: option.accessibilityLabel ?? option.label, accessibilityRole: "tab", accessibilityState: isDisabled ? { selected, disabled: true } : { selected }, activeOpacity: 0.7, disabled: isDisabled || undefined, hitSlop: 0, onPress: () => {
31
+ if (!isDisabled)
32
+ onChange(option.value);
33
+ }, style: optionStyle ? [baseStyle, optionStyle] : baseStyle, children: [hasIcon ? (_jsx(Icon, { color: iconColor, customIcon: option.customIcon, name: option.icon, size: 16, type: option.customIcon ? "custom" : "ionicons" })) : null, _jsx(Typography.Text, { ...labelTextProps, semibold: labelTextProps?.semibold ?? selected, style: [
34
+ {
35
+ color: iconColor,
36
+ fontSize: 14,
37
+ textAlign: "center",
38
+ flexShrink: 1,
39
+ },
40
+ labelTextProps?.style,
41
+ ], variant: labelTextProps?.variant ?? "caption", children: option.label })] }, option.value));
29
42
  }) }));
30
43
  }
31
44
  export default SegmentedControl;
@@ -12,16 +12,25 @@ import Steps from "./Navigation.Steps.js";
12
12
  import TabIcon from "./Navigation.TabIcon.js";
13
13
  export type { AppBarBackground } from "./Navigation.AppBar.js";
14
14
  export type { MenuSectionProps } from "./Navigation.MenuSection.js";
15
+ export type { SearchFilterSheetProps, SearchFiltersProps, SearchRootProps, } from "./Navigation.Search.js";
15
16
  export type { SearchBarProps } from "./Navigation.SearchBar.js";
16
17
  export type { SearchSuggestion, SearchSuggestionsProps, } from "./Navigation.SearchSuggestions.js";
17
18
  export declare const Navigation: {
18
- AppBar: (({ title, subtitle, onBack, leading, actions, subheader, variant, tone, accentBar, backVariant, border, safeArea, accessibleSizing, topSpacing, background, bottomDivider, className, testID, }: import("./Navigation.AppBar.js").AppBarProps) => import("react").JSX.Element) & {
19
- Action: ({ icon, label, onPress, badge, accessibilityLabel, tone, onDark, bare, accessibleSizing, testID, }: import("./Navigation.AppBar.js").AppBarActionProps) => import("react").JSX.Element;
19
+ AppBar: (({ title, subtitle, onBack, leading, actions, subheader, variant, tone, accentBar, backVariant, border, safeArea, topSpacing, background, bottomDivider, className, testID, }: import("./Navigation.AppBar.js").AppBarProps) => import("react").JSX.Element) & {
20
+ Action: ({ icon, label, onPress, badge, accessibilityLabel, tone, onDark, bare, testID, }: import("./Navigation.AppBar.js").AppBarActionProps) => import("react").JSX.Element;
20
21
  };
21
22
  BottomNav: typeof BottomNav;
22
23
  CollapsingHeader: typeof CollapsingHeader;
23
24
  FloatingBar: typeof FloatingBar;
24
25
  SearchBar: typeof SearchBar;
26
+ Search: {
27
+ Root: ({ children, className, testID }: import("./Navigation.Search.js").SearchRootProps) => import("react").JSX.Element;
28
+ Field: (props: import("./Navigation.SearchBar.js").SearchBarProps) => import("react").JSX.Element;
29
+ Filters: ({ children, className, testID }: import("./Navigation.Search.js").SearchFiltersProps) => import("react").JSX.Element;
30
+ FilterSheet: ({ children, activeCount, title, triggerLabel, applyLabel, clearLabel, onApply, onClear, testID, }: import("./Navigation.Search.js").SearchFilterSheetProps) => import("react").JSX.Element;
31
+ History: (props: import("./Navigation.Search.js").SearchHistoryProps) => import("react").JSX.Element;
32
+ Suggestions: (props: import("./Navigation.SearchSuggestions.js").SearchSuggestionsProps) => import("react").JSX.Element;
33
+ };
25
34
  SearchSuggestions: typeof SearchSuggestions;
26
35
  Header: ({ title, logo, logoSource, backButton, handleBackButton, className, }: import("./Navigation.Header.js").HeaderProps) => import("react").JSX.Element;
27
36
  HeaderToolbar: typeof HeaderToolbar;
@@ -7,6 +7,7 @@ import HeaderToolbar from "./Navigation.HeaderToolbar.js";
7
7
  import Menu from "./Navigation.Menu.js";
8
8
  import { MenuSection } from "./Navigation.MenuSection.js";
9
9
  import RowItem from "./Navigation.RowItem.js";
10
+ import Search from "./Navigation.Search.js";
10
11
  import SearchBar from "./Navigation.SearchBar.js";
11
12
  import SearchSuggestions from "./Navigation.SearchSuggestions.js";
12
13
  import SegmentedControl from "./Navigation.SegmentedControl.js";
@@ -20,6 +21,7 @@ export const Navigation = {
20
21
  CollapsingHeader,
21
22
  FloatingBar,
22
23
  SearchBar,
24
+ Search,
23
25
  SearchSuggestions,
24
26
  Header,
25
27
  HeaderToolbar,