@utilitywarehouse/hearth-react-native 0.16.1 → 0.17.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +14 -14
- package/CHANGELOG.md +156 -0
- package/build/components/Card/CardAction/CardActionRoot.js +12 -2
- package/build/components/Card/CardActions.context.d.ts +6 -0
- package/build/components/Card/CardActions.context.js +5 -0
- package/build/components/Card/CardActions.d.ts +7 -0
- package/build/components/Card/CardActions.js +29 -0
- package/build/components/Card/CardRoot.js +16 -104
- package/build/components/Card/helpers.d.ts +8 -0
- package/build/components/Card/helpers.js +146 -0
- package/build/components/Card/index.d.ts +2 -0
- package/build/components/Card/index.js +2 -0
- package/build/components/DateInput/DateInput.d.ts +1 -1
- package/build/components/DateInput/DateInput.js +2 -3
- package/build/components/DateInput/DateInput.props.d.ts +22 -1
- package/build/components/DateInput/DateInputSegment.d.ts +2 -16
- package/build/components/DateInput/DateInputSegment.js +3 -6
- package/build/components/ExpandableCard/ExpandableCardGroup.d.ts +1 -1
- package/build/components/ExpandableCard/ExpandableCardGroup.js +2 -2
- package/build/components/ExpandableCard/ExpandableCardGroup.props.d.ts +4 -0
- package/build/components/Input/Input.js +4 -3
- package/build/components/Input/Input.props.d.ts +9 -0
- package/build/components/List/List.context.d.ts +4 -2
- package/build/components/List/List.context.js +0 -2
- package/build/components/List/List.d.ts +1 -1
- package/build/components/List/List.js +25 -38
- package/build/components/List/List.props.d.ts +1 -0
- package/build/components/List/ListAction/ListAction.js +24 -7
- package/build/components/List/ListAction/ListAction.props.d.ts +1 -0
- package/build/components/List/ListItem/ListItemRoot.js +12 -4
- package/build/utils/isThemedImageProps.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/Card/Card.docs.mdx +224 -66
- package/src/components/Card/Card.stories.tsx +29 -25
- package/src/components/Card/CardAction/CardAction.stories.tsx +239 -93
- package/src/components/Card/CardAction/CardActionRoot.tsx +15 -2
- package/src/components/Card/CardActions.context.ts +12 -0
- package/src/components/Card/CardActions.tsx +40 -0
- package/src/components/Card/CardRoot.tsx +27 -132
- package/src/components/Card/helpers.tsx +195 -0
- package/src/components/Card/index.ts +2 -0
- package/src/components/DateInput/DateInput.docs.mdx +47 -29
- package/src/components/DateInput/DateInput.props.ts +32 -1
- package/src/components/DateInput/DateInput.stories.tsx +10 -0
- package/src/components/DateInput/DateInput.tsx +12 -1
- package/src/components/DateInput/DateInputSegment.tsx +8 -23
- package/src/components/ExpandableCard/ExpandableCard.figma.tsx +33 -38
- package/src/components/ExpandableCard/ExpandableCardGroup.figma.tsx +34 -17
- package/src/components/ExpandableCard/ExpandableCardGroup.props.ts +5 -0
- package/src/components/ExpandableCard/ExpandableCardGroup.tsx +2 -0
- package/src/components/HighlightBanner/HighlightBanner.figma.tsx +46 -0
- package/src/components/IconButton/IconButton.figma.tsx +20 -30
- package/src/components/IconContainer/IconContainer.figma.tsx +7 -13
- package/src/components/IndicatorIconButton/IndicatorIconButton.figma.tsx +16 -0
- package/src/components/Input/Input.docs.mdx +55 -15
- package/src/components/Input/Input.figma.tsx +106 -40
- package/src/components/Input/Input.props.ts +9 -0
- package/src/components/Input/Input.tsx +21 -0
- package/src/components/Link/Link.figma.tsx +31 -38
- package/src/components/List/List.context.ts +2 -4
- package/src/components/List/List.docs.mdx +10 -5
- package/src/components/List/List.figma.tsx +42 -28
- package/src/components/List/List.props.ts +1 -0
- package/src/components/List/List.stories.tsx +43 -0
- package/src/components/List/List.tsx +38 -51
- package/src/components/List/ListAction/ListAction.figma.tsx +5 -13
- package/src/components/List/ListAction/ListAction.props.ts +1 -0
- package/src/components/List/ListAction/ListAction.tsx +40 -10
- package/src/components/List/ListItem/ListItem.figma.tsx +43 -27
- package/src/components/List/ListItem/ListItemRoot.tsx +15 -4
- package/src/utils/isThemedImageProps.ts +1 -1
- package/src/components/InlineLink/InlineLink.figma.tsx +0 -33
|
@@ -3,8 +3,8 @@ import { View } from 'react-native';
|
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { FormField } from '../FormField';
|
|
5
5
|
import DateInputSegment from './DateInputSegment';
|
|
6
|
-
const DateInput = ({ label, helperText, helperIcon, validationStatus = 'initial', validText, invalidText, disabled, readonly, required, hideDay = false, hideMonth = false, hideYear = false, dayPlaceholder = 'DD', monthPlaceholder = 'MM', yearPlaceholder = 'YYYY', dayValue, monthValue, yearValue, onDayChange, onMonthChange, onYearChange, onDayFocus, onMonthFocus, onYearFocus, onDayBlur, onMonthBlur, onYearBlur, ...props }) => {
|
|
7
|
-
return (_jsx(FormField, { label: label, helperText: helperText, helperIcon: helperIcon, validationStatus: validationStatus, validText: validText, invalidText: invalidText, disabled: disabled, readonly: readonly, required: required, style: styles.wrap, ...props, children: _jsxs(View, { style: styles.container, children: [!hideDay && (_jsx(DateInputSegment, { label: "Day", placeholder: dayPlaceholder, value: dayValue, onChange: onDayChange, onFocus: onDayFocus, onBlur: onDayBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 2, testID: "date-input-day" })), !hideMonth && (_jsx(DateInputSegment, { label: "Month", placeholder: monthPlaceholder, value: monthValue, onChange: onMonthChange, onFocus: onMonthFocus, onBlur: onMonthBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 2, testID: "date-input-month" })), !hideYear && (_jsx(DateInputSegment, { label: "Year", placeholder: yearPlaceholder, value: yearValue, onChange: onYearChange, onFocus: onYearFocus, onBlur: onYearBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 4, testID: "date-input-year" }))] }) }));
|
|
6
|
+
const DateInput = ({ label, helperText, helperIcon, validationStatus = 'initial', validText, invalidText, disabled, readonly, required, hideDay = false, hideMonth = false, hideYear = false, dayPlaceholder = 'DD', monthPlaceholder = 'MM', yearPlaceholder = 'YYYY', dayValue, monthValue, yearValue, onDayChange, onMonthChange, onYearChange, onDayFocus, onMonthFocus, onYearFocus, onDayBlur, onMonthBlur, onYearBlur, inputLabelStyle, inputContainerStyle, inputStyle, ...props }) => {
|
|
7
|
+
return (_jsx(FormField, { label: label, helperText: helperText, helperIcon: helperIcon, validationStatus: validationStatus, validText: validText, invalidText: invalidText, disabled: disabled, readonly: readonly, required: required, style: styles.wrap, ...props, children: _jsxs(View, { style: styles.container, children: [!hideDay && (_jsx(DateInputSegment, { label: "Day", placeholder: dayPlaceholder, value: dayValue, onChange: onDayChange, onFocus: onDayFocus, onBlur: onDayBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 2, testID: "date-input-day", inputContainerStyle: inputContainerStyle, inputStyle: inputStyle, inputLabelStyle: inputLabelStyle })), !hideMonth && (_jsx(DateInputSegment, { label: "Month", placeholder: monthPlaceholder, value: monthValue, onChange: onMonthChange, onFocus: onMonthFocus, onBlur: onMonthBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 2, testID: "date-input-month", inputContainerStyle: inputContainerStyle, inputStyle: inputStyle, inputLabelStyle: inputLabelStyle })), !hideYear && (_jsx(DateInputSegment, { label: "Year", placeholder: yearPlaceholder, value: yearValue, onChange: onYearChange, onFocus: onYearFocus, onBlur: onYearBlur, disabled: disabled, required: required, readonly: readonly, validationStatus: validationStatus, maxLength: 4, testID: "date-input-year", inputContainerStyle: inputContainerStyle, inputStyle: inputStyle, inputLabelStyle: inputLabelStyle }))] }) }));
|
|
8
8
|
};
|
|
9
9
|
DateInput.displayName = 'DateInput';
|
|
10
10
|
const styles = StyleSheet.create(theme => ({
|
|
@@ -14,7 +14,6 @@ const styles = StyleSheet.create(theme => ({
|
|
|
14
14
|
container: {
|
|
15
15
|
flexDirection: 'row',
|
|
16
16
|
gap: theme.components.input.date.gap,
|
|
17
|
-
// alignItems: 'stretch',
|
|
18
17
|
},
|
|
19
18
|
}));
|
|
20
19
|
export default DateInput;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { TextInputProps } from 'react-native';
|
|
1
|
+
import type { TextInputProps, ViewProps } from 'react-native';
|
|
2
2
|
import type { FormFieldBaseProps } from '../FormField/FormField.props';
|
|
3
|
+
import LabelProps from '../Label/Label.props';
|
|
3
4
|
export interface DateInputProps extends FormFieldBaseProps {
|
|
4
5
|
/**
|
|
5
6
|
* Whether the day segment is hidden.
|
|
@@ -76,4 +77,24 @@ export interface DateInputProps extends FormFieldBaseProps {
|
|
|
76
77
|
* Callback fired when the year segment loses focus.
|
|
77
78
|
*/
|
|
78
79
|
onYearBlur?: TextInputProps['onBlur'];
|
|
80
|
+
inputContainerStyle?: ViewProps['style'];
|
|
81
|
+
inputStyle?: ViewProps['style'];
|
|
82
|
+
inputLabelStyle?: LabelProps['style'];
|
|
83
|
+
}
|
|
84
|
+
export interface DateInputSegmentProps {
|
|
85
|
+
label: string;
|
|
86
|
+
placeholder?: string;
|
|
87
|
+
value?: string;
|
|
88
|
+
onChange?: (text: string) => void;
|
|
89
|
+
onFocus?: DateInputProps['onDayFocus'];
|
|
90
|
+
onBlur?: DateInputProps['onDayBlur'];
|
|
91
|
+
disabled?: boolean;
|
|
92
|
+
required?: boolean;
|
|
93
|
+
validationStatus?: DateInputProps['validationStatus'];
|
|
94
|
+
maxLength?: number;
|
|
95
|
+
readonly?: boolean;
|
|
96
|
+
testID?: string;
|
|
97
|
+
inputContainerStyle?: ViewProps['style'];
|
|
98
|
+
inputStyle?: ViewProps['style'];
|
|
99
|
+
inputLabelStyle?: LabelProps['style'];
|
|
79
100
|
}
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
interface DateInputSegmentProps {
|
|
3
|
-
label: string;
|
|
4
|
-
placeholder?: string;
|
|
5
|
-
value?: string;
|
|
6
|
-
onChange?: (text: string) => void;
|
|
7
|
-
onFocus?: DateInputProps['onDayFocus'];
|
|
8
|
-
onBlur?: DateInputProps['onDayBlur'];
|
|
9
|
-
disabled?: boolean;
|
|
10
|
-
required?: boolean;
|
|
11
|
-
validationStatus?: DateInputProps['validationStatus'];
|
|
12
|
-
maxLength?: number;
|
|
13
|
-
readonly?: boolean;
|
|
14
|
-
testID?: string;
|
|
15
|
-
}
|
|
1
|
+
import type { DateInputSegmentProps } from './DateInput.props';
|
|
16
2
|
declare const DateInputSegment: {
|
|
17
|
-
({ label, placeholder, value, onChange, onFocus, onBlur, disabled, validationStatus, maxLength, readonly, testID, }: DateInputSegmentProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
({ label, placeholder, value, onChange, onFocus, onBlur, disabled, validationStatus, maxLength, readonly, testID, inputContainerStyle, inputStyle, inputLabelStyle, }: DateInputSegmentProps): import("react/jsx-runtime").JSX.Element;
|
|
18
4
|
displayName: string;
|
|
19
5
|
};
|
|
20
6
|
export default DateInputSegment;
|
|
@@ -3,16 +3,16 @@ import { View } from 'react-native';
|
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { BodyText } from '../BodyText';
|
|
5
5
|
import { Input } from '../Input';
|
|
6
|
-
const DateInputSegment = ({ label, placeholder, value, onChange, onFocus, onBlur, disabled, validationStatus, maxLength, readonly, testID, }) => {
|
|
6
|
+
const DateInputSegment = ({ label, placeholder, value, onChange, onFocus, onBlur, disabled, validationStatus, maxLength, readonly, testID, inputContainerStyle, inputStyle, inputLabelStyle, }) => {
|
|
7
7
|
styles.useVariants({ disabled });
|
|
8
|
-
return (_jsxs(View, { style: styles.container, children: [_jsx(BodyText, { size: "md", style: styles.label, children: label }), _jsx(Input, { value: value, onChangeText: onChange, onFocus: onFocus, onBlur: onBlur, placeholder: disabled ? undefined : placeholder, keyboardType: "number-pad", maxLength: maxLength, testID: testID, accessibilityLabel: label, disabled: disabled, validationStatus: validationStatus, readonly: readonly, style:
|
|
8
|
+
return (_jsxs(View, { style: [styles.container, inputContainerStyle], children: [_jsx(BodyText, { size: "md", style: [styles.label, inputLabelStyle], children: label }), _jsx(Input, { value: value, onChangeText: onChange, onFocus: onFocus, onBlur: onBlur, placeholder: disabled ? undefined : placeholder, keyboardType: "number-pad", maxLength: maxLength, testID: testID, accessibilityLabel: label, disabled: disabled, validationStatus: validationStatus, readonly: readonly, style: inputStyle })] }));
|
|
9
9
|
};
|
|
10
10
|
DateInputSegment.displayName = 'DateInputSegment';
|
|
11
11
|
const styles = StyleSheet.create(theme => ({
|
|
12
12
|
container: {
|
|
13
13
|
flex: 1,
|
|
14
14
|
gap: theme.components.input.gap,
|
|
15
|
-
|
|
15
|
+
maxWidth: 96,
|
|
16
16
|
},
|
|
17
17
|
label: {
|
|
18
18
|
variants: {
|
|
@@ -23,8 +23,5 @@ const styles = StyleSheet.create(theme => ({
|
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
|
-
input: {
|
|
27
|
-
// maxWidth: 96,
|
|
28
|
-
},
|
|
29
26
|
}));
|
|
30
27
|
export default DateInputSegment;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type ExpandableCardGroupProps from './ExpandableCardGroup.props';
|
|
2
2
|
declare const ExpandableCardGroup: {
|
|
3
|
-
({ heading, helperText, headerTrailingContent, children, style, testID, ...props }: ExpandableCardGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
({ heading, helperText, headerTrailingContent, children, style, testID, invalidText, ...props }: ExpandableCardGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
displayName: string;
|
|
5
5
|
};
|
|
6
6
|
export default ExpandableCardGroup;
|
|
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { SectionHeader } from '../SectionHeader';
|
|
5
|
-
const ExpandableCardGroup = ({ heading, helperText, headerTrailingContent, children, style, testID = 'expandable-card-group', ...props }) => {
|
|
6
|
-
return (_jsxs(View, { style: [styles.container, style], testID: testID, ...props, children: [heading ? (_jsx(SectionHeader, { heading: heading, helperText: helperText, trailingContent: headerTrailingContent })) : null, _jsx(View, { style: styles.cardsContainer, children: children })] }));
|
|
5
|
+
const ExpandableCardGroup = ({ heading, helperText, headerTrailingContent, children, style, testID = 'expandable-card-group', invalidText, ...props }) => {
|
|
6
|
+
return (_jsxs(View, { style: [styles.container, style], testID: testID, ...props, children: [heading ? (_jsx(SectionHeader, { heading: heading, helperText: helperText, trailingContent: headerTrailingContent, invalidText: invalidText })) : null, _jsx(View, { style: styles.cardsContainer, children: children })] }));
|
|
7
7
|
};
|
|
8
8
|
ExpandableCardGroup.displayName = 'ExpandableCardGroup';
|
|
9
9
|
const styles = StyleSheet.create(theme => ({
|
|
@@ -21,5 +21,9 @@ export interface ExpandableCardGroupProps extends ViewProps {
|
|
|
21
21
|
* Test ID for testing
|
|
22
22
|
*/
|
|
23
23
|
testID?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Validation text displayed below the helper text when in an invalid state
|
|
26
|
+
*/
|
|
27
|
+
invalidText?: string;
|
|
24
28
|
}
|
|
25
29
|
export default ExpandableCardGroupProps;
|
|
@@ -3,6 +3,7 @@ import { createInput } from '@gluestack-ui/input';
|
|
|
3
3
|
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
4
4
|
import { CloseSmallIcon, EyeOffSmallIcon, EyeSmallIcon, SearchMediumIcon, } from '@utilitywarehouse/hearth-react-native-icons';
|
|
5
5
|
import { useTheme } from '../../hooks';
|
|
6
|
+
import { BodyText } from '../BodyText';
|
|
6
7
|
import { FormField, useFormFieldContext } from '../FormField';
|
|
7
8
|
import { Spinner } from '../Spinner';
|
|
8
9
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
@@ -19,7 +20,7 @@ export const InputComponent = createInput({
|
|
|
19
20
|
export const InputSlot = InputComponent.Slot;
|
|
20
21
|
export const InputField = InputComponent.Input;
|
|
21
22
|
export const InputIcon = InputComponent.Icon;
|
|
22
|
-
const Input = forwardRef(({ validationStatus = 'initial', children, disabled, focused, readonly, leadingIcon, trailingIcon, type, showPasswordToggle = true, onClear, loading, clearable = false, required = true, inBottomSheet = false, style, label, labelVariant, helperText, helperIcon, validText, invalidText, ...props }, ref) => {
|
|
23
|
+
const Input = forwardRef(({ validationStatus = 'initial', children, disabled, focused, readonly, leadingIcon, trailingIcon, type, showPasswordToggle = true, onClear, loading, clearable = false, required = true, inBottomSheet = false, style, label, labelVariant, helperText, helperIcon, validText, invalidText, prefix, suffix, ...props }, ref) => {
|
|
23
24
|
const formFieldContext = useFormFieldContext();
|
|
24
25
|
const inputLabel = label ?? formFieldContext?.label;
|
|
25
26
|
const inputHelperText = helperText ?? formFieldContext?.helperText;
|
|
@@ -84,11 +85,11 @@ const Input = forwardRef(({ validationStatus = 'initial', children, disabled, fo
|
|
|
84
85
|
}
|
|
85
86
|
return accessibilityHint || props.accessibilityHint;
|
|
86
87
|
};
|
|
87
|
-
return (_jsx(FormField, { label: label, labelVariant: labelVariant, helperText: helperText, helperIcon: helperIcon, validText: validText, invalidText: invalidText, required: required, validationStatus: validationStatus, disabled: disabled, readonly: readonly, accessibilityHandledByChildren: true, children: _jsx(InputComponent, { ...(children ? props : {}), validationStatus: inputValidationStatus, isInvalid: inputValidationStatus === 'invalid', isReadOnly: inputReadonly, isDisabled: inputDisabled, isFocused: focused, type: type, isRequired: inputRequired, style: style, children: children ? (_jsx(_Fragment, { children: children })) : (_jsxs(_Fragment, { children: [!!leadingIconComponent && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: leadingIconComponent }) })), _jsx(InputField
|
|
88
|
+
return (_jsx(FormField, { label: label, labelVariant: labelVariant, helperText: helperText, helperIcon: helperIcon, validText: validText, invalidText: invalidText, required: required, validationStatus: validationStatus, disabled: disabled, readonly: readonly, accessibilityHandledByChildren: true, children: _jsx(InputComponent, { ...(children ? props : {}), validationStatus: inputValidationStatus, isInvalid: inputValidationStatus === 'invalid', isReadOnly: inputReadonly, isDisabled: inputDisabled, isFocused: focused, type: type, isRequired: inputRequired, style: style, children: children ? (_jsx(_Fragment, { children: children })) : (_jsxs(_Fragment, { children: [!!leadingIconComponent && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: leadingIconComponent }) })), !!prefix && (_jsx(InputSlot, { children: typeof prefix === 'string' || typeof prefix === 'number' ? (_jsx(BodyText, { children: prefix })) : (prefix) })), _jsx(InputField
|
|
88
89
|
// @ts-expect-error - ref forwarding issue
|
|
89
90
|
, {
|
|
90
91
|
// @ts-expect-error - ref forwarding issue
|
|
91
|
-
ref: inputRef, type: fieldType, inputMode: getInputMode, inBottomSheet: inBottomSheet, ...props, "aria-label": getAccessibilityLabel(), accessibilityHint: getAccessibilityHint() }), shouldShowClear && (_jsx(InputSlot, { children: _jsx(UnstyledIconButton, { onPress: onClear, icon: CloseSmallIcon }) })), loading && (_jsx(InputSlot, { children: _jsx(Spinner, { size: "xs", color: color.icon.primary }) })), shouldShowPasswordToggle && (_jsx(InputSlot, { children: _jsx(UnstyledIconButton, { onPress: toggleFieldType, icon: fieldType === 'password' ? EyeSmallIcon : EyeOffSmallIcon }) })), !!trailingIcon && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: trailingIcon }) }))] })) }) }));
|
|
92
|
+
ref: inputRef, type: fieldType, inputMode: getInputMode, inBottomSheet: inBottomSheet, ...props, "aria-label": getAccessibilityLabel(), accessibilityHint: getAccessibilityHint() }), shouldShowClear && (_jsx(InputSlot, { children: _jsx(UnstyledIconButton, { onPress: onClear, icon: CloseSmallIcon }) })), loading && (_jsx(InputSlot, { children: _jsx(Spinner, { size: "xs", color: color.icon.primary }) })), shouldShowPasswordToggle && (_jsx(InputSlot, { children: _jsx(UnstyledIconButton, { onPress: toggleFieldType, icon: fieldType === 'password' ? EyeSmallIcon : EyeOffSmallIcon }) })), !!suffix && (_jsx(InputSlot, { children: typeof suffix === 'string' || typeof suffix === 'number' ? (_jsx(BodyText, { children: suffix })) : (suffix) })), !!trailingIcon && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: trailingIcon }) }))] })) }) }));
|
|
92
93
|
});
|
|
93
94
|
Input.displayName = 'Input';
|
|
94
95
|
export default Input;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import type { TextInputProps, ViewProps } from 'react-native';
|
|
3
4
|
export interface InputBaseProps {
|
|
4
5
|
/**
|
|
@@ -43,12 +44,16 @@ export interface InputWithChildrenProps extends InputBaseProps, ViewProps {
|
|
|
43
44
|
onClear?: never;
|
|
44
45
|
leadingIcon?: never;
|
|
45
46
|
trailingIcon?: never;
|
|
47
|
+
prefix?: never;
|
|
48
|
+
suffix?: never;
|
|
46
49
|
}
|
|
47
50
|
interface InputWithoutChildrenBaseProps extends InputBaseProps, Omit<TextInputProps, 'children'> {
|
|
48
51
|
children?: never;
|
|
49
52
|
leadingIcon?: ComponentType;
|
|
50
53
|
trailingIcon?: ComponentType;
|
|
51
54
|
required?: boolean;
|
|
55
|
+
prefix?: string | number | React.ReactNode;
|
|
56
|
+
suffix?: string | number | React.ReactNode;
|
|
52
57
|
}
|
|
53
58
|
interface TextInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
54
59
|
type?: 'text';
|
|
@@ -65,6 +70,8 @@ interface PasswordInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
|
65
70
|
loading?: never;
|
|
66
71
|
clearable?: never;
|
|
67
72
|
onClear?: never;
|
|
73
|
+
prefix?: never;
|
|
74
|
+
suffix?: never;
|
|
68
75
|
}
|
|
69
76
|
interface SearchInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
70
77
|
type: 'search';
|
|
@@ -73,6 +80,8 @@ interface SearchInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
|
73
80
|
onClear?: () => void;
|
|
74
81
|
showPasswordToggle?: never;
|
|
75
82
|
format?: never;
|
|
83
|
+
prefix?: never;
|
|
84
|
+
suffix?: never;
|
|
76
85
|
}
|
|
77
86
|
export type InputWithoutChildrenProps = TextInputSpecificProps | PasswordInputSpecificProps | SearchInputSpecificProps;
|
|
78
87
|
/**
|
|
@@ -3,11 +3,13 @@ export declare const ListContext: import("react").Context<{
|
|
|
3
3
|
loading?: ListProps["loading"];
|
|
4
4
|
disabled?: ListProps["disabled"];
|
|
5
5
|
container?: ListProps["container"];
|
|
6
|
+
firstItemId?: string;
|
|
7
|
+
registerItem?: (id: string) => () => void;
|
|
6
8
|
}>;
|
|
7
9
|
export declare const useListContext: () => {
|
|
8
10
|
loading?: ListProps["loading"];
|
|
9
11
|
disabled?: ListProps["disabled"];
|
|
10
12
|
container?: ListProps["container"];
|
|
13
|
+
firstItemId?: string;
|
|
14
|
+
registerItem?: (id: string) => () => void;
|
|
11
15
|
};
|
|
12
|
-
export declare const ListFirstItemContext: import("react").Context<boolean>;
|
|
13
|
-
export declare const useListFirstItemContext: () => boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type ListProps from './List.props';
|
|
2
2
|
declare const List: {
|
|
3
|
-
({ children, heading, helperText, headerTrailingContent, ...props }: ListProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
({ children, heading, helperText, headerTrailingContent, invalidText, ...props }: ListProps): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
displayName: string;
|
|
5
5
|
};
|
|
6
6
|
export default List;
|
|
@@ -1,54 +1,41 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
5
|
import { Card } from '../Card';
|
|
6
6
|
import { SectionHeader } from '../SectionHeader';
|
|
7
|
-
import { ListContext
|
|
8
|
-
|
|
9
|
-
import { ListItem } from './ListItem';
|
|
10
|
-
const markFirstListItem = (children) => {
|
|
11
|
-
let found = false;
|
|
12
|
-
const recursiveClone = (children) => {
|
|
13
|
-
return React.Children.map(children, (child) => {
|
|
14
|
-
if (!React.isValidElement(child))
|
|
15
|
-
return child;
|
|
16
|
-
// Check if the current element is the ListItem or ListAction and hasn't been marked yet
|
|
17
|
-
if (!found) {
|
|
18
|
-
if (child.type === ListItem || child.type === ListAction) {
|
|
19
|
-
found = true;
|
|
20
|
-
return (_jsx(ListFirstItemContext.Provider, { value: true, children: child }));
|
|
21
|
-
}
|
|
22
|
-
// If the child has nested children, process them recursively
|
|
23
|
-
if (React.isValidElement(child) &&
|
|
24
|
-
child.props &&
|
|
25
|
-
typeof child.props === 'object' &&
|
|
26
|
-
child.props !== null &&
|
|
27
|
-
'children' in child.props &&
|
|
28
|
-
child.props.children) {
|
|
29
|
-
const clonedChildren = recursiveClone(child.props.children);
|
|
30
|
-
return React.cloneElement(child, { children: clonedChildren });
|
|
31
|
-
}
|
|
32
|
-
found = true;
|
|
33
|
-
return _jsx(ListFirstItemContext.Provider, { value: true, children: child });
|
|
34
|
-
}
|
|
35
|
-
return child;
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
return recursiveClone(children);
|
|
39
|
-
};
|
|
40
|
-
const List = ({ children, heading, helperText, headerTrailingContent, ...props }) => {
|
|
7
|
+
import { ListContext } from './List.context';
|
|
8
|
+
const List = ({ children, heading, helperText, headerTrailingContent, invalidText, ...props }) => {
|
|
41
9
|
const { loading, disabled, container = 'none' } = props;
|
|
10
|
+
const orderRef = useRef([]);
|
|
11
|
+
const [firstItemId, setFirstItemId] = useState(undefined);
|
|
42
12
|
const containerToCard = {
|
|
43
13
|
variant: container === 'subtleWhite' || container === 'subtleWarmWhite' ? 'subtle' : 'emphasis',
|
|
44
14
|
colorScheme: container === 'subtleWhite' || container === 'emphasisWhite'
|
|
45
15
|
? 'neutralStrong'
|
|
46
16
|
: 'neutralSubtle',
|
|
47
17
|
};
|
|
48
|
-
const
|
|
49
|
-
|
|
18
|
+
const registerItem = useCallback((id) => {
|
|
19
|
+
if (!orderRef.current.includes(id)) {
|
|
20
|
+
orderRef.current.push(id);
|
|
21
|
+
}
|
|
22
|
+
const nextFirst = orderRef.current[0];
|
|
23
|
+
setFirstItemId(prev => (prev === nextFirst ? prev : nextFirst));
|
|
24
|
+
return () => {
|
|
25
|
+
orderRef.current = orderRef.current.filter(currentId => currentId !== id);
|
|
26
|
+
const nextFirst = orderRef.current[0];
|
|
27
|
+
setFirstItemId(prev => (prev === nextFirst ? prev : nextFirst));
|
|
28
|
+
};
|
|
29
|
+
}, []);
|
|
30
|
+
const value = {
|
|
31
|
+
loading,
|
|
32
|
+
disabled,
|
|
33
|
+
container,
|
|
34
|
+
firstItemId,
|
|
35
|
+
registerItem,
|
|
36
|
+
};
|
|
50
37
|
styles.useVariants({ disabled });
|
|
51
|
-
return (_jsx(ListContext.Provider, { value: value, children: _jsxs(View, { ...props, style: [styles.container, props.style], children: [heading ? (_jsx(SectionHeader, { heading: heading, helperText: helperText, trailingContent: headerTrailingContent })) : null, container === 'none' ? (_jsx(View, { children:
|
|
38
|
+
return (_jsx(ListContext.Provider, { value: value, children: _jsxs(View, { ...props, style: [styles.container, props.style], children: [heading ? (_jsx(SectionHeader, { heading: heading, helperText: helperText, trailingContent: headerTrailingContent, invalidText: invalidText })) : null, container === 'none' ? (_jsx(View, { children: children })) : (React.Children.count(children) > 0 && (_jsx(Card, { ...containerToCard, noPadding: true, style: styles.card, children: _jsx(_Fragment, { children: children }) })))] }) }));
|
|
52
39
|
};
|
|
53
40
|
List.displayName = 'List';
|
|
54
41
|
const styles = StyleSheet.create(theme => ({
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { createPressable } from '@gluestack-ui/pressable';
|
|
3
3
|
import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
4
|
-
import {
|
|
4
|
+
import { useId, useLayoutEffect } from 'react';
|
|
5
|
+
import { Pressable, View } from 'react-native';
|
|
5
6
|
import { StyleSheet } from 'react-native-unistyles';
|
|
6
|
-
import {
|
|
7
|
+
import { Skeleton } from '../../Skeleton';
|
|
8
|
+
import { useListContext } from '../List.context';
|
|
7
9
|
import ListActionContent from './ListActionContent';
|
|
8
10
|
import ListActionText from './ListActionText';
|
|
9
11
|
import ListActionTrailingContent from './ListActionTrailingContent';
|
|
10
12
|
import ListActionTrailingIcon from './ListActionTrailingIcon';
|
|
11
|
-
const ListActionRoot = ({ heading, disabled, variant = 'subtle', ...props }) => {
|
|
13
|
+
const ListActionRoot = ({ heading, disabled, variant = 'subtle', loading, ...props }) => {
|
|
12
14
|
const { onPress } = props;
|
|
13
15
|
const listContext = useListContext();
|
|
14
|
-
const
|
|
16
|
+
const { registerItem, firstItemId } = listContext;
|
|
15
17
|
const { active } = props.states || { active: false };
|
|
16
18
|
const getListContainer = () => {
|
|
17
19
|
if (listContext?.container?.includes('subtle')) {
|
|
@@ -24,16 +26,24 @@ const ListActionRoot = ({ heading, disabled, variant = 'subtle', ...props }) =>
|
|
|
24
26
|
};
|
|
25
27
|
const isDisabled = disabled || listContext?.disabled || false;
|
|
26
28
|
const listItemVariant = getListContainer() || variant;
|
|
29
|
+
const actionId = useId();
|
|
30
|
+
useLayoutEffect(() => {
|
|
31
|
+
if (!registerItem) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
return registerItem(actionId);
|
|
35
|
+
}, [actionId, registerItem]);
|
|
36
|
+
const isFirstChild = firstItemId === actionId;
|
|
27
37
|
const testID = props.testID || 'list-action';
|
|
28
38
|
styles.useVariants({
|
|
29
39
|
variant: listItemVariant,
|
|
30
40
|
disabled: isDisabled,
|
|
31
41
|
active,
|
|
32
42
|
showDisabled: !listContext?.disabled && disabled,
|
|
33
|
-
isFirstChild
|
|
43
|
+
isFirstChild,
|
|
34
44
|
container: listContext?.container,
|
|
35
45
|
});
|
|
36
|
-
return (
|
|
46
|
+
return (_jsx(Pressable, { ...props, testID: testID, style: [styles.container, props.style], disabled: isDisabled || !onPress, children: loading ? (_jsxs(View, { style: styles.loadingWrap, children: [_jsx(Skeleton, { style: { flex: 1, maxWidth: 166 }, width: "auto", height: 24, borderRadius: "sm" }), _jsx(Skeleton, { width: 24, height: 24, borderRadius: "sm" })] })) : (_jsxs(_Fragment, { children: [_jsx(ListActionContent, { children: _jsx(ListActionText, { children: heading }) }), _jsx(ListActionTrailingContent, { style: styles.centeredTrailingIcon, children: _jsx(ListActionTrailingIcon, { as: ChevronRightSmallIcon }) })] })) }));
|
|
37
47
|
};
|
|
38
48
|
const ListAction = createPressable({
|
|
39
49
|
Root: ListActionRoot,
|
|
@@ -100,5 +110,12 @@ const styles = StyleSheet.create(theme => ({
|
|
|
100
110
|
centeredTrailingIcon: {
|
|
101
111
|
justifyContent: 'center',
|
|
102
112
|
},
|
|
113
|
+
loadingWrap: {
|
|
114
|
+
flexDirection: 'row',
|
|
115
|
+
justifyContent: 'space-between',
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
width: '100%',
|
|
118
|
+
gap: theme.space['200'],
|
|
119
|
+
},
|
|
103
120
|
}));
|
|
104
121
|
export default ListAction;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
|
-
import { useMemo } from 'react';
|
|
3
|
+
import { useId, useLayoutEffect, useMemo } from 'react';
|
|
4
4
|
import { Pressable } from 'react-native';
|
|
5
5
|
import { StyleSheet } from 'react-native-unistyles';
|
|
6
6
|
import { DetailText } from '../../DetailText';
|
|
7
7
|
import { Skeleton } from '../../Skeleton';
|
|
8
|
-
import { useListContext
|
|
8
|
+
import { useListContext } from '../List.context';
|
|
9
9
|
import { ListItemContext } from './ListItem.context';
|
|
10
10
|
import ListItemContent from './ListItemContent';
|
|
11
11
|
import ListItemHeading from './ListItemHeading';
|
|
@@ -16,8 +16,16 @@ import ListItemTrailingIcon from './ListItemTrailingIcon';
|
|
|
16
16
|
const ListItemRoot = ({ heading, helperText, leadingContent, trailingContent, disabled, loading, children, states, variant = 'subtle', badge, badgePosition = 'bottom', numericValue, truncateHeading = false, truncateHelperText = false, ...props }) => {
|
|
17
17
|
const { onPress } = props;
|
|
18
18
|
const listContext = useListContext();
|
|
19
|
-
const
|
|
19
|
+
const { registerItem, firstItemId } = listContext;
|
|
20
20
|
const { active } = states || { active: false };
|
|
21
|
+
const itemId = useId();
|
|
22
|
+
useLayoutEffect(() => {
|
|
23
|
+
if (!registerItem) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
return registerItem(itemId);
|
|
27
|
+
}, [itemId, registerItem]);
|
|
28
|
+
const isFirstChild = firstItemId === itemId;
|
|
21
29
|
const getListContainer = () => {
|
|
22
30
|
if (listContext?.container?.includes('subtle')) {
|
|
23
31
|
return 'subtle';
|
|
@@ -39,7 +47,7 @@ const ListItemRoot = ({ heading, helperText, leadingContent, trailingContent, di
|
|
|
39
47
|
active,
|
|
40
48
|
disabled: isDisabled || isLoading,
|
|
41
49
|
showDisabled: !listContext.disabled && disabled,
|
|
42
|
-
isFirstChild
|
|
50
|
+
isFirstChild,
|
|
43
51
|
container: listContext?.container,
|
|
44
52
|
});
|
|
45
53
|
const value = useMemo(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ImageProps } from 'react-native';
|
|
2
|
-
import { ThemedImageProps } from '
|
|
2
|
+
import { ThemedImageProps } from '../components';
|
|
3
3
|
declare const isThemedImageProps: (props: ThemedImageProps | ImageProps) => props is ThemedImageProps;
|
|
4
4
|
export default isThemedImageProps;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"vite-plugin-svgr": "^4.5.0",
|
|
57
57
|
"vitest": "^3.2.4",
|
|
58
58
|
"@utilitywarehouse/hearth-fonts": "^0.0.4",
|
|
59
|
-
"@utilitywarehouse/hearth-react-icons": "^0.7.4",
|
|
60
59
|
"@utilitywarehouse/hearth-react-native-icons": "^0.7.4",
|
|
60
|
+
"@utilitywarehouse/hearth-react-icons": "^0.7.4",
|
|
61
61
|
"@utilitywarehouse/hearth-svg-assets": "^0.3.0",
|
|
62
62
|
"@utilitywarehouse/hearth-tokens": "^0.2.2"
|
|
63
63
|
},
|