@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
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
2
|
import { cn } from "../theme/tw.js";
|
|
3
3
|
import { Typography } from "../Typography/index.js";
|
|
4
|
-
import { View } from "react-native";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { useWindowDimensions, View } from "react-native";
|
|
5
|
+
import CopyableText from "./Display.CopyableText";
|
|
6
|
+
function KeyValue({ label, value, align = "row", className, testID, copyable = false, selectable = false, }) {
|
|
7
|
+
const { width, fontScale } = useWindowDimensions();
|
|
8
|
+
const isRow = align === "row" || (align === "auto" && width >= 360 && fontScale <= 1.3);
|
|
9
|
+
return (_jsxs(View, { className: cn(isRow ? "flex-row items-center justify-between gap-3" : "gap-0.5", "py-2", className), testID: testID, children: [_jsx(Typography.Text, { className: "text-text-secondary", variant: "caption", children: label }), typeof value === "string" || typeof value === "number" ? (_jsx(View, { className: cn("flex-shrink min-w-0", isRow && "flex-1 items-end"), children: copyable ? (_jsx(CopyableText, { accessibilityLabel: `Copiar ${label}`, selectable: selectable, value: String(value) })) : selectable ? (_jsx(Typography.SelectableText, { className: cn("text-text-primary", isRow && "text-right"), semibold: isRow, style: { alignSelf: "stretch" }, value: String(value) })) : (_jsx(Typography.Text, { className: cn("text-text-primary", isRow && "text-right"), selectable: selectable, semibold: isRow, children: value })) })) : (value)] }));
|
|
8
10
|
}
|
|
9
11
|
export default KeyValue;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type SectionHeaderAction, type SectionHeaderProps } from "./Display.SectionHeader.js";
|
|
2
|
+
import { Typography } from "../Typography/index.js";
|
|
3
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
4
|
+
import { type StyleProp, type ViewStyle } from "react-native";
|
|
5
|
+
export interface ListGroupProps {
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
footer?: ReactNode;
|
|
9
|
+
action?: SectionHeaderAction;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
surface?: "inset" | "plain";
|
|
12
|
+
separators?: boolean;
|
|
13
|
+
className?: string;
|
|
14
|
+
contentClassName?: string;
|
|
15
|
+
testID?: string;
|
|
16
|
+
headerProps?: Omit<SectionHeaderProps, "title" | "subtitle" | "action">;
|
|
17
|
+
footerTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children">;
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
20
|
+
separatorStyle?: StyleProp<ViewStyle>;
|
|
21
|
+
}
|
|
22
|
+
declare function ListGroup({ title, description, footer, action, children, surface, separators, className, contentClassName, testID, headerProps, footerTextProps, style, contentStyle, separatorStyle, }: ListGroupProps): import("react").JSX.Element;
|
|
23
|
+
export default ListGroup;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import SectionHeader from "./Display.SectionHeader.js";
|
|
3
|
+
import { cn } from "../theme/tw.js";
|
|
4
|
+
import { Typography } from "../Typography/index.js";
|
|
5
|
+
import { Children } from "react";
|
|
6
|
+
import { StyleSheet, View } from "react-native";
|
|
7
|
+
function ListGroup({ title, description, footer, action, children, surface = "inset", separators = true, className, contentClassName, testID, headerProps, footerTextProps, style, contentStyle, separatorStyle, }) {
|
|
8
|
+
const rows = Children.toArray(children);
|
|
9
|
+
return (_jsxs(View, { className: cn("gap-2", className), style: style, testID: testID, children: [_jsx(SectionHeader, { ...headerProps, action: action, subtitle: description, title: title }), _jsx(View, { className: cn(surface === "inset" && "rounded-2xl bg-bg-card overflow-hidden", contentClassName), style: contentStyle, testID: testID ? `${testID}-content` : undefined, children: rows.map((row, index) => (_jsxs(View, { children: [row, separators && index < rows.length - 1 ? (_jsx(View, { accessibilityElementsHidden: true, className: "bg-border-default", importantForAccessibility: "no-hide-descendants", style: [
|
|
10
|
+
{
|
|
11
|
+
height: StyleSheet.hairlineWidth,
|
|
12
|
+
marginHorizontal: 16,
|
|
13
|
+
},
|
|
14
|
+
separatorStyle,
|
|
15
|
+
], testID: testID ? `${testID}-separator-${index}` : undefined })) : null] }, typeof row === "object" && "key" in row ? row.key : index))) }), typeof footer === "string" ? (_jsx(Typography.Text, { className: "text-text-secondary px-4", variant: "bodySmall", ...footerTextProps, children: footer })) : (footer)] }));
|
|
16
|
+
}
|
|
17
|
+
export default ListGroup;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Typography } from "../Typography/index.js";
|
|
2
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
3
|
+
import { type StyleProp, type ViewStyle } from "react-native";
|
|
2
4
|
export interface ListItemProps {
|
|
3
5
|
title: string;
|
|
4
6
|
subtitle?: string;
|
|
@@ -12,6 +14,18 @@ export interface ListItemProps {
|
|
|
12
14
|
showDivider?: boolean;
|
|
13
15
|
className?: string;
|
|
14
16
|
testID?: string;
|
|
17
|
+
accessibleSizing?: boolean;
|
|
18
|
+
accessibilityLabel?: string;
|
|
19
|
+
accessibilityHint?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
variant?: "default" | "mobile";
|
|
22
|
+
value?: string;
|
|
23
|
+
layout?: "auto" | "row" | "stacked";
|
|
24
|
+
density?: "compact" | "comfortable";
|
|
25
|
+
style?: StyleProp<ViewStyle>;
|
|
26
|
+
titleTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children">;
|
|
27
|
+
subtitleTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children">;
|
|
28
|
+
valueTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children">;
|
|
15
29
|
}
|
|
16
|
-
declare function ListItem({ title, subtitle, leading, trailing, chevron, onPress, showDivider, className, testID, }: ListItemProps): import("react").JSX.Element;
|
|
30
|
+
declare function ListItem({ title, subtitle, leading, trailing, chevron, onPress, showDivider, className, testID, accessibleSizing, accessibilityLabel, accessibilityHint, disabled, variant, value, layout, density, style, titleTextProps, subtitleTextProps, valueTextProps, }: ListItemProps): import("react").JSX.Element;
|
|
17
31
|
export default ListItem;
|
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "nativewind/jsx-runtime";
|
|
2
2
|
import Icon from "./Display.Icon.js";
|
|
3
3
|
import Pressable from "../primitives/Pressable.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useTheme } from "../theme/index.js";
|
|
5
5
|
import { cn } from "../theme/tw.js";
|
|
6
6
|
import { Typography } from "../Typography/index.js";
|
|
7
|
-
import { View } from "react-native";
|
|
8
|
-
function ListItem({ title, subtitle, leading, trailing, chevron = false, onPress, showDivider =
|
|
7
|
+
import { Pressable as NativePressable, useWindowDimensions, View, } from "react-native";
|
|
8
|
+
function ListItem({ title, subtitle, leading, trailing, chevron = false, onPress, showDivider, className, testID, accessibleSizing = false, accessibilityLabel, accessibilityHint, disabled = false, variant = "default", value, layout = "auto", density = "compact", style, titleTextProps, subtitleTextProps, valueTextProps, }) {
|
|
9
|
+
const theme = useTheme();
|
|
10
|
+
const { width, fontScale } = useWindowDimensions();
|
|
11
|
+
const mobile = variant === "mobile";
|
|
12
|
+
const comfortable = mobile && density === "comfortable";
|
|
13
|
+
const stacked = mobile &&
|
|
14
|
+
(value !== undefined || trailing != null) &&
|
|
15
|
+
(layout === "stacked" || (layout === "auto" && (fontScale > 1.3 || width < 360)));
|
|
9
16
|
const right = trailing ??
|
|
10
|
-
(chevron ? _jsx(Icon, { color: theme.icon.tertiary, name: "chevron-forward", size: 18 }) : null);
|
|
11
|
-
const content = (_jsxs(_Fragment, { children: [leading ? _jsx(View, { className: "mr-3 justify-center", children: leading }) : null, _jsxs(View, { className: "flex-1 gap-0.5", children: [_jsx(Typography.Text, { className: "text-text-primary", numberOfLines: 1, semibold:
|
|
12
|
-
const base = cn("flex-row items-center py-
|
|
17
|
+
(value || chevron ? (_jsxs(View, { className: "flex-row items-center gap-2", children: [value ? (_jsx(Typography.Text, { className: "text-text-secondary flex-shrink", variant: comfortable ? "bodyReadable" : "body", ...valueTextProps, children: value })) : null, chevron ? (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", children: _jsx(Icon, { color: theme.icon.tertiary, name: "chevron-forward", size: 18 }) })) : null] })) : null);
|
|
18
|
+
const content = (_jsxs(_Fragment, { children: [leading ? _jsx(View, { className: "mr-3 justify-center", children: leading }) : null, _jsxs(View, { className: cn("flex-1", mobile ? "min-w-0 gap-1" : "gap-0.5"), children: [_jsx(Typography.Text, { className: mobile && disabled ? "text-text-disabled" : "text-text-primary", numberOfLines: mobile || accessibleSizing ? undefined : 1, semibold: !mobile || comfortable, variant: comfortable ? "bodyReadable" : "body", ...titleTextProps, children: title }), subtitle ? (_jsx(Typography.Text, { className: "text-text-secondary", numberOfLines: mobile || accessibleSizing ? undefined : 1, variant: comfortable ? "bodyLarge" : "bodySmall", ...subtitleTextProps, children: subtitle })) : null, stacked && right ? _jsx(View, { className: "self-start mt-1", children: right }) : null] }), !stacked && right ? (_jsx(View, { className: cn("ml-3 justify-center", mobile && "flex-shrink"), style: mobile ? { maxWidth: "45%" } : undefined, children: right })) : null] }));
|
|
19
|
+
const base = cn("flex-row items-center px-4", mobile && !comfortable ? "py-2" : "py-3", !mobile && "bg-bg-card", (showDivider ?? !mobile) && "border-b border-border-default", disabled && !mobile && "opacity-50", className);
|
|
13
20
|
if (onPress) {
|
|
14
|
-
|
|
21
|
+
if (mobile) {
|
|
22
|
+
return (_jsx(NativePressable, { accessibilityHint: accessibilityHint ?? subtitle, accessibilityLabel: accessibilityLabel ?? title, accessibilityRole: "button", accessibilityState: { disabled }, accessibilityValue: value !== undefined ? { text: value } : undefined, className: base, disabled: disabled, onPress: onPress, style: ({ pressed }) => [
|
|
23
|
+
{
|
|
24
|
+
minHeight: 48,
|
|
25
|
+
minWidth: 48,
|
|
26
|
+
backgroundColor: pressed ? theme.bg.hover : undefined,
|
|
27
|
+
},
|
|
28
|
+
style,
|
|
29
|
+
], testID: testID, children: content }));
|
|
30
|
+
}
|
|
31
|
+
return (_jsx(Pressable, { accessibilityHint: accessibilityHint, accessibilityLabel: accessibilityLabel, className: base, disabled: disabled, onPress: onPress, style: [accessibleSizing ? { minHeight: 48 } : undefined, style], testID: testID, children: content }));
|
|
15
32
|
}
|
|
16
|
-
return (_jsx(View, { className: base, testID: testID, children: content }));
|
|
33
|
+
return (_jsx(View, { className: base, style: style, testID: testID, children: content }));
|
|
17
34
|
}
|
|
18
35
|
export default ListItem;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Typography } from "../Typography/index.js";
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
import { type StyleProp, type ViewStyle } from "react-native";
|
|
1
4
|
export interface SectionHeaderAction {
|
|
2
5
|
label: string;
|
|
3
6
|
onPress: () => void;
|
|
@@ -8,6 +11,13 @@ export interface SectionHeaderProps {
|
|
|
8
11
|
action?: SectionHeaderAction;
|
|
9
12
|
className?: string;
|
|
10
13
|
testID?: string;
|
|
14
|
+
variant?: "default" | "mobile";
|
|
15
|
+
titleTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children" | "accessibilityRole">;
|
|
16
|
+
subtitleTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children">;
|
|
17
|
+
style?: StyleProp<ViewStyle>;
|
|
18
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
19
|
+
actionStyle?: StyleProp<ViewStyle>;
|
|
20
|
+
actionTextProps?: Omit<ComponentProps<typeof Typography.Text>, "children">;
|
|
11
21
|
}
|
|
12
|
-
declare function SectionHeader({ title, subtitle, action, className, testID }: SectionHeaderProps): import("react").JSX.Element;
|
|
22
|
+
declare function SectionHeader({ title, subtitle, action, className, testID, variant, titleTextProps, subtitleTextProps, style, contentStyle, actionStyle, actionTextProps, }: SectionHeaderProps): import("react").JSX.Element;
|
|
13
23
|
export default SectionHeader;
|
|
@@ -3,7 +3,16 @@ import Pressable from "../primitives/Pressable.js";
|
|
|
3
3
|
import { cn } from "../theme/tw.js";
|
|
4
4
|
import { Typography } from "../Typography/index.js";
|
|
5
5
|
import { View } from "react-native";
|
|
6
|
-
function SectionHeader({ title, subtitle, action, className, testID }) {
|
|
7
|
-
return (_jsxs(View, { className: cn("flex-row items-center justify-between px-4 py-2", className), testID: testID, children: [_jsxs(View, { className: "flex-1", children: [_jsx(Typography.Text, { className: "text-text-primary", semibold: true, variant: "subtitle", children: title }), subtitle ? (_jsx(Typography.Text, { className: "text-text-secondary", variant: "bodySmall", children: subtitle })) : null] }), action ? (_jsx(Pressable, { accessibilityRole: "button", hitSlop: 8, onPress: action.onPress,
|
|
6
|
+
function SectionHeader({ title, subtitle, action, className, testID, variant = "default", titleTextProps, subtitleTextProps, style, contentStyle, actionStyle, actionTextProps, }) {
|
|
7
|
+
return (_jsxs(View, { className: cn("flex-row items-center justify-between px-4 py-2", className), style: style, testID: testID, children: [_jsxs(View, { className: "flex-1", style: contentStyle, children: [_jsx(Typography.Text, { accessibilityRole: "header", className: "text-text-primary", semibold: true, variant: variant === "mobile" ? "bodyReadable" : "subtitle", ...titleTextProps, children: title }), subtitle ? (_jsx(Typography.Text, { className: "text-text-secondary", variant: variant === "mobile" ? "bodyLarge" : "bodySmall", ...subtitleTextProps, children: subtitle })) : null] }), action ? (_jsx(Pressable, { accessibilityLabel: action.label, accessibilityRole: "button", hitSlop: 8, onPress: action.onPress, style: [
|
|
8
|
+
{
|
|
9
|
+
minHeight: 48,
|
|
10
|
+
minWidth: 48,
|
|
11
|
+
justifyContent: "center",
|
|
12
|
+
marginLeft: 12,
|
|
13
|
+
flexShrink: 1,
|
|
14
|
+
},
|
|
15
|
+
actionStyle,
|
|
16
|
+
], children: _jsx(Typography.Text, { className: "text-text-accent text-sm font-semibold", ...actionTextProps, children: action.label }) })) : null] }));
|
|
8
17
|
}
|
|
9
18
|
export default SectionHeader;
|
|
@@ -7,9 +7,11 @@ export interface SwipeRowAction {
|
|
|
7
7
|
}
|
|
8
8
|
export interface SwipeableRowProps {
|
|
9
9
|
children: ReactNode;
|
|
10
|
+
/** Visible alternative for essential swipe actions. */
|
|
11
|
+
visibleActions?: ReactNode;
|
|
10
12
|
rightActions?: SwipeRowAction[];
|
|
11
13
|
leftActions?: SwipeRowAction[];
|
|
12
14
|
testID?: string;
|
|
13
15
|
}
|
|
14
|
-
declare function SwipeableRow({ children, rightActions, leftActions, testID }: SwipeableRowProps): import("react").JSX.Element;
|
|
16
|
+
declare function SwipeableRow({ children, visibleActions, rightActions, leftActions, testID, }: SwipeableRowProps): import("react").JSX.Element;
|
|
15
17
|
export default SwipeableRow;
|
|
@@ -17,13 +17,13 @@ function ActionButtons({ actions, onDone, }) {
|
|
|
17
17
|
paddingHorizontal: 20,
|
|
18
18
|
}, children: [action.icon ? (_jsx(Icon, { color: theme.text.inverse, name: action.icon, size: 20 })) : null, action.label ? (_jsx(Typography.Text, { className: "text-text-inverse text-xs mt-1", children: action.label })) : null] }, index))) }));
|
|
19
19
|
}
|
|
20
|
-
function SwipeableRow({ children, rightActions, leftActions, testID }) {
|
|
20
|
+
function SwipeableRow({ children, visibleActions, rightActions, leftActions, testID, }) {
|
|
21
21
|
const ref = useRef(null);
|
|
22
22
|
const close = () => ref.current?.close();
|
|
23
|
-
return (
|
|
23
|
+
return (_jsxs(Swipeable, { ref: ref, renderLeftActions: leftActions?.length
|
|
24
24
|
? () => _jsx(ActionButtons, { actions: leftActions, onDone: close })
|
|
25
25
|
: undefined, renderRightActions: rightActions?.length
|
|
26
26
|
? () => _jsx(ActionButtons, { actions: rightActions, onDone: close })
|
|
27
|
-
: undefined, testID: testID, children: children }));
|
|
27
|
+
: undefined, testID: testID, children: [children, visibleActions ? _jsx(View, { className: "flex-row", children: visibleActions }) : null] }));
|
|
28
28
|
}
|
|
29
29
|
export default SwipeableRow;
|
package/dist/Display/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import GradientSurface from "./Display.GradientSurface.js";
|
|
|
12
12
|
import IconBadge from "./Display.IconBadge.js";
|
|
13
13
|
import DisplayImage from "./Display.Image.js";
|
|
14
14
|
import KeyValue from "./Display.KeyValue.js";
|
|
15
|
+
import ListGroup from "./Display.ListGroup.js";
|
|
15
16
|
import ListItem from "./Display.ListItem.js";
|
|
16
17
|
import MediaCard from "./Display.MediaCard.js";
|
|
17
18
|
import MediaEmbed from "./Display.MediaEmbed.js";
|
|
@@ -66,6 +67,7 @@ export declare const Display: {
|
|
|
66
67
|
StoryTray: typeof StoryTray;
|
|
67
68
|
StoryViewer: typeof StoryViewer;
|
|
68
69
|
ListItem: typeof ListItem;
|
|
70
|
+
ListGroup: typeof ListGroup;
|
|
69
71
|
KeyValue: typeof KeyValue;
|
|
70
72
|
DataTable: typeof DataTable;
|
|
71
73
|
Stat: typeof Stat;
|
|
@@ -96,6 +98,8 @@ export declare const Display: {
|
|
|
96
98
|
};
|
|
97
99
|
export { type BadgeProps, BadgeVariants };
|
|
98
100
|
export type { CircleButtonProps } from "./Display.CircleButton.js";
|
|
101
|
+
export type { ListGroupProps } from "./Display.ListGroup.js";
|
|
102
|
+
export type { ListItemProps } from "./Display.ListItem.js";
|
|
99
103
|
export type { MenuCardProps } from "./Display.MenuCard.js";
|
|
100
104
|
export type { MenuGridItem } from "./Display.MenuGrid.js";
|
|
101
105
|
export default Display;
|
package/dist/Display/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import Icon from "./Display.Icon.js";
|
|
|
16
16
|
import IconBadge from "./Display.IconBadge.js";
|
|
17
17
|
import DisplayImage from "./Display.Image.js";
|
|
18
18
|
import KeyValue from "./Display.KeyValue.js";
|
|
19
|
+
import ListGroup from "./Display.ListGroup.js";
|
|
19
20
|
import ListItem from "./Display.ListItem.js";
|
|
20
21
|
import Logo from "./Display.Logo.js";
|
|
21
22
|
import MediaCard from "./Display.MediaCard.js";
|
|
@@ -57,6 +58,7 @@ export const Display = {
|
|
|
57
58
|
StoryTray,
|
|
58
59
|
StoryViewer,
|
|
59
60
|
ListItem,
|
|
61
|
+
ListGroup,
|
|
60
62
|
KeyValue,
|
|
61
63
|
DataTable,
|
|
62
64
|
Stat,
|
|
@@ -30,6 +30,6 @@ const iconByTone = {
|
|
|
30
30
|
function Banner({ message, title, tone = "info", onClose, action, className, testID, }) {
|
|
31
31
|
const styles = banner({ tone });
|
|
32
32
|
const icon = iconByTone[tone ?? "info"];
|
|
33
|
-
return (_jsxs(View, { className: styles.root({ className }), testID: testID, children: [_jsx(Icon, { color: icon.color, name: icon.name, size: 20 }), _jsxs(View, { className: "flex-1 gap-0.5", children: [title ? _jsx(Typography.Text, { className: styles.title(), children: title }) : null, _jsx(Typography.Text, { className: styles.message(), children: message }), action ? _jsx(View, { className: "mt-2", children: action }) : null] }), onClose ? (_jsx(Pressable, { accessibilityLabel: "Cerrar", hitSlop: 8, onPress: onClose, children: _jsx(Icon, { color: theme.icon.secondary, name: "close", size: 18 }) })) : null] }));
|
|
33
|
+
return (_jsxs(View, { accessibilityLiveRegion: tone === "danger" ? "assertive" : "polite", accessibilityRole: tone === "danger" ? "alert" : undefined, className: styles.root({ className }), testID: testID, children: [_jsx(Icon, { color: icon.color, name: icon.name, size: 20 }), _jsxs(View, { className: "flex-1 gap-0.5", children: [title ? _jsx(Typography.Text, { className: styles.title(), children: title }) : null, _jsx(Typography.Text, { className: styles.message(), children: message }), action ? _jsx(View, { className: "mt-2", children: action }) : null] }), onClose ? (_jsx(Pressable, { accessibilityLabel: "Cerrar", accessibilityRole: "button", hitSlop: 8, onPress: onClose, children: _jsx(Icon, { color: theme.icon.secondary, name: "close", size: 18 }) })) : null] }));
|
|
34
34
|
}
|
|
35
35
|
export default Banner;
|
|
@@ -10,6 +10,7 @@ export interface EmptyStateProps {
|
|
|
10
10
|
children?: ReactNode;
|
|
11
11
|
className?: string;
|
|
12
12
|
testID?: string;
|
|
13
|
+
accessibleSizing?: boolean;
|
|
13
14
|
}
|
|
14
|
-
declare function EmptyState({ title, description, icon, image, actionLabel, onAction, children, className, testID, }: EmptyStateProps): import("react").JSX.Element;
|
|
15
|
+
declare function EmptyState({ title, description, icon, image, actionLabel, onAction, children, className, testID, accessibleSizing, }: EmptyStateProps): import("react").JSX.Element;
|
|
15
16
|
export default EmptyState;
|
|
@@ -5,7 +5,7 @@ import { theme } from "../theme/index.js";
|
|
|
5
5
|
import { cn } from "../theme/tw.js";
|
|
6
6
|
import { Typography } from "../Typography/index.js";
|
|
7
7
|
import { Image, View } from "react-native";
|
|
8
|
-
function EmptyState({ title, description, icon = "file-tray-outline", image, actionLabel, onAction, children, className, testID, }) {
|
|
9
|
-
return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-8 gap-3", className), testID: testID, children: [image ? (_jsx(Image, { resizeMode: "contain", source: image, style: { width: 140, height: 140 } })) : (_jsx(View, { className: "w-20 h-20 rounded-full bg-bg-muted items-center justify-center", children: _jsx(Icon, { color: theme.icon.tertiary, name: icon, size: 40 }) })), _jsx(Typography.Text, { align: "center", className: "text-text-primary", semibold: true, variant: "subtitle", children: title }), description ? (_jsx(Typography.Text, { align: "center", className: "text-text-secondary", variant: "body", children: description })) : null, actionLabel && onAction ? (_jsx(View, { className: "mt-2", children: _jsx(BaseButton, { onPress: onAction, title: actionLabel, variant: "primary" }) })) : null, children] }));
|
|
8
|
+
function EmptyState({ title, description, icon = "file-tray-outline", image, actionLabel, onAction, children, className, testID, accessibleSizing = false, }) {
|
|
9
|
+
return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-8 gap-3", className), testID: testID, children: [image ? (_jsx(Image, { resizeMode: "contain", source: image, style: { width: 140, height: 140 } })) : (_jsx(View, { className: "w-20 h-20 rounded-full bg-bg-muted items-center justify-center", children: _jsx(Icon, { color: theme.icon.tertiary, name: icon, size: 40 }) })), _jsx(Typography.Text, { accessibilityRole: "header", align: "center", className: "text-text-primary", semibold: true, variant: "subtitle", children: title }), description ? (_jsx(Typography.Text, { align: "center", className: "text-text-secondary", variant: "body", children: description })) : null, actionLabel && onAction ? (_jsx(View, { className: "mt-2", children: _jsx(BaseButton, { accessibleSizing: accessibleSizing, onPress: onAction, title: actionLabel, variant: "primary" }) })) : null, children] }));
|
|
10
10
|
}
|
|
11
11
|
export default EmptyState;
|
|
@@ -4,6 +4,8 @@ interface ErrorProps {
|
|
|
4
4
|
onBack?: () => void;
|
|
5
5
|
onHome?: () => void;
|
|
6
6
|
className?: string;
|
|
7
|
+
accessibleSizing?: boolean;
|
|
8
|
+
testID?: string;
|
|
7
9
|
}
|
|
8
|
-
declare function Error({ message, onRetry, onBack, onHome, className }: ErrorProps): import("react").JSX.Element;
|
|
10
|
+
declare function Error({ message, onRetry, onBack, onHome, className, accessibleSizing, testID, }: ErrorProps): import("react").JSX.Element;
|
|
9
11
|
export default Error;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
2
|
import Icon from "../Display/Display.Icon.js";
|
|
3
|
-
import Button from "../
|
|
3
|
+
import Button from "../primitives/BaseButton.js";
|
|
4
|
+
import { theme } from "../theme/index.js";
|
|
4
5
|
import { cn } from "../theme/tw.js";
|
|
5
6
|
import { Typography } from "../Typography/index.js";
|
|
6
7
|
import { View } from "react-native";
|
|
7
|
-
function Error({ message, onRetry, onBack, onHome, className }) {
|
|
8
|
-
return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), children: [_jsx(Icon, { color:
|
|
8
|
+
function Error({ message, onRetry, onBack, onHome, className, accessibleSizing = false, testID, }) {
|
|
9
|
+
return (_jsxs(View, { className: cn("flex-1 items-center justify-center px-4", className), testID: testID, children: [_jsx(Icon, { color: theme.danger, name: "exclamation-circle", size: 80, type: "fontawesome5" }), _jsx(Typography.Text, { accessibilityRole: "header", className: "pt-4 text-text-primary", semibold: true, variant: "h3", children: "\u00A1Error!" }), _jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "pt-2 text-center text-base leading-5.5 text-text-secondary", variant: "h5", children: message || "Ocurrió un error al cargar la información" }), _jsxs(View, { className: "mt-8 w-full gap-3", children: [onRetry && (_jsx(Button, { accessibleSizing: accessibleSizing, className: "w-full", onPress: onRetry, title: "Reintentar" })), onBack && (_jsx(Button, { accessibleSizing: accessibleSizing, className: "w-full", onPress: onBack, title: "Volver", variant: "secondary" })), onHome && (_jsx(Button, { accessibleSizing: accessibleSizing, className: "w-full", onPress: onHome, title: "Ir al inicio", variant: "outline" }))] })] }));
|
|
9
10
|
}
|
|
10
11
|
export default Error;
|
|
@@ -7,7 +7,10 @@ interface RowCheckBoxProps {
|
|
|
7
7
|
isChecked?: boolean;
|
|
8
8
|
onCheck?: (isChecked: boolean) => void;
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
accessibilityLabel?: string;
|
|
11
|
+
accessibleSizing?: boolean;
|
|
10
12
|
className?: string;
|
|
13
|
+
testID?: string;
|
|
11
14
|
}
|
|
12
|
-
declare function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked, onCheck, disabled, className, }: RowCheckBoxProps): import("react").JSX.Element;
|
|
15
|
+
declare function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked, onCheck, disabled, accessibilityLabel, accessibleSizing, className, testID, }: RowCheckBoxProps): import("react").JSX.Element;
|
|
13
16
|
export default RowCheckBox;
|
|
@@ -4,7 +4,7 @@ import { cn } from "../theme/tw.js";
|
|
|
4
4
|
import { Typography } from "../Typography/index.js";
|
|
5
5
|
import { Checkbox } from "expo-checkbox";
|
|
6
6
|
import { Pressable, View } from "react-native";
|
|
7
|
-
function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked = false, onCheck = () => { }, disabled = false, className, }) {
|
|
7
|
+
function RowCheckBox({ mainText, subText, rightText, rightComponent, leftComponent, isChecked = false, onCheck = () => { }, disabled = false, accessibilityLabel, accessibleSizing = false, className, testID, }) {
|
|
8
8
|
const handlePress = () => {
|
|
9
9
|
if (!disabled) {
|
|
10
10
|
onCheck(!isChecked);
|
|
@@ -25,6 +25,6 @@ function RowCheckBox({ mainText, subText, rightText, rightComponent, leftCompone
|
|
|
25
25
|
}
|
|
26
26
|
return null;
|
|
27
27
|
};
|
|
28
|
-
return (_jsxs(Pressable, { className: cn("flex-row justify-between items-center", disabled && "opacity-50", className), disabled: disabled, onPress: handlePress, children: [_jsx(View, { children: renderLeftComponent() }), _jsxs(View, { className: "flex-row items-center", children: [renderRightComponent(), _jsx(Checkbox, { className: "ml-2", color: theme.primary, disabled: disabled, pointerEvents: "none", value: isChecked })] })] }));
|
|
28
|
+
return (_jsxs(Pressable, { accessibilityLabel: accessibilityLabel ?? [mainText, subText, rightText].filter(Boolean).join(", "), accessibilityRole: "checkbox", accessibilityState: { checked: isChecked, disabled }, className: cn("flex-row justify-between items-center", disabled && "opacity-50", className), disabled: disabled, onPress: handlePress, style: accessibleSizing ? { minHeight: 48, paddingVertical: 8 } : undefined, testID: testID, children: [_jsx(View, { children: renderLeftComponent() }), _jsxs(View, { className: "flex-row items-center", children: [renderRightComponent(), _jsx(Checkbox, { className: "ml-2", color: theme.primary, disabled: disabled, pointerEvents: "none", value: isChecked })] })] }));
|
|
29
29
|
}
|
|
30
30
|
export default RowCheckBox;
|
|
@@ -8,6 +8,7 @@ export interface DatePickerProps {
|
|
|
8
8
|
minimumDate?: Date;
|
|
9
9
|
maximumDate?: Date;
|
|
10
10
|
disabled?: boolean;
|
|
11
|
+
accessibleSizing?: boolean;
|
|
11
12
|
className?: string;
|
|
12
13
|
testID?: string;
|
|
13
14
|
}
|
|
@@ -16,5 +17,5 @@ export interface DatePickerProps {
|
|
|
16
17
|
* (`@react-native-community/datetimepicker`) — a case where native wins (expected UX).
|
|
17
18
|
* Subpath `Form/DatePicker`; **optional** peer dep.
|
|
18
19
|
*/
|
|
19
|
-
declare function DatePicker({ value, onChange, mode, label, placeholder, format, minimumDate, maximumDate, disabled, className, testID, }: DatePickerProps): import("react").JSX.Element;
|
|
20
|
+
declare function DatePicker({ value, onChange, mode, label, placeholder, format, minimumDate, maximumDate, disabled, accessibleSizing, className, testID, }: DatePickerProps): import("react").JSX.Element;
|
|
20
21
|
export default DatePicker;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
|
2
2
|
import DateTimePicker from "@react-native-community/datetimepicker";
|
|
3
3
|
import Icon from "../Display/Display.Icon.js";
|
|
4
4
|
import Pressable from "../primitives/Pressable.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useTheme } from "../theme/index.js";
|
|
6
6
|
import { cn } from "../theme/tw.js";
|
|
7
7
|
import { Typography } from "../Typography/index.js";
|
|
8
8
|
import { useState } from "react";
|
|
@@ -24,8 +24,9 @@ const iconByMode = {
|
|
|
24
24
|
* (`@react-native-community/datetimepicker`) — a case where native wins (expected UX).
|
|
25
25
|
* Subpath `Form/DatePicker`; **optional** peer dep.
|
|
26
26
|
*/
|
|
27
|
-
function DatePicker({ value, onChange, mode = "date", label, placeholder = "Seleccionar…", format, minimumDate, maximumDate, disabled = false, className, testID, }) {
|
|
27
|
+
function DatePicker({ value, onChange, mode = "date", label, placeholder = "Seleccionar…", format, minimumDate, maximumDate, disabled = false, accessibleSizing = false, className, testID, }) {
|
|
28
28
|
const [open, setOpen] = useState(false);
|
|
29
|
+
const theme = useTheme();
|
|
29
30
|
const fmt = format ?? ((d) => defaultFormat(d, mode));
|
|
30
31
|
const handleValueChange = (_event, date) => {
|
|
31
32
|
if (Platform.OS === "android")
|
|
@@ -33,6 +34,6 @@ function DatePicker({ value, onChange, mode = "date", label, placeholder = "Sele
|
|
|
33
34
|
if (date)
|
|
34
35
|
onChange(date);
|
|
35
36
|
};
|
|
36
|
-
return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "font-medium text-text-primary", variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre el selector de fecha", accessibilityLabel: label ?? placeholder, accessibilityRole: "button", accessibilityState: { disabled }, className: "flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3 min-h-[44px]", disabled: disabled, onPress: () => setOpen(true), children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", value ? "text-text-primary" : "text-text-tertiary"), children: value ? fmt(value) : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: iconByMode[mode], size: 18 })] }), open ? (_jsx(DateTimePicker, { maximumDate: maximumDate, minimumDate: minimumDate, mode: mode === "datetime" ? "date" : mode, onDismiss: () => setOpen(false), onValueChange: handleValueChange, value: value ?? new Date() })) : null] }));
|
|
37
|
+
return (_jsxs(View, { className: cn("gap-1", className), testID: testID, children: [label ? (_jsx(Typography.Text, { className: "font-medium text-text-primary", variant: "bodySmall", children: label })) : null, _jsxs(Pressable, { accessibilityHint: "Abre el selector de fecha", accessibilityLabel: label ?? placeholder, accessibilityRole: "button", accessibilityState: { disabled }, accessibilityValue: { text: value ? fmt(value) : placeholder }, className: "flex-row items-center justify-between rounded-lg border border-input bg-bg-card px-3 min-h-[44px]", disabled: disabled, onPress: () => setOpen(true), style: accessibleSizing ? { minHeight: 48 } : undefined, children: [_jsx(Typography.Text, { className: cn("flex-1 text-sm", value ? "text-text-primary" : "text-text-tertiary"), children: value ? fmt(value) : placeholder }), _jsx(Icon, { color: theme.icon.secondary, name: iconByMode[mode], size: 18 })] }), open ? (_jsx(DateTimePicker, { maximumDate: maximumDate, minimumDate: minimumDate, mode: mode === "datetime" ? "date" : mode, onDismiss: () => setOpen(false), onValueChange: handleValueChange, value: value ?? new Date() })) : null] }));
|
|
37
38
|
}
|
|
38
39
|
export default DatePicker;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** @deprecated Use `Actions.FilterButton` from `@sincpro/mobile-ui/Actions` instead. */
|
|
2
|
-
export type { FilterButtonProps } from "../Actions/Actions.FilterButton.js";
|
|
2
|
+
export type { FilterButtonGroupProps, FilterButtonProps, } from "../Actions/Actions.FilterButton.js";
|
|
3
3
|
export { FilterButton, FilterButtonGroup, } from "../Actions/Actions.FilterButton.js";
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type TextInputProps } from "react-native";
|
|
1
|
+
import type { ComponentRef, Ref } from "react";
|
|
2
|
+
import { TextInput, type TextInputProps } from "react-native";
|
|
3
3
|
interface FormInputProps extends TextInputProps {
|
|
4
|
+
ref?: Ref<ComponentRef<typeof TextInput>>;
|
|
4
5
|
label: string;
|
|
5
6
|
error?: string;
|
|
6
7
|
tooltip?: string;
|
|
7
8
|
className?: string;
|
|
9
|
+
accessibleSizing?: boolean;
|
|
8
10
|
}
|
|
9
|
-
declare function FormInput({ label, error, tooltip, style, multiline, numberOfLines, onFocus, onBlur, className, ...props }: FormInputProps):
|
|
11
|
+
declare function FormInput({ label, error, tooltip, style, multiline, numberOfLines, onFocus, onBlur, className, accessibleSizing, ref, ...props }: FormInputProps): import("react").JSX.Element;
|
|
10
12
|
export default FormInput;
|
package/dist/Form/Form.Input.js
CHANGED
|
@@ -15,7 +15,7 @@ const inputContainer = tv({
|
|
|
15
15
|
},
|
|
16
16
|
defaultVariants: { state: "default" },
|
|
17
17
|
});
|
|
18
|
-
function FormInput({ label, error, tooltip, style, multiline = false, numberOfLines = 1, onFocus, onBlur, className, ...props }) {
|
|
18
|
+
function FormInput({ label, error, tooltip, style, multiline = false, numberOfLines = 1, onFocus, onBlur, className, accessibleSizing = false, ref, ...props }) {
|
|
19
19
|
const [isFocused, setIsFocused] = useState(false);
|
|
20
20
|
const hasError = Boolean(error);
|
|
21
21
|
const handleFocus = (e) => {
|
|
@@ -33,6 +33,6 @@ function FormInput({ label, error, tooltip, style, multiline = false, numberOfLi
|
|
|
33
33
|
return "focused";
|
|
34
34
|
return "default";
|
|
35
35
|
};
|
|
36
|
-
return (_jsxs(View, { className: cn("mb-2", className), children: [_jsx(Typography.Text, { className: "mb-1 font-medium text-text-primary", variant: "bodySmall", children: label }), _jsx(View, { className: inputContainer({ state: getState() }), children: _jsx(TextInput, { className: cn("px-3 py-2.5 text-sm min-h-[44px] text-text-primary", multiline && "min-h-[64px] pt-2.5"), multiline: multiline, numberOfLines: numberOfLines, onBlur: handleBlur, onFocus: handleFocus, placeholderTextColor: theme.text.tertiary, selectionColor: theme.primary, textAlignVertical: multiline ? "top" : "center", underlineColorAndroid: "transparent", ...props }) }), hasError && (_jsx(Typography.Text, { className: "mt-0.5 text-danger", variant: "bodySmall", children: error }))] }));
|
|
36
|
+
return (_jsxs(View, { className: cn("mb-2", className), children: [_jsx(Typography.Text, { className: "mb-1 font-medium text-text-primary", variant: "bodySmall", children: label }), _jsx(View, { className: inputContainer({ state: getState() }), children: _jsx(TextInput, { accessibilityHint: error || tooltip, accessibilityLabel: label, className: cn("px-3 py-2.5 text-sm min-h-[44px] text-text-primary", multiline && "min-h-[64px] pt-2.5"), multiline: multiline, numberOfLines: numberOfLines, onBlur: handleBlur, onFocus: handleFocus, placeholderTextColor: theme.text.tertiary, ref: ref, selectionColor: theme.primary, style: [accessibleSizing ? { minHeight: 48 } : undefined, style], textAlignVertical: multiline ? "top" : "center", underlineColorAndroid: "transparent", ...props }) }), hasError && (_jsx(Typography.Text, { accessibilityLiveRegion: "polite", className: "mt-0.5 text-danger", variant: "bodySmall", children: error }))] }));
|
|
37
37
|
}
|
|
38
38
|
export default FormInput;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type KeyboardToolbarProps } from "./Form.KeyboardToolbar.js";
|
|
2
|
+
import { type ReactNode, type Ref } from "react";
|
|
3
|
+
import type { KeyboardAwareScrollViewProps, KeyboardAwareScrollViewRef } from "react-native-keyboard-controller";
|
|
4
|
+
export interface KeyboardFormProps extends KeyboardAwareScrollViewProps {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
ref?: Ref<KeyboardAwareScrollViewRef>;
|
|
7
|
+
toolbar?: boolean;
|
|
8
|
+
toolbarProps?: Omit<KeyboardToolbarProps, "onHeightChange">;
|
|
9
|
+
toolbarLabels?: Pick<KeyboardToolbarProps, "previousLabel" | "nextLabel" | "doneLabel" | "showDoneLabel">;
|
|
10
|
+
}
|
|
11
|
+
export default function KeyboardForm({ children, toolbar, toolbarLabels, toolbarProps, bottomOffset, style, ...props }: KeyboardFormProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import KeyboardToolbar from "./Form.KeyboardToolbar.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { View } from "react-native";
|
|
5
|
+
import { KeyboardAwareScrollView, KeyboardToolbar as NativeToolbar, } from "react-native-keyboard-controller";
|
|
6
|
+
export default function KeyboardForm({ children, toolbar = true, toolbarLabels, toolbarProps, bottomOffset, style, ...props }) {
|
|
7
|
+
const [toolbarHeight, setToolbarHeight] = useState(48);
|
|
8
|
+
return (_jsxs(View, { style: { flex: 1 }, children: [_jsx(NativeToolbar.Group, { style: { flex: 1 }, children: _jsx(KeyboardAwareScrollView, { bottomOffset: bottomOffset ?? (toolbar ? toolbarHeight + 24 : 20), keyboardShouldPersistTaps: "handled", style: [{ flex: 1 }, style], ...props, children: children }) }), toolbar ? (_jsx(KeyboardToolbar, { ...toolbarLabels, ...toolbarProps, onHeightChange: setToolbarHeight })) : null] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type StyleProp, type TextStyle, type ViewStyle } from "react-native";
|
|
3
|
+
export type KeyboardToolbarAction = "previous" | "next" | "done";
|
|
4
|
+
export interface KeyboardToolbarProps {
|
|
5
|
+
previousLabel?: string;
|
|
6
|
+
nextLabel?: string;
|
|
7
|
+
doneLabel?: string;
|
|
8
|
+
showDoneLabel?: boolean;
|
|
9
|
+
onHeightChange?: (height: number) => void;
|
|
10
|
+
className?: string;
|
|
11
|
+
style?: StyleProp<ViewStyle>;
|
|
12
|
+
actionStyle?: StyleProp<ViewStyle>;
|
|
13
|
+
labelStyle?: StyleProp<TextStyle>;
|
|
14
|
+
iconSize?: number;
|
|
15
|
+
testID?: string;
|
|
16
|
+
renderActionContent?: (action: KeyboardToolbarAction, state: {
|
|
17
|
+
label: string;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
}) => ReactNode;
|
|
20
|
+
}
|
|
21
|
+
export default function KeyboardToolbar({ previousLabel, nextLabel, doneLabel, showDoneLabel, onHeightChange, className, style, actionStyle, labelStyle, iconSize, testID, renderActionContent, }: KeyboardToolbarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "nativewind/jsx-runtime";
|
|
2
|
+
import Icon from "../Display/Display.Icon.js";
|
|
3
|
+
import Pressable from "../primitives/Pressable.js";
|
|
4
|
+
import { useTheme } from "../theme/index.js";
|
|
5
|
+
import { Typography } from "../Typography/index.js";
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { StyleSheet, useWindowDimensions, View, } from "react-native";
|
|
8
|
+
import { FocusedInputEvents, KeyboardController, KeyboardStickyView, useKeyboardState, } from "react-native-keyboard-controller";
|
|
9
|
+
export default function KeyboardToolbar({ previousLabel = "Campo anterior", nextLabel = "Campo siguiente", doneLabel = "Ocultar teclado", showDoneLabel = false, onHeightChange, className, style, actionStyle, labelStyle, iconSize = 20, testID = "keyboard-toolbar", renderActionContent, }) {
|
|
10
|
+
const theme = useTheme();
|
|
11
|
+
const frame = useRef(null);
|
|
12
|
+
const { height: windowHeight } = useWindowDimensions();
|
|
13
|
+
const [bottomGap, setBottomGap] = useState(0);
|
|
14
|
+
const [height, setHeight] = useState(48);
|
|
15
|
+
const [inputs, setInputs] = useState({ current: 0, count: 0 });
|
|
16
|
+
const isVisible = useKeyboardState((state) => state.isVisible);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const subscription = FocusedInputEvents.addListener("focusDidSet", setInputs);
|
|
19
|
+
return () => subscription.remove();
|
|
20
|
+
}, []);
|
|
21
|
+
const previousDisabled = inputs.current === 0;
|
|
22
|
+
const nextDisabled = inputs.current >= inputs.count - 1;
|
|
23
|
+
const content = (action, label, disabled) => {
|
|
24
|
+
if (renderActionContent)
|
|
25
|
+
return renderActionContent(action, { label, disabled });
|
|
26
|
+
if (action === "done" && showDoneLabel)
|
|
27
|
+
return (_jsx(Typography.Text, { className: "text-text-accent", style: labelStyle, variant: "bodySmall", children: label }));
|
|
28
|
+
return (_jsx(Icon, { color: disabled ? theme.text.tertiary : theme.text.accent, name: action === "previous"
|
|
29
|
+
? "chevron-up"
|
|
30
|
+
: action === "next"
|
|
31
|
+
? "chevron-down"
|
|
32
|
+
: "checkmark", size: iconSize }));
|
|
33
|
+
};
|
|
34
|
+
return (_jsx(View, { accessibilityElementsHidden: !isVisible, importantForAccessibility: isVisible ? "auto" : "no-hide-descendants", onLayout: () => {
|
|
35
|
+
frame.current?.measureInWindow((_x, y, _width, frameHeight) => setBottomGap(Math.max(0, windowHeight - y - frameHeight)));
|
|
36
|
+
}, pointerEvents: "box-none", ref: frame, style: StyleSheet.absoluteFill, children: _jsx(KeyboardStickyView, { offset: { closed: height, opened: bottomGap - 8 }, style: { position: "absolute", bottom: 0, left: 0, right: 0 }, children: _jsxs(View, { className: className, onLayout: (event) => {
|
|
37
|
+
const nextHeight = event.nativeEvent.layout.height;
|
|
38
|
+
setHeight(nextHeight);
|
|
39
|
+
onHeightChange?.(nextHeight);
|
|
40
|
+
}, style: [
|
|
41
|
+
{
|
|
42
|
+
minHeight: 48,
|
|
43
|
+
flexDirection: "row",
|
|
44
|
+
alignItems: "center",
|
|
45
|
+
justifyContent: "space-between",
|
|
46
|
+
paddingHorizontal: 8,
|
|
47
|
+
backgroundColor: theme.bg.card,
|
|
48
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
49
|
+
borderTopColor: theme.border.light,
|
|
50
|
+
},
|
|
51
|
+
style,
|
|
52
|
+
], testID: testID, children: [_jsxs(View, { style: { flexDirection: "row" }, children: [_jsx(Pressable, { accessibilityLabel: previousLabel, disabled: previousDisabled, hitSlop: 0, onPress: () => KeyboardController.setFocusTo("prev"), style: [styles.arrow, actionStyle], children: content("previous", previousLabel, previousDisabled) }), _jsx(Pressable, { accessibilityLabel: nextLabel, disabled: nextDisabled, hitSlop: 0, onPress: () => KeyboardController.setFocusTo("next"), style: [styles.arrow, actionStyle], children: content("next", nextLabel, nextDisabled) })] }), _jsx(Pressable, { accessibilityLabel: doneLabel, hitSlop: 0, onPress: () => KeyboardController.dismiss(), style: [
|
|
53
|
+
{
|
|
54
|
+
minWidth: 48,
|
|
55
|
+
minHeight: 48,
|
|
56
|
+
flexShrink: 1,
|
|
57
|
+
justifyContent: "center",
|
|
58
|
+
padding: 8,
|
|
59
|
+
},
|
|
60
|
+
actionStyle,
|
|
61
|
+
], children: content("done", doneLabel, false) })] }) }) }));
|
|
62
|
+
}
|
|
63
|
+
const styles = StyleSheet.create({
|
|
64
|
+
arrow: {
|
|
65
|
+
minWidth: 48,
|
|
66
|
+
minHeight: 48,
|
|
67
|
+
alignItems: "center",
|
|
68
|
+
justifyContent: "center",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
@@ -10,8 +10,10 @@ export interface SelectProps<T extends string | number> {
|
|
|
10
10
|
placeholder?: string;
|
|
11
11
|
title?: string;
|
|
12
12
|
disabled?: boolean;
|
|
13
|
+
accessibleSizing?: boolean;
|
|
13
14
|
className?: string;
|
|
14
15
|
testID?: string;
|
|
16
|
+
showCloseButton?: boolean;
|
|
15
17
|
}
|
|
16
|
-
declare function Select<T extends string | number>({ options, value, onChange, label, placeholder, title, disabled, className, testID, }: SelectProps<T>): import("react").JSX.Element;
|
|
18
|
+
declare function Select<T extends string | number>({ options, value, onChange, label, placeholder, title, disabled, accessibleSizing, className, testID, showCloseButton, }: SelectProps<T>): import("react").JSX.Element;
|
|
17
19
|
export default Select;
|