@sincpro/mobile-ui 0.4.1 → 0.6.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.
- package/dist/Actions/Actions.Button.d.ts +151 -0
- package/dist/Actions/Actions.Button.js +217 -0
- package/dist/Actions/Actions.FAB.d.ts +10 -0
- package/dist/Actions/Actions.FAB.js +17 -0
- package/dist/Actions/Actions.FilterButton.d.ts +10 -0
- package/dist/Actions/Actions.FilterButton.js +28 -0
- package/dist/Actions/Actions.IconButton.d.ts +67 -0
- package/dist/Actions/Actions.IconButton.js +38 -0
- package/dist/Actions/index.d.ts +22 -0
- package/dist/Actions/index.js +17 -0
- package/dist/Dialog/BottomSheet/BottomSheet.Root.js +1 -0
- package/dist/Dialog/Dialog.Sheet.js +6 -4
- package/dist/Display/Display.CircleButton.d.ts +15 -0
- package/dist/Display/Display.CircleButton.js +39 -0
- package/dist/Display/Display.MenuButton.d.ts +51 -8
- package/dist/Display/Display.MenuButton.js +79 -13
- package/dist/Display/Display.MenuCard.d.ts +15 -0
- package/dist/Display/Display.MenuCard.js +24 -0
- package/dist/Display/Display.MenuGrid.d.ts +19 -0
- package/dist/Display/Display.MenuGrid.js +7 -0
- package/dist/Display/Display.SwipeableRow.js +2 -1
- package/dist/Display/Display.Tooltip.d.ts +1 -1
- package/dist/Display/Display.Tooltip.js +16 -5
- package/dist/Display/index.d.ts +9 -0
- package/dist/Display/index.js +6 -0
- package/dist/Feedback/Feedback.Empty.js +2 -2
- package/dist/Feedback/Feedback.Error.js +2 -2
- package/dist/Feedback/Feedback.Progress.js +2 -2
- package/dist/Feedback/Feedback.Skeleton.js +2 -2
- package/dist/Feedback/Feedback.Splash.js +1 -0
- package/dist/Form/Form.Button.d.ts +3 -151
- package/dist/Form/Form.Button.js +1 -283
- package/dist/Form/Form.FAB.d.ts +3 -10
- package/dist/Form/Form.FAB.js +1 -17
- package/dist/Form/Form.FilterButton.d.ts +3 -12
- package/dist/Form/Form.FilterButton.js +1 -34
- package/dist/Form/Form.IconButton.d.ts +3 -67
- package/dist/Form/Form.IconButton.js +1 -64
- package/dist/Navigation/Navigation.AppBar.d.ts +63 -4
- package/dist/Navigation/Navigation.AppBar.js +70 -18
- package/dist/Navigation/Navigation.BottomNav.js +2 -1
- package/dist/Navigation/Navigation.Header.js +3 -2
- package/dist/Navigation/Navigation.Menu.js +6 -3
- package/dist/Navigation/Navigation.MenuSection.d.ts +19 -0
- package/dist/Navigation/Navigation.MenuSection.js +25 -0
- package/dist/Navigation/Navigation.SegmentedControl.d.ts +15 -1
- package/dist/Navigation/Navigation.SegmentedControl.js +22 -4
- package/dist/Navigation/Navigation.TabIcon.js +1 -2
- package/dist/Navigation/Navigation.TabItem.js +2 -2
- package/dist/Navigation/Navigation.Tabs.d.ts +8 -3
- package/dist/Navigation/Navigation.Tabs.js +19 -6
- package/dist/Navigation/index.d.ts +8 -7
- package/dist/Navigation/index.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/layouts/BottomInset.d.ts +16 -0
- package/dist/layouts/BottomInset.js +17 -0
- package/dist/layouts/TabNavigatorLayout.js +2 -2
- package/dist/primitives/BaseButton.js +3 -3
- package/dist/theme/default.js +1 -1
- package/dist/views/FormViewV2.d.ts +99 -9
- package/dist/views/FormViewV2.js +79 -19
- package/dist/views/ListViewV2.d.ts +58 -5
- package/dist/views/ListViewV2.js +58 -11
- package/dist/widgets/CircleButton.d.ts +3 -19
- package/dist/widgets/CircleButton.js +1 -41
- package/dist/widgets/JSONViewer.js +7 -7
- package/dist/widgets/MenuCard.d.ts +3 -13
- package/dist/widgets/MenuCard.js +1 -8
- package/dist/widgets/MenuGrid.d.ts +5 -17
- package/dist/widgets/MenuGrid.js +1 -6
- package/dist/widgets/MenuSection.d.ts +3 -0
- package/dist/widgets/MenuSection.js +1 -0
- package/dist/widgets/PromoBanner.d.ts +38 -0
- package/dist/widgets/PromoBanner.js +61 -0
- package/dist/widgets/PromoModal.d.ts +41 -0
- package/dist/widgets/PromoModal.js +75 -0
- package/dist/widgets/QuickActions.d.ts +26 -0
- package/dist/widgets/QuickActions.js +41 -0
- package/dist/widgets/index.d.ts +11 -1
- package/dist/widgets/index.js +4 -0
- package/package.json +2 -1
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";
|
|
@@ -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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
package/dist/theme/default.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
|
|
2
|
+
import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl";
|
|
2
3
|
import React, { ReactNode } from "react";
|
|
3
4
|
import { type ImageSourcePropType } from "react-native";
|
|
4
5
|
interface FormViewProps<T> {
|
|
@@ -17,46 +18,135 @@ interface FormViewProps<T> {
|
|
|
17
18
|
children: ReactNode;
|
|
18
19
|
}
|
|
19
20
|
declare function FormViewRoot<T>({ name, description, item, isLoading, isEmpty, hasError, errorMessage, readonly, onRefresh, onRetry, onBack, withContainer, children, }: FormViewProps<T>): React.JSX.Element;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
variant?:
|
|
23
|
-
|
|
21
|
+
interface HeaderProps {
|
|
22
|
+
/** AppBar display variant. `large` = big title (list/index screens). `center` = modal/detail. */
|
|
23
|
+
variant?: "default" | "large" | "center";
|
|
24
|
+
/** Surface tone. `dark` = always-dark hero. `gradient` = brand accent gradient. */
|
|
25
|
+
tone?: "default" | "dark" | "gradient";
|
|
26
|
+
/** Actions rendered in the AppBar action slot (AppBar.Action nodes). */
|
|
27
|
+
actions?: ReactNode;
|
|
28
|
+
/** Subheader content rendered below the title bar (SearchBar, SegmentedControl, etc.). */
|
|
24
29
|
children?: ReactNode;
|
|
25
|
-
|
|
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;
|
|
40
|
+
/** @deprecated Pass `variant` prop instead. */
|
|
41
|
+
logoSource?: ImageSourcePropType;
|
|
42
|
+
}
|
|
43
|
+
declare function Header({ variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
|
|
26
44
|
declare function HeaderActions({ children }: {
|
|
27
45
|
children: ReactNode;
|
|
28
46
|
}): React.JSX.Element;
|
|
47
|
+
/** Secondary action bar docked below the AppBar — use for Save/Cancel, filter chips, etc. */
|
|
29
48
|
declare function HeaderActionsBar({ children }: {
|
|
30
49
|
children: ReactNode;
|
|
31
50
|
}): React.JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* Tab-like segmented switch for the header subheader (e.g. "Detalles" / "Historial").
|
|
53
|
+
* Use as a child of `<FormViewV2.Header>` — it lands in the AppBar subheader slot.
|
|
54
|
+
*/
|
|
55
|
+
declare function HeaderSegmented<T extends string>(props: SegmentedControlProps<T>): React.JSX.Element;
|
|
32
56
|
declare function Content({ withMargin, children, }: {
|
|
33
57
|
withMargin?: boolean;
|
|
34
58
|
children: ReactNode;
|
|
35
59
|
}): React.JSX.Element;
|
|
60
|
+
/**
|
|
61
|
+
* @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
|
|
62
|
+
* Use `<FormViewV2.Content>` directly — this wrapper is redundant.
|
|
63
|
+
*/
|
|
36
64
|
declare function Form({ children }: {
|
|
37
65
|
children: ReactNode;
|
|
38
66
|
}): React.JSX.Element;
|
|
67
|
+
/** Layout wrapper for a set of Group cards. Content already handles isEmpty/loading states. */
|
|
39
68
|
declare function Groups({ children }: {
|
|
40
69
|
children: ReactNode;
|
|
41
|
-
}): React.JSX.Element
|
|
42
|
-
|
|
70
|
+
}): React.JSX.Element;
|
|
71
|
+
/**
|
|
72
|
+
* Card-bordered input group for form fields. Use inside `Content.Groups`.
|
|
73
|
+
*
|
|
74
|
+
* Separation — by DEFAULT each Group is its own inset card (`withMargin=true`:
|
|
75
|
+
* margin 16/4, iOS "grouped" style), so several Groups read as separate sections.
|
|
76
|
+
* If you DON'T want separation, put all fields in a SINGLE Group (one continuous
|
|
77
|
+
* card), or pass `withMargin={false}` to render flush/edge-to-edge — e.g. when
|
|
78
|
+
* embedding inside your own layout.
|
|
79
|
+
*
|
|
80
|
+
* `title` renders a SectionHeader label above the card.
|
|
81
|
+
*/
|
|
82
|
+
declare function Group({ children, withMargin, elevation, padding, variant, title, }: {
|
|
43
83
|
children: ReactNode;
|
|
84
|
+
/** `true` (default) = inset card with margins; `false` = flush, no margin. */
|
|
44
85
|
withMargin?: boolean;
|
|
45
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Frame style. `card` (default) wraps fields in a Display.Card. `plain` drops the
|
|
88
|
+
* frame so the content sits directly on the page — useful when the children already
|
|
89
|
+
* carry their own surface (Form.Input, Display.Card, custom rows) and a wrapping card
|
|
90
|
+
* would just double up.
|
|
91
|
+
*/
|
|
92
|
+
variant?: "card" | "plain";
|
|
93
|
+
/**
|
|
94
|
+
* [card only] Shadow. `none` (default) = flat — the inputs already carry their own card
|
|
95
|
+
* surface, so a group shadow only adds a "floating" double-shadow. Opt into `sm`/`md`
|
|
96
|
+
* for a raised group when it sits directly on the page.
|
|
97
|
+
*/
|
|
98
|
+
elevation?: "none" | "sm" | "md" | "lg";
|
|
99
|
+
/**
|
|
100
|
+
* Inner padding (Display.Card scale). `md` (default) aligns the title with the labels.
|
|
101
|
+
* Applies to BOTH variants, so `plain` keeps the exact same content position as `card`.
|
|
102
|
+
*/
|
|
103
|
+
padding?: "none" | "sm" | "md" | "lg";
|
|
104
|
+
/** Label rendered above the group (SectionHeader style). */
|
|
105
|
+
title?: string;
|
|
106
|
+
}): React.JSX.Element;
|
|
107
|
+
/**
|
|
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.
|
|
111
|
+
*/
|
|
112
|
+
declare function Section({ title, footer, children, }: {
|
|
113
|
+
title?: string;
|
|
114
|
+
/** Hint text rendered below the card (e.g. "Toca para cambiar la configuración"). */
|
|
115
|
+
footer?: string;
|
|
116
|
+
children: ReactNode;
|
|
46
117
|
}): React.JSX.Element;
|
|
47
118
|
declare function Footer({ children }: {
|
|
48
119
|
children: ReactNode;
|
|
49
120
|
}): React.JSX.Element | null;
|
|
121
|
+
/**
|
|
122
|
+
* Screen container for a single record (detail / create / edit / settings).
|
|
123
|
+
* Owns safe-area, scroll (KeyboardAware), refresh, and the loading/empty/error states.
|
|
124
|
+
*
|
|
125
|
+
* API map:
|
|
126
|
+
* FormViewV2.Root — provider + container; pass `item`, `isLoading`, `hasError`, `onBack`…
|
|
127
|
+
* FormViewV2.Header — AppBar (variant: default | large | center · tone: default | dark | gradient)
|
|
128
|
+
* .Header child → subheader slot: <Header.Segmented> or any node (SearchBar, etc.)
|
|
129
|
+
* .Header.Segmented — tab-like switch in the subheader (Detalles / Historial)
|
|
130
|
+
* .Header.ActionsBar — SIBLING bar below the header (Guardar / Cancelar)
|
|
131
|
+
* .Header.Actions — inline action row (legacy helper)
|
|
132
|
+
* FormViewV2.Content — scroll body; renders Feedback for loading/empty/error
|
|
133
|
+
* .Content.Groups — layout wrapper for Group cards
|
|
134
|
+
* .Content.Group — bordered card of inputs (optional `title`)
|
|
135
|
+
* .Content.Section — SectionHeader + Card (settings/menu rows)
|
|
136
|
+
* FormViewV2.Footer — sticky bottom bar (primary CTA)
|
|
137
|
+
*/
|
|
50
138
|
export declare const FormViewV2: {
|
|
51
139
|
Root: typeof FormViewRoot;
|
|
52
140
|
Header: typeof Header & {
|
|
53
141
|
Actions: typeof HeaderActions;
|
|
54
142
|
ActionsBar: typeof HeaderActionsBar;
|
|
143
|
+
Segmented: typeof HeaderSegmented;
|
|
55
144
|
};
|
|
56
145
|
Content: typeof Content & {
|
|
57
146
|
Form: typeof Form;
|
|
58
147
|
Groups: typeof Groups;
|
|
59
148
|
Group: typeof Group;
|
|
149
|
+
Section: typeof Section;
|
|
60
150
|
};
|
|
61
151
|
Footer: typeof Footer;
|
|
62
152
|
};
|
package/dist/views/FormViewV2.js
CHANGED
|
@@ -1,33 +1,45 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "nativewind/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import { Display } from "../Display";
|
|
2
3
|
import { ErrorBoundary, Feedback } from "../Feedback";
|
|
4
|
+
import { useBottomInset } from "../layouts/BottomInset";
|
|
3
5
|
import Container from "../layouts/Container";
|
|
4
6
|
import { AppBar } from "../Navigation/Navigation.AppBar";
|
|
5
|
-
import {
|
|
7
|
+
import { MenuSection } from "../Navigation/Navigation.MenuSection";
|
|
8
|
+
import { SegmentedControl, } from "../Navigation/Navigation.SegmentedControl";
|
|
9
|
+
import { useTheme } from "../theme";
|
|
6
10
|
import { FormViewProvider, useFormView } from "../views/FormViewV2.context";
|
|
7
11
|
import React from "react";
|
|
8
12
|
import { RefreshControl, View } from "react-native";
|
|
9
13
|
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
|
|
10
14
|
function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRetry, onBack, withContainer = true, children, }) {
|
|
11
15
|
const Wrapper = withContainer ? Container : View;
|
|
12
|
-
// The header (AppBar) / parent layout owns the TOP safe-area; the inner Container only
|
|
13
|
-
// reserves the remaining edges to avoid stacking the top inset (double padding).
|
|
14
16
|
const wrapperProps = withContainer
|
|
15
17
|
? { edges: ["bottom", "left", "right"] }
|
|
16
18
|
: { style: { flex: 1 } };
|
|
17
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 }) }) }));
|
|
18
20
|
}
|
|
19
|
-
function Header({ children, }) {
|
|
21
|
+
function Header({ variant = "default", tone, actions, children, topSpacing, background, }) {
|
|
20
22
|
const { name, description, onBack } = useFormView();
|
|
21
|
-
return (_jsx(AppBar, { onBack: onBack, safeArea: false, subheader: children, subtitle: description
|
|
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 }));
|
|
22
24
|
}
|
|
23
25
|
function HeaderActions({ children }) {
|
|
24
|
-
return (_jsx(View, { className: "flex-row px-4 my-
|
|
26
|
+
return (_jsx(View, { className: "flex-row px-4 my-2 items-center justify-between flex-1 gap-2", children: children }));
|
|
25
27
|
}
|
|
28
|
+
/** Secondary action bar docked below the AppBar — use for Save/Cancel, filter chips, etc. */
|
|
26
29
|
function HeaderActionsBar({ children }) {
|
|
27
|
-
return (_jsx(View, { className: "bg-bg-card px-4
|
|
30
|
+
return (_jsx(View, { className: "bg-bg-card px-4 pt-1 pb-3 border-b border-border-light flex-row gap-2 items-center", children: children }));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Tab-like segmented switch for the header subheader (e.g. "Detalles" / "Historial").
|
|
34
|
+
* Use as a child of `<FormViewV2.Header>` — it lands in the AppBar subheader slot.
|
|
35
|
+
*/
|
|
36
|
+
function HeaderSegmented(props) {
|
|
37
|
+
return _jsx(SegmentedControl, { ...props });
|
|
28
38
|
}
|
|
29
39
|
function Content({ withMargin = false, children, }) {
|
|
30
40
|
const { name, isLoading, isEmpty, hasError, errorMessage, item, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useFormView();
|
|
41
|
+
const bottomInset = useBottomInset();
|
|
42
|
+
const theme = useTheme();
|
|
31
43
|
const handleRefresh = React.useCallback(async () => {
|
|
32
44
|
setShowRefreshing(true);
|
|
33
45
|
if (onRefresh) {
|
|
@@ -40,8 +52,13 @@ function Content({ withMargin = false, children, }) {
|
|
|
40
52
|
flexGrow: 1,
|
|
41
53
|
justifyContent: isCenteredContent ? "center" : "flex-start",
|
|
42
54
|
alignItems: isCenteredContent ? "center" : "stretch",
|
|
43
|
-
|
|
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 })) }));
|
|
44
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
|
|
60
|
+
* Use `<FormViewV2.Content>` directly — this wrapper is redundant.
|
|
61
|
+
*/
|
|
45
62
|
function Form({ children }) {
|
|
46
63
|
const { isEmpty, item } = useFormView();
|
|
47
64
|
if (isEmpty || !item) {
|
|
@@ -49,37 +66,80 @@ function Form({ children }) {
|
|
|
49
66
|
}
|
|
50
67
|
return _jsx(_Fragment, { children: children });
|
|
51
68
|
}
|
|
69
|
+
/** Layout wrapper for a set of Group cards. Content already handles isEmpty/loading states. */
|
|
52
70
|
function Groups({ children }) {
|
|
53
|
-
const { isEmpty } = useFormView();
|
|
54
|
-
if (isEmpty) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
71
|
return _jsx(_Fragment, { children: children });
|
|
58
72
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Card-bordered input group for form fields. Use inside `Content.Groups`.
|
|
75
|
+
*
|
|
76
|
+
* Separation — by DEFAULT each Group is its own inset card (`withMargin=true`:
|
|
77
|
+
* margin 16/4, iOS "grouped" style), so several Groups read as separate sections.
|
|
78
|
+
* If you DON'T want separation, put all fields in a SINGLE Group (one continuous
|
|
79
|
+
* card), or pass `withMargin={false}` to render flush/edge-to-edge — e.g. when
|
|
80
|
+
* embedding inside your own layout.
|
|
81
|
+
*
|
|
82
|
+
* `title` renders a SectionHeader label above the card.
|
|
83
|
+
*/
|
|
84
|
+
function Group({ children, withMargin = true, elevation = "none", padding = "md", variant = "card", title, }) {
|
|
85
|
+
// `plain` mirrors the card's inner padding so the content lands in the SAME position
|
|
86
|
+
// as `card` — only the frame (border/bg/shadow) is dropped.
|
|
87
|
+
const paddingClass = { none: "p-0", sm: "p-3", md: "p-4", lg: "p-6" }[padding];
|
|
88
|
+
const body = variant === "plain" ? (_jsx(View, { className: paddingClass, children: children })) : (_jsx(Display.Card, { elevation: elevation, padding: padding, children: children }));
|
|
89
|
+
// Title alignment is identical in both variants (same inset as the card's content).
|
|
90
|
+
const header = title ? _jsx(Display.SectionHeader, { title: title }) : null;
|
|
91
|
+
if (withMargin) {
|
|
92
|
+
return (_jsxs(View, { style: { marginHorizontal: 16, marginVertical: 6 }, children: [header, body] }));
|
|
93
|
+
}
|
|
94
|
+
return (_jsxs(_Fragment, { children: [header, body] }));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
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.
|
|
100
|
+
*/
|
|
101
|
+
function Section({ title, footer, children, }) {
|
|
102
|
+
return (_jsx(MenuSection, { footer: footer, title: title, children: children }));
|
|
64
103
|
}
|
|
65
104
|
function Footer({ children }) {
|
|
66
105
|
const { isEmpty, isLoading } = useFormView();
|
|
67
106
|
if (isEmpty || isLoading) {
|
|
68
107
|
return null;
|
|
69
108
|
}
|
|
70
|
-
|
|
109
|
+
// Sticky bottom bar (lives outside the scroll). No shadow — a top hairline integrates it
|
|
110
|
+
// with the form instead of reading as a detached floating card.
|
|
111
|
+
return (_jsx(View, { className: "bg-bg-card px-4 py-3 border-t border-border-light", children: children }));
|
|
71
112
|
}
|
|
72
113
|
// Nested Header components
|
|
73
114
|
const HeaderWithSubComponents = Object.assign(Header, {
|
|
74
115
|
Actions: HeaderActions,
|
|
75
116
|
ActionsBar: HeaderActionsBar,
|
|
117
|
+
Segmented: HeaderSegmented,
|
|
76
118
|
});
|
|
77
119
|
// Nested Content components
|
|
78
120
|
const ContentWithSubComponents = Object.assign(Content, {
|
|
79
121
|
Form,
|
|
80
122
|
Groups,
|
|
81
123
|
Group,
|
|
124
|
+
Section,
|
|
82
125
|
});
|
|
126
|
+
/**
|
|
127
|
+
* Screen container for a single record (detail / create / edit / settings).
|
|
128
|
+
* Owns safe-area, scroll (KeyboardAware), refresh, and the loading/empty/error states.
|
|
129
|
+
*
|
|
130
|
+
* API map:
|
|
131
|
+
* FormViewV2.Root — provider + container; pass `item`, `isLoading`, `hasError`, `onBack`…
|
|
132
|
+
* FormViewV2.Header — AppBar (variant: default | large | center · tone: default | dark | gradient)
|
|
133
|
+
* .Header child → subheader slot: <Header.Segmented> or any node (SearchBar, etc.)
|
|
134
|
+
* .Header.Segmented — tab-like switch in the subheader (Detalles / Historial)
|
|
135
|
+
* .Header.ActionsBar — SIBLING bar below the header (Guardar / Cancelar)
|
|
136
|
+
* .Header.Actions — inline action row (legacy helper)
|
|
137
|
+
* FormViewV2.Content — scroll body; renders Feedback for loading/empty/error
|
|
138
|
+
* .Content.Groups — layout wrapper for Group cards
|
|
139
|
+
* .Content.Group — bordered card of inputs (optional `title`)
|
|
140
|
+
* .Content.Section — SectionHeader + Card (settings/menu rows)
|
|
141
|
+
* FormViewV2.Footer — sticky bottom bar (primary CTA)
|
|
142
|
+
*/
|
|
83
143
|
export const FormViewV2 = {
|
|
84
144
|
Root: FormViewRoot,
|
|
85
145
|
Header: HeaderWithSubComponents,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type SearchHistoryProps } from "../Display/Display.SearchHistory";
|
|
2
|
+
import type { AppBarBackground } from "../Navigation/Navigation.AppBar";
|
|
3
|
+
import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl";
|
|
2
4
|
import React, { ReactNode } from "react";
|
|
3
5
|
import { StyleProp, ViewStyle } from "react-native";
|
|
4
6
|
interface ListViewProps<T> {
|
|
@@ -24,17 +26,47 @@ interface ListViewProps<T> {
|
|
|
24
26
|
children: ReactNode;
|
|
25
27
|
}
|
|
26
28
|
declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, backgroundColor, withContainer, children, }: ListViewProps<T>): React.JSX.Element;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
variant?:
|
|
29
|
+
interface HeaderProps {
|
|
30
|
+
/** AppBar display variant. `large` = big title (list/index). `center` = modal/detail. */
|
|
31
|
+
variant?: "default" | "large" | "center";
|
|
32
|
+
/** Surface tone. `dark` = always-dark hero. `gradient` = brand accent gradient. */
|
|
33
|
+
tone?: "default" | "dark" | "gradient";
|
|
34
|
+
/** Actions rendered in the AppBar action slot (AppBar.Action nodes). */
|
|
35
|
+
actions?: ReactNode;
|
|
36
|
+
/** Subheader content rendered below the title bar (SearchBar, SegmentedControl, etc.). */
|
|
30
37
|
children?: ReactNode;
|
|
31
|
-
|
|
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;
|
|
48
|
+
}
|
|
49
|
+
declare function Header({ variant, tone, actions, children, background, topSpacing, }: HeaderProps): React.JSX.Element;
|
|
32
50
|
declare function HeaderSearch({ searchQuery }: {
|
|
33
51
|
searchQuery?: string;
|
|
34
52
|
}): React.JSX.Element | null;
|
|
35
53
|
declare function HeaderActions({ children }: {
|
|
36
54
|
children: ReactNode;
|
|
37
55
|
}): React.JSX.Element;
|
|
56
|
+
/** Secondary action bar docked below the AppBar — use for Save/Cancel, filter chips, etc. */
|
|
57
|
+
declare function HeaderActionsBar({ children }: {
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}): React.JSX.Element;
|
|
60
|
+
/**
|
|
61
|
+
* Tab-like segmented switch for the header subheader (e.g. "Activos" / "Archivados").
|
|
62
|
+
* Use as a child of `<ListViewV2.Header>` — it lands in the AppBar subheader slot.
|
|
63
|
+
*/
|
|
64
|
+
declare function HeaderSegmented<T extends string>(props: SegmentedControlProps<T>): React.JSX.Element;
|
|
65
|
+
/**
|
|
66
|
+
* Recent-searches list. Render as a SIBLING of the Header (like Filters/ActionsBar) —
|
|
67
|
+
* typically while the search query is empty. Delegates to `Display.SearchHistory`.
|
|
68
|
+
*/
|
|
69
|
+
declare function HeaderSearchHistory(props: SearchHistoryProps): React.JSX.Element;
|
|
38
70
|
declare function HeaderFilters({ children }: {
|
|
39
71
|
children: ReactNode;
|
|
40
72
|
}): React.JSX.Element;
|
|
@@ -108,11 +140,32 @@ interface RowActionButtonProps {
|
|
|
108
140
|
onPress?: () => void;
|
|
109
141
|
}
|
|
110
142
|
declare function RowActionButton({ icon, onPress }: RowActionButtonProps): React.JSX.Element;
|
|
143
|
+
/**
|
|
144
|
+
* Screen container for a collection (list / index / search results).
|
|
145
|
+
* Owns safe-area, the FlatList (pagination + pull-to-refresh), record count, and the
|
|
146
|
+
* loading/empty/error states.
|
|
147
|
+
*
|
|
148
|
+
* API map:
|
|
149
|
+
* ListViewV2.Root — provider + container; pass `items`, `onSearch`, `onLoadMore`, `onBack`…
|
|
150
|
+
* ListViewV2.Header — AppBar (variant: default | large | center · tone: default | dark | gradient)
|
|
151
|
+
* .Header.Search — debounced SearchBar in the subheader (wires `onSearch`)
|
|
152
|
+
* .Header.SearchHistory — recent-searches list (SIBLING; show while query is empty)
|
|
153
|
+
* .Header.Segmented — tab-like switch in the subheader (Activos / Archivados)
|
|
154
|
+
* .Header.Filters(.Chips/.Chip) — horizontal filter pills (SIBLING below the header)
|
|
155
|
+
* .Header.ActionsBar — SIBLING bar below the header (bulk select, etc.)
|
|
156
|
+
* ListViewV2.Content — FlatList body; `children` is an optional custom row renderer
|
|
157
|
+
* .Content.Row(.Avatar/.Content/.Title/.Subtitle/.Footer/.Actions/.ActionButton)
|
|
158
|
+
* ListViewV2.Footer — pinned footer (hidden when empty)
|
|
159
|
+
* ListViewV2.FloatingButton — absolute FAB overlay
|
|
160
|
+
*/
|
|
111
161
|
export declare const ListViewV2: {
|
|
112
162
|
Root: typeof ListViewRoot;
|
|
113
163
|
Header: typeof Header & {
|
|
114
164
|
Search: typeof HeaderSearch;
|
|
165
|
+
SearchHistory: typeof HeaderSearchHistory;
|
|
166
|
+
Segmented: typeof HeaderSegmented;
|
|
115
167
|
Actions: typeof HeaderActions;
|
|
168
|
+
ActionsBar: typeof HeaderActionsBar;
|
|
116
169
|
Filters: typeof HeaderFilters & {
|
|
117
170
|
Chip: typeof FilterChip;
|
|
118
171
|
Chips: typeof FilterChips;
|