@sincpro/mobile-ui 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/Actions/Actions.Button.d.ts +2 -1
- package/dist/Actions/Actions.Button.js +31 -37
- package/dist/Actions/Actions.Dock.d.ts +6 -6
- package/dist/Actions/Actions.Dock.js +14 -72
- package/dist/Actions/Actions.FAB.d.ts +2 -1
- package/dist/Actions/Actions.FilterButton.js +1 -1
- package/dist/Actions/Actions.IconButton.d.ts +3 -4
- package/dist/Actions/Actions.IconButton.js +2 -2
- package/dist/Dialog/Confirmation.context.d.ts +5 -14
- package/dist/Dialog/Confirmation.context.js +11 -51
- package/dist/Dialog/Dialog.ActionSheet.d.ts +8 -7
- package/dist/Dialog/Dialog.ActionSheet.js +45 -19
- package/dist/Dialog/Dialog.Confirmation.d.ts +14 -11
- package/dist/Dialog/Dialog.Confirmation.js +38 -14
- package/dist/Dialog/Dialog.EditValue.d.ts +21 -16
- package/dist/Dialog/Dialog.EditValue.js +73 -14
- package/dist/Dialog/Dialog.Sheet.d.ts +8 -24
- package/dist/Dialog/Dialog.Sheet.js +15 -60
- package/dist/Dialog/index.d.ts +14 -14
- package/dist/Dialog/index.js +5 -3
- package/dist/Dialog/requestConfirmation.d.ts +3 -0
- package/dist/Dialog/requestConfirmation.js +13 -0
- package/dist/Display/Display.Accordion.js +1 -1
- package/dist/Display/Display.Avatar.d.ts +6 -6
- package/dist/Display/Display.Avatar.js +2 -2
- package/dist/Display/Display.Badge.js +4 -4
- package/dist/Display/Display.Card.js +5 -2
- package/dist/Display/Display.Carousel.js +1 -1
- package/dist/Display/Display.CircleButton.d.ts +3 -8
- package/dist/Display/Display.CircleButton.js +2 -2
- package/dist/Display/Display.CopyableText.js +1 -5
- package/dist/Display/Display.Date.d.ts +3 -2
- package/dist/Display/Display.Date.js +3 -3
- package/dist/Display/Display.GradientSurface.d.ts +52 -9
- package/dist/Display/Display.GradientSurface.js +9 -14
- package/dist/Display/Display.Icon.d.ts +7 -11
- package/dist/Display/Display.Icon.js +15 -30
- package/dist/Display/Display.Image.d.ts +2 -1
- package/dist/Display/Display.Image.js +1 -1
- package/dist/Display/Display.KeyValue.js +1 -1
- package/dist/Display/Display.ListItem.js +1 -1
- package/dist/Display/Display.MenuButton.js +4 -4
- package/dist/Display/Display.MenuCard.d.ts +3 -8
- package/dist/Display/Display.MenuCard.js +2 -2
- package/dist/Display/Display.MenuGrid.d.ts +2 -7
- package/dist/Display/Display.MenuGrid.js +1 -1
- package/dist/Display/Display.Monetary.d.ts +2 -6
- package/dist/Display/Display.Monetary.js +2 -2
- package/dist/Display/Display.PriceTag.js +1 -1
- package/dist/Display/Display.Rating.js +1 -1
- package/dist/Display/Display.SearchHistory.d.ts +2 -1
- package/dist/Display/Display.SearchHistory.js +1 -1
- package/dist/Display/Display.StatCard.d.ts +2 -1
- package/dist/Display/Display.StatCard.js +1 -1
- package/dist/Display/Display.StoryViewer.js +10 -6
- package/dist/Display/Display.SwipeableRow.d.ts +2 -1
- package/dist/Display/Display.Timeline.js +1 -1
- package/dist/Display/icon-map.d.ts +344 -0
- package/dist/Display/icon-map.js +86 -0
- package/dist/Display/index.d.ts +2 -0
- package/dist/Display/index.js +1 -0
- package/dist/Feedback/Feedback.Banner.js +13 -12
- package/dist/Feedback/Feedback.Empty.js +1 -1
- package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
- package/dist/Feedback/Feedback.EmptyState.js +1 -1
- package/dist/Feedback/Feedback.Error.js +1 -1
- package/dist/Feedback/Feedback.PermissionCard.d.ts +2 -1
- package/dist/Feedback/Feedback.PermissionCard.js +2 -2
- package/dist/Feedback/Toast.context.js +3 -3
- package/dist/Form/Form.ControlledDropdown.d.ts +5 -11
- package/dist/Form/Form.ControlledDropdown.js +5 -2
- package/dist/Form/Form.DatePicker.d.ts +5 -9
- package/dist/Form/Form.DatePicker.js +116 -21
- package/dist/Form/Form.Dropdown.d.ts +11 -16
- package/dist/Form/Form.Dropdown.js +54 -90
- package/dist/Form/Form.FractionalQuantityInput.js +4 -11
- package/dist/Form/Form.Input.d.ts +3 -3
- package/dist/Form/Form.KeyboardForm.d.ts +2 -2
- package/dist/Form/Form.KeyboardForm.js +2 -2
- package/dist/Form/Form.KeyboardToolbar.d.ts +2 -1
- package/dist/Form/Form.KeyboardToolbar.js +3 -7
- package/dist/Form/Form.PasswordInput.js +1 -1
- package/dist/Form/Form.QuantitySelector.js +1 -1
- package/dist/Form/Form.Select.d.ts +12 -8
- package/dist/Form/Form.Select.js +64 -31
- package/dist/Form/Form.Slider.d.ts +4 -7
- package/dist/Form/Form.Slider.js +11 -10
- package/dist/Form/Form.Switch.js +2 -1
- package/dist/Form/index.d.ts +6 -14
- package/dist/Form/index.js +0 -11
- package/dist/Navigation/Navigation.AppBar.d.ts +2 -1
- package/dist/Navigation/Navigation.AppBar.js +2 -2
- package/dist/Navigation/Navigation.BottomNav.d.ts +4 -8
- package/dist/Navigation/Navigation.BottomNav.js +2 -2
- package/dist/Navigation/Navigation.CollapsingHeader.js +3 -2
- package/dist/Navigation/Navigation.Menu.d.ts +16 -11
- package/dist/Navigation/Navigation.Menu.js +97 -42
- package/dist/Navigation/Navigation.Search.js +2 -2
- package/dist/Navigation/Navigation.SearchBar.js +3 -4
- package/dist/Navigation/Navigation.SegmentedControl.d.ts +7 -25
- package/dist/Navigation/Navigation.SegmentedControl.js +18 -39
- package/dist/Navigation/Navigation.Steps.js +1 -1
- package/dist/Navigation/Navigation.Tabs.d.ts +2 -1
- package/dist/Navigation/index.d.ts +0 -6
- package/dist/Navigation/index.js +0 -8
- package/dist/Typography/Typography.SelectableText.js +3 -7
- package/dist/Typography/Typography.Text.d.ts +1 -6
- package/dist/Typography/Typography.Text.js +3 -6
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/layouts/AuthContainer.d.ts +4 -2
- package/dist/layouts/AuthContainer.js +3 -4
- package/dist/layouts/Container.d.ts +0 -4
- package/dist/layouts/Container.js +3 -4
- package/dist/layouts/ScrollContainer.d.ts +7 -3
- package/dist/layouts/ScrollContainer.js +5 -4
- package/dist/primitives/BaseButton.d.ts +2 -1
- package/dist/theme/build.js +0 -4
- package/dist/theme/css-vars.js +0 -4
- package/dist/theme/dark.js +2 -6
- package/dist/theme/default.js +1 -5
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/types.d.ts +0 -11
- package/dist/theme/typography.d.ts +8 -7
- package/dist/theme/typography.js +25 -4
- package/dist/utils/timezone.d.ts +0 -5
- package/dist/views/AuthFormView.d.ts +3 -1
- package/dist/views/AuthFormView.js +2 -2
- package/dist/views/FormViewV2.d.ts +6 -13
- package/dist/views/FormViewV2.js +29 -25
- package/dist/views/ListViewV2.context.d.ts +5 -5
- package/dist/views/ListViewV2.d.ts +23 -18
- package/dist/views/ListViewV2.js +45 -48
- package/dist/views/types/IListView.d.ts +5 -4
- package/dist/views/types/IListView.js +18 -15
- package/dist/widgets/CollectionOptions.d.ts +2 -2
- package/dist/widgets/CollectionOptions.js +92 -65
- package/dist/widgets/FilterBar.js +1 -1
- package/dist/widgets/PromoModal.js +10 -4
- package/dist/widgets/QuickActions.d.ts +2 -7
- package/dist/widgets/QuickActions.js +1 -1
- package/dist/widgets/ReorderableList.js +3 -3
- package/dist/widgets/TimeZoneSelectorModal.d.ts +1 -6
- package/dist/widgets/TimeZoneSelectorModal.js +17 -12
- package/dist/widgets/index.d.ts +0 -11
- package/dist/widgets/index.js +0 -7
- package/package.json +35 -38
- package/tailwind.preset.js +0 -4
- package/dist/Dialog/BottomSheet/BottomSheet.Root.d.ts +0 -324
- package/dist/Dialog/BottomSheet/BottomSheet.Root.js +0 -373
- package/dist/Dialog/BottomSheet/index.d.ts +0 -1
- package/dist/Dialog/BottomSheet/index.js +0 -1
- package/dist/Dialog/Dialog.NativeSheet.d.ts +0 -10
- package/dist/Dialog/Dialog.NativeSheet.js +0 -21
- package/dist/Form/Form.Button.d.ts +0 -3
- package/dist/Form/Form.Button.js +0 -1
- package/dist/Form/Form.FAB.d.ts +0 -3
- package/dist/Form/Form.FAB.js +0 -1
- package/dist/Form/Form.FilterButton.d.ts +0 -3
- package/dist/Form/Form.FilterButton.js +0 -1
- package/dist/Form/Form.IconButton.d.ts +0 -3
- package/dist/Form/Form.IconButton.js +0 -1
- package/dist/Form/Form.SearchBar.d.ts +0 -13
- package/dist/Form/Form.SearchBar.js +0 -26
- package/dist/Navigation/Navigation.Header.d.ts +0 -16
- package/dist/Navigation/Navigation.Header.js +0 -17
- package/dist/Navigation/Navigation.HeaderToolbar.d.ts +0 -15
- package/dist/Navigation/Navigation.HeaderToolbar.js +0 -18
- package/dist/Navigation/Navigation.TabIcon.d.ts +0 -18
- package/dist/Navigation/Navigation.TabIcon.js +0 -12
- package/dist/Navigation/Navigation.TabItem.d.ts +0 -19
- package/dist/Navigation/Navigation.TabItem.js +0 -13
- package/dist/icons/OdooIcon.d.ts +0 -11
- package/dist/icons/OdooIcon.js +0 -11
- package/dist/icons/PrinterIcon.d.ts +0 -9
- package/dist/icons/PrinterIcon.js +0 -9
- package/dist/layouts/TabNavigatorLayout.d.ts +0 -26
- package/dist/layouts/TabNavigatorLayout.js +0 -60
- package/dist/utils/icon.d.ts +0 -1
- package/dist/utils/icon.js +0 -1
- package/dist/widgets/CircleButton.d.ts +0 -3
- package/dist/widgets/CircleButton.js +0 -1
- package/dist/widgets/GeoPermissionCard.d.ts +0 -13
- package/dist/widgets/GeoPermissionCard.js +0 -24
- package/dist/widgets/HomeHeader.d.ts +0 -15
- package/dist/widgets/HomeHeader.js +0 -13
- package/dist/widgets/MenuCard.d.ts +0 -3
- package/dist/widgets/MenuCard.js +0 -1
- package/dist/widgets/MenuGrid.d.ts +0 -5
- package/dist/widgets/MenuGrid.js +0 -1
- package/dist/widgets/MenuSection.d.ts +0 -3
- package/dist/widgets/MenuSection.js +0 -1
- package/dist/widgets/ScreenHeader.d.ts +0 -25
- package/dist/widgets/ScreenHeader.js +0 -32
- package/dist/widgets/TimeZoneSelector.d.ts +0 -12
- package/dist/widgets/TimeZoneSelector.js +0 -24
|
@@ -60,21 +60,21 @@ export declare const typographyVariants: {
|
|
|
60
60
|
readonly fontFamily: string;
|
|
61
61
|
readonly fontWeight: "900";
|
|
62
62
|
readonly lineHeight: number;
|
|
63
|
-
readonly letterSpacing: -
|
|
63
|
+
readonly letterSpacing: -1.2;
|
|
64
64
|
};
|
|
65
65
|
readonly h1: {
|
|
66
66
|
readonly fontSize: 32;
|
|
67
67
|
readonly fontFamily: string;
|
|
68
68
|
readonly fontWeight: "700";
|
|
69
69
|
readonly lineHeight: number;
|
|
70
|
-
readonly letterSpacing: -0.
|
|
70
|
+
readonly letterSpacing: -0.8;
|
|
71
71
|
};
|
|
72
72
|
readonly h2: {
|
|
73
73
|
readonly fontSize: 28;
|
|
74
74
|
readonly fontFamily: string;
|
|
75
75
|
readonly fontWeight: "700";
|
|
76
76
|
readonly lineHeight: number;
|
|
77
|
-
readonly letterSpacing: 0;
|
|
77
|
+
readonly letterSpacing: -0.4;
|
|
78
78
|
};
|
|
79
79
|
readonly h3: {
|
|
80
80
|
readonly fontSize: 24;
|
|
@@ -219,6 +219,7 @@ export declare const typographyVariants: {
|
|
|
219
219
|
};
|
|
220
220
|
};
|
|
221
221
|
export declare const variantFontRole: Record<keyof typeof typographyVariants, FontRole>;
|
|
222
|
+
export declare function appliedTextStyle(variant: keyof typeof typographyVariants, bold?: boolean, semibold?: boolean): TextStyle;
|
|
222
223
|
export declare const createTextStyle: (variant: keyof typeof typographyVariants, overrides?: Partial<TextStyle>) => TextStyle;
|
|
223
224
|
export declare const createCustomTextStyle: (fontSize: keyof typeof fontSizes, fontFamily: keyof typeof fontFamilies, options?: {
|
|
224
225
|
lineHeight?: keyof typeof lineHeights;
|
|
@@ -278,21 +279,21 @@ export declare const Typography: {
|
|
|
278
279
|
readonly fontFamily: string;
|
|
279
280
|
readonly fontWeight: "900";
|
|
280
281
|
readonly lineHeight: number;
|
|
281
|
-
readonly letterSpacing: -
|
|
282
|
+
readonly letterSpacing: -1.2;
|
|
282
283
|
};
|
|
283
284
|
readonly h1: {
|
|
284
285
|
readonly fontSize: 32;
|
|
285
286
|
readonly fontFamily: string;
|
|
286
287
|
readonly fontWeight: "700";
|
|
287
288
|
readonly lineHeight: number;
|
|
288
|
-
readonly letterSpacing: -0.
|
|
289
|
+
readonly letterSpacing: -0.8;
|
|
289
290
|
};
|
|
290
291
|
readonly h2: {
|
|
291
292
|
readonly fontSize: 28;
|
|
292
293
|
readonly fontFamily: string;
|
|
293
294
|
readonly fontWeight: "700";
|
|
294
295
|
readonly lineHeight: number;
|
|
295
|
-
readonly letterSpacing: 0;
|
|
296
|
+
readonly letterSpacing: -0.4;
|
|
296
297
|
};
|
|
297
298
|
readonly h3: {
|
|
298
299
|
readonly fontSize: 24;
|
|
@@ -436,7 +437,7 @@ export declare const Typography: {
|
|
|
436
437
|
readonly letterSpacing: 0;
|
|
437
438
|
};
|
|
438
439
|
};
|
|
439
|
-
readonly variantFontRole: Record<"display" | "
|
|
440
|
+
readonly variantFontRole: Record<"display" | "button" | "label" | "title" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "bodyReadable" | "bodyLarge" | "body" | "bodySmall" | "labelSmall" | "data" | "dataLarge" | "overline" | "caption" | "captionSmall" | "buttonSmall" | "buttonLarge" | "subtitle", FontRole>;
|
|
440
441
|
readonly createTextStyle: (variant: keyof typeof typographyVariants, overrides?: Partial<TextStyle>) => TextStyle;
|
|
441
442
|
readonly createCustomTextStyle: (fontSize: keyof typeof fontSizes, fontFamily: keyof typeof fontFamilies, options?: {
|
|
442
443
|
lineHeight?: keyof typeof lineHeights;
|
package/dist/theme/typography.js
CHANGED
|
@@ -91,7 +91,7 @@ export const typographyVariants = {
|
|
|
91
91
|
fontFamily: fontFamilies.titleBlack,
|
|
92
92
|
fontWeight: fontWeights.titleBlack,
|
|
93
93
|
lineHeight: fontSizes["7xl"] * lineHeights.tight,
|
|
94
|
-
letterSpacing:
|
|
94
|
+
letterSpacing: -1.2,
|
|
95
95
|
},
|
|
96
96
|
// Headings (title role)
|
|
97
97
|
h1: {
|
|
@@ -99,14 +99,14 @@ export const typographyVariants = {
|
|
|
99
99
|
fontFamily: fontFamilies.title,
|
|
100
100
|
fontWeight: fontWeights.title,
|
|
101
101
|
lineHeight: fontSizes["5xl"] * lineHeights.tight,
|
|
102
|
-
letterSpacing:
|
|
102
|
+
letterSpacing: -0.8,
|
|
103
103
|
},
|
|
104
104
|
h2: {
|
|
105
105
|
fontSize: fontSizes["4xl"],
|
|
106
106
|
fontFamily: fontFamilies.title,
|
|
107
107
|
fontWeight: fontWeights.title,
|
|
108
108
|
lineHeight: fontSizes["4xl"] * lineHeights.snug,
|
|
109
|
-
letterSpacing:
|
|
109
|
+
letterSpacing: -0.4,
|
|
110
110
|
},
|
|
111
111
|
h3: {
|
|
112
112
|
fontSize: fontSizes["3xl"],
|
|
@@ -286,9 +286,30 @@ export const variantFontRole = {
|
|
|
286
286
|
title: "semiBold",
|
|
287
287
|
subtitle: "medium",
|
|
288
288
|
};
|
|
289
|
+
/* The shipped files stop at Bold 700. Asking iOS/Android for 800 or 900 on
|
|
290
|
+
* that file synthesizes a faux face and drops the loaded family. Rendering
|
|
291
|
+
* uses the weight of the file that was actually registered. */
|
|
292
|
+
const loadedFaceWeight = {
|
|
293
|
+
IBMPlexMono_400Regular: "400",
|
|
294
|
+
IBMPlexMono_500Medium: "500",
|
|
295
|
+
IBMPlexSans_300Light: "300",
|
|
296
|
+
IBMPlexSans_400Regular: "400",
|
|
297
|
+
IBMPlexSans_500Medium: "500",
|
|
298
|
+
IBMPlexSans_600SemiBold: "600",
|
|
299
|
+
IBMPlexSans_700Bold: "700",
|
|
300
|
+
};
|
|
301
|
+
export function appliedTextStyle(variant, bold = false, semibold = false) {
|
|
302
|
+
const fontFamily = resolveTextFontFamily(variant, bold, semibold);
|
|
303
|
+
const { fontWeight, ...metrics } = typographyVariants[variant];
|
|
304
|
+
return {
|
|
305
|
+
...metrics,
|
|
306
|
+
fontFamily,
|
|
307
|
+
fontWeight: loadedFaceWeight[fontFamily] ?? fontWeight,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
289
310
|
/* -------------------- Utility Functions -------------------- */
|
|
290
311
|
export const createTextStyle = (variant, overrides) => ({
|
|
291
|
-
...
|
|
312
|
+
...appliedTextStyle(variant),
|
|
292
313
|
...overrides,
|
|
293
314
|
});
|
|
294
315
|
export const createCustomTextStyle = (fontSize, fontFamily, options) => {
|
package/dist/utils/timezone.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Region-coupled (LatAm locale data); relocated to the core alongside the
|
|
3
|
-
* TimeZone widgets. Import from `@sincpro/mobile/ui/components/molecules` instead. Kept
|
|
4
|
-
* here for backwards compatibility and removed in the next major.
|
|
5
|
-
*/
|
|
6
1
|
export interface TimezoneLocale {
|
|
7
2
|
timezone: string;
|
|
8
3
|
locale: string;
|
|
@@ -2,6 +2,7 @@ import type { KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
|
|
|
2
2
|
import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
|
|
3
3
|
import type { ImageSource } from "../utils/image.js";
|
|
4
4
|
import { ReactNode } from "react";
|
|
5
|
+
import type { Edge } from "react-native-safe-area-context";
|
|
5
6
|
interface AuthFormProps {
|
|
6
7
|
title?: string;
|
|
7
8
|
description?: string;
|
|
@@ -9,8 +10,9 @@ interface AuthFormProps {
|
|
|
9
10
|
onBack?: () => void;
|
|
10
11
|
FormNode: ReactNode;
|
|
11
12
|
background?: AppBarBackground;
|
|
13
|
+
edges?: Edge[];
|
|
12
14
|
keyboardToolbar?: boolean;
|
|
13
15
|
keyboardFormProps?: Omit<KeyboardFormProps, "children" | "toolbar">;
|
|
14
16
|
}
|
|
15
|
-
declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, keyboardToolbar, keyboardFormProps, }: AuthFormProps): import("react").JSX.Element;
|
|
17
|
+
declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, edges, keyboardToolbar, keyboardFormProps, }: AuthFormProps): import("react").JSX.Element;
|
|
16
18
|
export default AuthFormView;
|
|
@@ -5,7 +5,7 @@ import { Typography } from "../Typography/index.js";
|
|
|
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, background, keyboardToolbar, keyboardFormProps, }) {
|
|
8
|
+
function AuthFormView({ title, description, logoSource, onBack, FormNode, background, edges, keyboardToolbar, keyboardFormProps, }) {
|
|
9
9
|
const renderTitle = () => {
|
|
10
10
|
if (!title)
|
|
11
11
|
return null;
|
|
@@ -16,6 +16,6 @@ function AuthFormView({ title, description, logoSource, onBack, FormNode, backgr
|
|
|
16
16
|
return null;
|
|
17
17
|
return _jsx(Text, { variant: "body", children: description });
|
|
18
18
|
};
|
|
19
|
-
return (_jsx(AuthContainer, { background: background, header: _jsx(Logo, { size: "large", source: logoSource }), keyboardFormProps: keyboardFormProps, keyboardToolbar: keyboardToolbar, onBackPress: onBack, children: _jsxs(View, { className: "m-4", children: [_jsxs(View, { className: "mb-5", children: [renderTitle(), renderDescription()] }), FormNode] }) }));
|
|
19
|
+
return (_jsx(AuthContainer, { background: background, edges: edges, header: _jsx(Logo, { size: "large", source: logoSource }), keyboardFormProps: keyboardFormProps, keyboardToolbar: keyboardToolbar, 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,8 +1,8 @@
|
|
|
1
1
|
import { type KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
|
|
2
2
|
import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
|
|
3
3
|
import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
|
|
4
|
-
import type { ImageSource } from "../utils/image.js";
|
|
5
4
|
import React, { ReactNode } from "react";
|
|
5
|
+
import type { Edge } from "react-native-safe-area-context";
|
|
6
6
|
interface FormViewProps<T> {
|
|
7
7
|
name: string;
|
|
8
8
|
description?: string;
|
|
@@ -17,11 +17,14 @@ interface FormViewProps<T> {
|
|
|
17
17
|
onRetry?: () => void;
|
|
18
18
|
onBack?: () => void;
|
|
19
19
|
withContainer?: boolean;
|
|
20
|
+
edges?: Edge[];
|
|
20
21
|
children: ReactNode;
|
|
21
22
|
}
|
|
22
23
|
export declare function refreshFormData(onRefresh: (() => Promise<void>) | undefined, onRefreshError: ((error: unknown) => void) | undefined, setShowRefreshing: (value: boolean) => void): Promise<void>;
|
|
23
|
-
declare function FormViewRoot<T>({ name, description, item, isLoading, isEmpty, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onBack, withContainer, children, }: FormViewProps<T>): React.JSX.Element;
|
|
24
|
+
declare function FormViewRoot<T>({ name, description, item, isLoading, isEmpty, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onBack, withContainer, edges, children, }: FormViewProps<T>): React.JSX.Element;
|
|
24
25
|
interface HeaderProps {
|
|
26
|
+
/** Reserve the top inset; disable when the host already handles it. */
|
|
27
|
+
safeArea?: boolean;
|
|
25
28
|
/** AppBar display variant. `large` = big title (list/index screens). `center` = modal/detail. */
|
|
26
29
|
variant?: "default" | "large" | "center";
|
|
27
30
|
/** Surface tone. `dark` = always-dark hero. `gradient` = brand accent gradient. */
|
|
@@ -40,10 +43,8 @@ interface HeaderProps {
|
|
|
40
43
|
* See `AppBarBackground` for the full shape.
|
|
41
44
|
*/
|
|
42
45
|
background?: AppBarBackground;
|
|
43
|
-
/** @deprecated Pass `variant` prop instead. */
|
|
44
|
-
logoSource?: ImageSource;
|
|
45
46
|
}
|
|
46
|
-
declare function Header({ variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
|
|
47
|
+
declare function Header({ safeArea, variant, tone, actions, children, topSpacing, background, }: HeaderProps): React.JSX.Element;
|
|
47
48
|
declare function HeaderActions({ children }: {
|
|
48
49
|
children: ReactNode;
|
|
49
50
|
}): React.JSX.Element;
|
|
@@ -62,13 +63,6 @@ declare function Content({ withMargin, keyboardToolbar, keyboardFormProps, child
|
|
|
62
63
|
keyboardFormProps?: Omit<KeyboardFormProps, "children" | "toolbar">;
|
|
63
64
|
children: ReactNode;
|
|
64
65
|
}): React.JSX.Element;
|
|
65
|
-
/**
|
|
66
|
-
* @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
|
|
67
|
-
* Use `<FormViewV2.Content>` directly — this wrapper is redundant.
|
|
68
|
-
*/
|
|
69
|
-
declare function Form({ children }: {
|
|
70
|
-
children: ReactNode;
|
|
71
|
-
}): React.JSX.Element;
|
|
72
66
|
/** Layout wrapper for a set of Group cards. Content already handles isEmpty/loading states. */
|
|
73
67
|
declare function Groups({ children }: {
|
|
74
68
|
children: ReactNode;
|
|
@@ -148,7 +142,6 @@ export declare const FormViewV2: {
|
|
|
148
142
|
Segmented: typeof HeaderSegmented;
|
|
149
143
|
};
|
|
150
144
|
Content: typeof Content & {
|
|
151
|
-
Form: typeof Form;
|
|
152
145
|
Groups: typeof Groups;
|
|
153
146
|
Group: typeof Group;
|
|
154
147
|
Section: typeof Section;
|
package/dist/views/FormViewV2.js
CHANGED
|
@@ -10,8 +10,17 @@ import { SegmentedControl, } from "../Navigation/Navigation.SegmentedControl.js"
|
|
|
10
10
|
import { useTheme } from "../theme/index.js";
|
|
11
11
|
import { FormViewProvider, useFormView } from "./FormViewV2.context.js";
|
|
12
12
|
import React from "react";
|
|
13
|
-
import { RefreshControl, View } from "react-native";
|
|
13
|
+
import { RefreshControl, useWindowDimensions, View } from "react-native";
|
|
14
|
+
import { KeyboardStickyView } from "react-native-keyboard-controller";
|
|
14
15
|
const RefreshErrorContext = React.createContext(undefined);
|
|
16
|
+
const FooterHeightContext = React.createContext(null);
|
|
17
|
+
function useFooterHeight() {
|
|
18
|
+
const context = React.useContext(FooterHeightContext);
|
|
19
|
+
if (!context) {
|
|
20
|
+
throw new Error("FormViewV2.Content and Footer must be used within FormViewV2.Root");
|
|
21
|
+
}
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
15
24
|
export async function refreshFormData(onRefresh, onRefreshError, setShowRefreshing) {
|
|
16
25
|
setShowRefreshing(true);
|
|
17
26
|
try {
|
|
@@ -30,16 +39,16 @@ export async function refreshFormData(onRefresh, onRefreshError, setShowRefreshi
|
|
|
30
39
|
setShowRefreshing(false);
|
|
31
40
|
}
|
|
32
41
|
}
|
|
33
|
-
function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onBack, withContainer = true, children, }) {
|
|
42
|
+
function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onBack, withContainer = true, edges = ["left", "right"], children, }) {
|
|
43
|
+
const [footerHeight, setFooterHeight] = React.useState(0);
|
|
44
|
+
const footerContext = React.useMemo(() => ({ height: footerHeight, setHeight: setFooterHeight }), [footerHeight]);
|
|
34
45
|
const Wrapper = withContainer ? Container : View;
|
|
35
|
-
const wrapperProps = withContainer
|
|
36
|
-
|
|
37
|
-
: { style: { flex: 1 } };
|
|
38
|
-
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(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }) }));
|
|
46
|
+
const wrapperProps = withContainer ? { edges } : { style: { flex: 1 } };
|
|
47
|
+
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(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: _jsx(FooterHeightContext.Provider, { value: footerContext, children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }) }) }));
|
|
39
48
|
}
|
|
40
|
-
function Header({ variant = "default", tone, actions, children, topSpacing, background, }) {
|
|
49
|
+
function Header({ safeArea = true, variant = "default", tone, actions, children, topSpacing, background, }) {
|
|
41
50
|
const { name, description, onBack } = useFormView();
|
|
42
|
-
return (_jsx(AppBar, { actions: actions, background: background, onBack: onBack, safeArea:
|
|
51
|
+
return (_jsx(AppBar, { actions: actions, background: background, onBack: onBack, safeArea: safeArea, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
|
|
43
52
|
}
|
|
44
53
|
function HeaderActions({ children }) {
|
|
45
54
|
return (_jsx(View, { className: "flex-row px-4 my-2 items-center justify-between flex-1 gap-2", children: children }));
|
|
@@ -55,9 +64,11 @@ function HeaderActionsBar({ children }) {
|
|
|
55
64
|
function HeaderSegmented(props) {
|
|
56
65
|
return _jsx(SegmentedControl, { ...props });
|
|
57
66
|
}
|
|
58
|
-
function Content({ withMargin = false, keyboardToolbar =
|
|
67
|
+
function Content({ withMargin = false, keyboardToolbar = true, keyboardFormProps, children, }) {
|
|
59
68
|
const { name, isLoading, isEmpty, hasError, errorMessage, item, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useFormView();
|
|
60
69
|
const bottomInset = useBottomInset();
|
|
70
|
+
const { height: measuredFooterHeight } = useFooterHeight();
|
|
71
|
+
const stickyFooterHeight = isLoading || isEmpty ? 0 : measuredFooterHeight;
|
|
61
72
|
const theme = useTheme();
|
|
62
73
|
const onRefreshError = React.useContext(RefreshErrorContext);
|
|
63
74
|
const handleRefresh = React.useCallback(async () => {
|
|
@@ -72,18 +83,7 @@ function Content({ withMargin = false, keyboardToolbar = false, keyboardFormProp
|
|
|
72
83
|
paddingBottom: bottomInset,
|
|
73
84
|
},
|
|
74
85
|
keyboardFormProps?.contentContainerStyle,
|
|
75
|
-
], keyboardShouldPersistTaps: keyboardFormProps?.keyboardShouldPersistTaps ?? "handled", refreshControl: keyboardFormProps?.refreshControl ?? (_jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing })), style: [{ flex: 1, backgroundColor: theme.bg.page }, keyboardFormProps?.style], toolbar: keyboardToolbar, 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 })) }));
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
|
|
79
|
-
* Use `<FormViewV2.Content>` directly — this wrapper is redundant.
|
|
80
|
-
*/
|
|
81
|
-
function Form({ children }) {
|
|
82
|
-
const { isEmpty, item } = useFormView();
|
|
83
|
-
if (isEmpty || !item) {
|
|
84
|
-
return _jsx(Feedback.Empty, {});
|
|
85
|
-
}
|
|
86
|
-
return _jsx(_Fragment, { children: children });
|
|
86
|
+
], keyboardShouldPersistTaps: keyboardFormProps?.keyboardShouldPersistTaps ?? "handled", refreshControl: keyboardFormProps?.refreshControl ?? (_jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing })), stickyFooterHeight: stickyFooterHeight, style: [{ flex: 1, backgroundColor: theme.bg.page }, keyboardFormProps?.style], toolbar: keyboardToolbar, 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 })) }));
|
|
87
87
|
}
|
|
88
88
|
/** Layout wrapper for a set of Group cards. Content already handles isEmpty/loading states. */
|
|
89
89
|
function Groups({ children }) {
|
|
@@ -122,12 +122,17 @@ function Section({ title, footer, children, }) {
|
|
|
122
122
|
}
|
|
123
123
|
function Footer({ children }) {
|
|
124
124
|
const { isEmpty, isLoading } = useFormView();
|
|
125
|
+
const { setHeight } = useFooterHeight();
|
|
126
|
+
const frame = React.useRef(null);
|
|
127
|
+
const { height: windowHeight } = useWindowDimensions();
|
|
128
|
+
const [bottomGap, setBottomGap] = React.useState(0);
|
|
129
|
+
React.useEffect(() => () => setHeight(0), [setHeight]);
|
|
125
130
|
if (isEmpty || isLoading) {
|
|
126
131
|
return null;
|
|
127
132
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
return (_jsx(View, { onLayout: () => {
|
|
134
|
+
frame.current?.measureInWindow((_x, y, _width, height) => setBottomGap(Math.max(0, windowHeight - y - height)));
|
|
135
|
+
}, ref: frame, children: _jsx(KeyboardStickyView, { offset: { closed: 0, opened: bottomGap }, children: _jsx(View, { className: "bg-bg-card px-4 py-3 border-t border-border-light", onLayout: (event) => setHeight(event.nativeEvent.layout.height), children: children }) }) }));
|
|
131
136
|
}
|
|
132
137
|
// Nested Header components
|
|
133
138
|
const HeaderWithSubComponents = Object.assign(Header, {
|
|
@@ -137,7 +142,6 @@ const HeaderWithSubComponents = Object.assign(Header, {
|
|
|
137
142
|
});
|
|
138
143
|
// Nested Content components
|
|
139
144
|
const ContentWithSubComponents = Object.assign(Content, {
|
|
140
|
-
Form,
|
|
141
145
|
Groups,
|
|
142
146
|
Group,
|
|
143
147
|
Section,
|
|
@@ -2,7 +2,7 @@ import { ReactNode } from "react";
|
|
|
2
2
|
interface ListViewContextValue<T> {
|
|
3
3
|
name: string;
|
|
4
4
|
description?: string;
|
|
5
|
-
items: T[];
|
|
5
|
+
items: readonly T[];
|
|
6
6
|
isLoading: boolean;
|
|
7
7
|
hasError: boolean;
|
|
8
8
|
errorMessage?: string;
|
|
@@ -11,8 +11,8 @@ interface ListViewContextValue<T> {
|
|
|
11
11
|
setShowRefreshing: (value: boolean) => void;
|
|
12
12
|
onRefresh?: () => Promise<void>;
|
|
13
13
|
onRetry?: () => void;
|
|
14
|
-
onPressItem
|
|
15
|
-
onDeleteItem
|
|
14
|
+
onPressItem?(item: T): void;
|
|
15
|
+
onDeleteItem?(item: T): void;
|
|
16
16
|
onSearch?: (query: string) => void;
|
|
17
17
|
onBack?: () => void;
|
|
18
18
|
onLoadMore?: () => void;
|
|
@@ -22,7 +22,7 @@ interface ListViewContextValue<T> {
|
|
|
22
22
|
export declare function ListViewProvider<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, children, }: {
|
|
23
23
|
name: string;
|
|
24
24
|
description?: string;
|
|
25
|
-
items: T[];
|
|
25
|
+
items: readonly T[];
|
|
26
26
|
isLoading?: boolean;
|
|
27
27
|
hasError?: boolean;
|
|
28
28
|
errorMessage?: string;
|
|
@@ -38,5 +38,5 @@ export declare function ListViewProvider<T>({ name, description, items, isLoadin
|
|
|
38
38
|
hasMore?: boolean;
|
|
39
39
|
children: ReactNode;
|
|
40
40
|
}): import("react").JSX.Element;
|
|
41
|
-
export declare function useListView<T>(): ListViewContextValue<T>;
|
|
41
|
+
export declare function useListView<T = unknown>(): ListViewContextValue<T>;
|
|
42
42
|
export {};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { type FlashListProps, type FlashListRef } from "@shopify/flash-list";
|
|
1
2
|
import { type SearchHistoryProps } from "../Display/Display.SearchHistory.js";
|
|
2
3
|
import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
|
|
3
4
|
import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
|
|
4
|
-
import { IRowItem } from "./types/IListView.js";
|
|
5
|
+
import { type IRowItem } from "./types/IListView.js";
|
|
5
6
|
import React, { ReactNode, type Ref } from "react";
|
|
6
|
-
import {
|
|
7
|
+
import { type ScrollViewProps, StyleProp, ViewStyle } from "react-native";
|
|
8
|
+
import type { Edge } from "react-native-safe-area-context";
|
|
7
9
|
interface ListViewProps<T> {
|
|
8
10
|
name: string;
|
|
9
11
|
description?: string;
|
|
10
|
-
items: T[];
|
|
12
|
+
items: readonly T[];
|
|
11
13
|
isLoading?: boolean;
|
|
12
14
|
hasError?: boolean;
|
|
13
15
|
errorMessage?: string;
|
|
@@ -26,11 +28,14 @@ interface ListViewProps<T> {
|
|
|
26
28
|
hasMore?: boolean;
|
|
27
29
|
backgroundColor?: string;
|
|
28
30
|
withContainer?: boolean;
|
|
31
|
+
edges?: Edge[];
|
|
29
32
|
children: ReactNode;
|
|
30
33
|
}
|
|
31
34
|
export declare function refreshListData(onRefresh: (() => Promise<void>) | undefined, onRefreshError: ((error: unknown) => void) | undefined, setShowRefreshing: (value: boolean) => void): Promise<void>;
|
|
32
|
-
declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, backgroundColor, withContainer, children, }: ListViewProps<T>): React.JSX.Element;
|
|
35
|
+
declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, hasMore, backgroundColor, withContainer, edges, children, }: ListViewProps<T>): React.JSX.Element;
|
|
33
36
|
interface HeaderProps {
|
|
37
|
+
/** Reserve the top inset; disable when the host already handles it. */
|
|
38
|
+
safeArea?: boolean;
|
|
34
39
|
/** AppBar display variant. `large` = big title (list/index). `center` = modal/detail. */
|
|
35
40
|
variant?: "default" | "large" | "center";
|
|
36
41
|
/** Surface tone. `dark` = always-dark hero. `gradient` = brand accent gradient. */
|
|
@@ -50,7 +55,7 @@ interface HeaderProps {
|
|
|
50
55
|
*/
|
|
51
56
|
topSpacing?: number;
|
|
52
57
|
}
|
|
53
|
-
declare function Header({ variant, tone, actions, children, background, topSpacing, }: HeaderProps): React.JSX.Element;
|
|
58
|
+
declare function Header({ safeArea, variant, tone, actions, children, background, topSpacing, }: HeaderProps): React.JSX.Element;
|
|
54
59
|
declare function HeaderSearch({ searchQuery }: {
|
|
55
60
|
searchQuery?: string;
|
|
56
61
|
}): React.JSX.Element | null;
|
|
@@ -84,20 +89,20 @@ declare function FilterChips({ children }: {
|
|
|
84
89
|
children: ReactNode;
|
|
85
90
|
}): React.JSX.Element;
|
|
86
91
|
type ListScrollProps = Pick<ScrollViewProps, "keyboardShouldPersistTaps" | "keyboardDismissMode" | "testID">;
|
|
87
|
-
export type ListViewVirtualizationProps = Pick<
|
|
88
|
-
interface ListContentOptions {
|
|
89
|
-
listProps?: ListViewVirtualizationProps
|
|
90
|
-
listRef?: Ref<
|
|
92
|
+
export type ListViewVirtualizationProps<T = unknown> = Pick<FlashListProps<IRowItem<T>>, "drawDistance" | "maxItemsInRecyclePool" | "getItemType" | "extraData" | "initialScrollIndex" | "initialScrollIndexParams" | "onViewableItemsChanged" | "viewabilityConfig" | "viewabilityConfigCallbackPairs" | "onEndReachedThreshold" | "maintainVisibleContentPosition">;
|
|
93
|
+
interface ListContentOptions<T> {
|
|
94
|
+
listProps?: ListViewVirtualizationProps<T>;
|
|
95
|
+
listRef?: Ref<FlashListRef<IRowItem<T>>>;
|
|
91
96
|
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
92
97
|
}
|
|
93
|
-
declare function
|
|
94
|
-
children?: (item:
|
|
95
|
-
withMargin?: boolean;
|
|
96
|
-
} & ListScrollProps & ListContentOptions): React.JSX.Element;
|
|
97
|
-
declare function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
|
|
98
|
-
children?: (item: any) => ReactNode;
|
|
98
|
+
declare function ContentList<T = unknown>({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
|
|
99
|
+
children?: (item: T, row: IRowItem<T>) => ReactNode;
|
|
99
100
|
contentWithMargin: boolean;
|
|
100
|
-
} & ListScrollProps & ListContentOptions): React.JSX.Element;
|
|
101
|
+
} & ListScrollProps & ListContentOptions<T>): React.JSX.Element;
|
|
102
|
+
declare function Content<T = unknown>({ children, withMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }: {
|
|
103
|
+
children?: (item: T, row: IRowItem<T>) => ReactNode;
|
|
104
|
+
withMargin?: boolean;
|
|
105
|
+
} & ListScrollProps & ListContentOptions<T>): React.JSX.Element;
|
|
101
106
|
declare function Footer({ children }: {
|
|
102
107
|
children: ReactNode;
|
|
103
108
|
}): React.JSX.Element | null;
|
|
@@ -153,7 +158,7 @@ interface RowActionButtonProps {
|
|
|
153
158
|
declare function RowActionButton({ icon, onPress }: RowActionButtonProps): React.JSX.Element;
|
|
154
159
|
/**
|
|
155
160
|
* Screen container for a collection (list / index / search results).
|
|
156
|
-
* Owns safe-area, the
|
|
161
|
+
* Owns safe-area, the FlashList (pagination + pull-to-refresh), record count, and the
|
|
157
162
|
* loading/empty/error states.
|
|
158
163
|
*
|
|
159
164
|
* API map:
|
|
@@ -164,7 +169,7 @@ declare function RowActionButton({ icon, onPress }: RowActionButtonProps): React
|
|
|
164
169
|
* .Header.Segmented — tab-like switch in the subheader (Activos / Archivados)
|
|
165
170
|
* .Header.Filters(.Chips/.Chip) — horizontal filter pills (SIBLING below the header)
|
|
166
171
|
* .Header.ActionsBar — SIBLING bar below the header (bulk select, etc.)
|
|
167
|
-
* ListViewV2.Content —
|
|
172
|
+
* ListViewV2.Content — FlashList body; `children` is an optional custom row renderer
|
|
168
173
|
* .Content.Row(.Avatar/.Content/.Title/.Subtitle/.Footer/.Actions/.ActionButton)
|
|
169
174
|
* ListViewV2.Footer — pinned footer (hidden when empty)
|
|
170
175
|
* ListViewV2.FloatingButton — absolute FAB overlay
|
package/dist/views/ListViewV2.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import { FlashList, } from "@shopify/flash-list";
|
|
2
3
|
import { Display } from "../Display/index.js";
|
|
3
4
|
import SearchHistory from "../Display/Display.SearchHistory.js";
|
|
4
5
|
import { ErrorBoundary, Feedback } from "../Feedback/index.js";
|
|
@@ -12,7 +13,7 @@ import { Typography } from "../Typography/index.js";
|
|
|
12
13
|
import { ListViewProvider, useListView } from "./ListViewV2.context.js";
|
|
13
14
|
import { toRowItems } from "./types/IListView.js";
|
|
14
15
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
|
|
15
|
-
import {
|
|
16
|
+
import { RefreshControl, ScrollView, TouchableOpacity, View, } from "react-native";
|
|
16
17
|
const RefreshErrorContext = createContext(undefined);
|
|
17
18
|
export async function refreshListData(onRefresh, onRefreshError, setShowRefreshing) {
|
|
18
19
|
setShowRefreshing(true);
|
|
@@ -32,17 +33,13 @@ export async function refreshListData(onRefresh, onRefreshError, setShowRefreshi
|
|
|
32
33
|
setShowRefreshing(false);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
|
-
function ListViewRoot({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, hasMore = true, backgroundColor, withContainer = true, children, }) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
? { edges: ["bottom", "left", "right"] }
|
|
39
|
-
: { className: "flex-1 bg-bg-page" };
|
|
40
|
-
// Propagate contentWithMargin to ContentList via context
|
|
41
|
-
return (_jsx(ListViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, hasMore: hasMore, 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(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: _jsx(Wrapper, { ...wrapperProps, children: children }) }) }) }));
|
|
36
|
+
function ListViewRoot({ name, description, items, isLoading = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore = false, hasMore = true, backgroundColor, withContainer = true, edges, children, }) {
|
|
37
|
+
const style = backgroundColor ? { backgroundColor } : undefined;
|
|
38
|
+
return (_jsx(ListViewProvider, { description: description, errorMessage: errorMessage, hasError: hasError, hasMore: hasMore, 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(RefreshErrorContext.Provider, { value: onRefreshError, children: _jsx(ErrorBoundary, { children: withContainer ? (_jsx(Container, { edges: edges, style: style, children: children })) : (_jsx(View, { className: "flex-1 bg-bg-page", style: style, children: children })) }) }) }));
|
|
42
39
|
}
|
|
43
|
-
function Header({ variant = "large", tone, actions, children, background, topSpacing, }) {
|
|
40
|
+
function Header({ safeArea = true, variant = "large", tone, actions, children, background, topSpacing, }) {
|
|
44
41
|
const { name, description, onBack } = useListView();
|
|
45
|
-
return (_jsx(Navigation.AppBar, { actions: actions, background: background, onBack: onBack, safeArea:
|
|
42
|
+
return (_jsx(Navigation.AppBar, { actions: actions, background: background, onBack: onBack, safeArea: safeArea, subheader: children, subtitle: description ?? undefined, title: name, tone: tone, topSpacing: topSpacing, variant: variant }));
|
|
46
43
|
}
|
|
47
44
|
function HeaderSearch({ searchQuery = "" }) {
|
|
48
45
|
const { name, onSearch } = useListView();
|
|
@@ -118,8 +115,41 @@ function FilterChip({ label, active = false, onPress }) {
|
|
|
118
115
|
function FilterChips({ children }) {
|
|
119
116
|
return (_jsx(ScrollView, { contentContainerStyle: { paddingHorizontal: 16, gap: 8 }, horizontal: true, showsHorizontalScrollIndicator: false, style: { flexGrow: 0 }, children: children }));
|
|
120
117
|
}
|
|
121
|
-
function rowKey(
|
|
122
|
-
return String(
|
|
118
|
+
function rowKey(row) {
|
|
119
|
+
return String(row.id);
|
|
120
|
+
}
|
|
121
|
+
function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }) {
|
|
122
|
+
const { items, onRefresh, showRefreshing, setShowRefreshing, onPressItem, onDeleteItem, readonly, onLoadMore, loadingMore, hasMore, } = useListView();
|
|
123
|
+
const bottomInset = useBottomInset();
|
|
124
|
+
const theme = useTheme();
|
|
125
|
+
const rowItems = useMemo(() => toRowItems(items), [items]);
|
|
126
|
+
const onRefreshError = useContext(RefreshErrorContext);
|
|
127
|
+
const handleRefresh = useCallback(async () => {
|
|
128
|
+
await refreshListData(onRefresh, onRefreshError, setShowRefreshing);
|
|
129
|
+
}, [onRefresh, onRefreshError, setShowRefreshing]);
|
|
130
|
+
const renderItem = useCallback(({ item: row }) => {
|
|
131
|
+
if (children) {
|
|
132
|
+
return _jsx(_Fragment, { children: children(row.item, row) });
|
|
133
|
+
}
|
|
134
|
+
const onPress = () => onPressItem?.(row.item);
|
|
135
|
+
const onDelete = () => onDeleteItem?.(row.item);
|
|
136
|
+
const isReadOnly = row.readonly || readonly || false;
|
|
137
|
+
return (_jsx(Navigation.RowItem, { item: row, onDelete: onDelete, onPress: onPress, readonly: isReadOnly }));
|
|
138
|
+
}, [children, onPressItem, onDeleteItem, readonly]);
|
|
139
|
+
return (_jsx(View, { style: {
|
|
140
|
+
flex: 1,
|
|
141
|
+
marginTop: 2,
|
|
142
|
+
paddingHorizontal: contentWithMargin ? 4 : 0,
|
|
143
|
+
backgroundColor: theme.bg.page,
|
|
144
|
+
}, children: _jsx(FlashList, { ...listProps, contentContainerStyle: [
|
|
145
|
+
bottomInset ? { paddingBottom: bottomInset } : undefined,
|
|
146
|
+
contentContainerStyle,
|
|
147
|
+
], data: rowItems, keyboardDismissMode: keyboardDismissMode, keyboardShouldPersistTaps: keyboardShouldPersistTaps, keyExtractor: rowKey, ListFooterComponent: loadingMore ? (_jsx(View, { className: "py-4", children: _jsx(Feedback.Spinner, { size: "small" }) })) : undefined, maintainVisibleContentPosition: listProps?.maintainVisibleContentPosition ?? { disabled: true }, onEndReached: onLoadMore
|
|
148
|
+
? () => {
|
|
149
|
+
if (hasMore && !loadingMore && !showRefreshing)
|
|
150
|
+
onLoadMore();
|
|
151
|
+
}
|
|
152
|
+
: undefined, onEndReachedThreshold: listProps?.onEndReachedThreshold ?? 0.5, ref: listRef, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), renderItem: renderItem, showsVerticalScrollIndicator: false, style: { flex: 1 }, testID: testID }) }));
|
|
123
153
|
}
|
|
124
154
|
function Content({ children, withMargin = true, keyboardShouldPersistTaps = "handled", keyboardDismissMode = "none", testID, listProps, listRef, contentContainerStyle, }) {
|
|
125
155
|
const { name, items, isLoading, hasError, errorMessage, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useListView();
|
|
@@ -156,39 +186,6 @@ function Content({ children, withMargin = true, keyboardShouldPersistTaps = "han
|
|
|
156
186
|
}
|
|
157
187
|
return (_jsxs(View, { style: { flex: 1, backgroundColor: theme.bg.page }, children: [_jsx(Display.CountRecords, { count: items.length || 0, name: name }), _jsx(ContentList, { contentContainerStyle: contentContainerStyle, contentWithMargin: withMargin, keyboardDismissMode: keyboardDismissMode, keyboardShouldPersistTaps: keyboardShouldPersistTaps, listProps: listProps, listRef: listRef, testID: testID, children: children })] }));
|
|
158
188
|
}
|
|
159
|
-
function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, listProps, listRef, contentContainerStyle, }) {
|
|
160
|
-
const { items, onRefresh, showRefreshing, setShowRefreshing, onPressItem, onDeleteItem, readonly, onLoadMore, loadingMore, hasMore, } = useListView();
|
|
161
|
-
const bottomInset = useBottomInset();
|
|
162
|
-
const theme = useTheme();
|
|
163
|
-
const rowItems = useMemo(() => toRowItems(items), [items]);
|
|
164
|
-
const onRefreshError = useContext(RefreshErrorContext);
|
|
165
|
-
const handleRefresh = useCallback(async () => {
|
|
166
|
-
await refreshListData(onRefresh, onRefreshError, setShowRefreshing);
|
|
167
|
-
}, [onRefresh, onRefreshError, setShowRefreshing]);
|
|
168
|
-
const renderItem = useCallback(({ item }) => {
|
|
169
|
-
if (children) {
|
|
170
|
-
return _jsx(_Fragment, { children: children(item) });
|
|
171
|
-
}
|
|
172
|
-
const onPress = () => onPressItem?.(item);
|
|
173
|
-
const onDelete = () => onDeleteItem?.(item);
|
|
174
|
-
const isReadOnly = item.readonly || readonly || false;
|
|
175
|
-
return (_jsx(Navigation.RowItem, { item: { ...item }, onDelete: onDelete, onPress: onPress, readonly: isReadOnly }));
|
|
176
|
-
}, [children, onPressItem, onDeleteItem, readonly]);
|
|
177
|
-
return (_jsx(View, { style: {
|
|
178
|
-
flex: 1,
|
|
179
|
-
marginTop: 2,
|
|
180
|
-
paddingHorizontal: contentWithMargin ? 4 : 0,
|
|
181
|
-
backgroundColor: theme.bg.page,
|
|
182
|
-
}, children: _jsx(FlatList, { ...listProps, contentContainerStyle: [
|
|
183
|
-
bottomInset ? { paddingBottom: bottomInset } : undefined,
|
|
184
|
-
contentContainerStyle,
|
|
185
|
-
], data: rowItems, keyboardDismissMode: keyboardDismissMode, keyboardShouldPersistTaps: keyboardShouldPersistTaps, keyExtractor: rowKey, ListFooterComponent: loadingMore ? (_jsx(View, { className: "py-4", children: _jsx(Feedback.Spinner, { size: "small" }) })) : undefined, onEndReached: onLoadMore
|
|
186
|
-
? () => {
|
|
187
|
-
if (hasMore && !loadingMore && !showRefreshing)
|
|
188
|
-
onLoadMore();
|
|
189
|
-
}
|
|
190
|
-
: undefined, onEndReachedThreshold: listProps?.onEndReachedThreshold ?? 0.5, ref: listRef, refreshControl: _jsx(RefreshControl, { colors: [theme.primary], onRefresh: handleRefresh, refreshing: showRefreshing }), renderItem: renderItem, scrollEnabled: true, showsVerticalScrollIndicator: false, testID: testID }) }));
|
|
191
|
-
}
|
|
192
189
|
function Footer({ children }) {
|
|
193
190
|
const { items } = useListView();
|
|
194
191
|
if (items.length === 0) {
|
|
@@ -259,7 +256,7 @@ const ContentWithSubComponents = Object.assign(Content, {
|
|
|
259
256
|
});
|
|
260
257
|
/**
|
|
261
258
|
* Screen container for a collection (list / index / search results).
|
|
262
|
-
* Owns safe-area, the
|
|
259
|
+
* Owns safe-area, the FlashList (pagination + pull-to-refresh), record count, and the
|
|
263
260
|
* loading/empty/error states.
|
|
264
261
|
*
|
|
265
262
|
* API map:
|
|
@@ -270,7 +267,7 @@ const ContentWithSubComponents = Object.assign(Content, {
|
|
|
270
267
|
* .Header.Segmented — tab-like switch in the subheader (Activos / Archivados)
|
|
271
268
|
* .Header.Filters(.Chips/.Chip) — horizontal filter pills (SIBLING below the header)
|
|
272
269
|
* .Header.ActionsBar — SIBLING bar below the header (bulk select, etc.)
|
|
273
|
-
* ListViewV2.Content —
|
|
270
|
+
* ListViewV2.Content — FlashList body; `children` is an optional custom row renderer
|
|
274
271
|
* .Content.Row(.Avatar/.Content/.Title/.Subtitle/.Footer/.Actions/.ActionButton)
|
|
275
272
|
* ListViewV2.Footer — pinned footer (hidden when empty)
|
|
276
273
|
* ListViewV2.FloatingButton — absolute FAB overlay
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { UICollectionItem, UIItem } from "./UIItem.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
-
export type IRowItem<T> =
|
|
3
|
+
export type IRowItem<T> = UIItem & {
|
|
4
|
+
item: T;
|
|
4
5
|
id: string | number;
|
|
5
6
|
index: number;
|
|
6
7
|
rowTitle: string;
|
|
7
8
|
rowSubtitle?: string;
|
|
8
9
|
};
|
|
9
10
|
export interface IRowItemProps<T> extends UIItem {
|
|
10
|
-
item: IRowItem<T
|
|
11
|
+
item: IRowItem<T>;
|
|
11
12
|
}
|
|
12
13
|
export interface IListView<T> extends UICollectionItem<T> {
|
|
13
14
|
name: string;
|
|
14
15
|
description?: string;
|
|
15
|
-
items: T[];
|
|
16
|
+
items: readonly T[];
|
|
16
17
|
isLoading?: boolean;
|
|
17
18
|
onRefresh?: () => void | Promise<void>;
|
|
18
19
|
onBack?: () => void | Promise<void>;
|
|
@@ -31,4 +32,4 @@ export type RowKeys<T> = Partial<{
|
|
|
31
32
|
readOnly?: keyof T;
|
|
32
33
|
}>;
|
|
33
34
|
export declare function toRowItem<T>(item: T, index: number, mapKeys?: RowKeys<T>): IRowItem<T>;
|
|
34
|
-
export declare function toRowItems<T>(items: T[], mapKeys?: RowKeys<T>): IRowItem<T>[];
|
|
35
|
+
export declare function toRowItems<T>(items: readonly T[], mapKeys?: RowKeys<T>): IRowItem<T>[];
|