@sincpro/mobile-ui 0.5.0 → 0.7.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 (96) hide show
  1. package/dist/Actions/Actions.Button.d.ts +151 -0
  2. package/dist/Actions/Actions.Button.js +217 -0
  3. package/dist/Actions/Actions.FAB.d.ts +10 -0
  4. package/dist/Actions/Actions.FAB.js +17 -0
  5. package/dist/Actions/Actions.FilterButton.d.ts +10 -0
  6. package/dist/Actions/Actions.FilterButton.js +28 -0
  7. package/dist/Actions/Actions.IconButton.d.ts +67 -0
  8. package/dist/Actions/Actions.IconButton.js +38 -0
  9. package/dist/Actions/index.d.ts +22 -0
  10. package/dist/Actions/index.js +17 -0
  11. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +1 -0
  12. package/dist/Dialog/Confirmation.context.js +1 -6
  13. package/dist/Dialog/Dialog.Sheet.js +6 -4
  14. package/dist/Display/Display.CircleButton.d.ts +15 -0
  15. package/dist/Display/Display.CircleButton.js +39 -0
  16. package/dist/Display/Display.MenuButton.d.ts +51 -8
  17. package/dist/Display/Display.MenuButton.js +79 -13
  18. package/dist/Display/Display.MenuCard.d.ts +15 -0
  19. package/dist/Display/Display.MenuCard.js +24 -0
  20. package/dist/Display/Display.MenuGrid.d.ts +19 -0
  21. package/dist/Display/Display.MenuGrid.js +7 -0
  22. package/dist/Display/Display.SwipeableRow.js +2 -1
  23. package/dist/Display/Display.Tooltip.d.ts +1 -1
  24. package/dist/Display/Display.Tooltip.js +16 -5
  25. package/dist/Display/index.d.ts +9 -0
  26. package/dist/Display/index.js +6 -0
  27. package/dist/Feedback/Feedback.Empty.js +2 -2
  28. package/dist/Feedback/Feedback.Error.js +2 -2
  29. package/dist/Feedback/Feedback.Loading.d.ts +3 -7
  30. package/dist/Feedback/Feedback.Loading.js +2 -8
  31. package/dist/Feedback/Feedback.Progress.js +2 -2
  32. package/dist/Feedback/Feedback.Skeleton.js +2 -2
  33. package/dist/Feedback/Feedback.Spinner.d.ts +3 -0
  34. package/dist/Feedback/Feedback.Spinner.js +52 -2
  35. package/dist/Feedback/Feedback.Splash.js +1 -0
  36. package/dist/Feedback/index.d.ts +0 -2
  37. package/dist/Feedback/index.js +0 -2
  38. package/dist/Form/Form.Button.d.ts +3 -151
  39. package/dist/Form/Form.Button.js +1 -283
  40. package/dist/Form/Form.FAB.d.ts +3 -10
  41. package/dist/Form/Form.FAB.js +1 -17
  42. package/dist/Form/Form.FilterButton.d.ts +3 -12
  43. package/dist/Form/Form.FilterButton.js +1 -34
  44. package/dist/Form/Form.IconButton.d.ts +3 -67
  45. package/dist/Form/Form.IconButton.js +1 -64
  46. package/dist/Form/Form.PasswordInput.js +4 -2
  47. package/dist/Navigation/Navigation.AppBar.d.ts +56 -1
  48. package/dist/Navigation/Navigation.AppBar.js +43 -12
  49. package/dist/Navigation/Navigation.BottomNav.js +2 -1
  50. package/dist/Navigation/Navigation.Header.js +3 -2
  51. package/dist/Navigation/Navigation.Menu.js +6 -3
  52. package/dist/Navigation/Navigation.MenuSection.d.ts +19 -0
  53. package/dist/Navigation/Navigation.MenuSection.js +25 -0
  54. package/dist/Navigation/Navigation.SegmentedControl.d.ts +15 -1
  55. package/dist/Navigation/Navigation.SegmentedControl.js +22 -4
  56. package/dist/Navigation/Navigation.TabIcon.js +1 -2
  57. package/dist/Navigation/Navigation.TabItem.js +2 -2
  58. package/dist/Navigation/Navigation.Tabs.d.ts +8 -3
  59. package/dist/Navigation/Navigation.Tabs.js +19 -6
  60. package/dist/Navigation/index.d.ts +7 -6
  61. package/dist/Navigation/index.js +2 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.js +2 -0
  64. package/dist/layouts/AuthContainer.d.ts +4 -1
  65. package/dist/layouts/AuthContainer.js +7 -5
  66. package/dist/layouts/BottomInset.d.ts +16 -0
  67. package/dist/layouts/BottomInset.js +17 -0
  68. package/dist/layouts/TabNavigatorLayout.js +2 -2
  69. package/dist/primitives/BaseButton.js +3 -3
  70. package/dist/theme/default.js +1 -1
  71. package/dist/views/AppSplashView.d.ts +18 -0
  72. package/dist/views/AppSplashView.js +9 -0
  73. package/dist/views/AuthFormView.d.ts +3 -1
  74. package/dist/views/AuthFormView.js +2 -2
  75. package/dist/views/FormViewV2.d.ts +18 -6
  76. package/dist/views/FormViewV2.js +14 -11
  77. package/dist/views/ListViewV2.d.ts +12 -1
  78. package/dist/views/ListViewV2.js +16 -10
  79. package/dist/widgets/CircleButton.d.ts +3 -19
  80. package/dist/widgets/CircleButton.js +1 -41
  81. package/dist/widgets/JSONViewer.js +7 -7
  82. package/dist/widgets/MenuCard.d.ts +3 -13
  83. package/dist/widgets/MenuCard.js +1 -8
  84. package/dist/widgets/MenuGrid.d.ts +5 -17
  85. package/dist/widgets/MenuGrid.js +1 -6
  86. package/dist/widgets/MenuSection.d.ts +3 -0
  87. package/dist/widgets/MenuSection.js +1 -0
  88. package/dist/widgets/PromoBanner.d.ts +38 -0
  89. package/dist/widgets/PromoBanner.js +61 -0
  90. package/dist/widgets/PromoModal.d.ts +41 -0
  91. package/dist/widgets/PromoModal.js +75 -0
  92. package/dist/widgets/QuickActions.d.ts +26 -0
  93. package/dist/widgets/QuickActions.js +41 -0
  94. package/dist/widgets/index.d.ts +11 -1
  95. package/dist/widgets/index.js +4 -0
  96. package/package.json +2 -1
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { Actions } from "./Actions";
1
2
  export { Dialog } from "./Dialog";
2
3
  export { Display } from "./Display";
3
4
  export { Feedback } from "./Feedback";
@@ -8,6 +9,7 @@ export { Box, Row, Stack } from "./primitives";
8
9
  export { type BrandingConfig, getBrandingLogo, setBranding, } from "./branding";
9
10
  export { cn, tv, type VariantProps } from "./theme/tw";
10
11
  export { fontFamilies, fontSizes, fontWeights, letterSpacing, lineHeights, typographyVariants, } from "./theme/typography";
12
+ export { BottomInsetContext } from "./layouts/BottomInset";
11
13
  export { default as Container } from "./layouts/Container";
12
14
  export { default as GradientContainer } from "./layouts/GradientContainer";
13
15
  export { default as PlainLayout } from "./layouts/PlainLayout";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // Design System - Compound Components
2
+ export { Actions } from "./Actions";
2
3
  export { Dialog } from "./Dialog";
3
4
  export { Display } from "./Display";
4
5
  export { Feedback } from "./Feedback";
@@ -13,6 +14,7 @@ export { getBrandingLogo, setBranding, } from "./branding";
13
14
  export { cn, tv } from "./theme/tw";
14
15
  export { fontFamilies, fontSizes, fontWeights, letterSpacing, lineHeights, typographyVariants, } from "./theme/typography";
15
16
  // Layouts
17
+ export { BottomInsetContext } from "./layouts/BottomInset";
16
18
  export { default as Container } from "./layouts/Container";
17
19
  export { default as GradientContainer } from "./layouts/GradientContainer";
18
20
  export { default as PlainLayout } from "./layouts/PlainLayout";
@@ -1,9 +1,12 @@
1
+ import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
1
2
  import { ReactNode } from "react";
2
3
  interface AuthContainerProps {
3
4
  header: ReactNode;
4
5
  children: ReactNode;
5
6
  onBackPress?: () => void;
6
7
  className?: string;
8
+ /** Gradient + pattern surface for the header area. Defaults to `night-green` preset. */
9
+ background?: AppBarBackground;
7
10
  }
8
- declare function AuthContainer({ header, children, onBackPress, className }: AuthContainerProps): import("react").JSX.Element;
11
+ declare function AuthContainer({ header, children, onBackPress, background, className, }: AuthContainerProps): import("react").JSX.Element;
9
12
  export default AuthContainer;
@@ -1,13 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import { Display } from "../Display";
3
3
  import GradientSurface from "../Display/Display.GradientSurface";
4
- import { theme } from "../theme";
4
+ import { Pattern } from "../Display/Display.Pattern";
5
+ import { useTheme } from "../theme";
5
6
  import { cn } from "../theme/tw";
6
- import { Dimensions, Keyboard, TouchableOpacity, TouchableWithoutFeedback, View, } from "react-native";
7
+ import { Keyboard, TouchableOpacity, TouchableWithoutFeedback, useWindowDimensions, View, } from "react-native";
7
8
  import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
8
9
  import { SafeAreaView } from "react-native-safe-area-context";
9
- function AuthContainer({ header, children, onBackPress, className }) {
10
- const screenHeight = Dimensions.get("window").height;
11
- return (_jsx(GradientSurface, { className: cn("flex-1", className), preset: "night-green", radius: "none", style: { flex: 1 }, children: _jsx(SafeAreaView, { className: "flex-1", edges: ["top"], children: _jsxs(View, { className: "flex-1", children: [_jsx(TouchableWithoutFeedback, { onPress: Keyboard.dismiss, children: _jsxs(View, { className: "justify-center items-center relative", style: { height: screenHeight * 0.3 }, children: [onBackPress && (_jsx(TouchableOpacity, { className: "absolute top-4 left-4 p-2 z-[1]", onPress: onBackPress, children: _jsx(Display.Icon, { color: theme.text.inverse, name: "arrow-back", size: 24, type: "ionicons" }) })), header] }) }), _jsx(View, { className: "flex-1 bg-bg-card rounded-t-3xl shadow-lg", style: { minHeight: screenHeight * 0.7 }, children: _jsx(KeyboardAwareScrollView, { bottomOffset: 50, bounces: false, className: "flex-1", contentContainerClassName: "flex-grow pb-10", keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false, children: _jsx(View, { className: "p-4", children: children }) }) })] }) }) }));
10
+ function AuthContainer({ header, children, onBackPress, background, className, }) {
11
+ const { height: screenHeight } = useWindowDimensions();
12
+ const theme = useTheme();
13
+ return (_jsxs(GradientSurface, { className: cn("flex-1", className), colors: background?.colors, end: background?.end ?? (background?.colors ? { x: 0, y: 1 } : undefined), preset: background?.surface ?? "night-green", radius: "none", start: background?.start ?? (background?.colors ? { x: 0, y: 0 } : undefined), style: { flex: 1 }, children: [background?.pattern ? (_jsx(Pattern, { color: background.patternColor ?? "#FFFFFF", kind: background.pattern, opacity: background.patternOpacity ?? 0.18 })) : null, _jsx(SafeAreaView, { className: "flex-1", edges: ["top"], children: _jsxs(View, { className: "flex-1", children: [_jsx(TouchableWithoutFeedback, { onPress: Keyboard.dismiss, children: _jsxs(View, { className: "justify-center items-center relative", style: { height: screenHeight * 0.3 }, children: [onBackPress && (_jsx(TouchableOpacity, { className: "absolute top-4 left-4 p-2 z-[1]", onPress: onBackPress, children: _jsx(Display.Icon, { color: background?.textColor ?? theme.text.inverse, name: "arrow-back", size: 24, type: "ionicons" }) })), header] }) }), _jsx(View, { className: "flex-1 bg-bg-card rounded-t-3xl shadow-lg", style: { minHeight: screenHeight * 0.7 }, children: _jsx(KeyboardAwareScrollView, { bottomOffset: 50, bounces: false, className: "flex-1", contentContainerClassName: "flex-grow pb-10", keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false, children: _jsx(View, { className: "p-4", children: children }) }) })] }) })] }));
12
14
  }
13
15
  export default AuthContainer;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Extra bottom inset for scroll views, provided by the host app when a floating
3
+ * element (e.g. position:absolute tab bar) sits above the native safe area.
4
+ *
5
+ * FormViewV2.Content and ListViewV2.Content add this value as extra
6
+ * `contentContainerStyle.paddingBottom` so content can scroll fully above the bar.
7
+ *
8
+ * Usage in the host app:
9
+ * <BottomInsetContext.Provider value={72}>
10
+ * <Tabs.Navigator ...>
11
+ *
12
+ * Default: 0 — no extra inset (backwards compatible).
13
+ * The native safe-area inset is already handled by Container (SafeAreaView edges=["bottom"]).
14
+ */
15
+ export declare const BottomInsetContext: import("react").Context<number>;
16
+ export declare const useBottomInset: () => number;
@@ -0,0 +1,17 @@
1
+ import { createContext, useContext } from "react";
2
+ /**
3
+ * Extra bottom inset for scroll views, provided by the host app when a floating
4
+ * element (e.g. position:absolute tab bar) sits above the native safe area.
5
+ *
6
+ * FormViewV2.Content and ListViewV2.Content add this value as extra
7
+ * `contentContainerStyle.paddingBottom` so content can scroll fully above the bar.
8
+ *
9
+ * Usage in the host app:
10
+ * <BottomInsetContext.Provider value={72}>
11
+ * <Tabs.Navigator ...>
12
+ *
13
+ * Default: 0 — no extra inset (backwards compatible).
14
+ * The native safe-area inset is already handled by Container (SafeAreaView edges=["bottom"]).
15
+ */
16
+ export const BottomInsetContext = createContext(0);
17
+ export const useBottomInset = () => useContext(BottomInsetContext);
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
2
- import { Display } from "../Display";
2
+ import DisplayIcon from "../Display/Display.Icon";
3
3
  import Container from "../layouts/Container";
4
4
  import { Typography } from "../Typography";
5
5
  import { Children, isValidElement } from "react";
6
6
  import { TouchableOpacity, View } from "react-native";
7
7
  import { useSafeAreaInsets } from "react-native-safe-area-context";
8
8
  function TabIcon({ focused, Icon }) {
9
- return (_jsxs(View, { className: "justify-center items-center w-10 h-10", children: [focused && _jsx(View, { className: "absolute w-10 h-10 rounded-full bg-primary" }), _jsx(Display.Icon, { color: focused ? "#FFFFFF" : "#6B7280", customIcon: Icon, size: 24, type: "custom" })] }));
9
+ return (_jsxs(View, { className: "justify-center items-center w-10 h-10", children: [focused && _jsx(View, { className: "absolute w-10 h-10 rounded-full bg-primary" }), _jsx(DisplayIcon, { color: focused ? "#FFFFFF" : "#6B7280", customIcon: Icon, size: 24, type: "custom" })] }));
10
10
  }
11
11
  function TabNavigator({ items, currentPath, onTabPress, }) {
12
12
  const insets = useSafeAreaInsets();
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { Display } from "../Display";
2
+ import Icon from "../Display/Display.Icon";
3
3
  import { theme } from "../theme";
4
4
  import { tv } from "../theme/tw";
5
5
  import { Typography } from "../Typography";
@@ -222,9 +222,9 @@ export const BaseButtonIcon = ({ icon, onPress, variant = "primary", size = "med
222
222
  return (_jsx(LinearGradient, { colors: gradientColors, end: { x: 1, y: 1 }, start: { x: 0, y: 0 }, style: {
223
223
  borderRadius: 8,
224
224
  opacity: disabled ? 0.7 : 1,
225
- }, children: _jsx(TouchableOpacity, { activeOpacity: 0.8, disabled: disabled || loading, onPress: onPress, children: _jsx(View, { className: buttonIcon({ size, disabled }), style: { backgroundColor: "transparent" }, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Display.Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }) }) }));
225
+ }, children: _jsx(TouchableOpacity, { activeOpacity: 0.8, disabled: disabled || loading, onPress: onPress, children: _jsx(View, { className: buttonIcon({ size, disabled }), style: { backgroundColor: "transparent" }, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }) }) }));
226
226
  }
227
- return (_jsx(TouchableOpacity, { activeOpacity: 0.8, className: buttonIcon({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Display.Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }));
227
+ return (_jsx(TouchableOpacity, { activeOpacity: 0.8, className: buttonIcon({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }));
228
228
  };
229
229
  // ============================================================================
230
230
  // COMPOUND COMPONENT
@@ -4,7 +4,7 @@ export const DEFAULT_THEME = {
4
4
  secondary: "#64748B",
5
5
  accent: "#F59E0B",
6
6
  bg: {
7
- page: "#F9FAFB",
7
+ page: "#F0F1F3",
8
8
  card: "#FFFFFF",
9
9
  popover: "#FFFFFF",
10
10
  muted: "#F3F4F6",
@@ -0,0 +1,18 @@
1
+ import { type SpinnerVariant } from "../Feedback/Feedback.Spinner";
2
+ import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
3
+ import type { ReactNode } from "react";
4
+ import { type ImageSourcePropType, type ViewStyle } from "react-native";
5
+ export interface AppSplashViewProps {
6
+ /** Gradient + pattern background. Defaults to `night-green` preset. */
7
+ background?: AppBarBackground;
8
+ /** App logo shown centered above the loading indicator. */
9
+ logo?: ImageSourcePropType;
10
+ /** Built-in loading animation variant. Ignored when `loader` is provided. Defaults to `"dots"`. */
11
+ loadingVariant?: SpinnerVariant;
12
+ /** Fully custom loading element. Takes precedence over `loadingVariant`. */
13
+ loader?: ReactNode;
14
+ /** Override the outer container style. */
15
+ style?: ViewStyle;
16
+ }
17
+ export declare function AppSplashView({ background, logo, loadingVariant, loader, style, }: AppSplashViewProps): import("react").JSX.Element;
18
+ export default AppSplashView;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
+ import GradientSurface from "../Display/Display.GradientSurface";
3
+ import { Pattern } from "../Display/Display.Pattern";
4
+ import Spinner from "../Feedback/Feedback.Spinner";
5
+ import { Image, View } from "react-native";
6
+ export function AppSplashView({ background, logo, loadingVariant = "dots", loader, style, }) {
7
+ return (_jsxs(GradientSurface, { className: "flex-1", colors: background?.colors, end: background?.end, padding: "none", preset: background?.surface ?? "night-green", radius: "none", start: background?.start, style: { flex: 1, ...style }, children: [background?.pattern ? (_jsx(Pattern, { color: background.patternColor ?? "#FFFFFF", kind: background.pattern, opacity: background.patternOpacity ?? 0.18 })) : null, _jsxs(View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: [logo ? (_jsx(Image, { source: logo, style: { width: 140, height: 140, resizeMode: "contain", marginBottom: 40 } })) : null, loader ?? (_jsx(Spinner, { color: "rgba(255,255,255,0.9)", size: "large", variant: loadingVariant }))] })] }));
8
+ }
9
+ export default AppSplashView;
@@ -1,3 +1,4 @@
1
+ import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
1
2
  import { ReactNode } from "react";
2
3
  import { type ImageSourcePropType } from "react-native";
3
4
  interface AuthFormProps {
@@ -6,6 +7,7 @@ interface AuthFormProps {
6
7
  logoSource?: ImageSourcePropType;
7
8
  onBack?: () => void;
8
9
  FormNode: ReactNode;
10
+ background?: AppBarBackground;
9
11
  }
10
- declare function AuthFormView({ title, description, logoSource, onBack, FormNode }: AuthFormProps): import("react").JSX.Element;
12
+ declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, }: AuthFormProps): import("react").JSX.Element;
11
13
  export default AuthFormView;
@@ -5,7 +5,7 @@ import { Typography } from "../Typography";
5
5
  import { View } from "react-native";
6
6
  const Logo = Display.Logo;
7
7
  const Text = Typography.Text;
8
- function AuthFormView({ title, description, logoSource, onBack, FormNode }) {
8
+ function AuthFormView({ title, description, logoSource, onBack, FormNode, background, }) {
9
9
  const renderTitle = () => {
10
10
  if (!title)
11
11
  return null;
@@ -16,6 +16,6 @@ function AuthFormView({ title, description, logoSource, onBack, FormNode }) {
16
16
  return null;
17
17
  return _jsx(Text, { variant: "body", children: description });
18
18
  };
19
- return (_jsx(AuthContainer, { header: _jsx(Logo, { size: "large", source: logoSource }), onBackPress: onBack, children: _jsxs(View, { className: "m-4", children: [_jsxs(View, { className: "mb-5", children: [renderTitle(), renderDescription()] }), FormNode] }) }));
19
+ return (_jsx(AuthContainer, { background: background, header: _jsx(Logo, { size: "large", source: logoSource }), onBackPress: onBack, children: _jsxs(View, { className: "m-4", children: [_jsxs(View, { className: "mb-5", children: [renderTitle(), renderDescription()] }), FormNode] }) }));
20
20
  }
21
21
  export default AuthFormView;
@@ -1,3 +1,4 @@
1
+ import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
1
2
  import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl";
2
3
  import React, { ReactNode } from "react";
3
4
  import { type ImageSourcePropType } from "react-native";
@@ -26,10 +27,20 @@ interface HeaderProps {
26
27
  actions?: ReactNode;
27
28
  /** Subheader content rendered below the title bar (SearchBar, SegmentedControl, etc.). */
28
29
  children?: ReactNode;
30
+ /**
31
+ * [large variant] Extra top padding (px) above the title.
32
+ * Use ~16 on root screens (no back button) for more breathing room.
33
+ */
34
+ topSpacing?: number;
35
+ /**
36
+ * Gradient + texture surface. Automatically activates white text/icons — no need to set `tone="dark"`.
37
+ * See `AppBarBackground` for the full shape.
38
+ */
39
+ background?: AppBarBackground;
29
40
  /** @deprecated Pass `variant` prop instead. */
30
41
  logoSource?: ImageSourcePropType;
31
42
  }
32
- declare function Header({ variant, tone, actions, children }: HeaderProps): React.JSX.Element;
43
+ declare function Header({ variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
33
44
  declare function HeaderActions({ children }: {
34
45
  children: ReactNode;
35
46
  }): React.JSX.Element;
@@ -94,14 +105,15 @@ declare function Group({ children, withMargin, elevation, padding, variant, titl
94
105
  title?: string;
95
106
  }): React.JSX.Element;
96
107
  /**
97
- * Settings-style section: SectionHeader label + Card(padding="none") wrapper.
98
- * Use inside FormViewV2.Content for preference/config screens where rows are
99
- * Display.ListItem or Display.MenuButton (not form inputs).
108
+ * Settings-style section: titled card grouping Display.MenuButton rows.
109
+ * Delegates to the standalone `MenuSection` widget — single source of truth
110
+ * for this pattern whether used inside a FormView or on a standalone screen.
100
111
  */
101
- declare function Section({ title, children, withMargin, }: {
112
+ declare function Section({ title, footer, children, }: {
102
113
  title?: string;
114
+ /** Hint text rendered below the card (e.g. "Toca para cambiar la configuración"). */
115
+ footer?: string;
103
116
  children: ReactNode;
104
- withMargin?: boolean;
105
117
  }): React.JSX.Element;
106
118
  declare function Footer({ children }: {
107
119
  children: ReactNode;
@@ -1,26 +1,26 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import { Display } from "../Display";
3
3
  import { ErrorBoundary, Feedback } from "../Feedback";
4
+ import { useBottomInset } from "../layouts/BottomInset";
4
5
  import Container from "../layouts/Container";
5
6
  import { AppBar } from "../Navigation/Navigation.AppBar";
7
+ import { MenuSection } from "../Navigation/Navigation.MenuSection";
6
8
  import { SegmentedControl, } from "../Navigation/Navigation.SegmentedControl";
7
- import { theme } from "../theme";
9
+ import { useTheme } from "../theme";
8
10
  import { FormViewProvider, useFormView } from "../views/FormViewV2.context";
9
11
  import React from "react";
10
12
  import { RefreshControl, View } from "react-native";
11
13
  import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
12
14
  function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRetry, onBack, withContainer = true, children, }) {
13
15
  const Wrapper = withContainer ? Container : View;
14
- // The header (AppBar) / parent layout owns the TOP safe-area; the inner Container only
15
- // reserves the remaining edges to avoid stacking the top inset (double padding).
16
16
  const wrapperProps = withContainer
17
17
  ? { edges: ["bottom", "left", "right"] }
18
18
  : { style: { flex: 1 } };
19
19
  return (_jsx(FormViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, isEmpty: isEmpty, isLoading: isLoading, item: item, name: name, onBack: onBack, onRefresh: onRefresh, onRetry: onRetry, readonly: readonly, children: _jsx(ErrorBoundary, { children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }));
20
20
  }
21
- function Header({ variant = "default", tone, actions, children }) {
21
+ function Header({ variant = "default", tone, actions, children, topSpacing, background, }) {
22
22
  const { name, description, onBack } = useFormView();
23
- return (_jsx(AppBar, { actions: actions, onBack: onBack, safeArea: false, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, variant: variant }));
23
+ return (_jsx(AppBar, { actions: actions, background: background, onBack: onBack, safeArea: false, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
24
24
  }
25
25
  function HeaderActions({ children }) {
26
26
  return (_jsx(View, { className: "flex-row px-4 my-2 items-center justify-between flex-1 gap-2", children: children }));
@@ -38,6 +38,8 @@ function HeaderSegmented(props) {
38
38
  }
39
39
  function Content({ withMargin = false, children, }) {
40
40
  const { name, isLoading, isEmpty, hasError, errorMessage, item, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useFormView();
41
+ const bottomInset = useBottomInset();
42
+ const theme = useTheme();
41
43
  const handleRefresh = React.useCallback(async () => {
42
44
  setShowRefreshing(true);
43
45
  if (onRefresh) {
@@ -50,7 +52,8 @@ function Content({ withMargin = false, children, }) {
50
52
  flexGrow: 1,
51
53
  justifyContent: isCenteredContent ? "center" : "flex-start",
52
54
  alignItems: isCenteredContent ? "center" : "stretch",
53
- }, keyboardShouldPersistTaps: "handled", refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), style: { flex: 1 }, children: isCenteredContent ? (_jsx(_Fragment, { children: isLoading ? (_jsx(Feedback.Loading, { message: `Cargando ${name}...` })) : hasError ? (_jsx(Feedback.Error, { message: errorMessage, onBack: onBack, onRetry: onRetry })) : (_jsx(Feedback.Empty, {})) })) : (_jsx(View, { style: withMargin ? { paddingHorizontal: 16 } : undefined, children: children })) }));
55
+ paddingBottom: bottomInset,
56
+ }, keyboardShouldPersistTaps: "handled", refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), style: { flex: 1, backgroundColor: theme.bg.page }, children: isCenteredContent ? (_jsx(_Fragment, { children: isLoading ? (_jsx(Feedback.Loading, { message: `Cargando ${name}...` })) : hasError ? (_jsx(Feedback.Error, { message: errorMessage, onBack: onBack, onRetry: onRetry })) : (_jsx(Feedback.Empty, {})) })) : (_jsx(View, { style: withMargin ? { paddingHorizontal: 16 } : undefined, children: children })) }));
54
57
  }
55
58
  /**
56
59
  * @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
@@ -91,12 +94,12 @@ function Group({ children, withMargin = true, elevation = "none", padding = "md"
91
94
  return (_jsxs(_Fragment, { children: [header, body] }));
92
95
  }
93
96
  /**
94
- * Settings-style section: SectionHeader label + Card(padding="none") wrapper.
95
- * Use inside FormViewV2.Content for preference/config screens where rows are
96
- * Display.ListItem or Display.MenuButton (not form inputs).
97
+ * Settings-style section: titled card grouping Display.MenuButton rows.
98
+ * Delegates to the standalone `MenuSection` widget — single source of truth
99
+ * for this pattern whether used inside a FormView or on a standalone screen.
97
100
  */
98
- function Section({ title, children, withMargin = true, }) {
99
- return (_jsxs(View, { style: withMargin ? { marginHorizontal: 16 } : undefined, children: [title ? _jsx(Display.SectionHeader, { title: title }) : null, _jsx(Display.Card, { padding: "none", children: children })] }));
101
+ function Section({ title, footer, children, }) {
102
+ return (_jsx(MenuSection, { footer: footer, title: title, children: children }));
100
103
  }
101
104
  function Footer({ children }) {
102
105
  const { isEmpty, isLoading } = useFormView();
@@ -1,4 +1,5 @@
1
1
  import { type SearchHistoryProps } from "../Display/Display.SearchHistory";
2
+ import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
2
3
  import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl";
3
4
  import React, { ReactNode } from "react";
4
5
  import { StyleProp, ViewStyle } from "react-native";
@@ -34,8 +35,18 @@ interface HeaderProps {
34
35
  actions?: ReactNode;
35
36
  /** Subheader content rendered below the title bar (SearchBar, SegmentedControl, etc.). */
36
37
  children?: ReactNode;
38
+ /**
39
+ * Gradient + texture surface. Automatically activates white text/icons.
40
+ * See `AppBarBackground` for the full shape.
41
+ */
42
+ background?: AppBarBackground;
43
+ /**
44
+ * [large variant] Extra top padding (px) above the large title.
45
+ * Use ~16 on root screens (no back button) for breathing room.
46
+ */
47
+ topSpacing?: number;
37
48
  }
38
- declare function Header({ variant, tone, actions, children }: HeaderProps): React.JSX.Element;
49
+ declare function Header({ variant, tone, actions, children, background, topSpacing, }: HeaderProps): React.JSX.Element;
39
50
  declare function HeaderSearch({ searchQuery }: {
40
51
  searchQuery?: string;
41
52
  }): React.JSX.Element | null;
@@ -2,10 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/js
2
2
  import { Display } from "../Display";
3
3
  import SearchHistory from "../Display/Display.SearchHistory";
4
4
  import { ErrorBoundary, Feedback } from "../Feedback";
5
+ import { useBottomInset } from "../layouts/BottomInset";
5
6
  import Container from "../layouts/Container";
6
7
  import { Navigation } from "../Navigation";
7
8
  import { SegmentedControl, } from "../Navigation/Navigation.SegmentedControl";
8
- import { theme } from "../theme";
9
+ import { useTheme } from "../theme";
9
10
  import { cn, tv } from "../theme/tw";
10
11
  import { Typography } from "../Typography";
11
12
  import { ListViewProvider, useListView } from "../views/ListViewV2.context";
@@ -14,17 +15,15 @@ import { useCallback, useEffect, useMemo, useState } from "react";
14
15
  import { FlatList, RefreshControl, ScrollView, TouchableOpacity, View, } from "react-native";
15
16
  function ListViewRoot({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, backgroundColor, withContainer = true, children, }) {
16
17
  const Wrapper = withContainer ? Container : View;
17
- // The header (AppBar) / parent layout owns the TOP safe-area; the Container only reserves
18
- // the remaining edges to avoid stacking the top inset (double padding).
19
18
  const wrapperProps = withContainer
20
19
  ? { edges: ["bottom", "left", "right"] }
21
20
  : { className: "flex-1 bg-bg-page" };
22
21
  // Propagate contentWithMargin to ContentList via context
23
22
  return (_jsx(ListViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, isLoading: isLoading, items: items, loadingMore: loadingMore, name: name, onBack: onBack, onDeleteItem: onDeleteItem, onLoadMore: onLoadMore, onPressItem: onPressItem, onRefresh: onRefresh, onRetry: onRetry, onSearch: onSearch, readonly: readonly, children: _jsx(ErrorBoundary, { children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }));
24
23
  }
25
- function Header({ variant = "large", tone, actions, children }) {
24
+ function Header({ variant = "large", tone, actions, children, background, topSpacing, }) {
26
25
  const { name, description, onBack } = useListView();
27
- return (_jsx(Navigation.AppBar, { actions: actions, onBack: onBack, safeArea: false, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, variant: variant }));
26
+ return (_jsx(Navigation.AppBar, { actions: actions, background: background, onBack: onBack, safeArea: false, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
28
27
  }
29
28
  function HeaderSearch({ searchQuery = "" }) {
30
29
  const { name, onSearch } = useListView();
@@ -40,7 +39,7 @@ function HeaderSearch({ searchQuery = "" }) {
40
39
  if (onSearch && debouncedQuery !== undefined) {
41
40
  onSearch(debouncedQuery);
42
41
  }
43
- }, [debouncedQuery]);
42
+ }, [debouncedQuery, onSearch]);
44
43
  if (!onSearch) {
45
44
  return null;
46
45
  }
@@ -98,6 +97,8 @@ function FilterChips({ children }) {
98
97
  }
99
98
  function Content({ children, withMargin = true, }) {
100
99
  const { name, items, isLoading, hasError, errorMessage, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useListView();
100
+ const bottomInset = useBottomInset();
101
+ const theme = useTheme();
101
102
  const handleRefresh = useCallback(async () => {
102
103
  setShowRefreshing(true);
103
104
  if (onRefresh) {
@@ -113,19 +114,23 @@ function Content({ children, withMargin = true, }) {
113
114
  flexGrow: 1,
114
115
  justifyContent: "center",
115
116
  alignItems: "center",
116
- }, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), children: _jsx(Feedback.Error, { message: errorMessage, onBack: onBack, onRetry: onRetry }) }));
117
+ paddingBottom: bottomInset,
118
+ }, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), style: { backgroundColor: theme.bg.page }, children: _jsx(Feedback.Error, { message: errorMessage, onBack: onBack, onRetry: onRetry }) }));
117
119
  }
118
120
  if (items.length === 0) {
119
121
  return (_jsx(ScrollView, { contentContainerStyle: {
120
122
  flexGrow: 1,
121
123
  justifyContent: "center",
122
124
  alignItems: "center",
123
- }, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), children: _jsx(Feedback.Empty, {}) }));
125
+ paddingBottom: bottomInset,
126
+ }, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), style: { backgroundColor: theme.bg.page }, children: _jsx(Feedback.Empty, {}) }));
124
127
  }
125
- return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(Display.CountRecords, { count: items.length || 0, name: name }), _jsx(ContentList, { contentWithMargin: withMargin, children: children })] }));
128
+ return (_jsxs(View, { style: { flex: 1, backgroundColor: theme.bg.page }, children: [_jsx(Display.CountRecords, { count: items.length || 0, name: name }), _jsx(ContentList, { contentWithMargin: withMargin, children: children })] }));
126
129
  }
127
130
  function ContentList({ children, contentWithMargin, }) {
128
131
  const { items, onRefresh, showRefreshing, setShowRefreshing, onPressItem, onDeleteItem, readonly, onLoadMore, loadingMore, } = useListView();
132
+ const bottomInset = useBottomInset();
133
+ const theme = useTheme();
129
134
  const rowItems = useMemo(() => toRowItems(items), [items]);
130
135
  const handleRefresh = useCallback(async () => {
131
136
  setShowRefreshing(true);
@@ -147,7 +152,8 @@ function ContentList({ children, contentWithMargin, }) {
147
152
  flex: 1,
148
153
  marginTop: 2,
149
154
  paddingHorizontal: contentWithMargin ? 4 : 0,
150
- }, children: _jsx(FlatList, { data: rowItems, keyExtractor: (item) => String(item.index), ListFooterComponent: loadingMore ? (_jsx(View, { className: "py-4", children: _jsx(Feedback.Spinner, { size: "small" }) })) : null, onEndReached: onLoadMore ? () => onLoadMore() : undefined, onEndReachedThreshold: 0.5, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), renderItem: renderItem, scrollEnabled: true, showsVerticalScrollIndicator: false }) }));
155
+ backgroundColor: theme.bg.page,
156
+ }, children: _jsx(FlatList, { contentContainerStyle: bottomInset ? { paddingBottom: bottomInset } : undefined, data: rowItems, keyExtractor: (item) => String(item.index), ListFooterComponent: loadingMore ? (_jsx(View, { className: "py-4", children: _jsx(Feedback.Spinner, { size: "small" }) })) : null, onEndReached: onLoadMore ? () => onLoadMore() : undefined, onEndReachedThreshold: 0.5, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), renderItem: renderItem, scrollEnabled: true, showsVerticalScrollIndicator: false }) }));
151
157
  }
152
158
  function Footer({ children }) {
153
159
  const { items } = useListView();
@@ -1,19 +1,3 @@
1
- import { IconType } from "../utils/icon";
2
- import React, { ComponentType } from "react";
3
- interface CircleButtonProps {
4
- iconType?: IconType;
5
- size?: "sm" | "md" | "lg";
6
- color?: string;
7
- variant?: "primary" | "secondary" | "accent" | "success" | "danger" | "warning";
8
- className?: string;
9
- customIcon?: ComponentType<{
10
- size?: number;
11
- color?: string;
12
- }>;
13
- }
14
- /**
15
- * @deprecated Duplicates the icon-button. Use `Form.IconButton` (general) or
16
- * `Navigation.AppBar.Action` (headers). Kept for backwards compatibility.
17
- */
18
- export declare function CircleButton({ iconType, size, color, variant, className, customIcon, }: CircleButtonProps): React.JSX.Element;
19
- export {};
1
+ /** @deprecated Import from `@sincpro/mobile-ui/Display` → `Display.CircleButton` instead. */
2
+ export type { CircleButtonProps } from "../Display/Display.CircleButton";
3
+ export { CircleButton } from "../Display/Display.CircleButton";
@@ -1,41 +1 @@
1
- import { jsx as _jsx } from "nativewind/jsx-runtime";
2
- import { Display } from "../Display";
3
- import { theme } from "../theme";
4
- import { cn } from "../theme/tw";
5
- import { View } from "react-native";
6
- import { tv } from "tailwind-variants";
7
- const circleButtonVariants = tv({
8
- base: "rounded-full items-center justify-center shadow-sm",
9
- variants: {
10
- size: {
11
- sm: "w-12 h-12", // 48px
12
- md: "w-15 h-15", // 60px
13
- lg: "w-20 h-20", // 80px
14
- },
15
- variant: {
16
- primary: "bg-primary",
17
- secondary: "bg-secondary",
18
- accent: "bg-accent",
19
- success: "bg-success",
20
- danger: "bg-danger",
21
- warning: "bg-warning",
22
- },
23
- },
24
- defaultVariants: {
25
- size: "md",
26
- variant: "accent",
27
- },
28
- });
29
- const SIZE_MAP = {
30
- sm: { container: 48, icon: 24 },
31
- md: { container: 60, icon: 30 },
32
- lg: { container: 80, icon: 40 },
33
- };
34
- /**
35
- * @deprecated Duplicates the icon-button. Use `Form.IconButton` (general) or
36
- * `Navigation.AppBar.Action` (headers). Kept for backwards compatibility.
37
- */
38
- export function CircleButton({ iconType = "ionicons", size = "md", color = theme.text.inverse, variant = "accent", className, customIcon, }) {
39
- const iconSize = SIZE_MAP[size].icon;
40
- return (_jsx(View, { className: cn(circleButtonVariants({ size, variant }), className), children: _jsx(Display.Icon, { color: color, customIcon: customIcon, size: iconSize, type: iconType }) }));
41
- }
1
+ export { CircleButton } from "../Display/Display.CircleButton";
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
2
  import { ThemedActivityIndicator } from "../theme";
3
3
  import { fontFamilies } from "../theme/typography";
4
- import { useMemo, useState } from "react";
4
+ import { useEffect, useMemo, useState } from "react";
5
5
  import { Text, View } from "react-native";
6
6
  const parseDeepJson = (value) => {
7
7
  if (typeof value === "string") {
@@ -116,19 +116,19 @@ const JsonPreview = ({ selectedJson }) => {
116
116
  }, [selectedJson]);
117
117
  // Guard 4: colorize with processing control
118
118
  const coloredLines = useMemo(() => {
119
- setIsProcessing(true);
120
119
  try {
121
120
  return parseJsonToColoredLines(formattedJson);
122
121
  }
123
122
  catch (error) {
124
123
  console.warn("[JSONViewer] Error coloring JSON:", error);
125
- // Fallback a texto plano si falla el coloreado
126
124
  return [{ text: formattedJson, color: "#333333" }];
127
125
  }
128
- finally {
129
- // Small delay so the loading state is visible if needed
130
- setTimeout(() => setIsProcessing(false), 50);
131
- }
126
+ }, [formattedJson]);
127
+ useEffect(() => {
128
+ // eslint-disable-next-line react-hooks/set-state-in-effect
129
+ setIsProcessing(true);
130
+ const t = setTimeout(() => setIsProcessing(false), 50);
131
+ return () => clearTimeout(t);
132
132
  }, [formattedJson]);
133
133
  // Guard 5: show loading while processing large JSON
134
134
  if (isProcessing && formattedJson.length > 50_000) {
@@ -1,13 +1,3 @@
1
- import { IconType } from "../utils/icon";
2
- import { ComponentType } from "react";
3
- interface MenuCardProps {
4
- title: string;
5
- iconType?: IconType;
6
- onPress?: () => void;
7
- customIcon?: ComponentType<{
8
- size?: number;
9
- color?: string;
10
- }>;
11
- }
12
- export declare function MenuCard({ title, iconType, onPress, customIcon, }: MenuCardProps): import("react").JSX.Element;
13
- export {};
1
+ /** @deprecated Import from `@sincpro/mobile-ui/Display` → `Display.MenuCard` instead. */
2
+ export type { MenuCardProps } from "../Display/Display.MenuCard";
3
+ export { MenuCard } from "../Display/Display.MenuCard";
@@ -1,8 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
2
- import { theme } from "../theme/useTheme";
3
- import { Typography } from "../Typography";
4
- import { CircleButton } from "../widgets/CircleButton";
5
- import { TouchableOpacity, View } from "react-native";
6
- export function MenuCard({ title, iconType = "ionicons", onPress, customIcon, }) {
7
- return (_jsx(TouchableOpacity, { activeOpacity: 0.7, className: "bg-bg-card rounded-2xl p-4 m-2 w-[45%] shadow-sm", onPress: onPress, style: theme.shadow["sm"], children: _jsxs(View, { className: "items-center justify-center", children: [_jsx(CircleButton, { customIcon: customIcon, iconType: iconType }), _jsx(Typography.Text, { className: "text-text-primary mt-3 text-center", variant: "label", children: title })] }) }));
8
- }
1
+ export { MenuCard } from "../Display/Display.MenuCard";
@@ -1,17 +1,5 @@
1
- import { IconType } from "../utils/icon";
2
- import React, { ComponentType } from "react";
3
- export interface MenuItem {
4
- id: string;
5
- title: string;
6
- iconType?: IconType;
7
- onPress?: () => void;
8
- customIcon?: ComponentType<{
9
- size?: number;
10
- color?: string;
11
- }>;
12
- }
13
- interface MenuGridProps {
14
- readonly items: MenuItem[];
15
- }
16
- export declare const MenuGrid: React.FC<MenuGridProps>;
17
- export {};
1
+ /** @deprecated Import from `@sincpro/mobile-ui/Display` → `Display.MenuGrid` instead. */
2
+ export type { MenuGridItem, MenuGridProps, } from "../Display/Display.MenuGrid";
3
+ export { MenuGrid } from "../Display/Display.MenuGrid";
4
+ /** @deprecated Renamed to `MenuGridItem`. */
5
+ export type { MenuGridItem as MenuItem } from "../Display/Display.MenuGrid";
@@ -1,6 +1 @@
1
- import { jsx as _jsx } from "nativewind/jsx-runtime";
2
- import { MenuCard } from "../widgets/MenuCard";
3
- import { View } from "react-native";
4
- export const MenuGrid = ({ items }) => {
5
- return (_jsx(View, { className: "flex-row flex-wrap justify-start p-2 bg-bg-page", children: items.map((item) => (_jsx(MenuCard, { customIcon: item.customIcon, iconType: item.iconType, onPress: item.onPress, title: item.title }, item.id))) }));
6
- };
1
+ export { MenuGrid } from "../Display/Display.MenuGrid";
@@ -0,0 +1,3 @@
1
+ /** @deprecated Import from `@sincpro/mobile-ui/Navigation` → `Navigation.MenuSection` instead. */
2
+ export type { MenuSectionProps } from "../Navigation/Navigation.MenuSection";
3
+ export { MenuSection } from "../Navigation/Navigation.MenuSection";