@utilitywarehouse/hearth-react-native 0.14.0 → 0.15.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 +53 -0
- package/build/components/Card/CardAction/CardActionRoot.js +3 -3
- package/build/components/Checkbox/Checkbox.d.ts +1 -1
- package/build/components/Checkbox/Checkbox.js +2 -4
- package/build/components/DescriptionList/DescriptionListItem.js +8 -1
- package/build/components/HTMLElements/ListItem.d.ts +9 -1
- package/build/components/HTMLElements/ListItem.js +1 -2
- package/build/components/HTMLElements/OrderedList.d.ts +3 -2
- package/build/components/HTMLElements/OrderedList.js +29 -4
- package/build/components/HTMLElements/UnorderedList.d.ts +3 -2
- package/build/components/HTMLElements/UnorderedList.js +29 -4
- package/build/components/Helper/Helper.js +1 -1
- package/build/components/Helper/HelperText.js +1 -0
- package/build/components/Input/Input.js +2 -2
- package/build/components/PillGroup/PillGroup.props.d.ts +19 -7
- package/docs/introduction.mdx +10 -0
- package/package.json +2 -2
- package/src/components/Card/CardAction/CardActionRoot.tsx +3 -3
- package/src/components/Checkbox/Checkbox.tsx +7 -2
- package/src/components/DescriptionList/DescriptionListItem.tsx +8 -1
- package/src/components/HTMLElements/ListItem.tsx +11 -3
- package/src/components/HTMLElements/Lists.docs.mdx +64 -16
- package/src/components/HTMLElements/OrderedList.stories.tsx +33 -2
- package/src/components/HTMLElements/OrderedList.tsx +54 -6
- package/src/components/HTMLElements/UnorderedList.stories.tsx +63 -5
- package/src/components/HTMLElements/UnorderedList.tsx +50 -6
- package/src/components/Helper/Helper.tsx +1 -1
- package/src/components/Helper/HelperText.tsx +1 -0
- package/src/components/Input/Input.tsx +2 -0
- package/src/components/PillGroup/PillGroup.props.ts +25 -11
- package/src/components/PillGroup/PillGroup.stories.tsx +5 -6
- package/src/components/PillGroup/PillGroup.tsx +3 -3
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.15.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,58 @@
|
|
|
1
1
|
# @utilitywarehouse/hearth-react-native
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#766](https://github.com/utilitywarehouse/hearth/pull/766) [`183155a`](https://github.com/utilitywarehouse/hearth/commit/183155a1aaf7713f0ab8a39ab1e5684ef6190d0c) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE] Custom `ListItem` styles for `UL` and `OL` components
|
|
8
|
+
|
|
9
|
+
Added new props to `UL`, `OL`, and `ListItem` components to support custom list markers, including icons, images, and colors. This brings the functionality closer to CSS-like list styling. We also fixed a layout issue where list item text could overflow the container.
|
|
10
|
+
|
|
11
|
+
**Components affected**:
|
|
12
|
+
|
|
13
|
+
- `UL` (UnorderedList)
|
|
14
|
+
- `OL` (OrderedList)
|
|
15
|
+
- `ListItem`
|
|
16
|
+
|
|
17
|
+
**Developer changes**:
|
|
18
|
+
|
|
19
|
+
You can now customise list bullets/markers using the new `listStyle*` props. These can be set on the list container to apply to all items, or overridden on individual list items.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { UL, LI } from '@utilitywarehouse/hearth-react-native';
|
|
23
|
+
import { TickIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
24
|
+
|
|
25
|
+
<UL listStyleColour="feedbackPositiveSurfaceDefault" listStyleIcon={TickIcon}>
|
|
26
|
+
<LI>Success item 1</LI>
|
|
27
|
+
<LI listStyleColour="feedbackDangerSurfaceDefault">Error item override</LI>
|
|
28
|
+
</UL>;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Supported props:
|
|
32
|
+
|
|
33
|
+
- `listStyleImage`: React Element (e.g. `<Image />`)
|
|
34
|
+
- `listStyleIcon`: Icon component
|
|
35
|
+
- `listStyleWidth` / `listStyleHeight`: Dimensions for the marker (default: 20)
|
|
36
|
+
- `listStyleColour`: Color token or value for the marker
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- [#762](https://github.com/utilitywarehouse/hearth/pull/762) [`2d2bbd2`](https://github.com/utilitywarehouse/hearth/commit/2d2bbd2ba109b9acb2e0b220766eb02c0ad5710e) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Fixes Android error when passing boolean or number as a `Checkbox` value
|
|
41
|
+
|
|
42
|
+
- [#764](https://github.com/utilitywarehouse/hearth/pull/764) [`46f115d`](https://github.com/utilitywarehouse/hearth/commit/46f115dd4bd9da824496e8ec19e29276523931a1) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Fixes `HelperText` wrapping issue
|
|
43
|
+
|
|
44
|
+
- [#762](https://github.com/utilitywarehouse/hearth/pull/762) [`2d2bbd2`](https://github.com/utilitywarehouse/hearth/commit/2d2bbd2ba109b9acb2e0b220766eb02c0ad5710e) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Fixes `PillGroup` `onChange` prop types
|
|
45
|
+
|
|
46
|
+
- [#764](https://github.com/utilitywarehouse/hearth/pull/764) [`46f115d`](https://github.com/utilitywarehouse/hearth/commit/46f115dd4bd9da824496e8ec19e29276523931a1) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: `DescriptionListItem` description disappearing when direction is `column`
|
|
47
|
+
|
|
48
|
+
- [#762](https://github.com/utilitywarehouse/hearth/pull/762) [`2d2bbd2`](https://github.com/utilitywarehouse/hearth/commit/2d2bbd2ba109b9acb2e0b220766eb02c0ad5710e) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Input styles are now passed to the correct View
|
|
49
|
+
|
|
50
|
+
## 0.14.1
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- [#749](https://github.com/utilitywarehouse/hearth/pull/749) [`ded4b2e`](https://github.com/utilitywarehouse/hearth/commit/ded4b2e3dcad9c1cc4860a13120e44a43e5f0dde) Thanks [@jordmccord](https://github.com/jordmccord)! - Fixes `CardAction` padding
|
|
55
|
+
|
|
3
56
|
## 0.14.0
|
|
4
57
|
|
|
5
58
|
### Minor Changes
|
|
@@ -120,9 +120,9 @@ const styles = StyleSheet.create(theme => ({
|
|
|
120
120
|
withIconContainer: {
|
|
121
121
|
alignItems: 'center',
|
|
122
122
|
flexDirection: 'row',
|
|
123
|
-
paddingVertical: theme.components.
|
|
124
|
-
paddingHorizontal: theme.components.
|
|
125
|
-
gap: theme.components.
|
|
123
|
+
paddingVertical: theme.components.cardAction.content.paddingVertical,
|
|
124
|
+
paddingHorizontal: theme.components.cardAction.content.paddingHorizontal,
|
|
125
|
+
gap: theme.components.cardAction.content.gap,
|
|
126
126
|
flex: 1,
|
|
127
127
|
variants: {
|
|
128
128
|
hasIconContainer: {
|
|
@@ -10,7 +10,7 @@ declare const CheckboxIcon: import("react").ForwardRefExoticComponent<import("re
|
|
|
10
10
|
}>;
|
|
11
11
|
declare const CheckboxLabel: import("react").ForwardRefExoticComponent<import("react").RefAttributes<import("../Label/Label.props").default> & Omit<import("../Label/Label.props").default, "ref">>;
|
|
12
12
|
declare const Checkbox: {
|
|
13
|
-
({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type, image, ...props }: CheckboxProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type, image, value, ...props }: CheckboxProps): import("react/jsx-runtime").JSX.Element;
|
|
14
14
|
displayName: string;
|
|
15
15
|
};
|
|
16
16
|
declare const CheckboxTile: {
|
|
@@ -25,15 +25,13 @@ CheckboxGroup.displayName = 'CheckboxGroup';
|
|
|
25
25
|
CheckboxIndicator.displayName = 'CheckboxIndicator';
|
|
26
26
|
CheckboxIcon.displayName = 'CheckboxIcon';
|
|
27
27
|
CheckboxLabel.displayName = 'CheckboxLabel';
|
|
28
|
-
const Checkbox = ({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type = 'default', image, ...props }) => {
|
|
28
|
+
const Checkbox = ({ children, label, disabled, checked, helperIcon, helperText, invalidText, validText, validationStatus: validation, showValidationIcon, type = 'default', image, value, ...props }) => {
|
|
29
29
|
const { validationStatus: fieldValidationStatus } = useFormFieldContext();
|
|
30
30
|
const { validationStatus: groupValidationStatus, type: groupType } = useCheckboxGroupContext();
|
|
31
31
|
const validationStatus = fieldValidationStatus ?? groupValidationStatus ?? validation ?? 'initial';
|
|
32
32
|
const checkboxType = groupType ?? type;
|
|
33
33
|
const checkboxChildren = children ? (children) : (_jsxs(_Fragment, { children: [_jsx(CheckboxIndicator, { children: _jsx(CheckboxIcon, {}) }), image ? image : null, _jsxs(CheckboxTextContent, { children: [!!label && _jsx(CheckboxLabel, { children: label }), !!helperText && _jsx(Helper, { disabled: disabled, icon: helperIcon, text: helperText }), validationStatus === 'invalid' && !!invalidText && (_jsx(Helper, { showIcon: showValidationIcon, disabled: disabled, validationStatus: "invalid", text: invalidText })), validationStatus === 'valid' && !!validText && (_jsx(Helper, { disabled: disabled, showIcon: showValidationIcon, validationStatus: "valid", text: validText }))] })] }));
|
|
34
|
-
return (
|
|
35
|
-
// @ts-expect-error - type
|
|
36
|
-
_jsx(CheckboxComponent, { ...props, isDisabled: disabled, isChecked: checked, children: checkboxType === 'tile' ? (_jsx(CheckboxTileRoot, { children: checkboxChildren })) : (checkboxChildren) }));
|
|
34
|
+
return (_jsx(CheckboxComponent, { ...props, value: (value ?? '').toString(), isDisabled: disabled, isChecked: checked, children: checkboxType === 'tile' ? (_jsx(CheckboxTileRoot, { children: checkboxChildren })) : (checkboxChildren) }));
|
|
37
35
|
};
|
|
38
36
|
const CheckboxTile = ({ type = 'tile', ...props }) => {
|
|
39
37
|
return _jsx(Checkbox, { ...props, type: type });
|
|
@@ -44,7 +44,14 @@ const styles = StyleSheet.create(theme => ({
|
|
|
44
44
|
color: theme.color.text.secondary,
|
|
45
45
|
},
|
|
46
46
|
descriptionWrapper: {
|
|
47
|
-
|
|
47
|
+
variants: {
|
|
48
|
+
direction: {
|
|
49
|
+
row: {
|
|
50
|
+
flex: 1,
|
|
51
|
+
},
|
|
52
|
+
column: {},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
48
55
|
},
|
|
49
56
|
}));
|
|
50
57
|
export default DescriptionListItem;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { ViewProps } from 'react-native';
|
|
2
|
-
|
|
2
|
+
import { ColorValue } from '../../types';
|
|
3
|
+
export interface ListStyleProps {
|
|
4
|
+
listStyleImage?: React.ReactElement;
|
|
5
|
+
listStyleIcon?: React.ComponentType<any>;
|
|
6
|
+
listStyleWidth?: number;
|
|
7
|
+
listStyleHeight?: number;
|
|
8
|
+
listStyleColour?: ColorValue;
|
|
9
|
+
}
|
|
10
|
+
export interface ListItemProps extends ViewProps, ListStyleProps {
|
|
3
11
|
children: ViewProps['children'];
|
|
4
12
|
}
|
|
5
13
|
declare const ListItem: {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ViewProps, ViewStyle } from 'react-native';
|
|
2
2
|
import { SpaceValue } from '../../types';
|
|
3
|
-
|
|
3
|
+
import { ListStyleProps } from './ListItem';
|
|
4
|
+
export interface OrderedListProps extends ViewProps, ListStyleProps {
|
|
4
5
|
children: ViewProps['children'];
|
|
5
6
|
gap?: SpaceValue;
|
|
6
7
|
bulletStyle?: ViewStyle;
|
|
7
8
|
}
|
|
8
9
|
declare const OrderedList: {
|
|
9
|
-
({ children, gap, style, ...rest }: OrderedListProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
({ children, gap, style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }: OrderedListProps): import("react/jsx-runtime").JSX.Element;
|
|
10
11
|
displayName: string;
|
|
11
12
|
};
|
|
12
13
|
export default OrderedList;
|
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { StyleSheet, View } from 'react-native';
|
|
4
|
-
import { useStyleProps } from '../../hooks';
|
|
4
|
+
import { useStyleProps, useTheme } from '../../hooks';
|
|
5
|
+
import { getFlattenedColorValue } from '../../utils';
|
|
5
6
|
import { BodyText } from '../BodyText';
|
|
6
|
-
const OrderedList = ({ children, gap = '100', style, ...rest }) => {
|
|
7
|
+
const OrderedList = ({ children, gap = '100', style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }) => {
|
|
7
8
|
const { computedStyles } = useStyleProps({ gap });
|
|
9
|
+
const theme = useTheme();
|
|
8
10
|
let itemNumber = 0;
|
|
9
11
|
return (_jsx(View, { style: [computedStyles, style], ...rest, children: React.Children.map(children, child => {
|
|
10
12
|
if (React.isValidElement(child)) {
|
|
11
13
|
itemNumber++;
|
|
12
|
-
|
|
14
|
+
const childProps = child.props;
|
|
15
|
+
const image = childProps.listStyleImage ?? listStyleImage;
|
|
16
|
+
const Icon = childProps.listStyleIcon ?? listStyleIcon;
|
|
17
|
+
const width = childProps.listStyleWidth ?? listStyleWidth ?? 20;
|
|
18
|
+
const height = childProps.listStyleHeight ?? listStyleHeight ?? 20;
|
|
19
|
+
const colourRaw = childProps.listStyleColour ?? listStyleColour;
|
|
20
|
+
const colour = colourRaw ? getFlattenedColorValue(colourRaw, theme.color) : undefined;
|
|
21
|
+
let bullet;
|
|
22
|
+
if (image) {
|
|
23
|
+
const imageEl = image;
|
|
24
|
+
bullet = React.cloneElement(imageEl, {
|
|
25
|
+
style: [{ width, height }, imageEl.props.style],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else if (Icon) {
|
|
29
|
+
bullet = (_jsx(Icon, { width: width, height: height, color: colour ?? theme.color.text.primary }));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
bullet = (_jsx(BodyText, { style: [styles.number, colour && { color: colour }], children: `${itemNumber}.` }));
|
|
33
|
+
}
|
|
34
|
+
const isCustom = !!(image || Icon);
|
|
35
|
+
return (_jsxs(View, { style: styles.listItemContainer, children: [isCustom ? _jsx(View, { style: { marginRight: 8 }, children: bullet }) : bullet, React.cloneElement(child, {
|
|
36
|
+
style: [childProps.style, { flex: 1 }],
|
|
37
|
+
})] }));
|
|
13
38
|
}
|
|
14
39
|
return child;
|
|
15
40
|
}) }));
|
|
@@ -22,7 +47,7 @@ const styles = StyleSheet.create({
|
|
|
22
47
|
},
|
|
23
48
|
number: {
|
|
24
49
|
marginRight: 8,
|
|
25
|
-
lineHeight: undefined,
|
|
50
|
+
lineHeight: undefined,
|
|
26
51
|
},
|
|
27
52
|
});
|
|
28
53
|
export default OrderedList;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ViewProps, ViewStyle } from 'react-native';
|
|
2
2
|
import { SpaceValue } from '../../types';
|
|
3
|
-
|
|
3
|
+
import { ListStyleProps } from './ListItem';
|
|
4
|
+
export interface UnorderedListProps extends ViewProps, ListStyleProps {
|
|
4
5
|
children: ViewProps['children'];
|
|
5
6
|
gap?: SpaceValue;
|
|
6
7
|
bulletStyle?: ViewStyle;
|
|
7
8
|
}
|
|
8
9
|
declare const UnorderedList: {
|
|
9
|
-
({ children, gap, style, ...rest }: UnorderedListProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
({ children, gap, style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }: UnorderedListProps): import("react/jsx-runtime").JSX.Element;
|
|
10
11
|
displayName: string;
|
|
11
12
|
};
|
|
12
13
|
export default UnorderedList;
|
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { StyleSheet, View } from 'react-native';
|
|
4
|
-
import { useStyleProps } from '../../hooks';
|
|
4
|
+
import { useStyleProps, useTheme } from '../../hooks';
|
|
5
|
+
import { getFlattenedColorValue } from '../../utils';
|
|
5
6
|
import { BodyText } from '../BodyText';
|
|
6
|
-
const UnorderedList = ({ children, gap = '100', style, ...rest }) => {
|
|
7
|
+
const UnorderedList = ({ children, gap = '100', style, listStyleImage, listStyleIcon, listStyleWidth, listStyleHeight, listStyleColour, ...rest }) => {
|
|
7
8
|
const { computedStyles } = useStyleProps({ gap });
|
|
9
|
+
const theme = useTheme();
|
|
8
10
|
return (_jsx(View, { style: [computedStyles, style], ...rest, children: React.Children.map(children, child => {
|
|
9
11
|
if (React.isValidElement(child)) {
|
|
10
|
-
|
|
12
|
+
const childProps = child.props;
|
|
13
|
+
const image = childProps.listStyleImage ?? listStyleImage;
|
|
14
|
+
const Icon = childProps.listStyleIcon ?? listStyleIcon;
|
|
15
|
+
const width = childProps.listStyleWidth ?? listStyleWidth ?? 20;
|
|
16
|
+
const height = childProps.listStyleHeight ?? listStyleHeight ?? 20;
|
|
17
|
+
const colourRaw = childProps.listStyleColour ?? listStyleColour;
|
|
18
|
+
const colour = colourRaw ? getFlattenedColorValue(colourRaw, theme.color) : undefined;
|
|
19
|
+
let bullet;
|
|
20
|
+
if (image) {
|
|
21
|
+
const imageEl = image;
|
|
22
|
+
bullet = React.cloneElement(imageEl, {
|
|
23
|
+
style: [{ width, height }, imageEl.props.style],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else if (Icon) {
|
|
27
|
+
bullet = (_jsx(Icon, { width: width, height: height, color: colour ?? theme.color.text.primary }));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
bullet = _jsx(BodyText, { style: [styles.bullet, colour && { color: colour }], children: "\u2022" });
|
|
31
|
+
}
|
|
32
|
+
const isCustom = !!(image || Icon);
|
|
33
|
+
return (_jsxs(View, { style: styles.listItemContainer, children: [isCustom ? _jsx(View, { style: { marginRight: 8 }, children: bullet }) : bullet, React.cloneElement(child, {
|
|
34
|
+
style: [childProps.style, { flex: 1 }],
|
|
35
|
+
})] }));
|
|
11
36
|
}
|
|
12
37
|
return child;
|
|
13
38
|
}) }));
|
|
@@ -20,7 +45,7 @@ const styles = StyleSheet.create({
|
|
|
20
45
|
},
|
|
21
46
|
bullet: {
|
|
22
47
|
marginRight: 8,
|
|
23
|
-
lineHeight: undefined,
|
|
48
|
+
lineHeight: undefined,
|
|
24
49
|
},
|
|
25
50
|
});
|
|
26
51
|
export default UnorderedList;
|
|
@@ -19,7 +19,7 @@ export const InputComponent = createInput({
|
|
|
19
19
|
export const InputSlot = InputComponent.Slot;
|
|
20
20
|
export const InputField = InputComponent.Input;
|
|
21
21
|
export const InputIcon = InputComponent.Icon;
|
|
22
|
-
const Input = forwardRef(({ validationStatus = 'initial', children, disabled, focused, readonly, leadingIcon, trailingIcon, type, showPasswordToggle = true, onClear, format, loading, clearable = false, required, inBottomSheet = false, ...props }, ref) => {
|
|
22
|
+
const Input = forwardRef(({ validationStatus = 'initial', children, disabled, focused, readonly, leadingIcon, trailingIcon, type, showPasswordToggle = true, onClear, format, loading, clearable = false, required, inBottomSheet = false, style, ...props }, ref) => {
|
|
23
23
|
const formFieldContext = useFormFieldContext();
|
|
24
24
|
const { disabled: formFieldDisabled } = formFieldContext;
|
|
25
25
|
const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
|
|
@@ -46,7 +46,7 @@ const Input = forwardRef(({ validationStatus = 'initial', children, disabled, fo
|
|
|
46
46
|
}
|
|
47
47
|
return undefined;
|
|
48
48
|
})();
|
|
49
|
-
return (_jsx(InputComponent, { ...(children ? props : {}), validationStatus: validationStatusFromContext, isInvalid: validationStatusFromContext === 'invalid', isReadOnly: readonly, isDisabled: formFieldDisabled ?? disabled, isFocused: focused, type: type, isRequired: isRequired, children: children ? (_jsx(_Fragment, { children: children })) : (_jsxs(_Fragment, { children: [!!leadingIconComponent && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: leadingIconComponent }) })), _jsx(InputField
|
|
49
|
+
return (_jsx(InputComponent, { ...(children ? props : {}), validationStatus: validationStatusFromContext, isInvalid: validationStatusFromContext === 'invalid', isReadOnly: readonly, isDisabled: formFieldDisabled ?? disabled, isFocused: focused, type: type, isRequired: isRequired, style: style, children: children ? (_jsx(_Fragment, { children: children })) : (_jsxs(_Fragment, { children: [!!leadingIconComponent && (_jsx(InputSlot, { children: _jsx(InputIcon, { as: leadingIconComponent }) })), _jsx(InputField
|
|
50
50
|
// @ts-expect-error - ref forwarding issue
|
|
51
51
|
, {
|
|
52
52
|
// @ts-expect-error - ref forwarding issue
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ScrollViewProps, ViewStyle } from 'react-native';
|
|
3
|
-
export interface
|
|
4
|
-
/** Controlled selected value(s) */
|
|
5
|
-
value: string | string[];
|
|
6
|
-
/** Multi-select mode. Default = false */
|
|
7
|
-
multiple?: boolean;
|
|
3
|
+
export interface PillGroupBaseProps extends Omit<ScrollViewProps, 'horizontal' | 'contentContainerStyle' | 'showsHorizontalScrollIndicator'> {
|
|
8
4
|
/** Allow pills to wrap lines. Default = true */
|
|
9
5
|
wrap?: boolean;
|
|
10
|
-
/** Handle selection changes */
|
|
11
|
-
onChange?: (value: string | string[]) => void;
|
|
12
6
|
/** Children must be <Pill> elements */
|
|
13
7
|
children: React.ReactNode;
|
|
14
8
|
style?: ViewStyle | ViewStyle[];
|
|
15
9
|
}
|
|
10
|
+
interface SinglePillGroupProps extends PillGroupBaseProps {
|
|
11
|
+
/** Multi-select mode. Default = false */
|
|
12
|
+
multiple?: false;
|
|
13
|
+
/** Controlled selected value */
|
|
14
|
+
value: string;
|
|
15
|
+
/** Handle selection changes */
|
|
16
|
+
onChange?: (value: string) => void;
|
|
17
|
+
}
|
|
18
|
+
interface MultiPillGroupProps extends PillGroupBaseProps {
|
|
19
|
+
/** Multi-select mode. Default = false */
|
|
20
|
+
multiple: true;
|
|
21
|
+
/** Controlled selected value(s) */
|
|
22
|
+
value: string[];
|
|
23
|
+
/** Handle selection changes */
|
|
24
|
+
onChange?: (value: string[]) => void;
|
|
25
|
+
}
|
|
26
|
+
export type PillGroupProps = SinglePillGroupProps | MultiPillGroupProps;
|
|
27
|
+
export {};
|
package/docs/introduction.mdx
CHANGED
|
@@ -212,6 +212,16 @@ StyleSheet.configure({
|
|
|
212
212
|
breakpoints,
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
// For some reason react-native-reanimated's default mock doesn't mock useReducedMotion, so we need to override it here
|
|
216
|
+
// as we use that method in our components.
|
|
217
|
+
jest.mock('react-native-reanimated', () => {
|
|
218
|
+
return {
|
|
219
|
+
...jest.requireActual('react-native-reanimated/mock'),
|
|
220
|
+
useReducedMotion: () => {}, // Add this line
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
require('react-native-reanimated').setUpTests();
|
|
224
|
+
|
|
215
225
|
// rest of your setup...
|
|
216
226
|
```
|
|
217
227
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@utilitywarehouse/hearth-fonts": "^0.0.4",
|
|
59
59
|
"@utilitywarehouse/hearth-react-icons": "^0.7.4",
|
|
60
60
|
"@utilitywarehouse/hearth-react-native-icons": "^0.7.4",
|
|
61
|
-
"@utilitywarehouse/hearth-svg-assets": "^0.
|
|
61
|
+
"@utilitywarehouse/hearth-svg-assets": "^0.3.0",
|
|
62
62
|
"@utilitywarehouse/hearth-tokens": "^0.2.2"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
@@ -222,9 +222,9 @@ const styles = StyleSheet.create(theme => ({
|
|
|
222
222
|
withIconContainer: {
|
|
223
223
|
alignItems: 'center',
|
|
224
224
|
flexDirection: 'row',
|
|
225
|
-
paddingVertical: theme.components.
|
|
226
|
-
paddingHorizontal: theme.components.
|
|
227
|
-
gap: theme.components.
|
|
225
|
+
paddingVertical: theme.components.cardAction.content.paddingVertical,
|
|
226
|
+
paddingHorizontal: theme.components.cardAction.content.paddingHorizontal,
|
|
227
|
+
gap: theme.components.cardAction.content.gap,
|
|
228
228
|
flex: 1,
|
|
229
229
|
variants: {
|
|
230
230
|
hasIconContainer: {
|
|
@@ -42,6 +42,7 @@ const Checkbox = ({
|
|
|
42
42
|
showValidationIcon,
|
|
43
43
|
type = 'default',
|
|
44
44
|
image,
|
|
45
|
+
value,
|
|
45
46
|
...props
|
|
46
47
|
}: CheckboxProps) => {
|
|
47
48
|
const { validationStatus: fieldValidationStatus } = useFormFieldContext();
|
|
@@ -80,8 +81,12 @@ const Checkbox = ({
|
|
|
80
81
|
</>
|
|
81
82
|
);
|
|
82
83
|
return (
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
<CheckboxComponent
|
|
85
|
+
{...props}
|
|
86
|
+
value={(value ?? '').toString()}
|
|
87
|
+
isDisabled={disabled}
|
|
88
|
+
isChecked={checked}
|
|
89
|
+
>
|
|
85
90
|
{checkboxType === 'tile' ? (
|
|
86
91
|
<CheckboxTileRoot>{checkboxChildren}</CheckboxTileRoot>
|
|
87
92
|
) : (
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { StyleSheet, View, ViewProps } from 'react-native';
|
|
2
|
+
import { ColorValue } from '../../types';
|
|
2
3
|
import { BodyText } from '../BodyText';
|
|
3
4
|
|
|
4
|
-
export interface
|
|
5
|
+
export interface ListStyleProps {
|
|
6
|
+
listStyleImage?: React.ReactElement;
|
|
7
|
+
listStyleIcon?: React.ComponentType<any>;
|
|
8
|
+
listStyleWidth?: number;
|
|
9
|
+
listStyleHeight?: number;
|
|
10
|
+
listStyleColour?: ColorValue;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ListItemProps extends ViewProps, ListStyleProps {
|
|
5
14
|
children: ViewProps['children'];
|
|
6
15
|
}
|
|
7
16
|
|
|
@@ -17,8 +26,7 @@ ListItem.displayName = 'ListItem';
|
|
|
17
26
|
|
|
18
27
|
const styles = StyleSheet.create({
|
|
19
28
|
item: {
|
|
20
|
-
|
|
21
|
-
alignItems: 'flex-start',
|
|
29
|
+
flexShrink: 1,
|
|
22
30
|
},
|
|
23
31
|
});
|
|
24
32
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Canvas, Controls, Meta, Story } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import { BodyText, Center, LI, OL, UL } from '../..';
|
|
3
|
+
import { BackToTopButton, UsageWrap, ViewFigmaButton } from '../../../docs/components';
|
|
4
|
+
import * as UnorderedListStory from './UnorderedList.stories';
|
|
4
5
|
|
|
5
6
|
<Meta title="Utility Components / UL & OL (Lists)" />
|
|
6
7
|
|
|
@@ -11,6 +12,9 @@ import { ViewFigmaButton, BackToTopButton, UsageWrap } from '../../../docs/compo
|
|
|
11
12
|
The `UL` (Unordered List) and `OL` (Ordered List) components are used to display lists of items. `UL` displays a bulleted list, and `OL` displays a numbered list. The `LI` (List Item) component is used to define each item within these lists.
|
|
12
13
|
|
|
13
14
|
- [Usage](#usage)
|
|
15
|
+
- [Unordered List (UL)](#unordered-list-ul)
|
|
16
|
+
- [Ordered List (OL)](#ordered-list-ol)
|
|
17
|
+
- [Customising List Styles](#customising-list-styles)
|
|
14
18
|
- [Components](#components)
|
|
15
19
|
|
|
16
20
|
## Usage
|
|
@@ -73,32 +77,76 @@ const MyComponent = () => {
|
|
|
73
77
|
};
|
|
74
78
|
```
|
|
75
79
|
|
|
80
|
+
### Customising List Styles
|
|
81
|
+
|
|
82
|
+
You can customise the appearance of list markers using props like `listStyleIcon`, `listStyleImage`, `listStyleColour`, and dimensions. These props can be applied to the `UL`/`OL` container to affect all items, or on individual `LI` components to override them.
|
|
83
|
+
|
|
84
|
+
<Canvas of={UnorderedListStory.WithCustomIcon} />
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { UL, LI } from '@utilitywarehouse/native-ui';
|
|
88
|
+
import { TickMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
89
|
+
import { Image } from 'react-native';
|
|
90
|
+
|
|
91
|
+
const MyComponent = () => {
|
|
92
|
+
return (
|
|
93
|
+
<UL listStyleColour="feedbackDangerSurfaceDefault">
|
|
94
|
+
<LI>Primary colored bullet</LI>
|
|
95
|
+
<LI listStyleColour="feedbackPositiveSurfaceDefault">Danger colored bullet override</LI>
|
|
96
|
+
<LI listStyleIcon={TickMediumIcon}>Icon bullet</LI>
|
|
97
|
+
<LI
|
|
98
|
+
listStyleImage={<Image source={{ uri: '...' }} />}
|
|
99
|
+
listStyleWidth={20}
|
|
100
|
+
listStyleHeight={20}
|
|
101
|
+
>
|
|
102
|
+
Image bullet
|
|
103
|
+
</LI>
|
|
104
|
+
</UL>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
76
109
|
## Components
|
|
77
110
|
|
|
78
111
|
### `UL`
|
|
79
112
|
|
|
80
113
|
The `UL` component is a container for unordered list items. It inherits all the properties of React Native's [`View` component](https://reactnative.dev/docs/view).
|
|
81
114
|
|
|
82
|
-
| Property
|
|
83
|
-
|
|
|
84
|
-
| `gap`
|
|
85
|
-
| `bulletStyle`
|
|
86
|
-
| `children`
|
|
115
|
+
| Property | Type | Default | Description |
|
|
116
|
+
| ----------------- | --------------------- | ------- | -------------------------------------------------------- |
|
|
117
|
+
| `gap` | `SpaceValue` | `'100'` | The gap between the list items. |
|
|
118
|
+
| `bulletStyle` | `ViewStyle` | - | Custom style for the bullet points. |
|
|
119
|
+
| `children` | `React.ReactNode` | - | The `LI` components to be rendered within the list. |
|
|
120
|
+
| `listStyleImage` | `React.ReactElement` | - | Custom element (e.g. Image) to use as the bullet/marker. |
|
|
121
|
+
| `listStyleIcon` | `React.ComponentType` | - | Custom icon component to use as the bullet/marker. |
|
|
122
|
+
| `listStyleWidth` | `number` | `20` | Width of the custom bullet/marker. |
|
|
123
|
+
| `listStyleHeight` | `number` | `20` | Height of the custom bullet/marker. |
|
|
124
|
+
| `listStyleColour` | `ColorValue` | - | Color of the bullet/marker. |
|
|
87
125
|
|
|
88
126
|
### `OL`
|
|
89
127
|
|
|
90
128
|
The `OL` component is a container for ordered list items. It inherits all the properties of React Native's [`View` component](https://reactnative.dev/docs/view).
|
|
91
129
|
|
|
92
|
-
| Property
|
|
93
|
-
|
|
|
94
|
-
| `gap`
|
|
95
|
-
| `bulletStyle`
|
|
96
|
-
| `children`
|
|
130
|
+
| Property | Type | Default | Description |
|
|
131
|
+
| ----------------- | --------------------- | ------- | -------------------------------------------------------- |
|
|
132
|
+
| `gap` | `SpaceValue` | `'100'` | The gap between the list items. |
|
|
133
|
+
| `bulletStyle` | `ViewStyle` | - | Custom style for the numbers. |
|
|
134
|
+
| `children` | `React.ReactNode` | - | The `LI` components to be rendered within the list. |
|
|
135
|
+
| `listStyleImage` | `React.ReactElement` | - | Custom element (e.g. Image) to use as the bullet/marker. |
|
|
136
|
+
| `listStyleIcon` | `React.ComponentType` | - | Custom icon component to use as the bullet/marker. |
|
|
137
|
+
| `listStyleWidth` | `number` | `20` | Width of the custom bullet/marker. |
|
|
138
|
+
| `listStyleHeight` | `number` | `20` | Height of the custom bullet/marker. |
|
|
139
|
+
| `listStyleColour` | `ColorValue` | - | Color of the number/marker. |
|
|
97
140
|
|
|
98
141
|
### `LI`
|
|
99
142
|
|
|
100
143
|
The `LI` component represents an item in a list. It wraps its children in a `BodyText` component if the children are a string. It inherits all the properties of React Native's [`View` component](https://reactnative.dev/docs/view).
|
|
101
144
|
|
|
102
|
-
| Property
|
|
103
|
-
|
|
|
104
|
-
| `children`
|
|
145
|
+
| Property | Type | Default | Description |
|
|
146
|
+
| ----------------- | --------------------- | ------- | -------------------------------------------------------- |
|
|
147
|
+
| `children` | `React.ReactNode` | - | The content to be displayed inside the list item. |
|
|
148
|
+
| `listStyleImage` | `React.ReactElement` | - | Custom element (e.g. Image) to use as the bullet/marker. |
|
|
149
|
+
| `listStyleIcon` | `React.ComponentType` | - | Custom icon component to use as the bullet/marker. |
|
|
150
|
+
| `listStyleWidth` | `number` | `20` | Width of the custom bullet/marker. |
|
|
151
|
+
| `listStyleHeight` | `number` | `20` | Height of the custom bullet/marker. |
|
|
152
|
+
| `listStyleColour` | `ColorValue` | - | Color of the bullet/marker/number. |
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import OrderedList from './OrderedList';
|
|
3
|
-
import ListItem from './ListItem';
|
|
4
2
|
import { primitive } from '@utilitywarehouse/hearth-tokens/js';
|
|
3
|
+
import { View } from 'react-native';
|
|
5
4
|
import { InputType } from 'storybook/internal/types';
|
|
6
5
|
import { SpaceValue } from '../../types';
|
|
6
|
+
import ListItem from './ListItem';
|
|
7
|
+
import OrderedList from './OrderedList';
|
|
7
8
|
|
|
8
9
|
const gap: InputType = {
|
|
9
10
|
options: Object.keys(primitive.space),
|
|
@@ -53,3 +54,33 @@ export const WithCustomGap: Story = {
|
|
|
53
54
|
</OrderedList>
|
|
54
55
|
),
|
|
55
56
|
};
|
|
57
|
+
|
|
58
|
+
export const WithColoredNumbers: Story = {
|
|
59
|
+
render: ({ ...args }) => (
|
|
60
|
+
<OrderedList {...args} listStyleColour="piggyPink300">
|
|
61
|
+
<ListItem>Item 1</ListItem>
|
|
62
|
+
<ListItem>Item 2</ListItem>
|
|
63
|
+
</OrderedList>
|
|
64
|
+
),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const WithIconOverride: Story = {
|
|
68
|
+
render: ({ ...args }) => {
|
|
69
|
+
const CustomIcon = (props: any) => (
|
|
70
|
+
<View
|
|
71
|
+
style={{
|
|
72
|
+
width: props.width,
|
|
73
|
+
height: props.height,
|
|
74
|
+
backgroundColor: props.color || 'blue',
|
|
75
|
+
borderRadius: 4,
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
return (
|
|
80
|
+
<OrderedList {...args} listStyleIcon={CustomIcon}>
|
|
81
|
+
<ListItem>Item 1 (overridden)</ListItem>
|
|
82
|
+
<ListItem>Item 2 (overridden)</ListItem>
|
|
83
|
+
</OrderedList>
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -1,27 +1,75 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { StyleSheet, View, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
-
import { useStyleProps } from '../../hooks';
|
|
3
|
+
import { useStyleProps, useTheme } from '../../hooks';
|
|
4
4
|
import { SpaceValue } from '../../types';
|
|
5
|
+
import { getFlattenedColorValue } from '../../utils';
|
|
5
6
|
import { BodyText } from '../BodyText';
|
|
7
|
+
import { ListItemProps, ListStyleProps } from './ListItem';
|
|
6
8
|
|
|
7
|
-
export interface OrderedListProps extends ViewProps {
|
|
9
|
+
export interface OrderedListProps extends ViewProps, ListStyleProps {
|
|
8
10
|
children: ViewProps['children'];
|
|
9
11
|
gap?: SpaceValue;
|
|
10
12
|
bulletStyle?: ViewStyle;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
const OrderedList = ({
|
|
15
|
+
const OrderedList = ({
|
|
16
|
+
children,
|
|
17
|
+
gap = '100',
|
|
18
|
+
style,
|
|
19
|
+
listStyleImage,
|
|
20
|
+
listStyleIcon,
|
|
21
|
+
listStyleWidth,
|
|
22
|
+
listStyleHeight,
|
|
23
|
+
listStyleColour,
|
|
24
|
+
...rest
|
|
25
|
+
}: OrderedListProps) => {
|
|
14
26
|
const { computedStyles } = useStyleProps({ gap });
|
|
27
|
+
const theme = useTheme();
|
|
15
28
|
let itemNumber = 0;
|
|
29
|
+
|
|
16
30
|
return (
|
|
17
31
|
<View style={[computedStyles, style]} {...rest}>
|
|
18
32
|
{React.Children.map(children, child => {
|
|
19
33
|
if (React.isValidElement(child)) {
|
|
20
34
|
itemNumber++;
|
|
35
|
+
const childProps = child.props as ListItemProps;
|
|
36
|
+
|
|
37
|
+
const image = childProps.listStyleImage ?? listStyleImage;
|
|
38
|
+
const Icon = childProps.listStyleIcon ?? listStyleIcon;
|
|
39
|
+
const width = childProps.listStyleWidth ?? listStyleWidth ?? 20;
|
|
40
|
+
const height = childProps.listStyleHeight ?? listStyleHeight ?? 20;
|
|
41
|
+
const colourRaw = childProps.listStyleColour ?? listStyleColour;
|
|
42
|
+
|
|
43
|
+
const colour = colourRaw ? getFlattenedColorValue(colourRaw, theme.color) : undefined;
|
|
44
|
+
|
|
45
|
+
let bullet;
|
|
46
|
+
if (image) {
|
|
47
|
+
const imageEl = image as React.ReactElement<any>;
|
|
48
|
+
bullet = React.cloneElement(imageEl, {
|
|
49
|
+
style: [{ width, height }, imageEl.props.style],
|
|
50
|
+
});
|
|
51
|
+
} else if (Icon) {
|
|
52
|
+
bullet = (
|
|
53
|
+
<Icon width={width} height={height} color={colour ?? theme.color.text.primary} />
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
bullet = (
|
|
57
|
+
<BodyText
|
|
58
|
+
style={[styles.number, colour && { color: colour }]}
|
|
59
|
+
>{`${itemNumber}.`}</BodyText>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const isCustom = !!(image || Icon);
|
|
64
|
+
|
|
21
65
|
return (
|
|
22
66
|
<View style={styles.listItemContainer}>
|
|
23
|
-
<
|
|
24
|
-
{
|
|
67
|
+
{isCustom ? <View style={{ marginRight: 8 }}>{bullet}</View> : bullet}
|
|
68
|
+
{
|
|
69
|
+
React.cloneElement(child as React.ReactElement<ListItemProps>, {
|
|
70
|
+
style: [childProps.style, { flex: 1 }],
|
|
71
|
+
}) as ViewProps['children']
|
|
72
|
+
}
|
|
25
73
|
</View>
|
|
26
74
|
);
|
|
27
75
|
}
|
|
@@ -40,7 +88,7 @@ const styles = StyleSheet.create({
|
|
|
40
88
|
},
|
|
41
89
|
number: {
|
|
42
90
|
marginRight: 8,
|
|
43
|
-
lineHeight: undefined,
|
|
91
|
+
lineHeight: undefined,
|
|
44
92
|
},
|
|
45
93
|
});
|
|
46
94
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
2
|
-
import
|
|
3
|
-
import ListItem from './ListItem';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-native';
|
|
2
|
+
import { TickMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
4
3
|
import { primitive } from '@utilitywarehouse/hearth-tokens/js';
|
|
5
|
-
import {
|
|
4
|
+
import { Image, View } from 'react-native';
|
|
5
|
+
import ListItem from './ListItem';
|
|
6
|
+
import UnorderedList from './UnorderedList';
|
|
6
7
|
|
|
7
|
-
const gap
|
|
8
|
+
const gap = {
|
|
8
9
|
options: Object.keys(primitive.space),
|
|
9
10
|
control: 'select',
|
|
10
11
|
description: 'Gap between list items.',
|
|
@@ -50,3 +51,60 @@ export const WithCustomGap: Story = {
|
|
|
50
51
|
</UnorderedList>
|
|
51
52
|
),
|
|
52
53
|
};
|
|
54
|
+
|
|
55
|
+
export const WithCustomIcon: Story = {
|
|
56
|
+
render: ({ ...args }) => {
|
|
57
|
+
return (
|
|
58
|
+
<UnorderedList
|
|
59
|
+
{...args}
|
|
60
|
+
listStyleIcon={TickMediumIcon}
|
|
61
|
+
listStyleColour="feedbackDangerSurfaceDefault"
|
|
62
|
+
>
|
|
63
|
+
<ListItem>List item 1 with icon</ListItem>
|
|
64
|
+
<ListItem>List item 2 with icon</ListItem>
|
|
65
|
+
<ListItem>
|
|
66
|
+
List item 3 with icon is a long example to test alignment, lorem ipsum dolor sit amet
|
|
67
|
+
</ListItem>
|
|
68
|
+
</UnorderedList>
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const WithCustomImage: Story = {
|
|
74
|
+
render: ({ ...args }) => (
|
|
75
|
+
<UnorderedList
|
|
76
|
+
{...args}
|
|
77
|
+
listStyleImage={
|
|
78
|
+
<Image
|
|
79
|
+
source={{ uri: 'https://placehold.co/20x20.png' }}
|
|
80
|
+
style={{ width: 20, height: 20 }}
|
|
81
|
+
/>
|
|
82
|
+
}
|
|
83
|
+
>
|
|
84
|
+
<ListItem>List item 1 with image</ListItem>
|
|
85
|
+
<ListItem>List item 2 with image</ListItem>
|
|
86
|
+
</UnorderedList>
|
|
87
|
+
),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const WithIndividualItemOverride: Story = {
|
|
91
|
+
render: ({ ...args }) => {
|
|
92
|
+
const CheckIcon = (props: any) => (
|
|
93
|
+
<View
|
|
94
|
+
style={{
|
|
95
|
+
width: props.width,
|
|
96
|
+
height: props.height,
|
|
97
|
+
backgroundColor: 'green',
|
|
98
|
+
borderRadius: 10,
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
return (
|
|
103
|
+
<UnorderedList {...args}>
|
|
104
|
+
<ListItem>Default bullet item</ListItem>
|
|
105
|
+
<ListItem listStyleIcon={CheckIcon}>Success item</ListItem>
|
|
106
|
+
<ListItem listStyleColour="blue600">Colored bullet item</ListItem>
|
|
107
|
+
</UnorderedList>
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
@@ -1,25 +1,69 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { StyleSheet, View, ViewProps, ViewStyle } from 'react-native';
|
|
3
|
-
import { useStyleProps } from '../../hooks';
|
|
3
|
+
import { useStyleProps, useTheme } from '../../hooks';
|
|
4
4
|
import { SpaceValue } from '../../types';
|
|
5
|
+
import { getFlattenedColorValue } from '../../utils';
|
|
5
6
|
import { BodyText } from '../BodyText';
|
|
7
|
+
import { ListItemProps, ListStyleProps } from './ListItem';
|
|
6
8
|
|
|
7
|
-
export interface UnorderedListProps extends ViewProps {
|
|
9
|
+
export interface UnorderedListProps extends ViewProps, ListStyleProps {
|
|
8
10
|
children: ViewProps['children'];
|
|
9
11
|
gap?: SpaceValue;
|
|
10
12
|
bulletStyle?: ViewStyle;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
const UnorderedList = ({
|
|
15
|
+
const UnorderedList = ({
|
|
16
|
+
children,
|
|
17
|
+
gap = '100',
|
|
18
|
+
style,
|
|
19
|
+
listStyleImage,
|
|
20
|
+
listStyleIcon,
|
|
21
|
+
listStyleWidth,
|
|
22
|
+
listStyleHeight,
|
|
23
|
+
listStyleColour,
|
|
24
|
+
...rest
|
|
25
|
+
}: UnorderedListProps) => {
|
|
14
26
|
const { computedStyles } = useStyleProps({ gap });
|
|
27
|
+
const theme = useTheme();
|
|
28
|
+
|
|
15
29
|
return (
|
|
16
30
|
<View style={[computedStyles, style]} {...rest}>
|
|
17
31
|
{React.Children.map(children, child => {
|
|
18
32
|
if (React.isValidElement(child)) {
|
|
33
|
+
const childProps = child.props as ListItemProps;
|
|
34
|
+
|
|
35
|
+
const image = childProps.listStyleImage ?? listStyleImage;
|
|
36
|
+
const Icon = childProps.listStyleIcon ?? listStyleIcon;
|
|
37
|
+
const width = childProps.listStyleWidth ?? listStyleWidth ?? 20;
|
|
38
|
+
const height = childProps.listStyleHeight ?? listStyleHeight ?? 20;
|
|
39
|
+
const colourRaw = childProps.listStyleColour ?? listStyleColour;
|
|
40
|
+
|
|
41
|
+
const colour = colourRaw ? getFlattenedColorValue(colourRaw, theme.color) : undefined;
|
|
42
|
+
|
|
43
|
+
let bullet;
|
|
44
|
+
if (image) {
|
|
45
|
+
const imageEl = image as React.ReactElement<any>;
|
|
46
|
+
bullet = React.cloneElement(imageEl, {
|
|
47
|
+
style: [{ width, height }, imageEl.props.style],
|
|
48
|
+
});
|
|
49
|
+
} else if (Icon) {
|
|
50
|
+
bullet = (
|
|
51
|
+
<Icon width={width} height={height} color={colour ?? theme.color.text.primary} />
|
|
52
|
+
);
|
|
53
|
+
} else {
|
|
54
|
+
bullet = <BodyText style={[styles.bullet, colour && { color: colour }]}>•</BodyText>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const isCustom = !!(image || Icon);
|
|
58
|
+
|
|
19
59
|
return (
|
|
20
60
|
<View style={styles.listItemContainer}>
|
|
21
|
-
<
|
|
22
|
-
{
|
|
61
|
+
{isCustom ? <View style={{ marginRight: 8 }}>{bullet}</View> : bullet}
|
|
62
|
+
{
|
|
63
|
+
React.cloneElement(child as React.ReactElement<ListItemProps>, {
|
|
64
|
+
style: [childProps.style, { flex: 1 }],
|
|
65
|
+
}) as ViewProps['children']
|
|
66
|
+
}
|
|
23
67
|
</View>
|
|
24
68
|
);
|
|
25
69
|
}
|
|
@@ -38,7 +82,7 @@ const styles = StyleSheet.create({
|
|
|
38
82
|
},
|
|
39
83
|
bullet: {
|
|
40
84
|
marginRight: 8,
|
|
41
|
-
lineHeight: undefined,
|
|
85
|
+
lineHeight: undefined,
|
|
42
86
|
},
|
|
43
87
|
});
|
|
44
88
|
|
|
@@ -48,6 +48,7 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
48
48
|
clearable = false,
|
|
49
49
|
required,
|
|
50
50
|
inBottomSheet = false,
|
|
51
|
+
style,
|
|
51
52
|
...props
|
|
52
53
|
},
|
|
53
54
|
ref
|
|
@@ -96,6 +97,7 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
96
97
|
isFocused={focused}
|
|
97
98
|
type={type as undefined}
|
|
98
99
|
isRequired={isRequired}
|
|
100
|
+
style={style}
|
|
99
101
|
>
|
|
100
102
|
{children ? (
|
|
101
103
|
<>{children}</>
|
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ScrollViewProps, ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
|
-
export interface
|
|
5
|
-
extends Omit<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** Multi-select mode. Default = false */
|
|
10
|
-
multiple?: boolean;
|
|
11
|
-
|
|
4
|
+
export interface PillGroupBaseProps
|
|
5
|
+
extends Omit<
|
|
6
|
+
ScrollViewProps,
|
|
7
|
+
'horizontal' | 'contentContainerStyle' | 'showsHorizontalScrollIndicator'
|
|
8
|
+
> {
|
|
12
9
|
/** Allow pills to wrap lines. Default = true */
|
|
13
10
|
wrap?: boolean;
|
|
14
11
|
|
|
15
|
-
/** Handle selection changes */
|
|
16
|
-
onChange?: (value: string | string[]) => void;
|
|
17
|
-
|
|
18
12
|
/** Children must be <Pill> elements */
|
|
19
13
|
children: React.ReactNode;
|
|
20
14
|
|
|
21
15
|
style?: ViewStyle | ViewStyle[];
|
|
22
16
|
}
|
|
17
|
+
|
|
18
|
+
interface SinglePillGroupProps extends PillGroupBaseProps {
|
|
19
|
+
/** Multi-select mode. Default = false */
|
|
20
|
+
multiple?: false;
|
|
21
|
+
/** Controlled selected value */
|
|
22
|
+
value: string;
|
|
23
|
+
/** Handle selection changes */
|
|
24
|
+
onChange?: (value: string) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface MultiPillGroupProps extends PillGroupBaseProps {
|
|
28
|
+
/** Multi-select mode. Default = false */
|
|
29
|
+
multiple: true;
|
|
30
|
+
/** Controlled selected value(s) */
|
|
31
|
+
value: string[];
|
|
32
|
+
/** Handle selection changes */
|
|
33
|
+
onChange?: (value: string[]) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PillGroupProps = SinglePillGroupProps | MultiPillGroupProps;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
1
|
import { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
2
|
import { HeartMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
|
+
import { useState } from 'react';
|
|
4
4
|
import { PillGroup } from '.';
|
|
5
|
-
import { Pill } from './Pill';
|
|
6
5
|
import { VariantTitle } from '../../../docs/components';
|
|
7
6
|
import { BodyText } from '../BodyText';
|
|
8
7
|
import { Flex } from '../Flex';
|
|
9
|
-
import {
|
|
8
|
+
import { Pill } from './Pill';
|
|
10
9
|
|
|
11
10
|
const meta = {
|
|
12
11
|
title: 'Stories / PillGroup',
|
|
@@ -104,7 +103,7 @@ export const WrapBehavior: Story = {
|
|
|
104
103
|
return (
|
|
105
104
|
<Flex space="xl" direction="column" align="center">
|
|
106
105
|
<VariantTitle title="Wrap: False">
|
|
107
|
-
<PillGroup wrap={false} value={value1} onChange={
|
|
106
|
+
<PillGroup wrap={false} value={value1} onChange={setValue1}>
|
|
108
107
|
<Pill value="1" label="New" />
|
|
109
108
|
<Pill value="2" label="Some label" />
|
|
110
109
|
<Pill value="3" label="Short" />
|
|
@@ -113,7 +112,7 @@ export const WrapBehavior: Story = {
|
|
|
113
112
|
</PillGroup>
|
|
114
113
|
</VariantTitle>
|
|
115
114
|
<VariantTitle title="Wrap: True">
|
|
116
|
-
<PillGroup wrap={true} value={value2} onChange={
|
|
115
|
+
<PillGroup wrap={true} value={value2} onChange={setValue2}>
|
|
117
116
|
<Pill value="6" label="New" />
|
|
118
117
|
<Pill value="7" label="Some label" />
|
|
119
118
|
<Pill value="8" label="Short" />
|
|
@@ -141,7 +140,7 @@ export const Multiple: Story = {
|
|
|
141
140
|
|
|
142
141
|
return (
|
|
143
142
|
<Flex space="lg" direction="column" align="center" style={{ maxWidth: 400 }}>
|
|
144
|
-
<PillGroup wrap={true} multiple value={selectedCategories} onChange={
|
|
143
|
+
<PillGroup wrap={true} multiple value={selectedCategories} onChange={setSelectedCategories}>
|
|
145
144
|
<Pill value="unread" label="Unread" />
|
|
146
145
|
<Pill value="new" label="New" icon={HeartMediumIcon} />
|
|
147
146
|
<Pill value="favourites" label="My favourites" icon={HeartMediumIcon} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
2
|
import { ScrollView } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { Box } from '../Box';
|
|
@@ -24,9 +24,9 @@ export const PillGroup = ({
|
|
|
24
24
|
const newValue = normalizedValue.includes(pillValue)
|
|
25
25
|
? normalizedValue.filter(v => v !== pillValue)
|
|
26
26
|
: [...normalizedValue, pillValue];
|
|
27
|
-
onChange?.(newValue);
|
|
27
|
+
(onChange as (value: string[]) => void)?.(newValue);
|
|
28
28
|
} else {
|
|
29
|
-
onChange?.(pillValue);
|
|
29
|
+
(onChange as (value: string) => void)?.(pillValue);
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
}),
|