@utilitywarehouse/hearth-react-native 0.16.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +14 -14
- package/CHANGELOG.md +174 -0
- package/build/components/BodyText/BodyText.js +2 -2
- package/build/components/Card/CardAction/CardActionRoot.js +12 -2
- package/build/components/Card/CardActions.context.d.ts +6 -0
- package/build/components/Card/CardActions.context.js +5 -0
- package/build/components/Card/CardActions.d.ts +7 -0
- package/build/components/Card/CardActions.js +29 -0
- package/build/components/Card/CardRoot.js +16 -104
- package/build/components/Card/helpers.d.ts +8 -0
- package/build/components/Card/helpers.js +146 -0
- package/build/components/Card/index.d.ts +2 -0
- package/build/components/Card/index.js +2 -0
- package/build/components/ExpandableCard/ExpandableCardGroup.d.ts +1 -1
- package/build/components/ExpandableCard/ExpandableCardGroup.js +2 -2
- package/build/components/ExpandableCard/ExpandableCardGroup.props.d.ts +4 -0
- package/build/components/IconButton/IconButton.props.d.ts +19 -0
- package/build/components/IconButton/IconButtonRoot.d.ts +1 -1
- package/build/components/IconButton/IconButtonRoot.js +43 -2
- package/build/components/Input/Input.js +4 -3
- package/build/components/Input/Input.props.d.ts +9 -0
- package/build/components/List/List.context.d.ts +4 -2
- package/build/components/List/List.context.js +0 -2
- package/build/components/List/List.d.ts +1 -1
- package/build/components/List/List.js +25 -38
- package/build/components/List/List.props.d.ts +1 -0
- package/build/components/List/ListAction/ListAction.js +24 -7
- package/build/components/List/ListAction/ListAction.props.d.ts +1 -0
- package/build/components/List/ListItem/ListItemHelperText.d.ts +1 -1
- package/build/components/List/ListItem/ListItemHelperText.js +2 -2
- package/build/components/List/ListItem/ListItemRoot.js +12 -4
- package/build/utils/isThemedImageProps.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/BodyText/BodyText.tsx +2 -2
- package/src/components/Card/Card.docs.mdx +224 -66
- package/src/components/Card/Card.stories.tsx +29 -25
- package/src/components/Card/CardAction/CardAction.stories.tsx +239 -93
- package/src/components/Card/CardAction/CardActionRoot.tsx +15 -2
- package/src/components/Card/CardActions.context.ts +12 -0
- package/src/components/Card/CardActions.tsx +40 -0
- package/src/components/Card/CardRoot.tsx +27 -132
- package/src/components/Card/helpers.tsx +195 -0
- package/src/components/Card/index.ts +2 -0
- package/src/components/ExpandableCard/ExpandableCard.figma.tsx +33 -38
- package/src/components/ExpandableCard/ExpandableCardGroup.figma.tsx +34 -17
- package/src/components/ExpandableCard/ExpandableCardGroup.props.ts +5 -0
- package/src/components/ExpandableCard/ExpandableCardGroup.tsx +2 -0
- package/src/components/HighlightBanner/HighlightBanner.figma.tsx +46 -0
- package/src/components/IconButton/IconButton.docs.mdx +91 -9
- package/src/components/IconButton/IconButton.figma.tsx +20 -30
- package/src/components/IconButton/IconButton.props.ts +19 -0
- package/src/components/IconButton/IconButton.stories.tsx +56 -0
- package/src/components/IconButton/IconButtonRoot.tsx +54 -1
- package/src/components/IconContainer/IconContainer.figma.tsx +7 -13
- package/src/components/IndicatorIconButton/IndicatorIconButton.figma.tsx +16 -0
- package/src/components/Input/Input.docs.mdx +55 -15
- package/src/components/Input/Input.figma.tsx +106 -40
- package/src/components/Input/Input.props.ts +9 -0
- package/src/components/Input/Input.tsx +21 -0
- package/src/components/Link/Link.figma.tsx +31 -38
- package/src/components/List/List.context.ts +2 -4
- package/src/components/List/List.docs.mdx +10 -5
- package/src/components/List/List.figma.tsx +42 -28
- package/src/components/List/List.props.ts +1 -0
- package/src/components/List/List.stories.tsx +43 -0
- package/src/components/List/List.tsx +38 -51
- package/src/components/List/ListAction/ListAction.figma.tsx +5 -13
- package/src/components/List/ListAction/ListAction.props.ts +1 -0
- package/src/components/List/ListAction/ListAction.tsx +40 -10
- package/src/components/List/ListItem/ListItem.figma.tsx +43 -27
- package/src/components/List/ListItem/ListItemHelperText.tsx +2 -2
- package/src/components/List/ListItem/ListItemRoot.tsx +15 -4
- package/src/utils/isThemedImageProps.ts +1 -1
- package/src/components/InlineLink/InlineLink.figma.tsx +0 -33
|
@@ -1,57 +1,123 @@
|
|
|
1
1
|
import figma from '@figma/code-connect';
|
|
2
|
-
import Input from '
|
|
2
|
+
import { Input } from '../';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
const props = {
|
|
5
|
+
disabled: figma.enum('State', {
|
|
6
|
+
Disabled: true,
|
|
7
|
+
}),
|
|
8
|
+
readonly: figma.enum('State', {
|
|
9
|
+
'Read-only': true,
|
|
10
|
+
}),
|
|
11
|
+
placeholder: figma.enum('Value type', {
|
|
12
|
+
Placeholder: figma.string('Value'),
|
|
13
|
+
}),
|
|
14
|
+
value: figma.enum('Value type', {
|
|
15
|
+
Filled: figma.string('Value'),
|
|
16
|
+
}),
|
|
17
|
+
label: figma.string('Label'),
|
|
18
|
+
labelVariant: figma.enum('Label variant', {
|
|
19
|
+
Body: 'body',
|
|
20
|
+
Heading: 'heading',
|
|
21
|
+
}),
|
|
22
|
+
helperText: figma.boolean('Helper text?', {
|
|
23
|
+
true: figma.string('Helper text'),
|
|
24
|
+
}),
|
|
25
|
+
validationStatus: figma.enum('State', {
|
|
26
|
+
Invalid: 'invalid',
|
|
27
|
+
Valid: 'valid',
|
|
28
|
+
}),
|
|
29
|
+
invalidText: figma.enum('State', {
|
|
30
|
+
Invalid: figma.string('Validation'),
|
|
31
|
+
}),
|
|
32
|
+
validText: figma.enum('State', {
|
|
33
|
+
Valid: figma.string('Validation'),
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
10
36
|
|
|
11
37
|
figma.connect(Input, 'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=2685%3A7021', {
|
|
12
38
|
props: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
39
|
+
...props,
|
|
40
|
+
required: figma.boolean('Optional?', {
|
|
41
|
+
true: false,
|
|
42
|
+
false: true,
|
|
16
43
|
}),
|
|
17
|
-
|
|
18
|
-
'
|
|
44
|
+
prefix: figma.boolean('Prefix?', {
|
|
45
|
+
true: figma.string('Prefix'),
|
|
19
46
|
}),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
focusable: figma.boolean('Focus?'),
|
|
23
|
-
hasTVPreferredFocus: figma.boolean('Focus?'),
|
|
24
|
-
'aria-disabled': figma.enum('State', {
|
|
25
|
-
Disabled: true,
|
|
47
|
+
suffix: figma.boolean('Suffix?', {
|
|
48
|
+
true: figma.string('Suffix'),
|
|
26
49
|
}),
|
|
27
|
-
// No matching props could be found for these Figma properties:
|
|
28
|
-
// "helperText": figma.boolean('Helper text?'),
|
|
29
|
-
// "label": figma.string('Label'),
|
|
30
|
-
// "validation": figma.string('Validation'),
|
|
31
|
-
// "helperText": figma.string('Helper text'),
|
|
32
|
-
// "value": figma.string('Value'),
|
|
33
|
-
// "suffix": figma.boolean('Suffix?'),
|
|
34
|
-
// "prefix": figma.boolean('Prefix?'),
|
|
35
|
-
// "prefix": figma.string('Prefix'),
|
|
36
|
-
// "suffix": figma.string('Suffix'),
|
|
37
|
-
// "optional": figma.boolean('Optional?'),
|
|
38
|
-
// "valueType": figma.enum('Value type', {
|
|
39
|
-
// "Empty": "empty",
|
|
40
|
-
// "Placeholder": "placeholder",
|
|
41
|
-
// "Filled": "filled"
|
|
42
|
-
// }),
|
|
43
|
-
// "labelVariant": figma.enum('Label variant', {
|
|
44
|
-
// "Body": "body",
|
|
45
|
-
// "Heading": "heading"
|
|
46
|
-
// })
|
|
47
50
|
},
|
|
48
51
|
example: props => (
|
|
49
52
|
<Input
|
|
50
53
|
disabled={props.disabled}
|
|
51
54
|
readonly={props.readonly}
|
|
52
|
-
focused={props.focused}
|
|
53
55
|
placeholder={props.placeholder}
|
|
54
|
-
|
|
56
|
+
value={props.value}
|
|
57
|
+
label={props.label}
|
|
58
|
+
labelVariant={props.labelVariant}
|
|
59
|
+
helperText={props.helperText}
|
|
60
|
+
required={props.required}
|
|
61
|
+
prefix={props.prefix}
|
|
62
|
+
suffix={props.suffix}
|
|
63
|
+
validationStatus={props.validationStatus}
|
|
64
|
+
invalidText={props.invalidText}
|
|
65
|
+
validText={props.validText}
|
|
55
66
|
/>
|
|
56
67
|
),
|
|
57
68
|
});
|
|
69
|
+
|
|
70
|
+
figma.connect(
|
|
71
|
+
Input,
|
|
72
|
+
'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=2251-10106&t=3uUSBVdxldgG5uz3-4',
|
|
73
|
+
{
|
|
74
|
+
props,
|
|
75
|
+
example: props => (
|
|
76
|
+
<Input
|
|
77
|
+
type="password"
|
|
78
|
+
disabled={props.disabled}
|
|
79
|
+
readonly={props.readonly}
|
|
80
|
+
placeholder={props.placeholder}
|
|
81
|
+
value={props.value}
|
|
82
|
+
label={props.label}
|
|
83
|
+
labelVariant={props.labelVariant}
|
|
84
|
+
helperText={props.helperText}
|
|
85
|
+
validationStatus={props.validationStatus}
|
|
86
|
+
invalidText={props.invalidText}
|
|
87
|
+
validText={props.validText}
|
|
88
|
+
/>
|
|
89
|
+
),
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
figma.connect(
|
|
94
|
+
Input,
|
|
95
|
+
'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=2161-1311&t=3uUSBVdxldgG5uz3-4',
|
|
96
|
+
{
|
|
97
|
+
props: {
|
|
98
|
+
loading: figma.enum('State', {
|
|
99
|
+
Loading: true,
|
|
100
|
+
}),
|
|
101
|
+
placeholder: figma.enum('State', {
|
|
102
|
+
Placeholder: figma.string('Value'),
|
|
103
|
+
}),
|
|
104
|
+
value: figma.enum('State', {
|
|
105
|
+
Filled: figma.string('Value'),
|
|
106
|
+
}),
|
|
107
|
+
label: figma.string('Label'),
|
|
108
|
+
helperText: figma.boolean('Helper text?', {
|
|
109
|
+
true: figma.string('Helper text'),
|
|
110
|
+
}),
|
|
111
|
+
},
|
|
112
|
+
example: props => (
|
|
113
|
+
<Input
|
|
114
|
+
type="search"
|
|
115
|
+
loading={props.loading}
|
|
116
|
+
placeholder={props.placeholder}
|
|
117
|
+
value={props.value}
|
|
118
|
+
label={props.label}
|
|
119
|
+
helperText={props.helperText}
|
|
120
|
+
/>
|
|
121
|
+
),
|
|
122
|
+
}
|
|
123
|
+
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
|
+
import React from 'react';
|
|
2
3
|
import type { TextInputProps, ViewProps } from 'react-native';
|
|
3
4
|
|
|
4
5
|
// Base props common to all input types
|
|
@@ -47,6 +48,8 @@ export interface InputWithChildrenProps extends InputBaseProps, ViewProps {
|
|
|
47
48
|
onClear?: never;
|
|
48
49
|
leadingIcon?: never;
|
|
49
50
|
trailingIcon?: never;
|
|
51
|
+
prefix?: never;
|
|
52
|
+
suffix?: never;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
// Base for inputs without children
|
|
@@ -55,6 +58,8 @@ interface InputWithoutChildrenBaseProps extends InputBaseProps, Omit<TextInputPr
|
|
|
55
58
|
leadingIcon?: ComponentType;
|
|
56
59
|
trailingIcon?: ComponentType;
|
|
57
60
|
required?: boolean;
|
|
61
|
+
prefix?: string | number | React.ReactNode;
|
|
62
|
+
suffix?: string | number | React.ReactNode;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
// Specific input types with their unique props
|
|
@@ -74,6 +79,8 @@ interface PasswordInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
|
74
79
|
loading?: never;
|
|
75
80
|
clearable?: never;
|
|
76
81
|
onClear?: never;
|
|
82
|
+
prefix?: never;
|
|
83
|
+
suffix?: never;
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
interface SearchInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
@@ -83,6 +90,8 @@ interface SearchInputSpecificProps extends InputWithoutChildrenBaseProps {
|
|
|
83
90
|
onClear?: () => void;
|
|
84
91
|
showPasswordToggle?: never;
|
|
85
92
|
format?: never;
|
|
93
|
+
prefix?: never;
|
|
94
|
+
suffix?: never;
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
// Union of all input types
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
SearchMediumIcon,
|
|
11
11
|
} from '@utilitywarehouse/hearth-react-native-icons';
|
|
12
12
|
import { useTheme } from '../../hooks';
|
|
13
|
+
import { BodyText } from '../BodyText';
|
|
13
14
|
import { FormField, useFormFieldContext } from '../FormField';
|
|
14
15
|
import { Spinner } from '../Spinner';
|
|
15
16
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
@@ -54,6 +55,8 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
54
55
|
helperIcon,
|
|
55
56
|
validText,
|
|
56
57
|
invalidText,
|
|
58
|
+
prefix,
|
|
59
|
+
suffix,
|
|
57
60
|
...props
|
|
58
61
|
},
|
|
59
62
|
ref
|
|
@@ -169,6 +172,15 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
169
172
|
<InputIcon as={leadingIconComponent} />
|
|
170
173
|
</InputSlot>
|
|
171
174
|
)}
|
|
175
|
+
{!!prefix && (
|
|
176
|
+
<InputSlot>
|
|
177
|
+
{typeof prefix === 'string' || typeof prefix === 'number' ? (
|
|
178
|
+
<BodyText>{prefix}</BodyText>
|
|
179
|
+
) : (
|
|
180
|
+
prefix
|
|
181
|
+
)}
|
|
182
|
+
</InputSlot>
|
|
183
|
+
)}
|
|
172
184
|
<InputField
|
|
173
185
|
// @ts-expect-error - ref forwarding issue
|
|
174
186
|
ref={inputRef}
|
|
@@ -197,6 +209,15 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
197
209
|
/>
|
|
198
210
|
</InputSlot>
|
|
199
211
|
)}
|
|
212
|
+
{!!suffix && (
|
|
213
|
+
<InputSlot>
|
|
214
|
+
{typeof suffix === 'string' || typeof suffix === 'number' ? (
|
|
215
|
+
<BodyText>{suffix}</BodyText>
|
|
216
|
+
) : (
|
|
217
|
+
suffix
|
|
218
|
+
)}
|
|
219
|
+
</InputSlot>
|
|
220
|
+
)}
|
|
200
221
|
{!!trailingIcon && (
|
|
201
222
|
<InputSlot>
|
|
202
223
|
<InputIcon as={trailingIcon} />
|
|
@@ -1,42 +1,35 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Link from
|
|
3
|
-
import figma from "@figma/code-connect"
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
import { Link } from '../';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
props: {
|
|
17
|
-
// These props were automatically mapped based on your linked code:
|
|
18
|
-
inverted: figma.boolean("Inverted?"),
|
|
19
|
-
disabled: figma.enum("State", {
|
|
20
|
-
Active: true,
|
|
4
|
+
figma.connect(Link, 'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=163%3A562', {
|
|
5
|
+
props: {
|
|
6
|
+
inverted: figma.boolean('Inverted?'),
|
|
7
|
+
showIcon: figma.boolean('Icon right?', {
|
|
8
|
+
false: figma.boolean('Icon left?', {
|
|
9
|
+
false: false,
|
|
10
|
+
}),
|
|
11
|
+
}),
|
|
12
|
+
iconPosition: figma.boolean('Icon right?', {
|
|
13
|
+
false: figma.boolean('Icon left?', {
|
|
14
|
+
true: 'left',
|
|
21
15
|
}),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
}),
|
|
17
|
+
text: figma.string('Text'),
|
|
18
|
+
icon: figma.boolean('Icon right?', {
|
|
19
|
+
true: figma.instance('Icon right-20'),
|
|
20
|
+
false: figma.boolean('Icon left?', {
|
|
21
|
+
true: figma.instance('Icon left-20'),
|
|
25
22
|
}),
|
|
26
|
-
|
|
27
|
-
// "iconLeft": figma.boolean('Icon left?'),
|
|
28
|
-
// "iconRight": figma.boolean('Icon right?'),
|
|
29
|
-
// "iconRight20": figma.instance('Icon right-20'),
|
|
30
|
-
// "iconLeft20": figma.instance('Icon left-20'),
|
|
31
|
-
// "text": figma.string('Text')
|
|
32
|
-
},
|
|
33
|
-
example: (props) => (
|
|
34
|
-
<Link
|
|
35
|
-
inverted={props.inverted}
|
|
36
|
-
disabled={props.disabled}
|
|
37
|
-
showIcon={props.showIcon}
|
|
38
|
-
focusable={props.focusable}
|
|
39
|
-
/>
|
|
40
|
-
),
|
|
23
|
+
}),
|
|
41
24
|
},
|
|
42
|
-
|
|
25
|
+
example: props => (
|
|
26
|
+
<Link
|
|
27
|
+
icon={props.icon}
|
|
28
|
+
showIcon={props.showIcon}
|
|
29
|
+
iconPosition={props.iconPosition}
|
|
30
|
+
inverted={props.inverted}
|
|
31
|
+
>
|
|
32
|
+
{props.text}
|
|
33
|
+
</Link>
|
|
34
|
+
),
|
|
35
|
+
});
|
|
@@ -5,6 +5,8 @@ export const ListContext = createContext<{
|
|
|
5
5
|
loading?: ListProps['loading'];
|
|
6
6
|
disabled?: ListProps['disabled'];
|
|
7
7
|
container?: ListProps['container'];
|
|
8
|
+
firstItemId?: string;
|
|
9
|
+
registerItem?: (id: string) => () => void;
|
|
8
10
|
}>({});
|
|
9
11
|
|
|
10
12
|
export const useListContext = () => {
|
|
@@ -12,7 +14,3 @@ export const useListContext = () => {
|
|
|
12
14
|
|
|
13
15
|
return context;
|
|
14
16
|
};
|
|
15
|
-
|
|
16
|
-
export const ListFirstItemContext = createContext<boolean>(false);
|
|
17
|
-
|
|
18
|
-
export const useListFirstItemContext = () => useContext(ListFirstItemContext);
|
|
@@ -100,6 +100,7 @@ const MyComponent = () => (
|
|
|
100
100
|
| heading | `string` | | The text to display in the heading of the list. |
|
|
101
101
|
| helperText | `string` | | The supporting text to display in the heading of the list. |
|
|
102
102
|
| headerTrailingContent | `ReactNode` | | Optional content to display on the right side of the header. |
|
|
103
|
+
| invalidText | `string` | | Validation error text to display in the heading of the list. |
|
|
103
104
|
| loading | `boolean` | `false` | Whether to show the list items in loading state. |
|
|
104
105
|
| disabled | `boolean` | `false` | Whether to disable the list. |
|
|
105
106
|
|
|
@@ -121,13 +122,17 @@ const MyComponent = () => (
|
|
|
121
122
|
| truncateHeading | `boolean` | `false` | Whether to truncate the heading text if it overflows. |
|
|
122
123
|
| truncateHelperText | `boolean` | `false` | Whether to truncate the helper text if it overflows. |
|
|
123
124
|
|
|
125
|
+
First-item styling is applied to the first rendered `ListItem` or `ListAction`. Wrapper components that render `null`
|
|
126
|
+
are ignored, so conditional list items will not affect which item loses the top border.
|
|
127
|
+
|
|
124
128
|
### `ListAction`
|
|
125
129
|
|
|
126
|
-
| Name | Type | Default | Description
|
|
127
|
-
| -------- | ------------ | ------- |
|
|
128
|
-
| heading | `string` | | The text to display in the list action item.
|
|
129
|
-
| onPress | `() => void` | | A callback function to be called
|
|
130
|
-
| disabled | `boolean` | `false` | Whether to disable the list action item.
|
|
130
|
+
| Name | Type | Default | Description |
|
|
131
|
+
| -------- | ------------ | ------- | ---------------------------------------------------------------------- |
|
|
132
|
+
| heading | `string` | | The text to display in the list action item. |
|
|
133
|
+
| onPress | `() => void` | | A callback function to be called when the list action item is pressed. |
|
|
134
|
+
| disabled | `boolean` | `false` | Whether to disable the list action item. |
|
|
135
|
+
| loading | `boolean` | `false` | Whether to show the list action in loading state. |
|
|
131
136
|
|
|
132
137
|
#### - `ListItemLeadingContent`
|
|
133
138
|
|
|
@@ -1,31 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
import List from
|
|
3
|
-
import figma from "@figma/code-connect"
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
import { List } from '../';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
figma.connect(List, 'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=2437%3A621', {
|
|
5
|
+
props: {
|
|
6
|
+
container: figma.enum('Container', {
|
|
7
|
+
'Subtle White': 'subtleWhite',
|
|
8
|
+
'Emphasis White': 'emphasisWhite',
|
|
9
|
+
'Subtle Warm White': 'subtleWarmWhite',
|
|
10
|
+
'Emphasis Warm White': 'emphasisWarmWhite',
|
|
11
|
+
}),
|
|
12
|
+
sectionHeader: figma.boolean('Section header?', {
|
|
13
|
+
true: figma.nestedProps('Section Header', {
|
|
14
|
+
heading: figma.string('Heading'),
|
|
15
|
+
helperText: figma.boolean('Helper text?', {
|
|
16
|
+
true: figma.string('Helper text'),
|
|
17
|
+
}),
|
|
18
|
+
trailingContent: figma.boolean('Trailing content?', {
|
|
19
|
+
true: figma.nestedProps('Trailing content', {
|
|
20
|
+
headerTrailingContent: figma.instance('Variant'),
|
|
21
|
+
}),
|
|
22
|
+
}),
|
|
23
|
+
invalidText: figma.enum('State', {
|
|
24
|
+
Invalid: figma.nestedProps('Validation Text', {
|
|
25
|
+
invalidText: figma.string('Text'),
|
|
26
|
+
}),
|
|
27
|
+
}),
|
|
24
28
|
}),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
example: (props) => <List container={props.container} />,
|
|
29
|
+
}),
|
|
30
|
+
listItems: figma.children('List Item'),
|
|
31
|
+
listActions: figma.children('List Action'),
|
|
30
32
|
},
|
|
31
|
-
|
|
33
|
+
example: props => (
|
|
34
|
+
<List
|
|
35
|
+
container={props.container}
|
|
36
|
+
heading={props.sectionHeader?.heading}
|
|
37
|
+
helperText={props.sectionHeader?.helperText}
|
|
38
|
+
headerTrailingContent={props.sectionHeader?.trailingContent?.headerTrailingContent}
|
|
39
|
+
invalidText={props.sectionHeader?.invalidText?.invalidText}
|
|
40
|
+
>
|
|
41
|
+
{props.listItems}
|
|
42
|
+
{props.listActions}
|
|
43
|
+
</List>
|
|
44
|
+
),
|
|
45
|
+
});
|
|
@@ -233,6 +233,49 @@ export const WithCustomListItemComponent: Story = {
|
|
|
233
233
|
),
|
|
234
234
|
};
|
|
235
235
|
|
|
236
|
+
const CustomListAction = () => (
|
|
237
|
+
<ListAction
|
|
238
|
+
heading="Custom List Action"
|
|
239
|
+
onPress={() => console.log('Custom List Action pressed')}
|
|
240
|
+
/>
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const CustomNull = () => null;
|
|
244
|
+
|
|
245
|
+
export const WithMappedCustomListItems: Story = {
|
|
246
|
+
parameters: {
|
|
247
|
+
controls: { include: [] },
|
|
248
|
+
},
|
|
249
|
+
render: () => {
|
|
250
|
+
const listData = [
|
|
251
|
+
{ heading: 'Custom Item 1', helperText: 'Supporting text 1' },
|
|
252
|
+
{ heading: 'Custom Item 2', helperText: 'Supporting text 2' },
|
|
253
|
+
{ heading: 'Custom Item 3', helperText: 'Supporting text 3' },
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<List container="subtleWarmWhite">
|
|
258
|
+
<CustomNull />
|
|
259
|
+
<ListItem
|
|
260
|
+
heading="Refer a friend"
|
|
261
|
+
helperText="Get rewarded with a friend"
|
|
262
|
+
leadingContent={<ListItemIcon as={UserMediumIcon} />}
|
|
263
|
+
trailingContent={<ListItemTrailingIcon as={ChevronRightSmallIcon} />}
|
|
264
|
+
onPress={() => console.log('Refer a friend pressed')}
|
|
265
|
+
/>
|
|
266
|
+
{listData.map((item, index) => (
|
|
267
|
+
<CustomListItem key={index} />
|
|
268
|
+
))}
|
|
269
|
+
{listData.map((item, index) => (
|
|
270
|
+
<CustomListAction key={index} />
|
|
271
|
+
))}
|
|
272
|
+
<CustomListAction />
|
|
273
|
+
<CustomListAction />
|
|
274
|
+
</List>
|
|
275
|
+
);
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
236
279
|
export const WithListAction: Story = {
|
|
237
280
|
parameters: {
|
|
238
281
|
controls: { include: [] },
|
|
@@ -1,55 +1,22 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { View
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
2
|
+
import { View } 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
|
|
6
|
+
import { ListContext } from './List.context';
|
|
7
7
|
import type ListProps from './List.props';
|
|
8
|
-
import { ListAction } from './ListAction';
|
|
9
|
-
import { ListItem } from './ListItem';
|
|
10
8
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
}
|
|
26
|
-
|
|
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>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return child;
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
return recursiveClone(children) as ViewProps['children'];
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const List = ({ children, heading, helperText, headerTrailingContent, ...props }: ListProps) => {
|
|
9
|
+
const List = ({
|
|
10
|
+
children,
|
|
11
|
+
heading,
|
|
12
|
+
helperText,
|
|
13
|
+
headerTrailingContent,
|
|
14
|
+
invalidText,
|
|
15
|
+
...props
|
|
16
|
+
}: ListProps) => {
|
|
52
17
|
const { loading, disabled, container = 'none' } = props;
|
|
18
|
+
const orderRef = useRef<string[]>([]);
|
|
19
|
+
const [firstItemId, setFirstItemId] = useState<string | undefined>(undefined);
|
|
53
20
|
const containerToCard: {
|
|
54
21
|
variant: 'subtle' | 'emphasis';
|
|
55
22
|
colorScheme: 'neutralStrong' | 'neutralSubtle';
|
|
@@ -60,8 +27,27 @@ const List = ({ children, heading, helperText, headerTrailingContent, ...props }
|
|
|
60
27
|
? 'neutralStrong'
|
|
61
28
|
: 'neutralSubtle',
|
|
62
29
|
};
|
|
63
|
-
|
|
64
|
-
const
|
|
30
|
+
|
|
31
|
+
const registerItem = useCallback((id: string) => {
|
|
32
|
+
if (!orderRef.current.includes(id)) {
|
|
33
|
+
orderRef.current.push(id);
|
|
34
|
+
}
|
|
35
|
+
const nextFirst = orderRef.current[0];
|
|
36
|
+
setFirstItemId(prev => (prev === nextFirst ? prev : nextFirst));
|
|
37
|
+
return () => {
|
|
38
|
+
orderRef.current = orderRef.current.filter(currentId => currentId !== id);
|
|
39
|
+
const nextFirst = orderRef.current[0];
|
|
40
|
+
setFirstItemId(prev => (prev === nextFirst ? prev : nextFirst));
|
|
41
|
+
};
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
const value = {
|
|
45
|
+
loading,
|
|
46
|
+
disabled,
|
|
47
|
+
container,
|
|
48
|
+
firstItemId,
|
|
49
|
+
registerItem,
|
|
50
|
+
};
|
|
65
51
|
styles.useVariants({ disabled });
|
|
66
52
|
return (
|
|
67
53
|
<ListContext.Provider value={value}>
|
|
@@ -71,14 +57,15 @@ const List = ({ children, heading, helperText, headerTrailingContent, ...props }
|
|
|
71
57
|
heading={heading}
|
|
72
58
|
helperText={helperText}
|
|
73
59
|
trailingContent={headerTrailingContent}
|
|
60
|
+
invalidText={invalidText}
|
|
74
61
|
/>
|
|
75
62
|
) : null}
|
|
76
63
|
{container === 'none' ? (
|
|
77
|
-
<View>{
|
|
64
|
+
<View>{children}</View>
|
|
78
65
|
) : (
|
|
79
|
-
React.Children.count(
|
|
66
|
+
React.Children.count(children) > 0 && (
|
|
80
67
|
<Card {...containerToCard} noPadding style={styles.card}>
|
|
81
|
-
<>{
|
|
68
|
+
<>{children}</>
|
|
82
69
|
</Card>
|
|
83
70
|
)
|
|
84
71
|
)}
|
|
@@ -1,29 +1,21 @@
|
|
|
1
1
|
import figma from '@figma/code-connect';
|
|
2
2
|
import ListAction from './ListAction';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* -- This file was auto-generated by Code Connect --
|
|
6
|
-
* `props` includes a mapping from your code props to Figma properties.
|
|
7
|
-
* You should check this is correct, and update the `example` function
|
|
8
|
-
* to return the code example you'd like to see in Figma
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
4
|
figma.connect(
|
|
12
5
|
ListAction,
|
|
13
6
|
'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=9661%3A5128',
|
|
14
7
|
{
|
|
15
8
|
props: {
|
|
16
|
-
// These props were automatically mapped based on your linked code:
|
|
17
9
|
heading: figma.string('Action heading'),
|
|
18
10
|
disabled: figma.enum('State', {
|
|
19
11
|
Disabled: true,
|
|
20
12
|
}),
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
loading: figma.enum('State', {
|
|
14
|
+
Loading: true,
|
|
23
15
|
}),
|
|
24
|
-
// No matching props could be found for these Figma properties:
|
|
25
|
-
// "actionHeading": figma.string('Action heading')
|
|
26
16
|
},
|
|
27
|
-
example: props =>
|
|
17
|
+
example: props => (
|
|
18
|
+
<ListAction heading={props.heading} disabled={props.disabled} loading={props.loading} />
|
|
19
|
+
),
|
|
28
20
|
}
|
|
29
21
|
);
|