@utilitywarehouse/hearth-react-native 0.13.0 → 0.14.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 +1 -1
- package/CHANGELOG.md +16 -0
- package/build/components/BodyText/index.d.ts +1 -0
- package/build/components/Checkbox/CheckboxGroup.context.d.ts +2 -0
- package/build/components/Checkbox/CheckboxGroup.js +5 -5
- package/build/components/Checkbox/CheckboxTextContent.js +0 -1
- package/build/components/Checkbox/CheckboxTileRoot.js +9 -1
- package/build/components/FormField/FormField.d.ts +5 -5
- package/build/components/List/List.context.d.ts +2 -0
- package/build/components/List/List.context.js +2 -0
- package/build/components/List/List.js +19 -15
- package/build/components/List/ListAction/ListAction.js +3 -2
- package/build/components/List/ListAction/ListAction.props.d.ts +0 -1
- package/build/components/List/ListItem/ListItem.props.d.ts +4 -1
- package/build/components/List/ListItem/ListItemHeading.d.ts +13 -0
- package/build/components/List/ListItem/ListItemHeading.js +12 -0
- package/build/components/List/ListItem/ListItemHelperText.d.ts +2 -2
- package/build/components/List/ListItem/ListItemRoot.d.ts +1 -1
- package/build/components/List/ListItem/ListItemRoot.js +7 -6
- package/build/components/List/ListItem/index.d.ts +1 -1
- package/build/components/List/ListItem/index.js +1 -1
- package/build/components/Radio/RadioGroup.context.d.ts +2 -0
- package/build/components/Radio/RadioGroup.js +1 -1
- package/build/components/Radio/RadioTextContent.js +0 -1
- package/build/components/Radio/RadioTileRoot.js +9 -1
- package/package.json +2 -2
- package/src/components/BodyText/index.ts +1 -0
- package/src/components/Checkbox/CheckboxGroup.context.ts +1 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +7 -8
- package/src/components/Checkbox/CheckboxTextContent.tsx +0 -1
- package/src/components/Checkbox/CheckboxTileRoot.tsx +9 -1
- package/src/components/List/List.context.ts +4 -0
- package/src/components/List/List.docs.mdx +30 -24
- package/src/components/List/List.stories.tsx +26 -1
- package/src/components/List/List.tsx +26 -19
- package/src/components/List/ListAction/ListAction.props.ts +0 -1
- package/src/components/List/ListAction/ListAction.tsx +3 -2
- package/src/components/List/ListItem/ListItem.props.ts +4 -1
- package/src/components/List/ListItem/ListItemHeading.tsx +20 -0
- package/src/components/List/ListItem/ListItemHelperText.tsx +2 -3
- package/src/components/List/ListItem/ListItemRoot.tsx +11 -6
- package/src/components/List/ListItem/index.ts +1 -1
- package/src/components/Modal/Modal.docs.mdx +23 -20
- package/src/components/Radio/Radio.stories.tsx +24 -0
- package/src/components/Radio/RadioGroup.context.ts +1 -0
- package/src/components/Radio/RadioGroup.tsx +2 -2
- package/src/components/Radio/RadioTextContent.tsx +0 -1
- package/src/components/Radio/RadioTileRoot.tsx +9 -1
- package/build/components/List/ListItem/ListItemText.d.ts +0 -6
- package/build/components/List/ListItem/ListItemText.js +0 -7
- package/src/components/List/ListItem/ListItemText.tsx +0 -14
package/.turbo/turbo-build.log
CHANGED
package/.turbo/turbo-lint.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @utilitywarehouse/hearth-react-native@0.
|
|
2
|
+
> @utilitywarehouse/hearth-react-native@0.14.0 lint /home/runner/work/hearth/hearth/packages/react-native
|
|
3
3
|
> TIMING=1 eslint --max-warnings 0
|
|
4
4
|
|
|
5
5
|
Rule | Time (ms) | Relative
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @utilitywarehouse/hearth-react-native
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#741](https://github.com/utilitywarehouse/hearth/pull/741) [`acf081a`](https://github.com/utilitywarehouse/hearth/commit/acf081a8e10722ea1e9106f8111f5d9548815646) Thanks [@jordmccord](https://github.com/jordmccord)! - Renames `ListItemText` to `ListItemHeading` (`ListItemText` is deprecated and will be removed in a later release)
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#741](https://github.com/utilitywarehouse/hearth/pull/741) [`acf081a`](https://github.com/utilitywarehouse/hearth/commit/acf081a8e10722ea1e9106f8111f5d9548815646) Thanks [@jordmccord](https://github.com/jordmccord)! - Adds `truncateHeading` and `truncateHelperText` props to `ListItem`
|
|
12
|
+
|
|
13
|
+
- [#740](https://github.com/utilitywarehouse/hearth/pull/740) [`16f1ce0`](https://github.com/utilitywarehouse/hearth/commit/16f1ce073d62c4e72693e7e07233a7498c0d0602) Thanks [@jordmccord](https://github.com/jordmccord)! - Fixes custom `ListItem` first child border issue
|
|
14
|
+
|
|
15
|
+
- [#737](https://github.com/utilitywarehouse/hearth/pull/737) [`85f76db`](https://github.com/utilitywarehouse/hearth/commit/85f76dbbf7c90db96d7e89e5f5e353a772dd84c2) Thanks [@jordmccord](https://github.com/jordmccord)! - Fixes `Radio` and `Checkbox` Groups row direction issue
|
|
16
|
+
|
|
17
|
+
- [#741](https://github.com/utilitywarehouse/hearth/pull/741) [`acf081a`](https://github.com/utilitywarehouse/hearth/commit/acf081a8e10722ea1e9106f8111f5d9548815646) Thanks [@jordmccord](https://github.com/jordmccord)! - Fixes `ListItemText` and `ListItemHelperText` prop types
|
|
18
|
+
|
|
3
19
|
## 0.13.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -2,9 +2,11 @@ export declare const CheckboxGroupContext: import("react").Context<{
|
|
|
2
2
|
disabled?: boolean;
|
|
3
3
|
validationStatus?: "valid" | "invalid" | "initial";
|
|
4
4
|
type?: "default" | "tile";
|
|
5
|
+
direction?: "column" | "row";
|
|
5
6
|
}>;
|
|
6
7
|
export declare const useCheckboxGroupContext: () => {
|
|
7
8
|
disabled?: boolean;
|
|
8
9
|
validationStatus?: "valid" | "invalid" | "initial";
|
|
9
10
|
type?: "default" | "tile";
|
|
11
|
+
direction?: "column" | "row";
|
|
10
12
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useMemo } from 'react';
|
|
3
|
-
import { CheckboxGroup as CheckboxGroupComponent } from './Checkbox';
|
|
4
|
-
import { CheckboxGroupContext } from './CheckboxGroup.context';
|
|
5
3
|
import { View } from 'react-native';
|
|
6
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
7
|
-
import CheckboxGroupTextContent from './CheckboxGroupTextContent';
|
|
8
|
-
import { Label } from '../Label';
|
|
9
5
|
import { Helper } from '../Helper';
|
|
6
|
+
import { Label } from '../Label';
|
|
7
|
+
import { CheckboxGroup as CheckboxGroupComponent } from './Checkbox';
|
|
8
|
+
import { CheckboxGroupContext } from './CheckboxGroup.context';
|
|
9
|
+
import CheckboxGroupTextContent from './CheckboxGroupTextContent';
|
|
10
10
|
const CheckboxGroup = ({ children, disabled, readonly, validationStatus, label, helperText, invalidText, validText, showValidationIcon = true, helperIcon, type, direction = 'column', gap, ...props }) => {
|
|
11
|
-
const value = useMemo(() => ({ disabled, validationStatus, type }), [disabled, validationStatus, type]);
|
|
11
|
+
const value = useMemo(() => ({ disabled, validationStatus, type, direction }), [disabled, validationStatus, type, direction]);
|
|
12
12
|
const showHeader = !!label || !!helperText || !!invalidText || !!validText;
|
|
13
13
|
const childrenArray = React.Children.toArray(children);
|
|
14
14
|
const childIsCard = type === 'tile' ||
|
|
@@ -2,10 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { useCheckboxContext } from './Checkbox.context';
|
|
5
|
+
import { useCheckboxGroupContext } from './CheckboxGroup.context';
|
|
5
6
|
const CheckboxTileRoot = ({ children }) => {
|
|
7
|
+
const { direction } = useCheckboxGroupContext();
|
|
6
8
|
const { checked } = useCheckboxContext();
|
|
7
9
|
styles.useVariants({
|
|
8
10
|
checked,
|
|
11
|
+
direction,
|
|
9
12
|
});
|
|
10
13
|
return _jsx(View, { style: styles.container, children: children });
|
|
11
14
|
};
|
|
@@ -13,7 +16,6 @@ const styles = StyleSheet.create(theme => ({
|
|
|
13
16
|
container: {
|
|
14
17
|
flexDirection: 'row',
|
|
15
18
|
justifyContent: 'flex-start',
|
|
16
|
-
flex: 1,
|
|
17
19
|
alignSelf: 'stretch',
|
|
18
20
|
gap: theme.components.checkbox.gap,
|
|
19
21
|
padding: theme.components.checkbox.tile.padding,
|
|
@@ -29,6 +31,12 @@ const styles = StyleSheet.create(theme => ({
|
|
|
29
31
|
margin: -theme.components.checkbox.tile.borderWidthSelected / 2,
|
|
30
32
|
},
|
|
31
33
|
},
|
|
34
|
+
direction: {
|
|
35
|
+
row: {},
|
|
36
|
+
column: {
|
|
37
|
+
flex: 1,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
32
40
|
},
|
|
33
41
|
},
|
|
34
42
|
}));
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { View } from 'react-native';
|
|
2
2
|
import FormFieldProps from './FormField.props';
|
|
3
|
-
export declare const FormFieldComponent: import("@gluestack-ui/form-control/lib/types").IFormControlComponentType<import("react-native").ViewProps, Omit<import("../Helper/Helper.props").default, "validationStatus">, Omit<import("../Helper/Helper.props").default, "validationStatus">, Omit<import("..").IconProps, "as">, unknown, Omit<import("../Label/Label.props").default, "disabled">, unknown, Omit<import("../Helper/Helper.props").default, "validationStatus">, import("
|
|
3
|
+
export declare const FormFieldComponent: import("@gluestack-ui/form-control/lib/types").IFormControlComponentType<import("react-native").ViewProps, Omit<import("../Helper/Helper.props").default, "validationStatus">, Omit<import("../Helper/Helper.props").default, "validationStatus">, Omit<import("..").IconProps, "as">, unknown, Omit<import("../Label/Label.props").default, "disabled">, unknown, Omit<import("../Helper/Helper.props").default, "validationStatus">, import("..").BodyTextProps>;
|
|
4
4
|
export declare const FormFieldLabel: import("react").ForwardRefExoticComponent<import("react").RefAttributes<unknown>> & {
|
|
5
5
|
Text: import("react").ForwardRefExoticComponent<Omit<Omit<import("../Label/Label.props").default, "disabled">, "ref"> & import("react").RefAttributes<Omit<import("../Label/Label.props").default, "disabled">>>;
|
|
6
6
|
};
|
|
7
7
|
export declare const FormFieldLabelText: import("react").ForwardRefExoticComponent<Omit<Omit<import("../Label/Label.props").default, "disabled">, "ref"> & import("react").RefAttributes<Omit<import("../Label/Label.props").default, "disabled">>>;
|
|
8
8
|
export declare const FormFieldHelper: import("react").ForwardRefExoticComponent<Omit<import("../Helper/Helper.props").default, "validationStatus"> & import("react").RefAttributes<Omit<import("../Helper/Helper.props").default, "validationStatus">>> & {
|
|
9
|
-
Text: import("react").ForwardRefExoticComponent<Omit<import("
|
|
9
|
+
Text: import("react").ForwardRefExoticComponent<Omit<import("..").BodyTextProps, "ref"> & import("react").RefAttributes<import("..").BodyTextProps>>;
|
|
10
10
|
};
|
|
11
|
-
export declare const FormFieldHelperText: import("react").ForwardRefExoticComponent<Omit<import("
|
|
11
|
+
export declare const FormFieldHelperText: import("react").ForwardRefExoticComponent<Omit<import("..").BodyTextProps, "ref"> & import("react").RefAttributes<import("..").BodyTextProps>>;
|
|
12
12
|
export declare const FormFieldHelperIcon: {
|
|
13
13
|
(props: import("..").IconProps): import("react/jsx-runtime").JSX.Element;
|
|
14
14
|
displayName: string;
|
|
15
15
|
};
|
|
16
16
|
export declare const FormFieldValidText: {
|
|
17
|
-
(props: import("
|
|
17
|
+
(props: import("..").BodyTextProps): import("react/jsx-runtime").JSX.Element;
|
|
18
18
|
displayName: string;
|
|
19
19
|
};
|
|
20
20
|
export declare const FormFieldInvalidText: {
|
|
21
|
-
(props: import("
|
|
21
|
+
(props: import("..").BodyTextProps): import("react/jsx-runtime").JSX.Element;
|
|
22
22
|
displayName: string;
|
|
23
23
|
};
|
|
24
24
|
export declare const FormFieldTextContent: typeof View;
|
|
@@ -4,7 +4,8 @@ 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 } from './List.context';
|
|
7
|
+
import { ListContext, ListFirstItemContext } from './List.context';
|
|
8
|
+
import { ListAction } from './ListAction';
|
|
8
9
|
import { ListItem } from './ListItem';
|
|
9
10
|
const markFirstListItem = (children) => {
|
|
10
11
|
let found = false;
|
|
@@ -12,21 +13,24 @@ const markFirstListItem = (children) => {
|
|
|
12
13
|
return React.Children.map(children, (child) => {
|
|
13
14
|
if (!React.isValidElement(child))
|
|
14
15
|
return child;
|
|
15
|
-
// Check if the current element is the ListItem and hasn't been marked yet
|
|
16
|
-
if (!found
|
|
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
|
+
}
|
|
17
32
|
found = true;
|
|
18
|
-
|
|
19
|
-
return React.cloneElement(child, { isFirst: true });
|
|
20
|
-
}
|
|
21
|
-
// If the child has nested children, process them recursively
|
|
22
|
-
if (React.isValidElement(child) &&
|
|
23
|
-
child.props &&
|
|
24
|
-
typeof child.props === 'object' &&
|
|
25
|
-
child.props !== null &&
|
|
26
|
-
'children' in child.props &&
|
|
27
|
-
child.props.children) {
|
|
28
|
-
const clonedChildren = recursiveClone(child.props.children);
|
|
29
|
-
return React.cloneElement(child, { children: clonedChildren });
|
|
33
|
+
return _jsx(ListFirstItemContext.Provider, { value: true, children: child });
|
|
30
34
|
}
|
|
31
35
|
return child;
|
|
32
36
|
});
|
|
@@ -3,7 +3,7 @@ import { createPressable } from '@gluestack-ui/pressable';
|
|
|
3
3
|
import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
4
4
|
import { Pressable } from 'react-native';
|
|
5
5
|
import { StyleSheet } from 'react-native-unistyles';
|
|
6
|
-
import { useListContext } from '../List.context';
|
|
6
|
+
import { useListContext, useListFirstItemContext } from '../List.context';
|
|
7
7
|
import ListActionContent from './ListActionContent';
|
|
8
8
|
import ListActionText from './ListActionText';
|
|
9
9
|
import ListActionTrailingContent from './ListActionTrailingContent';
|
|
@@ -11,6 +11,7 @@ import ListActionTrailingIcon from './ListActionTrailingIcon';
|
|
|
11
11
|
const ListActionRoot = ({ heading, disabled, variant = 'subtle', ...props }) => {
|
|
12
12
|
const { onPress } = props;
|
|
13
13
|
const listContext = useListContext();
|
|
14
|
+
const isFirstContext = useListFirstItemContext();
|
|
14
15
|
const { active } = props.states || { active: false };
|
|
15
16
|
const getListContainer = () => {
|
|
16
17
|
if (listContext?.container?.includes('subtle')) {
|
|
@@ -29,7 +30,7 @@ const ListActionRoot = ({ heading, disabled, variant = 'subtle', ...props }) =>
|
|
|
29
30
|
disabled: isDisabled,
|
|
30
31
|
active,
|
|
31
32
|
showDisabled: !listContext?.disabled && disabled,
|
|
32
|
-
isFirstChild:
|
|
33
|
+
isFirstChild: isFirstContext,
|
|
33
34
|
container: listContext?.container,
|
|
34
35
|
});
|
|
35
36
|
return (_jsxs(Pressable, { ...props, testID: testID, style: [styles.container, props.style], disabled: isDisabled || !onPress, children: [_jsx(ListActionContent, { children: _jsx(ListActionText, { children: heading }) }), _jsx(ListActionTrailingContent, { style: styles.centeredTrailingIcon, children: _jsx(ListActionTrailingIcon, { as: ChevronRightSmallIcon }) })] }));
|
|
@@ -4,7 +4,6 @@ interface ListItemBaseProps extends Omit<PressableProps, 'children'> {
|
|
|
4
4
|
loading?: boolean;
|
|
5
5
|
disabled?: boolean;
|
|
6
6
|
variant?: 'subtle' | 'emphasis';
|
|
7
|
-
isFirst?: boolean;
|
|
8
7
|
}
|
|
9
8
|
export interface ListItemWithChildren extends ListItemBaseProps {
|
|
10
9
|
children: ViewProps['children'];
|
|
@@ -15,6 +14,8 @@ export interface ListItemWithChildren extends ListItemBaseProps {
|
|
|
15
14
|
numericValue?: never;
|
|
16
15
|
badge?: never;
|
|
17
16
|
badgePosition?: never;
|
|
17
|
+
truncateHeading?: never;
|
|
18
|
+
truncateHelperText?: never;
|
|
18
19
|
}
|
|
19
20
|
export interface ListItemWithoutChildren extends ListItemBaseProps {
|
|
20
21
|
children?: never;
|
|
@@ -25,6 +26,8 @@ export interface ListItemWithoutChildren extends ListItemBaseProps {
|
|
|
25
26
|
numericValue?: string | number;
|
|
26
27
|
badge?: ReactNode;
|
|
27
28
|
badgePosition?: 'top' | 'bottom';
|
|
29
|
+
truncateHeading?: boolean;
|
|
30
|
+
truncateHelperText?: boolean;
|
|
28
31
|
}
|
|
29
32
|
type ListItemProps = ListItemWithChildren | ListItemWithoutChildren;
|
|
30
33
|
export default ListItemProps;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BodyTextProps } from '../../BodyText';
|
|
2
|
+
declare const ListItemHeading: {
|
|
3
|
+
({ children, ...props }: BodyTextProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
displayName: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated Use `ListItemHeading` instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ListItemText: {
|
|
10
|
+
({ children, ...props }: BodyTextProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
displayName: string;
|
|
12
|
+
};
|
|
13
|
+
export default ListItemHeading;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { BodyText } from '../../BodyText';
|
|
3
|
+
const ListItemHeading = ({ children, ...props }) => {
|
|
4
|
+
return (_jsx(BodyText, { size: "lg", ...props, children: children }));
|
|
5
|
+
};
|
|
6
|
+
ListItemHeading.displayName = 'ListItemHeading';
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Use `ListItemHeading` instead.
|
|
9
|
+
*/
|
|
10
|
+
export const ListItemText = ListItemHeading;
|
|
11
|
+
ListItemText.displayName = 'ListItemText';
|
|
12
|
+
export default ListItemHeading;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BodyTextProps } from '../../BodyText';
|
|
2
2
|
declare const ListItemHelperText: {
|
|
3
|
-
({ children, ...props }:
|
|
3
|
+
({ children, ...props }: BodyTextProps): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
displayName: string;
|
|
5
5
|
};
|
|
6
6
|
export default ListItemHelperText;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type ListItemProps from './ListItem.props';
|
|
2
2
|
declare const ListItemRoot: {
|
|
3
|
-
({ heading, helperText, leadingContent, trailingContent, disabled, loading, children, states, variant, badge, badgePosition, numericValue, ...props }: ListItemProps & {
|
|
3
|
+
({ heading, helperText, leadingContent, trailingContent, disabled, loading, children, states, variant, badge, badgePosition, numericValue, truncateHeading, truncateHelperText, ...props }: ListItemProps & {
|
|
4
4
|
states?: {
|
|
5
5
|
active?: boolean;
|
|
6
6
|
disabled?: boolean;
|
|
@@ -5,17 +5,18 @@ 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 } from '../List.context';
|
|
8
|
+
import { useListContext, useListFirstItemContext } from '../List.context';
|
|
9
9
|
import { ListItemContext } from './ListItem.context';
|
|
10
10
|
import ListItemContent from './ListItemContent';
|
|
11
|
+
import ListItemHeading from './ListItemHeading';
|
|
11
12
|
import ListItemHelperText from './ListItemHelperText';
|
|
12
13
|
import ListItemLeadingContent from './ListItemLeadingContent';
|
|
13
|
-
import ListItemText from './ListItemText';
|
|
14
14
|
import ListItemTrailingContent from './ListItemTrailingContent';
|
|
15
15
|
import ListItemTrailingIcon from './ListItemTrailingIcon';
|
|
16
|
-
const ListItemRoot = ({ heading, helperText, leadingContent, trailingContent, disabled, loading, children, states, variant = 'subtle', badge, badgePosition = 'bottom', numericValue, ...props }) => {
|
|
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 isFirstContext = useListFirstItemContext();
|
|
19
20
|
const { active } = states || { active: false };
|
|
20
21
|
const getListContainer = () => {
|
|
21
22
|
if (listContext?.container?.includes('subtle')) {
|
|
@@ -37,8 +38,8 @@ const ListItemRoot = ({ heading, helperText, leadingContent, trailingContent, di
|
|
|
37
38
|
showPressed,
|
|
38
39
|
active,
|
|
39
40
|
disabled: isDisabled || isLoading,
|
|
40
|
-
showDisabled: !listContext
|
|
41
|
-
isFirstChild:
|
|
41
|
+
showDisabled: !listContext.disabled && disabled,
|
|
42
|
+
isFirstChild: isFirstContext,
|
|
42
43
|
container: listContext?.container,
|
|
43
44
|
});
|
|
44
45
|
const value = useMemo(() => {
|
|
@@ -52,7 +53,7 @@ const ListItemRoot = ({ heading, helperText, leadingContent, trailingContent, di
|
|
|
52
53
|
if (loading || listContext?.loading) {
|
|
53
54
|
return (_jsxs(Pressable, { ...props, testID: loadingTestID, style: [styles.container, props.style], disabled: isDisabled, children: [leadingContent ? _jsx(Skeleton, { width: 24, height: 24 }) : null, _jsxs(ListItemContent, { children: [_jsx(Skeleton, { width: "80%", height: 20 }), _jsx(Skeleton, { width: "100%", height: 16 })] }), onPress || trailingContent ? _jsx(Skeleton, { width: 24, height: 24 }) : null] }));
|
|
54
55
|
}
|
|
55
|
-
return (_jsx(ListItemContext.Provider, { value: value, children: _jsx(Pressable, { ...props, testID: testID, style: [styles.container, props.style], disabled: isDisabled, accessibilityRole: onPress ? 'button' : undefined, children: children ? (children) : (_jsxs(_Fragment, { children: [leadingContent ? (_jsx(ListItemLeadingContent, { children: leadingContent })) : null, _jsxs(ListItemContent, { children: [badgePosition === 'top' && badge ? badge : null, _jsx(
|
|
56
|
+
return (_jsx(ListItemContext.Provider, { value: value, children: _jsx(Pressable, { ...props, testID: testID, style: [styles.container, props.style], disabled: isDisabled, accessibilityRole: onPress ? 'button' : undefined, children: children ? (children) : (_jsxs(_Fragment, { children: [leadingContent ? (_jsx(ListItemLeadingContent, { children: leadingContent })) : null, _jsxs(ListItemContent, { children: [badgePosition === 'top' && badge ? badge : null, _jsx(ListItemHeading, { truncated: truncateHeading, children: heading }), helperText ? (_jsx(ListItemHelperText, { truncated: truncateHelperText, children: helperText })) : null, badgePosition === 'bottom' && badge ? badge : null] }), !!numericValue && _jsx(DetailText, { size: "lg", children: numericValue }), trailingContent ? (_jsx(ListItemTrailingContent, { children: trailingContent })) : onPress ? (_jsx(ListItemTrailingContent, { style: styles.centeredTrailingIcon, children: _jsx(ListItemTrailingIcon, { as: ChevronRightSmallIcon }) })) : null] })) }) }));
|
|
56
57
|
};
|
|
57
58
|
ListItemRoot.displayName = 'ListItemRoot';
|
|
58
59
|
const styles = StyleSheet.create(theme => ({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { default as ListItem } from './ListItem';
|
|
2
2
|
export type { default as ListItemProps } from './ListItem.props';
|
|
3
3
|
export { default as ListItemContent } from './ListItemContent';
|
|
4
|
+
export { default as ListItemHeading, ListItemText } from './ListItemHeading';
|
|
4
5
|
export { default as ListItemHelperText } from './ListItemHelperText';
|
|
5
6
|
export { default as ListItemIcon } from './ListItemIcon';
|
|
6
7
|
export { default as ListItemLeadingContent } from './ListItemLeadingContent';
|
|
7
|
-
export { default as ListItemText } from './ListItemText';
|
|
8
8
|
export { default as ListItemTrailingContent } from './ListItemTrailingContent';
|
|
9
9
|
export { default as ListItemTrailingIcon } from './ListItemTrailingIcon';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { default as ListItem } from './ListItem';
|
|
2
2
|
export { default as ListItemContent } from './ListItemContent';
|
|
3
|
+
export { default as ListItemHeading, ListItemText } from './ListItemHeading';
|
|
3
4
|
export { default as ListItemHelperText } from './ListItemHelperText';
|
|
4
5
|
export { default as ListItemIcon } from './ListItemIcon';
|
|
5
6
|
export { default as ListItemLeadingContent } from './ListItemLeadingContent';
|
|
6
|
-
export { default as ListItemText } from './ListItemText';
|
|
7
7
|
export { default as ListItemTrailingContent } from './ListItemTrailingContent';
|
|
8
8
|
export { default as ListItemTrailingIcon } from './ListItemTrailingIcon';
|
|
@@ -2,9 +2,11 @@ export declare const RadioGroupContext: import("react").Context<{
|
|
|
2
2
|
disabled?: boolean;
|
|
3
3
|
validationStatus?: "valid" | "invalid" | "initial";
|
|
4
4
|
type?: "default" | "tile";
|
|
5
|
+
direction?: "column" | "row";
|
|
5
6
|
}>;
|
|
6
7
|
export declare const useRadioGroupContext: () => {
|
|
7
8
|
disabled?: boolean;
|
|
8
9
|
validationStatus?: "valid" | "invalid" | "initial";
|
|
9
10
|
type?: "default" | "tile";
|
|
11
|
+
direction?: "column" | "row";
|
|
10
12
|
};
|
|
@@ -8,7 +8,7 @@ import { RadioGroup as RadioGroupComponent } from './Radio';
|
|
|
8
8
|
import { RadioGroupContext } from './RadioGroup.context';
|
|
9
9
|
import RadioGroupTextContent from './RadioGroupTextContent';
|
|
10
10
|
const RadioGroup = ({ children, disabled, readonly, validationStatus, label, helperText, invalidText, validText, showValidationIcon = true, helperIcon, type, direction = 'column', gap, ...props }) => {
|
|
11
|
-
const value = useMemo(() => ({ disabled, validationStatus, type }), [disabled, validationStatus, type]);
|
|
11
|
+
const value = useMemo(() => ({ disabled, validationStatus, type, direction }), [disabled, validationStatus, type, direction]);
|
|
12
12
|
const showHeader = !!label || !!helperText || !!invalidText || !!validText;
|
|
13
13
|
const childrenArray = React.Children.toArray(children);
|
|
14
14
|
const childIsCard = type === 'tile' ||
|
|
@@ -2,10 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { useRadioContext } from './Radio.context';
|
|
5
|
+
import { useRadioGroupContext } from './RadioGroup.context';
|
|
5
6
|
const RadioTileRoot = ({ children }) => {
|
|
7
|
+
const { direction } = useRadioGroupContext();
|
|
6
8
|
const { checked } = useRadioContext();
|
|
7
9
|
styles.useVariants({
|
|
8
10
|
checked,
|
|
11
|
+
direction,
|
|
9
12
|
});
|
|
10
13
|
return _jsx(View, { style: styles.container, children: children });
|
|
11
14
|
};
|
|
@@ -13,7 +16,6 @@ const styles = StyleSheet.create(theme => ({
|
|
|
13
16
|
container: {
|
|
14
17
|
flexDirection: 'row',
|
|
15
18
|
justifyContent: 'flex-start',
|
|
16
|
-
flex: 1,
|
|
17
19
|
alignSelf: 'stretch',
|
|
18
20
|
gap: theme.components.radio.gap,
|
|
19
21
|
padding: theme.components.radio.tile.padding,
|
|
@@ -29,6 +31,12 @@ const styles = StyleSheet.create(theme => ({
|
|
|
29
31
|
margin: -theme.components.radio.tile.borderWidthSelected / 2,
|
|
30
32
|
},
|
|
31
33
|
},
|
|
34
|
+
direction: {
|
|
35
|
+
row: {},
|
|
36
|
+
column: {
|
|
37
|
+
flex: 1,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
32
40
|
},
|
|
33
41
|
},
|
|
34
42
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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-native-icons": "^0.7.4",
|
|
60
59
|
"@utilitywarehouse/hearth-react-icons": "^0.7.4",
|
|
60
|
+
"@utilitywarehouse/hearth-react-native-icons": "^0.7.4",
|
|
61
61
|
"@utilitywarehouse/hearth-svg-assets": "^0.2.0",
|
|
62
62
|
"@utilitywarehouse/hearth-tokens": "^0.2.2"
|
|
63
63
|
},
|
|
@@ -4,6 +4,7 @@ export const CheckboxGroupContext = createContext<{
|
|
|
4
4
|
disabled?: boolean;
|
|
5
5
|
validationStatus?: 'valid' | 'invalid' | 'initial';
|
|
6
6
|
type?: 'default' | 'tile';
|
|
7
|
+
direction?: 'column' | 'row';
|
|
7
8
|
}>({});
|
|
8
9
|
|
|
9
10
|
export const useCheckboxGroupContext = () => useContext(CheckboxGroupContext);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
2
1
|
import React, { useMemo } from 'react';
|
|
3
|
-
import { CheckboxGroup as CheckboxGroupComponent } from './Checkbox';
|
|
4
|
-
import CheckboxGroupProps from './CheckboxGroup.props';
|
|
5
|
-
import { CheckboxGroupContext } from './CheckboxGroup.context';
|
|
6
2
|
import { View } from 'react-native';
|
|
7
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
8
|
-
import CheckboxGroupTextContent from './CheckboxGroupTextContent';
|
|
9
|
-
import { Label } from '../Label';
|
|
10
4
|
import { Helper } from '../Helper';
|
|
5
|
+
import { Label } from '../Label';
|
|
6
|
+
import { CheckboxGroup as CheckboxGroupComponent } from './Checkbox';
|
|
7
|
+
import { CheckboxGroupContext } from './CheckboxGroup.context';
|
|
8
|
+
import CheckboxGroupProps from './CheckboxGroup.props';
|
|
9
|
+
import CheckboxGroupTextContent from './CheckboxGroupTextContent';
|
|
11
10
|
|
|
12
11
|
const CheckboxGroup = ({
|
|
13
12
|
children,
|
|
@@ -26,8 +25,8 @@ const CheckboxGroup = ({
|
|
|
26
25
|
...props
|
|
27
26
|
}: CheckboxGroupProps) => {
|
|
28
27
|
const value = useMemo(
|
|
29
|
-
() => ({ disabled, validationStatus, type }),
|
|
30
|
-
[disabled, validationStatus, type]
|
|
28
|
+
() => ({ disabled, validationStatus, type, direction }),
|
|
29
|
+
[disabled, validationStatus, type, direction]
|
|
31
30
|
);
|
|
32
31
|
const showHeader = !!label || !!helperText || !!invalidText || !!validText;
|
|
33
32
|
const childrenArray = React.Children.toArray(children as any);
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { View, ViewProps } from 'react-native';
|
|
2
2
|
import { StyleSheet } from 'react-native-unistyles';
|
|
3
3
|
import { useCheckboxContext } from './Checkbox.context';
|
|
4
|
+
import { useCheckboxGroupContext } from './CheckboxGroup.context';
|
|
4
5
|
|
|
5
6
|
const CheckboxTileRoot = ({ children }: { children: ViewProps['children'] }) => {
|
|
7
|
+
const { direction } = useCheckboxGroupContext();
|
|
6
8
|
const { checked } = useCheckboxContext();
|
|
7
9
|
styles.useVariants({
|
|
8
10
|
checked,
|
|
11
|
+
direction,
|
|
9
12
|
});
|
|
10
13
|
return <View style={styles.container}>{children}</View>;
|
|
11
14
|
};
|
|
@@ -14,7 +17,6 @@ const styles = StyleSheet.create(theme => ({
|
|
|
14
17
|
container: {
|
|
15
18
|
flexDirection: 'row',
|
|
16
19
|
justifyContent: 'flex-start',
|
|
17
|
-
flex: 1,
|
|
18
20
|
alignSelf: 'stretch',
|
|
19
21
|
gap: theme.components.checkbox.gap,
|
|
20
22
|
padding: theme.components.checkbox.tile.padding,
|
|
@@ -30,6 +32,12 @@ const styles = StyleSheet.create(theme => ({
|
|
|
30
32
|
margin: -theme.components.checkbox.tile.borderWidthSelected / 2,
|
|
31
33
|
},
|
|
32
34
|
},
|
|
35
|
+
direction: {
|
|
36
|
+
row: {},
|
|
37
|
+
column: {
|
|
38
|
+
flex: 1,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
33
41
|
},
|
|
34
42
|
},
|
|
35
43
|
}));
|
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
ListAction,
|
|
21
21
|
ListItem,
|
|
22
22
|
ListItemContent,
|
|
23
|
+
ListItemHeading,
|
|
23
24
|
ListItemHelperText,
|
|
24
25
|
ListItemIcon,
|
|
25
26
|
ListItemLeadingContent,
|
|
26
|
-
ListItemText,
|
|
27
27
|
ListItemTrailingContent,
|
|
28
28
|
ListItemTrailingIcon,
|
|
29
29
|
SectionHeader,
|
|
@@ -105,19 +105,21 @@ const MyComponent = () => (
|
|
|
105
105
|
|
|
106
106
|
### `ListItem`
|
|
107
107
|
|
|
108
|
-
| Name
|
|
109
|
-
|
|
|
110
|
-
| heading
|
|
111
|
-
| onPress
|
|
112
|
-
| helperText
|
|
113
|
-
| leadingContent
|
|
114
|
-
| trailingContent
|
|
115
|
-
| variant
|
|
116
|
-
| disabled
|
|
117
|
-
| loading
|
|
118
|
-
| badge
|
|
119
|
-
| badgePosition
|
|
120
|
-
| numericValue
|
|
108
|
+
| Name | Type | Default | Description |
|
|
109
|
+
| ------------------ | ------------------------ | -------- | --------------------------------------------------------------- |
|
|
110
|
+
| heading | `string` | | The text to display in the list item. |
|
|
111
|
+
| onPress | `() => void` | | A callback function to be called when the list item is pressed. |
|
|
112
|
+
| helperText | `string` | | The supporting text to display in the list item. |
|
|
113
|
+
| leadingContent | `ReactNode` | | The leading content to display in the list item. |
|
|
114
|
+
| trailingContent | `ReactNode` | | The trailing content to display in the list item. |
|
|
115
|
+
| variant | `'subtle' \| 'emphasis'` | | The variant style of the list item. |
|
|
116
|
+
| disabled | `boolean` | `false` | Whether to disable the list item. |
|
|
117
|
+
| loading | `boolean` | `false` | Whether to show the list item in loading state. |
|
|
118
|
+
| badge | `ReactNode` | | The badge component to display in the list item. |
|
|
119
|
+
| badgePosition | `'top' \| 'bottom'` | `bottom` | Position of the badge in the list item. |
|
|
120
|
+
| numericValue | `string \| number` | | A numeric value to display on the right side of the item. |
|
|
121
|
+
| truncateHeading | `boolean` | `false` | Whether to truncate the heading text if it overflows. |
|
|
122
|
+
| truncateHelperText | `boolean` | `false` | Whether to truncate the helper text if it overflows. |
|
|
121
123
|
|
|
122
124
|
### `ListAction`
|
|
123
125
|
|
|
@@ -137,10 +139,14 @@ const MyComponent = () => (
|
|
|
137
139
|
|
|
138
140
|
#### - `ListItemContent`
|
|
139
141
|
|
|
140
|
-
#### - `
|
|
142
|
+
#### - `ListItemHeading`
|
|
143
|
+
|
|
144
|
+
Has all props of the [`BodyText` component](?path=/docs/typography-body-text--docs).
|
|
141
145
|
|
|
142
146
|
#### - `ListItemHelperText`
|
|
143
147
|
|
|
148
|
+
Has all props of the [`BodyText` component](?path=/docs/typography-body-text--docs).
|
|
149
|
+
|
|
144
150
|
#### - `ListItemTrailingContent`
|
|
145
151
|
|
|
146
152
|
#### - `ListItemTrailingIcon`
|
|
@@ -478,7 +484,7 @@ If you need to use the `<List>` component in a more advanced way, you can use th
|
|
|
478
484
|
<ListItemIcon as={BillMediumIcon} />
|
|
479
485
|
</ListItemLeadingContent>
|
|
480
486
|
<ListItemContent>
|
|
481
|
-
<
|
|
487
|
+
<ListItemHeading>Bills</ListItemHeading>
|
|
482
488
|
<ListItemHelperText>View your bills</ListItemHelperText>
|
|
483
489
|
</ListItemContent>
|
|
484
490
|
<ListItemTrailingContent>
|
|
@@ -490,7 +496,7 @@ If you need to use the `<List>` component in a more advanced way, you can use th
|
|
|
490
496
|
<ListItemIcon as={PaymentMediumIcon} />
|
|
491
497
|
</ListItemLeadingContent>
|
|
492
498
|
<ListItemContent>
|
|
493
|
-
<
|
|
499
|
+
<ListItemHeading>Payments</ListItemHeading>
|
|
494
500
|
<ListItemHelperText>Make a payment</ListItemHelperText>
|
|
495
501
|
</ListItemContent>
|
|
496
502
|
<ListItemTrailingContent>
|
|
@@ -502,7 +508,7 @@ If you need to use the `<List>` component in a more advanced way, you can use th
|
|
|
502
508
|
<ListItemIcon as={HomeMediumIcon} />
|
|
503
509
|
</ListItemLeadingContent>
|
|
504
510
|
<ListItemContent>
|
|
505
|
-
<
|
|
511
|
+
<ListItemHeading>Moving home</ListItemHeading>
|
|
506
512
|
<ListItemHelperText>Tell us if your moving</ListItemHelperText>
|
|
507
513
|
</ListItemContent>
|
|
508
514
|
<ListItemTrailingContent>
|
|
@@ -514,7 +520,7 @@ If you need to use the `<List>` component in a more advanced way, you can use th
|
|
|
514
520
|
<ListItemIcon as={UserMediumIcon} />
|
|
515
521
|
</ListItemLeadingContent>
|
|
516
522
|
<ListItemContent>
|
|
517
|
-
<
|
|
523
|
+
<ListItemHeading>Refer a friend</ListItemHeading>
|
|
518
524
|
<ListItemHelperText>Get rewarded with a friend</ListItemHelperText>
|
|
519
525
|
</ListItemContent>
|
|
520
526
|
<ListItemTrailingContent>
|
|
@@ -532,7 +538,7 @@ import {
|
|
|
532
538
|
ListItemIcon,
|
|
533
539
|
ListItemLeadingContent,
|
|
534
540
|
ListItemHelperText,
|
|
535
|
-
|
|
541
|
+
ListItemHeading,
|
|
536
542
|
ListItemTrailingContent,
|
|
537
543
|
ListItemTrailingIcon,
|
|
538
544
|
ListItemContent,
|
|
@@ -553,7 +559,7 @@ const MyComponent = () => (
|
|
|
553
559
|
<ListItemIcon as={BillMediumIcon} />
|
|
554
560
|
</ListItemLeadingContent>
|
|
555
561
|
<ListItemContent>
|
|
556
|
-
<
|
|
562
|
+
<ListItemHeading>Bills</ListItemHeading>
|
|
557
563
|
<ListItemHelperText>View your bills</ListItemHelperText>
|
|
558
564
|
</ListItemContent>
|
|
559
565
|
<ListItemTrailingContent>
|
|
@@ -565,7 +571,7 @@ const MyComponent = () => (
|
|
|
565
571
|
<ListItemIcon as={PaymentMediumIcon} />
|
|
566
572
|
</ListItemLeadingContent>
|
|
567
573
|
<ListItemContent>
|
|
568
|
-
<
|
|
574
|
+
<ListItemHeading>Payments</ListItemHeading>
|
|
569
575
|
<ListItemHelperText>Make a payment</ListItemHelperText>
|
|
570
576
|
</ListItemContent>
|
|
571
577
|
<ListItemTrailingContent>
|
|
@@ -577,7 +583,7 @@ const MyComponent = () => (
|
|
|
577
583
|
<ListItemIcon as={HomeMediumIcon} />
|
|
578
584
|
</ListItemLeadingContent>
|
|
579
585
|
<ListItemContent>
|
|
580
|
-
<
|
|
586
|
+
<ListItemHeading>Moving home</ListItemHeading>
|
|
581
587
|
<ListItemHelperText>Tell us if your moving</ListItemHelperText>
|
|
582
588
|
</ListItemContent>
|
|
583
589
|
<ListItemTrailingContent>
|
|
@@ -589,7 +595,7 @@ const MyComponent = () => (
|
|
|
589
595
|
<ListItemIcon as={UserMediumIcon} />
|
|
590
596
|
</ListItemLeadingContent>
|
|
591
597
|
<ListItemContent>
|
|
592
|
-
<
|
|
598
|
+
<ListItemHeading>Refer a friend</ListItemHeading>
|
|
593
599
|
<ListItemHelperText>Get rewarded with a friend</ListItemHelperText>
|
|
594
600
|
</ListItemContent>
|
|
595
601
|
<ListItemTrailingContent>
|
|
@@ -212,6 +212,30 @@ export const WithSectionHeader: Story = {
|
|
|
212
212
|
),
|
|
213
213
|
};
|
|
214
214
|
|
|
215
|
+
const CustomListItem = () => (
|
|
216
|
+
<ListItem
|
|
217
|
+
heading="Custom List Item"
|
|
218
|
+
helperText="This is a custom list item component"
|
|
219
|
+
leadingContent={<ListItemIcon as={SettingsMediumIcon} />}
|
|
220
|
+
trailingContent={<ListItemTrailingIcon as={ChevronRightSmallIcon} />}
|
|
221
|
+
onPress={() => console.log('Custom List Item pressed')}
|
|
222
|
+
/>
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
export const WithCustomListItemComponent: Story = {
|
|
226
|
+
parameters: {
|
|
227
|
+
controls: { include: [] },
|
|
228
|
+
},
|
|
229
|
+
render: () => (
|
|
230
|
+
<List container="subtleWarmWhite">
|
|
231
|
+
<CustomListItem />
|
|
232
|
+
<CustomListItem />
|
|
233
|
+
<CustomListItem />
|
|
234
|
+
<ListAction heading="Contact support" onPress={() => console.log('Contact pressed')} />
|
|
235
|
+
</List>
|
|
236
|
+
),
|
|
237
|
+
};
|
|
238
|
+
|
|
215
239
|
export const WithListAction: Story = {
|
|
216
240
|
parameters: {
|
|
217
241
|
controls: { include: [] },
|
|
@@ -219,7 +243,8 @@ export const WithListAction: Story = {
|
|
|
219
243
|
render: () => (
|
|
220
244
|
<List container="subtleWarmWhite">
|
|
221
245
|
<ListItem
|
|
222
|
-
heading="Upgrade your plan"
|
|
246
|
+
heading="Upgrade your plan this is really long text to test wrapping"
|
|
247
|
+
truncateHeading={true}
|
|
223
248
|
helperText="Get more features with a premium plan"
|
|
224
249
|
onPress={() => console.log('Upgrade pressed')}
|
|
225
250
|
/>
|
|
@@ -3,9 +3,10 @@ import { View, ViewProps } from 'react-native';
|
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { Card } from '../Card';
|
|
5
5
|
import { SectionHeader } from '../SectionHeader';
|
|
6
|
-
import { ListContext } from './List.context';
|
|
6
|
+
import { ListContext, ListFirstItemContext } from './List.context';
|
|
7
7
|
import type ListProps from './List.props';
|
|
8
|
-
import {
|
|
8
|
+
import { ListAction } from './ListAction';
|
|
9
|
+
import { ListItem } from './ListItem';
|
|
9
10
|
|
|
10
11
|
const markFirstListItem = (children: ReactNode): ViewProps['children'] => {
|
|
11
12
|
let found = false;
|
|
@@ -14,24 +15,30 @@ const markFirstListItem = (children: ReactNode): ViewProps['children'] => {
|
|
|
14
15
|
return React.Children.map(children, (child: ReactNode): ReactNode => {
|
|
15
16
|
if (!React.isValidElement(child)) return child;
|
|
16
17
|
|
|
17
|
-
// Check if the current element is the ListItem and hasn't been marked yet
|
|
18
|
-
if (!found
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
// Check if the current element is the ListItem or ListAction and hasn't been marked yet
|
|
19
|
+
if (!found) {
|
|
20
|
+
if (child.type === ListItem || child.type === ListAction) {
|
|
21
|
+
found = true;
|
|
22
|
+
return (
|
|
23
|
+
<ListFirstItemContext.Provider value={true}>{child}</ListFirstItemContext.Provider>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
// If the child has nested children, process them recursively
|
|
28
|
+
if (
|
|
29
|
+
React.isValidElement(child) &&
|
|
30
|
+
child.props &&
|
|
31
|
+
typeof child.props === 'object' &&
|
|
32
|
+
child.props !== null &&
|
|
33
|
+
'children' in child.props &&
|
|
34
|
+
child.props.children
|
|
35
|
+
) {
|
|
36
|
+
const clonedChildren = recursiveClone((child.props as any).children);
|
|
37
|
+
return React.cloneElement(child, { children: clonedChildren } as any);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
found = true;
|
|
41
|
+
return <ListFirstItemContext.Provider value={true}>{child}</ListFirstItemContext.Provider>;
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
return child;
|
|
@@ -2,7 +2,7 @@ import { createPressable } from '@gluestack-ui/pressable';
|
|
|
2
2
|
import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
3
|
import { Pressable, ViewStyle } from 'react-native';
|
|
4
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
|
-
import { useListContext } from '../List.context';
|
|
5
|
+
import { useListContext, useListFirstItemContext } from '../List.context';
|
|
6
6
|
import type ListActionProps from './ListAction.props';
|
|
7
7
|
import ListActionContent from './ListActionContent';
|
|
8
8
|
import ListActionText from './ListActionText';
|
|
@@ -17,6 +17,7 @@ const ListActionRoot = ({
|
|
|
17
17
|
}: ListActionProps & { states?: { active?: boolean; disabled?: boolean } }) => {
|
|
18
18
|
const { onPress } = props;
|
|
19
19
|
const listContext = useListContext();
|
|
20
|
+
const isFirstContext = useListFirstItemContext();
|
|
20
21
|
|
|
21
22
|
const { active } = props.states || { active: false };
|
|
22
23
|
|
|
@@ -40,7 +41,7 @@ const ListActionRoot = ({
|
|
|
40
41
|
disabled: isDisabled,
|
|
41
42
|
active,
|
|
42
43
|
showDisabled: !listContext?.disabled && disabled,
|
|
43
|
-
isFirstChild:
|
|
44
|
+
isFirstChild: isFirstContext,
|
|
44
45
|
container: listContext?.container,
|
|
45
46
|
});
|
|
46
47
|
|
|
@@ -5,7 +5,6 @@ interface ListItemBaseProps extends Omit<PressableProps, 'children'> {
|
|
|
5
5
|
loading?: boolean;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
variant?: 'subtle' | 'emphasis';
|
|
8
|
-
isFirst?: boolean;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
export interface ListItemWithChildren extends ListItemBaseProps {
|
|
@@ -17,6 +16,8 @@ export interface ListItemWithChildren extends ListItemBaseProps {
|
|
|
17
16
|
numericValue?: never;
|
|
18
17
|
badge?: never;
|
|
19
18
|
badgePosition?: never;
|
|
19
|
+
truncateHeading?: never;
|
|
20
|
+
truncateHelperText?: never;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface ListItemWithoutChildren extends ListItemBaseProps {
|
|
@@ -28,6 +29,8 @@ export interface ListItemWithoutChildren extends ListItemBaseProps {
|
|
|
28
29
|
numericValue?: string | number;
|
|
29
30
|
badge?: ReactNode;
|
|
30
31
|
badgePosition?: 'top' | 'bottom';
|
|
32
|
+
truncateHeading?: boolean;
|
|
33
|
+
truncateHelperText?: boolean;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
type ListItemProps = ListItemWithChildren | ListItemWithoutChildren;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BodyText, BodyTextProps } from '../../BodyText';
|
|
2
|
+
|
|
3
|
+
const ListItemHeading = ({ children, ...props }: BodyTextProps) => {
|
|
4
|
+
return (
|
|
5
|
+
<BodyText size="lg" {...props}>
|
|
6
|
+
{children}
|
|
7
|
+
</BodyText>
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
ListItemHeading.displayName = 'ListItemHeading';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated Use `ListItemHeading` instead.
|
|
15
|
+
*/
|
|
16
|
+
export const ListItemText = ListItemHeading;
|
|
17
|
+
|
|
18
|
+
ListItemText.displayName = 'ListItemText';
|
|
19
|
+
|
|
20
|
+
export default ListItemHeading;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { TextProps } from 'react-native';
|
|
2
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
3
|
-
import { BodyText } from '../../BodyText';
|
|
2
|
+
import { BodyText, BodyTextProps } from '../../BodyText';
|
|
4
3
|
|
|
5
|
-
const ListItemHelperText = ({ children, ...props }:
|
|
4
|
+
const ListItemHelperText = ({ children, ...props }: BodyTextProps) => {
|
|
6
5
|
return (
|
|
7
6
|
<BodyText size="md" {...props} style={[styles.text, props.style]}>
|
|
8
7
|
{children}
|
|
@@ -4,13 +4,13 @@ import { Pressable, ViewStyle } from 'react-native';
|
|
|
4
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
5
|
import { DetailText } from '../../DetailText';
|
|
6
6
|
import { Skeleton } from '../../Skeleton';
|
|
7
|
-
import { useListContext } from '../List.context';
|
|
7
|
+
import { useListContext, useListFirstItemContext } from '../List.context';
|
|
8
8
|
import { IListItemContext, ListItemContext } from './ListItem.context';
|
|
9
9
|
import type ListItemProps from './ListItem.props';
|
|
10
10
|
import ListItemContent from './ListItemContent';
|
|
11
|
+
import ListItemHeading from './ListItemHeading';
|
|
11
12
|
import ListItemHelperText from './ListItemHelperText';
|
|
12
13
|
import ListItemLeadingContent from './ListItemLeadingContent';
|
|
13
|
-
import ListItemText from './ListItemText';
|
|
14
14
|
import ListItemTrailingContent from './ListItemTrailingContent';
|
|
15
15
|
import ListItemTrailingIcon from './ListItemTrailingIcon';
|
|
16
16
|
|
|
@@ -27,10 +27,13 @@ const ListItemRoot = ({
|
|
|
27
27
|
badge,
|
|
28
28
|
badgePosition = 'bottom',
|
|
29
29
|
numericValue,
|
|
30
|
+
truncateHeading = false,
|
|
31
|
+
truncateHelperText = false,
|
|
30
32
|
...props
|
|
31
33
|
}: ListItemProps & { states?: { active?: boolean; disabled?: boolean } }) => {
|
|
32
34
|
const { onPress } = props;
|
|
33
35
|
const listContext = useListContext();
|
|
36
|
+
const isFirstContext = useListFirstItemContext();
|
|
34
37
|
const { active } = states || { active: false };
|
|
35
38
|
|
|
36
39
|
const getListContainer = (): ListItemProps['variant'] => {
|
|
@@ -56,8 +59,8 @@ const ListItemRoot = ({
|
|
|
56
59
|
showPressed,
|
|
57
60
|
active,
|
|
58
61
|
disabled: isDisabled || isLoading,
|
|
59
|
-
showDisabled: !listContext
|
|
60
|
-
isFirstChild:
|
|
62
|
+
showDisabled: !listContext.disabled && disabled,
|
|
63
|
+
isFirstChild: isFirstContext,
|
|
61
64
|
container: listContext?.container,
|
|
62
65
|
});
|
|
63
66
|
|
|
@@ -106,8 +109,10 @@ const ListItemRoot = ({
|
|
|
106
109
|
) : null}
|
|
107
110
|
<ListItemContent>
|
|
108
111
|
{badgePosition === 'top' && badge ? badge : null}
|
|
109
|
-
<
|
|
110
|
-
{helperText ?
|
|
112
|
+
<ListItemHeading truncated={truncateHeading}>{heading}</ListItemHeading>
|
|
113
|
+
{helperText ? (
|
|
114
|
+
<ListItemHelperText truncated={truncateHelperText}>{helperText}</ListItemHelperText>
|
|
115
|
+
) : null}
|
|
111
116
|
{badgePosition === 'bottom' && badge ? badge : null}
|
|
112
117
|
</ListItemContent>
|
|
113
118
|
{!!numericValue && <DetailText size="lg">{numericValue}</DetailText>}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { default as ListItem } from './ListItem';
|
|
2
2
|
export type { default as ListItemProps } from './ListItem.props';
|
|
3
3
|
export { default as ListItemContent } from './ListItemContent';
|
|
4
|
+
export { default as ListItemHeading, ListItemText } from './ListItemHeading';
|
|
4
5
|
export { default as ListItemHelperText } from './ListItemHelperText';
|
|
5
6
|
export { default as ListItemIcon } from './ListItemIcon';
|
|
6
7
|
export { default as ListItemLeadingContent } from './ListItemLeadingContent';
|
|
7
|
-
export { default as ListItemText } from './ListItemText';
|
|
8
8
|
export { default as ListItemTrailingContent } from './ListItemTrailingContent';
|
|
9
9
|
export { default as ListItemTrailingIcon } from './ListItemTrailingIcon';
|
|
@@ -87,26 +87,29 @@ const MyComponent = () => {
|
|
|
87
87
|
|
|
88
88
|
The Modal component extends the `BottomSheetModal` component and accepts all of its props, plus the following additional props:
|
|
89
89
|
|
|
90
|
-
| Property | Type
|
|
91
|
-
| ----------------------------- |
|
|
92
|
-
| `heading` | `string`
|
|
93
|
-
| `description` | `string`
|
|
94
|
-
| `showCloseButton` | `boolean`
|
|
95
|
-
| `primaryButtonText` | `string`
|
|
96
|
-
| `secondaryButtonText` | `string`
|
|
97
|
-
| `onPressPrimaryButton` | `() => void`
|
|
98
|
-
| `onPressSecondaryButton` | `() => void`
|
|
99
|
-
| `onPressCloseButton` | `() => void`
|
|
100
|
-
| `closeOnPrimaryButtonPress` | `boolean`
|
|
101
|
-
| `closeOnSecondaryButtonPress` | `boolean`
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
90
|
+
| Property | Type | Description | Default |
|
|
91
|
+
| ----------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------- |
|
|
92
|
+
| `heading` | `string` | The heading text displayed at the top of the modal | - |
|
|
93
|
+
| `description` | `string` | The description text displayed below the heading | - |
|
|
94
|
+
| `showCloseButton` | `boolean` | Whether to show the close button in the top-right corner | `true` |
|
|
95
|
+
| `primaryButtonText` | `string` | Text for the primary action button | - |
|
|
96
|
+
| `secondaryButtonText` | `string` | Text for the secondary action button | - |
|
|
97
|
+
| `onPressPrimaryButton` | `() => void` | Callback function called when the primary button is pressed | - |
|
|
98
|
+
| `onPressSecondaryButton` | `() => void` | Callback function called when the secondary button is pressed | - |
|
|
99
|
+
| `onPressCloseButton` | `() => void` | Callback function called when the close button is pressed | - |
|
|
100
|
+
| `closeOnPrimaryButtonPress` | `boolean` | Whether to automatically close the modal when the primary button is pressed | `true` |
|
|
101
|
+
| `closeOnSecondaryButtonPress` | `boolean` | Whether to automatically close the modal when the secondary button is pressed | `true` |
|
|
102
|
+
| `onChange` | `(index: number, position: number, `<br />` type: number) => void` | Callback function called when the modal's position changes \* | - |
|
|
103
|
+
| `loading` | `boolean` | Whether to show a loading state with spinner | `false` |
|
|
104
|
+
| `image` | `ImageProps` | Image to display in the modal (shows as centered content with text below) | - |
|
|
105
|
+
| `children` | `ReactNode` | Custom content to display in the modal body | - |
|
|
106
|
+
| `primaryButtonProps` | `Omit<ButtonWithoutChildrenProps, 'children'>` | Additional props to pass to the primary button (colorScheme defaults to 'highlight', variant to 'solid') | - |
|
|
107
|
+
| `secondaryButtonProps` | `Omit<ButtonWithoutChildrenProps, 'children'>` | Additional props to pass to the secondary button (colorScheme defaults to 'functional', variant to 'outline') | - |
|
|
108
|
+
| `closeButtonProps` | `Omit<UnstyledIconButtonProps, 'children'>` | Additional props to pass to the close button | - |
|
|
109
|
+
| `fullscreen` | `boolean` | Whether the modal should take up the full screen height | `false` |
|
|
110
|
+
| `inNavModal` | `boolean` | Renders the modal correctly when used inside a navigation modal | `false` |
|
|
111
|
+
|
|
112
|
+
\* use this to detect if the modal has been opened or closed, index 0 indicates open state and -1 indicates closed state
|
|
110
113
|
|
|
111
114
|
### `ModalImage` Props
|
|
112
115
|
|
|
@@ -6,6 +6,7 @@ import bankLogo from '../../../docs/assets/bank-logo.png';
|
|
|
6
6
|
import bankLogo1 from '../../../docs/assets/bank-logo1.png';
|
|
7
7
|
import { VariantTitle } from '../../../docs/components';
|
|
8
8
|
import { Flex } from '../Flex';
|
|
9
|
+
import { FormField } from '../FormField';
|
|
9
10
|
|
|
10
11
|
const meta = {
|
|
11
12
|
title: 'Stories / Radio',
|
|
@@ -206,3 +207,26 @@ export const Variants: Story = {
|
|
|
206
207
|
);
|
|
207
208
|
},
|
|
208
209
|
};
|
|
210
|
+
|
|
211
|
+
export const WithFormFieldExample: Story = () => (
|
|
212
|
+
<>
|
|
213
|
+
<FormField
|
|
214
|
+
label="Account type"
|
|
215
|
+
helperText="Is this account used for personal or business purposes?"
|
|
216
|
+
>
|
|
217
|
+
<RadioGroup type="tile">
|
|
218
|
+
<Radio label="Personal" value="Personal" />
|
|
219
|
+
<Radio label="Business" value="Business" />
|
|
220
|
+
</RadioGroup>
|
|
221
|
+
</FormField>
|
|
222
|
+
<RadioGroup
|
|
223
|
+
direction="row"
|
|
224
|
+
label="Account type"
|
|
225
|
+
helperText="Is this account used for personal or business purposes?"
|
|
226
|
+
type="tile"
|
|
227
|
+
>
|
|
228
|
+
<Radio label="Personal" value="Personal" />
|
|
229
|
+
<Radio label="Business" value="Business" />
|
|
230
|
+
</RadioGroup>
|
|
231
|
+
</>
|
|
232
|
+
);
|
|
@@ -4,6 +4,7 @@ export const RadioGroupContext = createContext<{
|
|
|
4
4
|
disabled?: boolean;
|
|
5
5
|
validationStatus?: 'valid' | 'invalid' | 'initial';
|
|
6
6
|
type?: 'default' | 'tile';
|
|
7
|
+
direction?: 'column' | 'row';
|
|
7
8
|
}>({});
|
|
8
9
|
|
|
9
10
|
export const useRadioGroupContext = () => useContext(RadioGroupContext);
|
|
@@ -25,8 +25,8 @@ const RadioGroup = ({
|
|
|
25
25
|
...props
|
|
26
26
|
}: RadioGroupProps) => {
|
|
27
27
|
const value = useMemo(
|
|
28
|
-
() => ({ disabled, validationStatus, type }),
|
|
29
|
-
[disabled, validationStatus, type]
|
|
28
|
+
() => ({ disabled, validationStatus, type, direction }),
|
|
29
|
+
[disabled, validationStatus, type, direction]
|
|
30
30
|
);
|
|
31
31
|
const showHeader = !!label || !!helperText || !!invalidText || !!validText;
|
|
32
32
|
const childrenArray = React.Children.toArray(children as any);
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { View, ViewProps } from 'react-native';
|
|
2
2
|
import { StyleSheet } from 'react-native-unistyles';
|
|
3
3
|
import { useRadioContext } from './Radio.context';
|
|
4
|
+
import { useRadioGroupContext } from './RadioGroup.context';
|
|
4
5
|
|
|
5
6
|
const RadioTileRoot = ({ children }: { children: ViewProps['children'] }) => {
|
|
7
|
+
const { direction } = useRadioGroupContext();
|
|
6
8
|
const { checked } = useRadioContext();
|
|
7
9
|
styles.useVariants({
|
|
8
10
|
checked,
|
|
11
|
+
direction,
|
|
9
12
|
});
|
|
10
13
|
return <View style={styles.container}>{children}</View>;
|
|
11
14
|
};
|
|
@@ -14,7 +17,6 @@ const styles = StyleSheet.create(theme => ({
|
|
|
14
17
|
container: {
|
|
15
18
|
flexDirection: 'row',
|
|
16
19
|
justifyContent: 'flex-start',
|
|
17
|
-
flex: 1,
|
|
18
20
|
alignSelf: 'stretch',
|
|
19
21
|
gap: theme.components.radio.gap,
|
|
20
22
|
padding: theme.components.radio.tile.padding,
|
|
@@ -30,6 +32,12 @@ const styles = StyleSheet.create(theme => ({
|
|
|
30
32
|
margin: -theme.components.radio.tile.borderWidthSelected / 2,
|
|
31
33
|
},
|
|
32
34
|
},
|
|
35
|
+
direction: {
|
|
36
|
+
row: {},
|
|
37
|
+
column: {
|
|
38
|
+
flex: 1,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
33
41
|
},
|
|
34
42
|
},
|
|
35
43
|
}));
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { BodyText } from '../../BodyText';
|
|
3
|
-
const ListItemText = ({ children, ...props }) => {
|
|
4
|
-
return (_jsx(BodyText, { size: "lg", ...props, children: children }));
|
|
5
|
-
};
|
|
6
|
-
ListItemText.displayName = 'ListItemText';
|
|
7
|
-
export default ListItemText;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { TextProps } from 'react-native';
|
|
2
|
-
import { BodyText } from '../../BodyText';
|
|
3
|
-
|
|
4
|
-
const ListItemText = ({ children, ...props }: TextProps) => {
|
|
5
|
-
return (
|
|
6
|
-
<BodyText size="lg" {...props}>
|
|
7
|
-
{children}
|
|
8
|
-
</BodyText>
|
|
9
|
-
);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
ListItemText.displayName = 'ListItemText';
|
|
13
|
-
|
|
14
|
-
export default ListItemText;
|