@sincpro/mobile-ui 1.0.1 → 1.1.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 +572 -229
- package/dist/Actions/Actions.Button.d.ts +7 -2
- package/dist/Actions/Actions.Button.js +23 -10
- package/dist/Actions/Actions.FAB.d.ts +3 -1
- package/dist/Actions/Actions.FAB.js +7 -3
- package/dist/Actions/Actions.FilterButton.d.ts +10 -2
- package/dist/Actions/Actions.FilterButton.js +21 -6
- package/dist/Actions/Actions.IconButton.d.ts +5 -2
- package/dist/Actions/Actions.IconButton.js +7 -3
- package/dist/Actions/index.d.ts +2 -2
- package/dist/Actions/index.js +1 -1
- package/dist/Dialog/Dialog.ActionSheet.d.ts +2 -1
- package/dist/Dialog/Dialog.ActionSheet.js +15 -5
- package/dist/Dialog/Dialog.Confirmation.js +3 -3
- package/dist/Dialog/Dialog.NativeSheet.d.ts +10 -0
- package/dist/Dialog/Dialog.NativeSheet.js +21 -0
- package/dist/Dialog/Dialog.Sheet.d.ts +5 -1
- package/dist/Dialog/Dialog.Sheet.js +14 -8
- package/dist/Display/Display.CopyableText.d.ts +9 -2
- package/dist/Display/Display.CopyableText.js +75 -7
- package/dist/Display/Display.Icon.js +2 -1
- package/dist/Display/Display.KeyValue.d.ts +11 -4
- package/dist/Display/Display.KeyValue.js +6 -4
- package/dist/Display/Display.ListGroup.d.ts +23 -0
- package/dist/Display/Display.ListGroup.js +17 -0
- package/dist/Display/Display.ListItem.d.ts +16 -2
- package/dist/Display/Display.ListItem.js +25 -8
- package/dist/Display/Display.SectionHeader.d.ts +11 -1
- package/dist/Display/Display.SectionHeader.js +11 -2
- package/dist/Display/Display.SwipeableRow.d.ts +3 -1
- package/dist/Display/Display.SwipeableRow.js +3 -3
- package/dist/Display/index.d.ts +4 -0
- package/dist/Display/index.js +2 -0
- package/dist/Feedback/Feedback.Banner.js +1 -1
- package/dist/Feedback/Feedback.EmptyState.d.ts +2 -1
- package/dist/Feedback/Feedback.EmptyState.js +2 -2
- package/dist/Feedback/Feedback.Error.d.ts +3 -1
- package/dist/Feedback/Feedback.Error.js +4 -3
- package/dist/Form/Form.Checkbox.d.ts +4 -1
- package/dist/Form/Form.Checkbox.js +2 -2
- package/dist/Form/Form.DatePicker.d.ts +2 -1
- package/dist/Form/Form.DatePicker.js +4 -3
- package/dist/Form/Form.FilterButton.d.ts +1 -1
- package/dist/Form/Form.Input.d.ts +5 -3
- package/dist/Form/Form.Input.js +2 -2
- package/dist/Form/Form.KeyboardForm.d.ts +11 -0
- package/dist/Form/Form.KeyboardForm.js +9 -0
- package/dist/Form/Form.KeyboardToolbar.d.ts +21 -0
- package/dist/Form/Form.KeyboardToolbar.js +70 -0
- package/dist/Form/Form.Select.d.ts +3 -1
- package/dist/Form/Form.Select.js +23 -10
- package/dist/Form/index.d.ts +6 -0
- package/dist/Form/index.js +4 -0
- package/dist/Navigation/Navigation.AppBar.d.ts +6 -2
- package/dist/Navigation/Navigation.AppBar.js +7 -7
- package/dist/Navigation/Navigation.BottomNav.d.ts +3 -1
- package/dist/Navigation/Navigation.BottomNav.js +9 -5
- package/dist/Navigation/Navigation.RowItem.d.ts +1 -1
- package/dist/Navigation/Navigation.RowItem.js +2 -2
- package/dist/Navigation/Navigation.SearchBar.d.ts +6 -1
- package/dist/Navigation/Navigation.SearchBar.js +29 -8
- package/dist/Navigation/Navigation.SearchSuggestions.d.ts +14 -0
- package/dist/Navigation/Navigation.SearchSuggestions.js +7 -0
- package/dist/Navigation/Navigation.SegmentedControl.d.ts +3 -1
- package/dist/Navigation/Navigation.SegmentedControl.js +2 -1
- package/dist/Navigation/index.d.ts +6 -2
- package/dist/Navigation/index.js +2 -0
- package/dist/Typography/Typography.Heading.js +1 -1
- package/dist/Typography/Typography.SelectableText.d.ts +10 -0
- package/dist/Typography/Typography.SelectableText.js +16 -0
- package/dist/Typography/Typography.Text.js +4 -20
- package/dist/Typography/index.d.ts +3 -0
- package/dist/Typography/index.js +3 -0
- package/dist/layouts/AuthContainer.d.ts +4 -1
- package/dist/layouts/AuthContainer.js +3 -3
- package/dist/primitives/BaseButton.d.ts +7 -2
- package/dist/primitives/BaseButton.js +19 -5
- package/dist/theme/typography.d.ts +16 -1
- package/dist/theme/typography.js +19 -0
- package/dist/utils/criteria.d.ts +56 -0
- package/dist/utils/criteria.js +99 -0
- package/dist/utils/reorder.d.ts +4 -0
- package/dist/utils/reorder.js +32 -0
- package/dist/views/AuthFormView.d.ts +4 -1
- package/dist/views/AuthFormView.js +2 -2
- package/dist/views/FormViewV2.d.ts +7 -2
- package/dist/views/FormViewV2.js +35 -16
- package/dist/views/ListViewV2.d.ts +9 -6
- package/dist/views/ListViewV2.js +34 -21
- package/dist/widgets/CollectionOptions.d.ts +15 -0
- package/dist/widgets/CollectionOptions.js +127 -0
- package/dist/widgets/FilterBar.d.ts +21 -0
- package/dist/widgets/FilterBar.js +14 -0
- package/dist/widgets/FilterBuilder.d.ts +10 -0
- package/dist/widgets/FilterBuilder.js +115 -0
- package/dist/widgets/QueryChoice.d.ts +10 -0
- package/dist/widgets/QueryChoice.js +13 -0
- package/dist/widgets/ReorderableList.d.ts +51 -0
- package/dist/widgets/ReorderableList.js +138 -0
- package/dist/widgets/index.d.ts +4 -0
- package/dist/widgets/index.js +3 -0
- package/package.json +15 -1
|
@@ -2,14 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
|
2
2
|
import { Display } from "../Display/index.js";
|
|
3
3
|
import GradientSurface from "../Display/Display.GradientSurface.js";
|
|
4
4
|
import { Pattern } from "../Display/Display.Pattern.js";
|
|
5
|
+
import KeyboardForm from "../Form/Form.KeyboardForm.js";
|
|
5
6
|
import { useTheme } from "../theme/index.js";
|
|
6
7
|
import { cn } from "../theme/tw.js";
|
|
7
8
|
import { Keyboard, TouchableOpacity, TouchableWithoutFeedback, useWindowDimensions, View, } from "react-native";
|
|
8
|
-
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
|
|
9
9
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
10
|
-
function AuthContainer({ header, children, onBackPress, background, className, }) {
|
|
10
|
+
function AuthContainer({ header, children, onBackPress, background, className, keyboardToolbar = false, keyboardFormProps, }) {
|
|
11
11
|
const { height: screenHeight } = useWindowDimensions();
|
|
12
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 ?? "warm", 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(
|
|
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 ?? "warm", 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(KeyboardForm, { bottomOffset: keyboardToolbar ? undefined : 50, bounces: false, className: "flex-1", contentContainerClassName: "flex-grow pb-10", keyboardShouldPersistTaps: "handled", showsVerticalScrollIndicator: false, ...keyboardFormProps, toolbar: keyboardToolbar, children: _jsx(View, { className: "p-4", children: children }) }) })] }) })] }));
|
|
14
14
|
}
|
|
15
15
|
export default AuthContainer;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type VariantProps } from "../theme/tw.js";
|
|
2
2
|
import type React from "react";
|
|
3
3
|
import { FC } from "react";
|
|
4
|
+
import type { AccessibilityProps } from "react-native";
|
|
4
5
|
declare const button: import("tailwind-variants").TVReturnType<{
|
|
5
6
|
variant: {
|
|
6
7
|
primary: "bg-accent";
|
|
@@ -123,21 +124,25 @@ declare const buttonIcon: import("tailwind-variants").TVReturnType<{
|
|
|
123
124
|
true: "opacity-70";
|
|
124
125
|
};
|
|
125
126
|
}, undefined>>;
|
|
126
|
-
export interface BaseButtonProps extends VariantProps<typeof button
|
|
127
|
+
export interface BaseButtonProps extends VariantProps<typeof button>, AccessibilityProps {
|
|
127
128
|
title: string;
|
|
128
129
|
onPress?: () => void;
|
|
129
130
|
loading?: boolean;
|
|
130
131
|
disabled?: boolean;
|
|
131
132
|
className?: string;
|
|
132
133
|
icon?: React.ReactNode;
|
|
134
|
+
accessibleSizing?: boolean;
|
|
135
|
+
testID?: string;
|
|
133
136
|
}
|
|
134
137
|
export declare const BaseButton: FC<BaseButtonProps>;
|
|
135
|
-
export interface BaseButtonIconProps extends VariantProps<typeof buttonIcon
|
|
138
|
+
export interface BaseButtonIconProps extends VariantProps<typeof buttonIcon>, AccessibilityProps {
|
|
136
139
|
icon: string;
|
|
137
140
|
onPress?: () => void;
|
|
138
141
|
loading?: boolean;
|
|
139
142
|
disabled?: boolean;
|
|
140
143
|
className?: string;
|
|
144
|
+
accessibleSizing?: boolean;
|
|
145
|
+
testID?: string;
|
|
141
146
|
}
|
|
142
147
|
export declare const BaseButtonIcon: FC<BaseButtonIconProps>;
|
|
143
148
|
declare const BaseButtonWithIcon: React.FC<BaseButtonProps> & {
|
|
@@ -192,7 +192,7 @@ function getIconSize(size) {
|
|
|
192
192
|
function getTextVariant(size) {
|
|
193
193
|
return textVariantMap[size] ?? "button";
|
|
194
194
|
}
|
|
195
|
-
export const BaseButton = ({ title, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, icon, }) => {
|
|
195
|
+
export const BaseButton = ({ title, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, icon, accessibleSizing = false, testID, accessibilityLabel, accessibilityState, ...accessibilityProps }) => {
|
|
196
196
|
const textColorClass = textColor({
|
|
197
197
|
variant: variant ?? "primary",
|
|
198
198
|
disabled,
|
|
@@ -200,15 +200,29 @@ export const BaseButton = ({ title, onPress, variant = "primary", size = "medium
|
|
|
200
200
|
const loadingColorValue = getLoadingColor(variant ?? "primary");
|
|
201
201
|
const textVariantValue = getTextVariant(size ?? "medium");
|
|
202
202
|
if (variant === "link") {
|
|
203
|
-
return (_jsx(TouchableOpacity, { activeOpacity: 0.7, className: className, disabled: disabled || loading, onPress: onPress,
|
|
203
|
+
return (_jsx(TouchableOpacity, { activeOpacity: 0.7, className: className, disabled: disabled || loading, onPress: onPress, ...accessibilityProps, accessibilityLabel: accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
|
|
204
|
+
...accessibilityState,
|
|
205
|
+
disabled: disabled || loading,
|
|
206
|
+
busy: loading,
|
|
207
|
+
}, style: accessibleSizing
|
|
208
|
+
? { minHeight: 48, minWidth: 48, justifyContent: "center" }
|
|
209
|
+
: undefined, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Typography.Text, { className: textColorClass, variant: textVariantValue, children: title })) }));
|
|
204
210
|
}
|
|
205
|
-
return (_jsxs(TouchableOpacity, { activeOpacity: 0.8, className: button({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress,
|
|
211
|
+
return (_jsxs(TouchableOpacity, { activeOpacity: 0.8, className: button({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress, ...accessibilityProps, accessibilityLabel: accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: {
|
|
212
|
+
...accessibilityState,
|
|
213
|
+
disabled: disabled || loading,
|
|
214
|
+
busy: loading,
|
|
215
|
+
}, style: accessibleSizing ? { minHeight: 48, minWidth: 48 } : undefined, testID: testID, children: [icon && !loading && _jsx(View, { className: "mr-2", children: icon }), loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Typography.Text, { className: textColorClass, semibold: variant === "primary", variant: textVariantValue, children: title }))] }));
|
|
206
216
|
};
|
|
207
|
-
export const BaseButtonIcon = ({ icon, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, }) => {
|
|
217
|
+
export const BaseButtonIcon = ({ icon, onPress, variant = "primary", size = "medium", loading = false, disabled = false, className, accessibleSizing = false, testID, accessibilityState, ...accessibilityProps }) => {
|
|
208
218
|
const iconColorValue = getIconColor(variant ?? "primary", disabled);
|
|
209
219
|
const loadingColorValue = getLoadingColor(variant ?? "primary");
|
|
210
220
|
const iconSizeValue = getIconSize(size ?? "medium");
|
|
211
|
-
return (_jsx(TouchableOpacity, { activeOpacity: 0.8, className: buttonIcon({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress,
|
|
221
|
+
return (_jsx(TouchableOpacity, { activeOpacity: 0.8, className: buttonIcon({ variant, size, disabled, className }), disabled: disabled || loading, onPress: onPress, ...accessibilityProps, accessibilityRole: "button", accessibilityState: {
|
|
222
|
+
...accessibilityState,
|
|
223
|
+
disabled: disabled || loading,
|
|
224
|
+
busy: loading,
|
|
225
|
+
}, style: accessibleSizing ? { minHeight: 48, minWidth: 48 } : undefined, testID: testID, children: loading ? (_jsx(ActivityIndicator, { color: loadingColorValue, size: "small" })) : (_jsx(Icon, { color: iconColorValue, name: icon, size: iconSizeValue })) }));
|
|
212
226
|
};
|
|
213
227
|
// ============================================================================
|
|
214
228
|
// COMPOUND COMPONENT
|
|
@@ -10,6 +10,7 @@ export declare const fontFamilies: Record<FontRole, string>;
|
|
|
10
10
|
export declare function configureFonts(overrides: Partial<Record<FontRole, string>>): void;
|
|
11
11
|
/** Reset all font roles to the defaults (IBM Plex Sans + IBM Plex Mono). Useful in tests and stories. */
|
|
12
12
|
export declare function resetFonts(): void;
|
|
13
|
+
export declare function resolveTextFontFamily(variant: keyof typeof typographyVariants, bold?: boolean, semibold?: boolean): string;
|
|
13
14
|
export declare const fontWeights: {
|
|
14
15
|
readonly light: "300";
|
|
15
16
|
readonly regular: "400";
|
|
@@ -103,6 +104,13 @@ export declare const typographyVariants: {
|
|
|
103
104
|
readonly lineHeight: number;
|
|
104
105
|
readonly letterSpacing: 0;
|
|
105
106
|
};
|
|
107
|
+
readonly bodyReadable: {
|
|
108
|
+
readonly fontSize: 17;
|
|
109
|
+
readonly fontFamily: string;
|
|
110
|
+
readonly fontWeight: "400";
|
|
111
|
+
readonly lineHeight: number;
|
|
112
|
+
readonly letterSpacing: 0;
|
|
113
|
+
};
|
|
106
114
|
readonly bodyLarge: {
|
|
107
115
|
readonly fontSize: 16;
|
|
108
116
|
readonly fontFamily: string;
|
|
@@ -314,6 +322,13 @@ export declare const Typography: {
|
|
|
314
322
|
readonly lineHeight: number;
|
|
315
323
|
readonly letterSpacing: 0;
|
|
316
324
|
};
|
|
325
|
+
readonly bodyReadable: {
|
|
326
|
+
readonly fontSize: 17;
|
|
327
|
+
readonly fontFamily: string;
|
|
328
|
+
readonly fontWeight: "400";
|
|
329
|
+
readonly lineHeight: number;
|
|
330
|
+
readonly letterSpacing: 0;
|
|
331
|
+
};
|
|
317
332
|
readonly bodyLarge: {
|
|
318
333
|
readonly fontSize: 16;
|
|
319
334
|
readonly fontFamily: string;
|
|
@@ -421,7 +436,7 @@ export declare const Typography: {
|
|
|
421
436
|
readonly letterSpacing: 0;
|
|
422
437
|
};
|
|
423
438
|
};
|
|
424
|
-
readonly variantFontRole: Record<"label" | "button" | "title" | "display" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "bodyLarge" | "body" | "bodySmall" | "labelSmall" | "data" | "dataLarge" | "overline" | "caption" | "captionSmall" | "buttonSmall" | "buttonLarge" | "subtitle", FontRole>;
|
|
439
|
+
readonly variantFontRole: Record<"label" | "button" | "title" | "display" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "bodyReadable" | "bodyLarge" | "body" | "bodySmall" | "labelSmall" | "data" | "dataLarge" | "overline" | "caption" | "captionSmall" | "buttonSmall" | "buttonLarge" | "subtitle", FontRole>;
|
|
425
440
|
readonly createTextStyle: (variant: keyof typeof typographyVariants, overrides?: Partial<TextStyle>) => TextStyle;
|
|
426
441
|
readonly createCustomTextStyle: (fontSize: keyof typeof fontSizes, fontFamily: keyof typeof fontFamilies, options?: {
|
|
427
442
|
lineHeight?: keyof typeof lineHeights;
|
package/dist/theme/typography.js
CHANGED
|
@@ -26,6 +26,17 @@ export function configureFonts(overrides) {
|
|
|
26
26
|
export function resetFonts() {
|
|
27
27
|
Object.assign(fontFamilies, DEFAULT_FONT_FAMILIES);
|
|
28
28
|
}
|
|
29
|
+
export function resolveTextFontFamily(variant, bold = false, semibold = false) {
|
|
30
|
+
const variantFamily = fontFamilies[variantFontRole[variant]] ?? typographyVariants[variant].fontFamily;
|
|
31
|
+
const isBrandedFace = !variantFamily.startsWith("Inter_") && !variantFamily.startsWith("IBMPlexSans_");
|
|
32
|
+
return isBrandedFace
|
|
33
|
+
? variantFamily
|
|
34
|
+
: bold
|
|
35
|
+
? fontFamilies.extraBold
|
|
36
|
+
: semibold
|
|
37
|
+
? fontFamilies.semiBold
|
|
38
|
+
: variantFamily;
|
|
39
|
+
}
|
|
29
40
|
export const fontWeights = {
|
|
30
41
|
light: "300",
|
|
31
42
|
regular: "400",
|
|
@@ -126,6 +137,13 @@ export const typographyVariants = {
|
|
|
126
137
|
letterSpacing: letterSpacing.normal,
|
|
127
138
|
},
|
|
128
139
|
// Body
|
|
140
|
+
bodyReadable: {
|
|
141
|
+
fontSize: 17,
|
|
142
|
+
fontFamily: fontFamilies.regular,
|
|
143
|
+
fontWeight: fontWeights.regular,
|
|
144
|
+
lineHeight: 17 * lineHeights.normal,
|
|
145
|
+
letterSpacing: letterSpacing.normal,
|
|
146
|
+
},
|
|
129
147
|
bodyLarge: {
|
|
130
148
|
fontSize: fontSizes.lg,
|
|
131
149
|
fontFamily: fontFamilies.regular,
|
|
@@ -251,6 +269,7 @@ export const variantFontRole = {
|
|
|
251
269
|
h4: "medium",
|
|
252
270
|
h5: "medium",
|
|
253
271
|
h6: "medium",
|
|
272
|
+
bodyReadable: "regular",
|
|
254
273
|
bodyLarge: "regular",
|
|
255
274
|
body: "regular",
|
|
256
275
|
bodySmall: "regular",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type CriteriaValue = string | number | boolean;
|
|
2
|
+
export interface CriteriaOperator {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
arity: "none" | "one" | "range";
|
|
6
|
+
}
|
|
7
|
+
export interface CriteriaField {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
type: "text" | "number" | "date" | "choice";
|
|
11
|
+
operators: readonly CriteriaOperator[];
|
|
12
|
+
options?: readonly {
|
|
13
|
+
label: string;
|
|
14
|
+
value: CriteriaValue;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
export interface CriteriaCondition {
|
|
18
|
+
kind: "condition";
|
|
19
|
+
field: string;
|
|
20
|
+
operator: string;
|
|
21
|
+
values: readonly CriteriaValue[];
|
|
22
|
+
}
|
|
23
|
+
export interface CriteriaGroup {
|
|
24
|
+
kind: "group";
|
|
25
|
+
join: "all" | "any" | "none";
|
|
26
|
+
children: readonly CriteriaNode[];
|
|
27
|
+
}
|
|
28
|
+
export type CriteriaNode = CriteriaCondition | CriteriaGroup;
|
|
29
|
+
export interface CollectionSort {
|
|
30
|
+
field: string;
|
|
31
|
+
direction: "asc" | "desc";
|
|
32
|
+
}
|
|
33
|
+
export interface CollectionQuery {
|
|
34
|
+
search: string;
|
|
35
|
+
filter: CriteriaGroup;
|
|
36
|
+
sort: readonly CollectionSort[];
|
|
37
|
+
groupBy: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export interface CollectionField {
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
ascLabel?: string;
|
|
43
|
+
descLabel?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface SavedCollectionQuery {
|
|
46
|
+
id: string;
|
|
47
|
+
label: string;
|
|
48
|
+
query: CollectionQuery;
|
|
49
|
+
}
|
|
50
|
+
export declare function parseCriteriaDate(value: CriteriaValue | undefined): Date | null;
|
|
51
|
+
export declare function criteriaAt(root: CriteriaNode, path: readonly number[]): CriteriaNode;
|
|
52
|
+
export declare function replaceCriteria(root: CriteriaGroup, path: readonly number[], replacement: CriteriaNode | null): CriteriaGroup;
|
|
53
|
+
export declare function describeCriteria(node: CriteriaNode, fields: readonly CriteriaField[]): string;
|
|
54
|
+
export declare function validateCondition(condition: CriteriaCondition, fields: readonly CriteriaField[]): string | undefined;
|
|
55
|
+
export declare function validateCriteria(group: CriteriaGroup, fields: readonly CriteriaField[]): string | undefined;
|
|
56
|
+
export declare function moveCollectionEntry<T>(items: readonly T[], index: number, direction: -1 | 1): T[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export function parseCriteriaDate(value) {
|
|
2
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
3
|
+
return null;
|
|
4
|
+
const date = new Date(`${value}T00:00:00Z`);
|
|
5
|
+
if (!Number.isFinite(date.getTime()) || date.toISOString().slice(0, 10) !== value)
|
|
6
|
+
return null;
|
|
7
|
+
return new Date(`${value}T12:00:00`);
|
|
8
|
+
}
|
|
9
|
+
export function criteriaAt(root, path) {
|
|
10
|
+
let node = root;
|
|
11
|
+
for (const index of path) {
|
|
12
|
+
if (node.kind !== "group" || !node.children[index])
|
|
13
|
+
throw new Error("Invalid criteria path");
|
|
14
|
+
node = node.children[index];
|
|
15
|
+
}
|
|
16
|
+
return node;
|
|
17
|
+
}
|
|
18
|
+
export function replaceCriteria(root, path, replacement) {
|
|
19
|
+
if (path.length === 0) {
|
|
20
|
+
if (replacement?.kind !== "group")
|
|
21
|
+
throw new Error("Criteria root must remain a group");
|
|
22
|
+
return replacement;
|
|
23
|
+
}
|
|
24
|
+
const [index, ...rest] = path;
|
|
25
|
+
const child = root.children[index];
|
|
26
|
+
if (!child)
|
|
27
|
+
throw new Error("Invalid criteria path");
|
|
28
|
+
if (rest.length && child.kind !== "group")
|
|
29
|
+
throw new Error("Invalid criteria path");
|
|
30
|
+
const next = rest.length && child.kind === "group"
|
|
31
|
+
? replaceCriteria(child, rest, replacement)
|
|
32
|
+
: replacement;
|
|
33
|
+
return {
|
|
34
|
+
...root,
|
|
35
|
+
children: root.children.flatMap((item, position) => position !== index ? [item] : next ? [next] : []),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function describeCriteria(node, fields) {
|
|
39
|
+
if (node.kind === "group") {
|
|
40
|
+
if (!node.children.length)
|
|
41
|
+
return "Sin condiciones";
|
|
42
|
+
const join = { all: "Todas", any: "Cualquiera", none: "Ninguna" }[node.join];
|
|
43
|
+
return `${join}: (${node.children.map((child) => describeCriteria(child, fields)).join("; ")})`;
|
|
44
|
+
}
|
|
45
|
+
const field = fields.find((item) => item.id === node.field);
|
|
46
|
+
const operator = field?.operators.find((item) => item.id === node.operator);
|
|
47
|
+
const values = node.values.map((value) => field?.options?.find((option) => option.value === value)?.label ?? String(value));
|
|
48
|
+
return `${field?.label ?? node.field} ${operator?.label ?? node.operator}${values.length ? ` ${values.join(" — ")}` : ""}`;
|
|
49
|
+
}
|
|
50
|
+
export function validateCondition(condition, fields) {
|
|
51
|
+
const field = fields.find((item) => item.id === condition.field);
|
|
52
|
+
if (!field)
|
|
53
|
+
return "El campo ya no está disponible.";
|
|
54
|
+
const operator = field.operators.find((item) => item.id === condition.operator);
|
|
55
|
+
if (!operator)
|
|
56
|
+
return "El operador no está disponible para este campo.";
|
|
57
|
+
const count = { none: 0, one: 1, range: 2 }[operator.arity];
|
|
58
|
+
if (condition.values.length !== count)
|
|
59
|
+
return "Completa los valores de la condición.";
|
|
60
|
+
for (const value of condition.values) {
|
|
61
|
+
if (field.type === "number" && (typeof value !== "number" || !Number.isFinite(value)))
|
|
62
|
+
return "Introduce un número válido.";
|
|
63
|
+
if (field.type === "text" && (typeof value !== "string" || !value.trim()))
|
|
64
|
+
return "Introduce un texto.";
|
|
65
|
+
if (field.type === "date" && !parseCriteriaDate(value))
|
|
66
|
+
return "Introduce una fecha válida (AAAA-MM-DD).";
|
|
67
|
+
if (field.type === "choice" && !field.options?.some((option) => option.value === value))
|
|
68
|
+
return "Selecciona un valor disponible.";
|
|
69
|
+
}
|
|
70
|
+
if (operator.arity === "range" &&
|
|
71
|
+
field.type === "number" &&
|
|
72
|
+
Number(condition.values[0]) > Number(condition.values[1]))
|
|
73
|
+
return "El inicio no puede superar el final.";
|
|
74
|
+
if (operator.arity === "range" &&
|
|
75
|
+
field.type === "date" &&
|
|
76
|
+
String(condition.values[0]) > String(condition.values[1]))
|
|
77
|
+
return "El inicio no puede superar el final.";
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
export function validateCriteria(group, fields) {
|
|
81
|
+
for (const child of group.children) {
|
|
82
|
+
if (child.kind === "group" && !child.children.length)
|
|
83
|
+
return "Completa o elimina el grupo vacío.";
|
|
84
|
+
const error = child.kind === "group"
|
|
85
|
+
? validateCriteria(child, fields)
|
|
86
|
+
: validateCondition(child, fields);
|
|
87
|
+
if (error)
|
|
88
|
+
return error;
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
export function moveCollectionEntry(items, index, direction) {
|
|
93
|
+
const target = index + direction;
|
|
94
|
+
if (index < 0 || index >= items.length || target < 0 || target >= items.length)
|
|
95
|
+
throw new Error("Invalid collection reorder");
|
|
96
|
+
const next = [...items];
|
|
97
|
+
[next[index], next[target]] = [next[target], next[index]];
|
|
98
|
+
return next;
|
|
99
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function reorderKeys<T>(items: readonly T[], keyExtractor: (item: T) => string): string[];
|
|
2
|
+
export declare function sameOrder(left: readonly string[], right: readonly string[]): boolean;
|
|
3
|
+
export declare function moveReorderItem<T>(items: readonly T[], fromIndex: number, toIndex: number): T[];
|
|
4
|
+
export declare function applyReorder<T>(items: readonly T[], keyExtractor: (item: T) => string, order: readonly string[]): T[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function reorderKeys(items, keyExtractor) {
|
|
2
|
+
const keys = items.map(keyExtractor);
|
|
3
|
+
if (keys.some((key) => typeof key !== "string" || !key.trim()) ||
|
|
4
|
+
new Set(keys).size !== keys.length)
|
|
5
|
+
throw new Error("ReorderableList requires unique, non-empty string keys.");
|
|
6
|
+
return keys;
|
|
7
|
+
}
|
|
8
|
+
export function sameOrder(left, right) {
|
|
9
|
+
return left.length === right.length && left.every((key, index) => key === right[index]);
|
|
10
|
+
}
|
|
11
|
+
export function moveReorderItem(items, fromIndex, toIndex) {
|
|
12
|
+
if (!Number.isInteger(fromIndex) ||
|
|
13
|
+
!Number.isInteger(toIndex) ||
|
|
14
|
+
fromIndex < 0 ||
|
|
15
|
+
fromIndex >= items.length ||
|
|
16
|
+
toIndex < 0 ||
|
|
17
|
+
toIndex >= items.length)
|
|
18
|
+
throw new Error("ReorderableList received an invalid move.");
|
|
19
|
+
const next = [...items];
|
|
20
|
+
const [item] = next.splice(fromIndex, 1);
|
|
21
|
+
next.splice(toIndex, 0, item);
|
|
22
|
+
return next;
|
|
23
|
+
}
|
|
24
|
+
export function applyReorder(items, keyExtractor, order) {
|
|
25
|
+
const keys = reorderKeys(items, keyExtractor);
|
|
26
|
+
const indices = new Map(keys.map((key, index) => [key, index]));
|
|
27
|
+
if (order.length !== keys.length ||
|
|
28
|
+
new Set(order).size !== order.length ||
|
|
29
|
+
order.some((key) => !indices.has(key)))
|
|
30
|
+
throw new Error("ReorderableList received an order that does not match the current items.");
|
|
31
|
+
return order.map((key) => items[indices.get(key)]);
|
|
32
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
|
|
1
2
|
import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
import { type ImageSourcePropType } from "react-native";
|
|
@@ -8,6 +9,8 @@ interface AuthFormProps {
|
|
|
8
9
|
onBack?: () => void;
|
|
9
10
|
FormNode: ReactNode;
|
|
10
11
|
background?: AppBarBackground;
|
|
12
|
+
keyboardToolbar?: boolean;
|
|
13
|
+
keyboardFormProps?: Omit<KeyboardFormProps, "children" | "toolbar">;
|
|
11
14
|
}
|
|
12
|
-
declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, }: AuthFormProps): import("react").JSX.Element;
|
|
15
|
+
declare function AuthFormView({ title, description, logoSource, onBack, FormNode, background, keyboardToolbar, keyboardFormProps, }: AuthFormProps): import("react").JSX.Element;
|
|
13
16
|
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, }) {
|
|
8
|
+
function AuthFormView({ title, description, logoSource, onBack, FormNode, background, 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 }), 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 }), 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,3 +1,4 @@
|
|
|
1
|
+
import { type KeyboardFormProps } from "../Form/Form.KeyboardForm.js";
|
|
1
2
|
import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
|
|
2
3
|
import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
|
|
3
4
|
import React, { ReactNode } from "react";
|
|
@@ -12,12 +13,14 @@ interface FormViewProps<T> {
|
|
|
12
13
|
errorMessage?: string;
|
|
13
14
|
readonly?: boolean;
|
|
14
15
|
onRefresh?: () => Promise<void>;
|
|
16
|
+
onRefreshError?: (error: unknown) => void;
|
|
15
17
|
onRetry?: () => void;
|
|
16
18
|
onBack?: () => void;
|
|
17
19
|
withContainer?: boolean;
|
|
18
20
|
children: ReactNode;
|
|
19
21
|
}
|
|
20
|
-
declare function
|
|
22
|
+
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;
|
|
21
24
|
interface HeaderProps {
|
|
22
25
|
/** AppBar display variant. `large` = big title (list/index screens). `center` = modal/detail. */
|
|
23
26
|
variant?: "default" | "large" | "center";
|
|
@@ -53,8 +56,10 @@ declare function HeaderActionsBar({ children }: {
|
|
|
53
56
|
* Use as a child of `<FormViewV2.Header>` — it lands in the AppBar subheader slot.
|
|
54
57
|
*/
|
|
55
58
|
declare function HeaderSegmented<T extends string>(props: SegmentedControlProps<T>): React.JSX.Element;
|
|
56
|
-
declare function Content({ withMargin, children, }: {
|
|
59
|
+
declare function Content({ withMargin, keyboardToolbar, keyboardFormProps, children, }: {
|
|
57
60
|
withMargin?: boolean;
|
|
61
|
+
keyboardToolbar?: boolean;
|
|
62
|
+
keyboardFormProps?: Omit<KeyboardFormProps, "children" | "toolbar">;
|
|
58
63
|
children: ReactNode;
|
|
59
64
|
}): React.JSX.Element;
|
|
60
65
|
/**
|
package/dist/views/FormViewV2.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
2
|
import { Display } from "../Display/index.js";
|
|
3
3
|
import { ErrorBoundary, Feedback } from "../Feedback/index.js";
|
|
4
|
+
import KeyboardForm from "../Form/Form.KeyboardForm.js";
|
|
4
5
|
import { useBottomInset } from "../layouts/BottomInset.js";
|
|
5
6
|
import Container from "../layouts/Container.js";
|
|
6
7
|
import { AppBar } from "../Navigation/Navigation.AppBar.js";
|
|
@@ -10,13 +11,31 @@ import { useTheme } from "../theme/index.js";
|
|
|
10
11
|
import { FormViewProvider, useFormView } from "./FormViewV2.context.js";
|
|
11
12
|
import React from "react";
|
|
12
13
|
import { RefreshControl, View } from "react-native";
|
|
13
|
-
|
|
14
|
-
function
|
|
14
|
+
const RefreshErrorContext = React.createContext(undefined);
|
|
15
|
+
export async function refreshFormData(onRefresh, onRefreshError, setShowRefreshing) {
|
|
16
|
+
setShowRefreshing(true);
|
|
17
|
+
try {
|
|
18
|
+
await onRefresh?.();
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error("[FormViewV2] Refresh failed", error);
|
|
22
|
+
try {
|
|
23
|
+
onRefreshError?.(error);
|
|
24
|
+
}
|
|
25
|
+
catch (callbackError) {
|
|
26
|
+
console.error("[FormViewV2] onRefreshError callback failed", callbackError);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
setShowRefreshing(false);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function FormViewRoot({ name, description, item, isLoading = false, isEmpty = false, hasError = false, errorMessage, readonly = false, onRefresh, onRefreshError, onRetry, onBack, withContainer = true, children, }) {
|
|
15
34
|
const Wrapper = withContainer ? Container : View;
|
|
16
35
|
const wrapperProps = withContainer
|
|
17
36
|
? { edges: ["bottom", "left", "right"] }
|
|
18
37
|
: { style: { flex: 1 } };
|
|
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 }) }) }));
|
|
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 }) }) }) }));
|
|
20
39
|
}
|
|
21
40
|
function Header({ variant = "default", tone, actions, children, topSpacing, background, }) {
|
|
22
41
|
const { name, description, onBack } = useFormView();
|
|
@@ -36,24 +55,24 @@ function HeaderActionsBar({ children }) {
|
|
|
36
55
|
function HeaderSegmented(props) {
|
|
37
56
|
return _jsx(SegmentedControl, { ...props });
|
|
38
57
|
}
|
|
39
|
-
function Content({ withMargin = false, children, }) {
|
|
58
|
+
function Content({ withMargin = false, keyboardToolbar = false, keyboardFormProps, children, }) {
|
|
40
59
|
const { name, isLoading, isEmpty, hasError, errorMessage, item, onRefresh, onRetry, onBack, showRefreshing, setShowRefreshing, } = useFormView();
|
|
41
60
|
const bottomInset = useBottomInset();
|
|
42
61
|
const theme = useTheme();
|
|
62
|
+
const onRefreshError = React.useContext(RefreshErrorContext);
|
|
43
63
|
const handleRefresh = React.useCallback(async () => {
|
|
44
|
-
setShowRefreshing
|
|
45
|
-
|
|
46
|
-
await onRefresh();
|
|
47
|
-
}
|
|
48
|
-
setShowRefreshing(false);
|
|
49
|
-
}, [onRefresh, setShowRefreshing]);
|
|
64
|
+
await refreshFormData(onRefresh, onRefreshError, setShowRefreshing);
|
|
65
|
+
}, [onRefresh, onRefreshError, setShowRefreshing]);
|
|
50
66
|
const isCenteredContent = isLoading || isEmpty || !item || hasError;
|
|
51
|
-
return (_jsx(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
return (_jsx(KeyboardForm, { ...keyboardFormProps, contentContainerStyle: [
|
|
68
|
+
{
|
|
69
|
+
flexGrow: 1,
|
|
70
|
+
justifyContent: isCenteredContent ? "center" : "flex-start",
|
|
71
|
+
alignItems: isCenteredContent ? "center" : "stretch",
|
|
72
|
+
paddingBottom: bottomInset,
|
|
73
|
+
},
|
|
74
|
+
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 })) }));
|
|
57
76
|
}
|
|
58
77
|
/**
|
|
59
78
|
* @deprecated Content already handles isEmpty/!item by rendering Feedback.Empty internally.
|
|
@@ -2,7 +2,7 @@ import { type SearchHistoryProps } from "../Display/Display.SearchHistory.js";
|
|
|
2
2
|
import type { AppBarBackground } from "../Navigation/Navigation.AppBar.js";
|
|
3
3
|
import { type SegmentedControlProps } from "../Navigation/Navigation.SegmentedControl.js";
|
|
4
4
|
import React, { ReactNode } from "react";
|
|
5
|
-
import { StyleProp, ViewStyle } from "react-native";
|
|
5
|
+
import { type ScrollViewProps, StyleProp, ViewStyle } from "react-native";
|
|
6
6
|
interface ListViewProps<T> {
|
|
7
7
|
name: string;
|
|
8
8
|
description?: string;
|
|
@@ -12,6 +12,7 @@ interface ListViewProps<T> {
|
|
|
12
12
|
errorMessage?: string;
|
|
13
13
|
readonly?: boolean;
|
|
14
14
|
onRefresh?: () => Promise<void>;
|
|
15
|
+
onRefreshError?: (error: unknown) => void;
|
|
15
16
|
onRetry?: () => void;
|
|
16
17
|
onPressItem?: (item: T) => void;
|
|
17
18
|
onDeleteItem?: (item: T) => void;
|
|
@@ -25,7 +26,8 @@ interface ListViewProps<T> {
|
|
|
25
26
|
withContainer?: boolean;
|
|
26
27
|
children: ReactNode;
|
|
27
28
|
}
|
|
28
|
-
declare function
|
|
29
|
+
export declare function refreshListData(onRefresh: (() => Promise<void>) | undefined, onRefreshError: ((error: unknown) => void) | undefined, setShowRefreshing: (value: boolean) => void): Promise<void>;
|
|
30
|
+
declare function ListViewRoot<T>({ name, description, items, isLoading, hasError, errorMessage, readonly, onRefresh, onRefreshError, onRetry, onPressItem, onDeleteItem, onSearch, onBack, onLoadMore, loadingMore, backgroundColor, withContainer, children, }: ListViewProps<T>): React.JSX.Element;
|
|
29
31
|
interface HeaderProps {
|
|
30
32
|
/** AppBar display variant. `large` = big title (list/index). `center` = modal/detail. */
|
|
31
33
|
variant?: "default" | "large" | "center";
|
|
@@ -79,14 +81,15 @@ declare function FilterChip({ label, active, onPress }: FilterChipProps): React.
|
|
|
79
81
|
declare function FilterChips({ children }: {
|
|
80
82
|
children: ReactNode;
|
|
81
83
|
}): React.JSX.Element;
|
|
82
|
-
|
|
84
|
+
type ListScrollProps = Pick<ScrollViewProps, "keyboardShouldPersistTaps" | "keyboardDismissMode" | "testID">;
|
|
85
|
+
declare function Content({ children, withMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, }: {
|
|
83
86
|
children?: (item: any) => ReactNode;
|
|
84
87
|
withMargin?: boolean;
|
|
85
|
-
}): React.JSX.Element;
|
|
86
|
-
declare function ContentList({ children, contentWithMargin, }: {
|
|
88
|
+
} & ListScrollProps): React.JSX.Element;
|
|
89
|
+
declare function ContentList({ children, contentWithMargin, keyboardShouldPersistTaps, keyboardDismissMode, testID, }: {
|
|
87
90
|
children?: (item: any) => ReactNode;
|
|
88
91
|
contentWithMargin: boolean;
|
|
89
|
-
}): React.JSX.Element;
|
|
92
|
+
} & ListScrollProps): React.JSX.Element;
|
|
90
93
|
declare function Footer({ children }: {
|
|
91
94
|
children: ReactNode;
|
|
92
95
|
}): React.JSX.Element | null;
|