@utilitywarehouse/hearth-react-native 0.31.1 → 0.32.1
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 +15 -18
- package/CHANGELOG.md +62 -0
- package/build/components/Card/Card.props.d.ts +1 -1
- package/build/components/Card/CardRoot.js +19 -0
- package/build/components/Input/Input.js +13 -31
- package/build/components/Rating/Rating.d.ts +6 -0
- package/build/components/Rating/Rating.js +76 -0
- package/build/components/Rating/Rating.props.d.ts +18 -0
- package/build/components/Rating/Rating.props.js +1 -0
- package/build/components/Rating/RatingStarEmpty.d.ts +6 -0
- package/build/components/Rating/RatingStarEmpty.js +9 -0
- package/build/components/Rating/RatingStarFilled.d.ts +6 -0
- package/build/components/Rating/RatingStarFilled.js +9 -0
- package/build/components/Rating/index.d.ts +2 -0
- package/build/components/Rating/index.js +1 -0
- package/build/components/Roundel/Roundel.d.ts +6 -0
- package/build/components/Roundel/Roundel.js +40 -0
- package/build/components/Roundel/Roundel.props.d.ts +6 -0
- package/build/components/Roundel/Roundel.props.js +1 -0
- package/build/components/Roundel/index.d.ts +2 -0
- package/build/components/Roundel/index.js +1 -0
- package/build/components/StepperInput/StepperButton.d.ts +22 -0
- package/build/components/StepperInput/StepperButton.js +55 -0
- package/build/components/StepperInput/StepperInput.d.ts +6 -0
- package/build/components/StepperInput/StepperInput.js +179 -0
- package/build/components/StepperInput/StepperInput.props.d.ts +31 -0
- package/build/components/StepperInput/StepperInput.props.js +1 -0
- package/build/components/StepperInput/index.d.ts +2 -0
- package/build/components/StepperInput/index.js +1 -0
- package/build/components/Textarea/Textarea.d.ts +1 -1
- package/build/components/Textarea/Textarea.js +21 -32
- package/build/components/Textarea/Textarea.props.d.ts +11 -0
- package/build/components/VerificationInput/VerificationInput.js +12 -22
- package/build/components/index.d.ts +3 -0
- package/build/components/index.js +3 -0
- package/build/hooks/index.d.ts +1 -0
- package/build/hooks/index.js +1 -0
- package/build/hooks/useFormFieldAccessibility.d.ts +17 -0
- package/build/hooks/useFormFieldAccessibility.js +32 -0
- package/build/hooks/useFormFieldAccessibility.test.d.ts +1 -0
- package/build/hooks/useFormFieldAccessibility.test.js +56 -0
- package/docs/adding-shadows.mdx +2 -2
- package/docs/changelog.mdx +16 -0
- package/docs/components/AllComponents.web.tsx +30 -1
- package/docs/dark-mode-best-practice.mdx +328 -0
- package/package.json +6 -4
- package/src/components/Banner/Banner.stories.tsx +14 -0
- package/src/components/Card/Card.docs.mdx +16 -17
- package/src/components/Card/Card.props.ts +1 -0
- package/src/components/Card/Card.stories.tsx +35 -21
- package/src/components/Card/CardRoot.tsx +19 -0
- package/src/components/Icon/Icon.docs.mdx +1 -1
- package/src/components/Input/Input.tsx +14 -35
- package/src/components/List/List.docs.mdx +4 -2
- package/src/components/Modal/Modal.docs.mdx +58 -4
- package/src/components/NavModal/NavModal.docs.mdx +2 -2
- package/src/components/Rating/Rating.docs.mdx +178 -0
- package/src/components/Rating/Rating.figma.tsx +20 -0
- package/src/components/Rating/Rating.props.ts +22 -0
- package/src/components/Rating/Rating.stories.tsx +95 -0
- package/src/components/Rating/Rating.tsx +140 -0
- package/src/components/Rating/RatingStarEmpty.tsx +22 -0
- package/src/components/Rating/RatingStarFilled.tsx +27 -0
- package/src/components/Rating/index.ts +2 -0
- package/src/components/Roundel/Roundel.docs.mdx +48 -0
- package/src/components/Roundel/Roundel.figma.tsx +17 -0
- package/src/components/Roundel/Roundel.props.ts +8 -0
- package/src/components/Roundel/Roundel.stories.tsx +49 -0
- package/src/components/Roundel/Roundel.tsx +51 -0
- package/src/components/Roundel/index.ts +2 -0
- package/src/components/StepperInput/StepperButton.tsx +83 -0
- package/src/components/StepperInput/StepperInput.docs.mdx +121 -0
- package/src/components/StepperInput/StepperInput.figma.tsx +45 -0
- package/src/components/StepperInput/StepperInput.props.ts +39 -0
- package/src/components/StepperInput/StepperInput.stories.tsx +270 -0
- package/src/components/StepperInput/StepperInput.tsx +322 -0
- package/src/components/StepperInput/index.ts +2 -0
- package/src/components/Textarea/Textarea.docs.mdx +2 -0
- package/src/components/Textarea/Textarea.props.ts +11 -0
- package/src/components/Textarea/Textarea.stories.tsx +14 -0
- package/src/components/Textarea/Textarea.tsx +22 -34
- package/src/components/VerificationInput/VerificationInput.tsx +13 -25
- package/src/components/index.ts +3 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useFormFieldAccessibility.test.tsx +74 -0
- package/src/hooks/useFormFieldAccessibility.ts +67 -0
|
@@ -87,12 +87,15 @@ import {
|
|
|
87
87
|
RadioCard,
|
|
88
88
|
RadioCardGroup,
|
|
89
89
|
RadioGroup,
|
|
90
|
+
Rating,
|
|
91
|
+
Roundel,
|
|
90
92
|
SectionHeader,
|
|
91
93
|
SegmentedControl,
|
|
92
94
|
SegmentedControlOption,
|
|
93
95
|
Select,
|
|
94
96
|
Skeleton,
|
|
95
97
|
Spinner,
|
|
98
|
+
StepperInput,
|
|
96
99
|
Switch,
|
|
97
100
|
Tab,
|
|
98
101
|
Table,
|
|
@@ -145,6 +148,8 @@ const ComponentWrapper = ({
|
|
|
145
148
|
const AllComponents: React.FC = () => {
|
|
146
149
|
const [comboboxValue, setComboboxValue] = React.useState<string | null>('uk');
|
|
147
150
|
const [selectValue, setSelectValue] = React.useState('1');
|
|
151
|
+
const [stepperValue, setStepperValue] = React.useState('10');
|
|
152
|
+
const [ratingValue, setRatingValue] = React.useState<0 | 1 | 2 | 3 | 4 | 5>(3);
|
|
148
153
|
const [toggleButtonValue, setToggleButtonValue] = React.useState('');
|
|
149
154
|
const bottomSheetRef = useRef<BottomSheet>(null);
|
|
150
155
|
const handleOpenPress = useCallback(() => {
|
|
@@ -233,6 +238,7 @@ const AllComponents: React.FC = () => {
|
|
|
233
238
|
</View>
|
|
234
239
|
</Center>
|
|
235
240
|
</ComponentWrapper>
|
|
241
|
+
|
|
236
242
|
<ComponentWrapper name="Banner" link="components-banner">
|
|
237
243
|
<Center flex={1} p="200">
|
|
238
244
|
<Banner
|
|
@@ -733,7 +739,20 @@ const AllComponents: React.FC = () => {
|
|
|
733
739
|
</RadioCardGroup>
|
|
734
740
|
</Center>
|
|
735
741
|
</ComponentWrapper>
|
|
736
|
-
|
|
742
|
+
<ComponentWrapper name="Rating" link="components-rating">
|
|
743
|
+
<Center flex={1} padding="200">
|
|
744
|
+
<Rating value={ratingValue} onChange={setRatingValue} />
|
|
745
|
+
</Center>
|
|
746
|
+
</ComponentWrapper>
|
|
747
|
+
<ComponentWrapper name="Roundel" link="components-roundel">
|
|
748
|
+
<Center flex={1}>
|
|
749
|
+
<Flex direction="row" spacing="md" alignItems="center">
|
|
750
|
+
<Roundel variant="success" />
|
|
751
|
+
<Roundel variant="pending" />
|
|
752
|
+
<Roundel variant="error" />
|
|
753
|
+
</Flex>
|
|
754
|
+
</Center>
|
|
755
|
+
</ComponentWrapper>
|
|
737
756
|
<ComponentWrapper name="Section Header" link="components-section-header">
|
|
738
757
|
<Center flex={1} p="300">
|
|
739
758
|
<SectionHeader
|
|
@@ -790,6 +809,16 @@ const AllComponents: React.FC = () => {
|
|
|
790
809
|
<Switch value={switchEnabled} onValueChange={toggleSwitch} />
|
|
791
810
|
</Center>
|
|
792
811
|
</ComponentWrapper>
|
|
812
|
+
<ComponentWrapper name="Stepper Input" link="forms-stepper-input">
|
|
813
|
+
<Center flex={1} padding="200">
|
|
814
|
+
<StepperInput
|
|
815
|
+
label="Label"
|
|
816
|
+
helperText="Helper text"
|
|
817
|
+
value={stepperValue}
|
|
818
|
+
onChangeText={setStepperValue}
|
|
819
|
+
/>
|
|
820
|
+
</Center>
|
|
821
|
+
</ComponentWrapper>
|
|
793
822
|
<ComponentWrapper name="Table" link="components-table">
|
|
794
823
|
<Center flex={1} px="300">
|
|
795
824
|
<Box style={{ width: 360 }}>
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import { SearchMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
|
+
import SceneBroadbandDark from '@utilitywarehouse/hearth-svg-assets/lib/scene-broadband-dark.svg';
|
|
4
|
+
import SceneBroadbandLight from '@utilitywarehouse/hearth-svg-assets/lib/scene-broadband-light.svg';
|
|
5
|
+
import SpotPiggyBankDark from '@utilitywarehouse/hearth-svg-assets/lib/spot-piggy-bank-dark.svg';
|
|
6
|
+
import SpotPiggyBankLight from '@utilitywarehouse/hearth-svg-assets/lib/spot-piggy-bank-light.svg';
|
|
7
|
+
import { ScopedTheme } from 'react-native-unistyles';
|
|
8
|
+
import StorybookLink from '../../../shared/storybook/StorybookLink';
|
|
9
|
+
import { Alert, BodyText, Box, Center, Flex, Heading, Icon } from '../src';
|
|
10
|
+
import { BackToTopButton, NextPrevPage, UsageWrap } from './components';
|
|
11
|
+
|
|
12
|
+
<Meta title="Guides / Dark Mode Best Practice" />
|
|
13
|
+
<BackToTopButton />
|
|
14
|
+
|
|
15
|
+
# Dark Mode Best Practice
|
|
16
|
+
|
|
17
|
+
When designing for dark mode in Hearth React Native, it's important to ensure that your UI remains visually appealing and accessible. Here are some best practices to follow:
|
|
18
|
+
|
|
19
|
+
- [Setting Up Dark Mode](#setting-up-dark-mode)
|
|
20
|
+
- [Use Semantic Theme Colours](#use-semantic-theme-colours)
|
|
21
|
+
- [Use Semantic Utility Props](#use-semantic-utility-props)
|
|
22
|
+
- [Use the asset libraries](#use-the-asset-libraries)
|
|
23
|
+
- [Icons](#icons)
|
|
24
|
+
- [Illustrations](#illustrations)
|
|
25
|
+
- [Animations](#animations)
|
|
26
|
+
- [`ThemedImage` component](#themedimage-component)
|
|
27
|
+
|
|
28
|
+
<br />
|
|
29
|
+
<Box my="400">
|
|
30
|
+
<Alert text="By default, all components adapt to the current themed colour mode. This guide will help you understand how to work with and easily support dark mode in your apps using Hearth React Native." />
|
|
31
|
+
</Box>
|
|
32
|
+
<br />
|
|
33
|
+
<br />
|
|
34
|
+
|
|
35
|
+
## Setting Up Dark Mode
|
|
36
|
+
|
|
37
|
+
By default Hearth React Native theme is set to light mode. To enable dark mode, you can set the `colorMode` property in your theme configuration to `'dark'`.
|
|
38
|
+
This will automatically apply the dark mode color palette and styles across your app.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { useEffect } from 'react';
|
|
42
|
+
import { Appearance } from 'react-native';
|
|
43
|
+
import { useColorMode } from '@utilitywarehouse/hearth-react-native';
|
|
44
|
+
|
|
45
|
+
const App = () => {
|
|
46
|
+
// To set the colour mode use the useColorMode hook and set the color mode
|
|
47
|
+
// to the current system preference on app load
|
|
48
|
+
const [colorMode, setColorMode] = useColorMode();
|
|
49
|
+
|
|
50
|
+
// You can optionally set the theme from the system preference on app load or
|
|
51
|
+
// load from async storage if you are persisting the user's theme choice
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
setColorMode(Appearance.getColorScheme() || 'light');
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const toggleColorMode = () => {
|
|
57
|
+
setColorMode(colorMode === 'light' ? 'dark' : 'light');
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (/* Your app content */);
|
|
61
|
+
};
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
You can also use the Unistyles runtime to set the theme outside of React components, [read more here](https://www.unistyl.es/v3/guides/theming/#change-theme):
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
68
|
+
|
|
69
|
+
const toggleTheme = () => {
|
|
70
|
+
const theme = UnistylesRuntime.getTheme();
|
|
71
|
+
UnistylesRuntime.setTheme(theme === 'dark' ? 'light' : 'dark');
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Use Semantic Theme Colours
|
|
76
|
+
|
|
77
|
+
When styling your components, it's best to use the semantic colour tokens provided by the Hearth theme. This ensures that your colours will
|
|
78
|
+
automatically adapt to both light and dark modes without needing to write custom styles for each mode.
|
|
79
|
+
|
|
80
|
+
To use the semantic colours, you can access them from the theme object in your styles:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { StyleSheet } from '@utilitywarehouse/hearth-react-native';
|
|
84
|
+
|
|
85
|
+
const styles = StyleSheet.create(theme => ({
|
|
86
|
+
text: {
|
|
87
|
+
color: theme.colors.text.primary, // Use semantic colour token
|
|
88
|
+
},
|
|
89
|
+
}));
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
You can also use the `useTheme` hook to access the theme colours directly in your components:
|
|
93
|
+
|
|
94
|
+
<UsageWrap>
|
|
95
|
+
<Center>
|
|
96
|
+
<Flex direction="row" spacing="lg">
|
|
97
|
+
<ScopedTheme name="dark">
|
|
98
|
+
<Box backgroundColor="primary" p="400">
|
|
99
|
+
<Center>
|
|
100
|
+
<BodyText>This text adapts to dark mode!</BodyText>
|
|
101
|
+
</Center>
|
|
102
|
+
</Box>
|
|
103
|
+
</ScopedTheme>
|
|
104
|
+
<ScopedTheme name="light">
|
|
105
|
+
<Box backgroundColor="primary" p="400">
|
|
106
|
+
<Center>
|
|
107
|
+
<BodyText>This text adapts to light mode!</BodyText>
|
|
108
|
+
</Center>
|
|
109
|
+
</Box>
|
|
110
|
+
</ScopedTheme>
|
|
111
|
+
</Flex>
|
|
112
|
+
</Center>
|
|
113
|
+
</UsageWrap>
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { useTheme } from '@utilitywarehouse/hearth-react-native';
|
|
117
|
+
|
|
118
|
+
const MyComponent = () => {
|
|
119
|
+
const theme = useTheme();
|
|
120
|
+
|
|
121
|
+
return <Text style={{ color: theme.colors.text.primary }}>This text adapts to dark mode!</Text>;
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
To learn more about the available colours in the Hearth theme, check out the <StorybookLink to="theme-tokens">theme tokens documentation</StorybookLink>.
|
|
126
|
+
|
|
127
|
+
## Use Semantic Utility Props
|
|
128
|
+
|
|
129
|
+
In addition to using semantic colour tokens, you should also use the semantic utility props provided by Hearth components.
|
|
130
|
+
These props are designed to work with the theme and will automatically adjust their styles based on the current colour mode.
|
|
131
|
+
|
|
132
|
+
For example, instead of setting a background colour directly, you can use the `backgroundColor` or, for text, the `color` prop with a semantic colour value:
|
|
133
|
+
|
|
134
|
+
<UsageWrap>
|
|
135
|
+
<Center spacing="lg">
|
|
136
|
+
<Box>
|
|
137
|
+
<BodyText weight="semibold" mb="100">
|
|
138
|
+
{'Light Mode'}
|
|
139
|
+
</BodyText>
|
|
140
|
+
<Flex direction="row" spacing="lg">
|
|
141
|
+
<ScopedTheme name="light">
|
|
142
|
+
<Box backgroundColor="brand" p="400">
|
|
143
|
+
<BodyText color="inverted">
|
|
144
|
+
This branded box and text adapt to light or dark mode!
|
|
145
|
+
</BodyText>
|
|
146
|
+
</Box>
|
|
147
|
+
<Box backgroundColor="secondary" p="400">
|
|
148
|
+
<BodyText color="primary">This box and text adapt to light or dark mode!</BodyText>
|
|
149
|
+
<BodyText color="secondary">This secondary text adapt to light or dark mode!</BodyText>
|
|
150
|
+
</Box>
|
|
151
|
+
</ScopedTheme>
|
|
152
|
+
</Flex>
|
|
153
|
+
</Box>
|
|
154
|
+
<Box>
|
|
155
|
+
<BodyText weight="semibold" mb="100">
|
|
156
|
+
{'Dark Mode'}
|
|
157
|
+
</BodyText>
|
|
158
|
+
<Flex direction="row" spacing="lg">
|
|
159
|
+
<ScopedTheme name="dark">
|
|
160
|
+
<Box backgroundColor="brand" p="400">
|
|
161
|
+
<BodyText color="inverted">
|
|
162
|
+
This branded box and text adapt to light or dark mode!
|
|
163
|
+
</BodyText>
|
|
164
|
+
</Box>
|
|
165
|
+
<Box backgroundColor="secondary" p="400">
|
|
166
|
+
<BodyText color="primary">This box and text adapt to light or dark mode!</BodyText>
|
|
167
|
+
<BodyText color="secondary">This secondary text adapt to light or dark mode!</BodyText>
|
|
168
|
+
</Box>
|
|
169
|
+
</ScopedTheme>
|
|
170
|
+
</Flex>
|
|
171
|
+
</Box>
|
|
172
|
+
</Center>
|
|
173
|
+
</UsageWrap>
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<Box backgroundColor="brand" p="400">
|
|
177
|
+
<BodyText color="inverted">This branded box and text adapt to light or dark mode!</BodyText>
|
|
178
|
+
</Box>
|
|
179
|
+
<Box backgroundColor="secondary" p="400">
|
|
180
|
+
<BodyText color="primary">This box and text adapt to light or dark mode!</BodyText>
|
|
181
|
+
<BodyText color="secondary">This secondary text adapt to light or dark mode!</BodyText>
|
|
182
|
+
</Box>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
By using these semantic utility props, you can ensure that your components will look great and be accessible in both light and dark
|
|
186
|
+
modes without needing to write custom styles for each mode.
|
|
187
|
+
|
|
188
|
+
## Use the asset libraries
|
|
189
|
+
|
|
190
|
+
Hearth provides asset libraries for icons and illustrations that are designed to work well in both light and dark modes.
|
|
191
|
+
When using these assets, make sure to choose the appropriate version (light or dark) based on the current colour mode.
|
|
192
|
+
|
|
193
|
+
### Icons
|
|
194
|
+
|
|
195
|
+
When using icons from the `@utilitywarehouse/hearth-react-native-icons` library, you can automatically handle light and dark mode
|
|
196
|
+
by using the `color` prop and the `Icon` component with a semantic colour token (by default the `Icon` will use the `theme.colors.icon.primary`
|
|
197
|
+
colour which adapts to light and dark mode):
|
|
198
|
+
|
|
199
|
+
<UsageWrap>
|
|
200
|
+
<Center>
|
|
201
|
+
<Flex direction="row" spacing="lg">
|
|
202
|
+
<ScopedTheme name="light">
|
|
203
|
+
<Box p="400" backgroundColor="secondary">
|
|
204
|
+
<Icon as={SearchMediumIcon} />
|
|
205
|
+
</Box>
|
|
206
|
+
</ScopedTheme>
|
|
207
|
+
<ScopedTheme name="dark">
|
|
208
|
+
<Box p="400" backgroundColor="secondary">
|
|
209
|
+
<Icon as={SearchMediumIcon} color="warmWhite0" />
|
|
210
|
+
</Box>
|
|
211
|
+
</ScopedTheme>
|
|
212
|
+
</Flex>
|
|
213
|
+
</Center>
|
|
214
|
+
</UsageWrap>
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
import { Icon , useTheme } from '@utilitywarehouse/hearth-react-native';
|
|
218
|
+
import { SearchMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
219
|
+
|
|
220
|
+
<Icon as={SearchMediumIcon} />;
|
|
221
|
+
|
|
222
|
+
// Or to specify a colour
|
|
223
|
+
const theme = useTheme();
|
|
224
|
+
...
|
|
225
|
+
<SearchMediumIcon color={theme.colors.icon.primary} />;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
To learn more about the available icons, check out the [icon library documentation](https://hearth.prod.uw.systems/icons/?path=/docs/introduction--docs).
|
|
229
|
+
|
|
230
|
+
### Illustrations
|
|
231
|
+
|
|
232
|
+
When using illustrations from the `@utilitywarehouse/hearth-svg-assets` library, you'll need to import both the light and dark versions
|
|
233
|
+
of the illustration and conditionally render the appropriate one based on the current colour mode.
|
|
234
|
+
|
|
235
|
+
<UsageWrap>
|
|
236
|
+
<Center>
|
|
237
|
+
<Flex direction="row" spacing="lg">
|
|
238
|
+
<ScopedTheme name="light">
|
|
239
|
+
<Box p="400" backgroundColor="secondary">
|
|
240
|
+
<SceneBroadbandLight width={200} height={200} />
|
|
241
|
+
</Box>
|
|
242
|
+
</ScopedTheme>
|
|
243
|
+
<ScopedTheme name="dark">
|
|
244
|
+
<Box p="400" backgroundColor="secondary">
|
|
245
|
+
<SceneBroadbandDark width={200} height={200} />
|
|
246
|
+
</Box>
|
|
247
|
+
</ScopedTheme>
|
|
248
|
+
</Flex>
|
|
249
|
+
</Center>
|
|
250
|
+
</UsageWrap>
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
import { useColorMode } from '@utilitywarehouse/hearth-react-native';
|
|
254
|
+
import SceneBroadbandDark from '@utilitywarehouse/hearth-svg-assets/lib/scene-broadband-dark.svg';
|
|
255
|
+
import SceneBroadbandLight from '@utilitywarehouse/hearth-svg-assets/lib/scene-broadband-light.svg';
|
|
256
|
+
|
|
257
|
+
const MyComponent = () => {
|
|
258
|
+
const [colorMode] = useColorMode();
|
|
259
|
+
|
|
260
|
+
return colorMode === 'dark' ? (
|
|
261
|
+
<SceneBroadbandDark width={200} height={200} />
|
|
262
|
+
) : (
|
|
263
|
+
<SceneBroadbandLight width={200} height={200} />
|
|
264
|
+
);
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The above example is to illustrate the concept of handling light and dark mode with illustrations,
|
|
269
|
+
but you should use a reusable `ThemedImage` component to simplify this pattern, which we will cover in the next section.
|
|
270
|
+
|
|
271
|
+
You can view the available illustrations in the [Hearth SVG asset library documentation](https://hearth.prod.uw.systems/assets/?path=/docs/introduction--docs).
|
|
272
|
+
|
|
273
|
+
### Animations
|
|
274
|
+
|
|
275
|
+
When using animations from the `@utilitywarehouse/hearth-json-assets` library, you can also import both light and dark versions of the animation.
|
|
276
|
+
|
|
277
|
+
We currently only have the light variation of the animations available, but when the dark versions are added you can use a similar approach to
|
|
278
|
+
the illustrations example above to conditionally render the appropriate version based on the current colour mode.
|
|
279
|
+
|
|
280
|
+
You can view the available animations in the [Hearth JSON asset library documentation](https://hearth.prod.uw.systems/assets/?path=/docs/introduction--docs).
|
|
281
|
+
|
|
282
|
+
## `ThemedImage` component
|
|
283
|
+
|
|
284
|
+
To simplify the process of handling light and dark mode for images, you can create a reusable `ThemedImage` component that takes both light and dark versions of an image as props and automatically renders the correct one based on the current colour mode.
|
|
285
|
+
Here's an example implementation of a `ThemedImage` component:
|
|
286
|
+
|
|
287
|
+
<UsageWrap>
|
|
288
|
+
<Center>
|
|
289
|
+
<Flex direction="row" spacing="lg">
|
|
290
|
+
<ScopedTheme name="light">
|
|
291
|
+
<Box p="400" backgroundColor="secondary">
|
|
292
|
+
<SpotPiggyBankLight width={200} height={200} />
|
|
293
|
+
</Box>
|
|
294
|
+
</ScopedTheme>
|
|
295
|
+
<ScopedTheme name="dark">
|
|
296
|
+
<Box p="400" backgroundColor="secondary">
|
|
297
|
+
<SpotPiggyBankDark width={200} height={200} />
|
|
298
|
+
</Box>
|
|
299
|
+
</ScopedTheme>
|
|
300
|
+
</Flex>
|
|
301
|
+
</Center>
|
|
302
|
+
</UsageWrap>
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
import React from 'react';
|
|
306
|
+
import { ThemedImage } from '@utilitywarehouse/hearth-react-native';
|
|
307
|
+
import SpotPiggyBankLight from '@utilitywarehouse/hearth-svg-assets/lib/spot-piggy-bank-light.svg';
|
|
308
|
+
import SpotPiggyBankDark from '@utilitywarehouse/hearth-svg-assets/lib/spot-piggy-bank-dark.svg';
|
|
309
|
+
|
|
310
|
+
const MyComponent = () => {
|
|
311
|
+
return (
|
|
312
|
+
<ThemedImage
|
|
313
|
+
light={SpotPiggyBankLight}
|
|
314
|
+
dark={SpotPiggyBankDark}
|
|
315
|
+
style={{ width: 200, height: 200 }}
|
|
316
|
+
/>
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
See the full `ThemedImage` docs and implementation in the <StorybookLink to="utility-components-themed-image">`ThemedImage` documentation</StorybookLink>.
|
|
322
|
+
|
|
323
|
+
<NextPrevPage
|
|
324
|
+
prevLink="guides-adding-shadows"
|
|
325
|
+
prevTitle="Adding Shadows"
|
|
326
|
+
nextLink="primitives-box"
|
|
327
|
+
nextTitle="Box"
|
|
328
|
+
/>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.1",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@storybook/addon-vitest": "^10.2.1",
|
|
34
34
|
"@storybook/react-native-web-vite": "^10.2.1",
|
|
35
35
|
"@types/prismjs": "^1.26.5",
|
|
36
|
+
"@types/react-dom": "^19.1.6",
|
|
36
37
|
"@types/react": "^19.1.10",
|
|
37
38
|
"@vitest/browser": "^3.2.4",
|
|
38
39
|
"@vitest/coverage-v8": "^3.2.4",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"playwright": "^1.55.1",
|
|
42
43
|
"prismjs": "^1.30.0",
|
|
43
44
|
"react": "^19.1.0",
|
|
45
|
+
"react-dom": "^19.1.0",
|
|
44
46
|
"react-native": "0.80.0",
|
|
45
47
|
"react-native-edge-to-edge": "1.6.1",
|
|
46
48
|
"react-native-gesture-handler": "2.28.0",
|
|
@@ -58,10 +60,10 @@
|
|
|
58
60
|
"vite-plugin-svgr": "^4.5.0",
|
|
59
61
|
"vitest": "^3.2.4",
|
|
60
62
|
"@utilitywarehouse/hearth-fonts": "^0.0.4",
|
|
63
|
+
"@utilitywarehouse/hearth-react-icons": "^0.8.0",
|
|
61
64
|
"@utilitywarehouse/hearth-react-native-icons": "^0.8.0",
|
|
62
65
|
"@utilitywarehouse/hearth-svg-assets": "^0.6.0",
|
|
63
|
-
"@utilitywarehouse/hearth-tokens": "^0.2.4"
|
|
64
|
-
"@utilitywarehouse/hearth-react-icons": "^0.8.0"
|
|
66
|
+
"@utilitywarehouse/hearth-tokens": "^0.2.4"
|
|
65
67
|
},
|
|
66
68
|
"peerDependencies": {
|
|
67
69
|
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
@@ -86,7 +88,7 @@
|
|
|
86
88
|
"watch": "tsc --watch",
|
|
87
89
|
"figma:create": "figma connect create",
|
|
88
90
|
"figma:publish": "figma connect publish",
|
|
89
|
-
"test": "
|
|
91
|
+
"test": "vitest run --config vitest.unit.config.ts",
|
|
90
92
|
"test:storybook": "vitest run --project storybook",
|
|
91
93
|
"dev": "npm run copyChangelog && storybook dev -p 6006",
|
|
92
94
|
"dev:docs": "storybook dev -p 6002 --no-open --docs",
|
|
@@ -55,6 +55,7 @@ const meta = {
|
|
|
55
55
|
'insurance',
|
|
56
56
|
'cashback',
|
|
57
57
|
'pig',
|
|
58
|
+
'highlight',
|
|
58
59
|
],
|
|
59
60
|
},
|
|
60
61
|
iconContainerVariant: {
|
|
@@ -548,6 +549,19 @@ export const ColorSchemes: Story = {
|
|
|
548
549
|
</Button>
|
|
549
550
|
}
|
|
550
551
|
/>
|
|
552
|
+
<Banner
|
|
553
|
+
icon={HomeMediumIcon}
|
|
554
|
+
iconContainerColor="highlight"
|
|
555
|
+
iconContainerVariant="emphasis"
|
|
556
|
+
colorScheme="highlight"
|
|
557
|
+
heading="Highlight Yellow"
|
|
558
|
+
description="Banner with highlight color scheme."
|
|
559
|
+
button={
|
|
560
|
+
<Button size="sm" onPress={() => console.log('Action pressed')}>
|
|
561
|
+
Action
|
|
562
|
+
</Button>
|
|
563
|
+
}
|
|
564
|
+
/>
|
|
551
565
|
</Flex>
|
|
552
566
|
</View>
|
|
553
567
|
);
|
|
@@ -61,23 +61,22 @@ const MyComponent = () => (
|
|
|
61
61
|
|
|
62
62
|
## Props
|
|
63
63
|
|
|
64
|
-
| Property | Type
|
|
65
|
-
| -------------- |
|
|
66
|
-
| variant | `'subtle' \| 'emphasis' `
|
|
67
|
-
| colorScheme | `'neutralStrong' \| 'neutralSubtle' \| '
|
|
68
|
-
| shadowColor | `'functional' \| 'brand' \| 'energy' \| 'broadband' \| 'mobile' `<br /> `'insurance' \| 'cashback' \| 'pig'`
|
|
69
|
-
| noPadding | `boolean`
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| columnGap | `number` | The column gap between the content. | `0` |
|
|
64
|
+
| Property | Type | Description | Default |
|
|
65
|
+
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------------- |
|
|
66
|
+
| variant | `'subtle' \| 'emphasis' ` | The variant of the card. | `'subtle'` |
|
|
67
|
+
| colorScheme | `'neutralStrong' \| 'neutralSubtle' \| 'brand' \| 'energy'` <br /> `'broadband' \| 'mobile' \| 'insurance' \| 'cashback' \|` <br /> `'highlight' \| 'pig'` | The color scheme of the card. | `'neutralStrong'` |
|
|
68
|
+
| shadowColor | `'functional' \| 'brand' \| 'energy' \| 'broadband' \| 'mobile' `<br /> `'insurance' \| 'cashback' \| 'pig'` | The shadow color of the card. | `-` |
|
|
69
|
+
| noPadding | `boolean` | Whether or not the card has padding. | `false` |
|
|
70
|
+
| onPress | `() => void` | Callback function to be called. | `-` |
|
|
71
|
+
| disabled | `boolean` | Whether the card is disabled. | `false` |
|
|
72
|
+
| spacing | `'none' \| '2xs' \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | The space between the content. | `none` |
|
|
73
|
+
| alignItems | `'flex-start' \| 'flex-end' \| `<br />`'center' \| 'stretch' \| 'baseline'` | The align items of the flex container. | `flex-start` |
|
|
74
|
+
| justifyContent | `'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| `<br />` 'space-around' \| 'space-evenly'` | The justify content of the flex container. | `flex-start` |
|
|
75
|
+
| flexWrap | `'wrap' \| 'nowrap' \| 'wrap-reverse'` | The wrap of the flex container. | `nowrap` |
|
|
76
|
+
| flexDirection | `'row' \| 'column' \| 'row-reverse' \| 'column-reverse'` | The direction of the flex container. | `column` |
|
|
77
|
+
| gap | `number` | The gap between the content. | `0` |
|
|
78
|
+
| rowGap | `number` | The row gap between the content. | `0` |
|
|
79
|
+
| columnGap | `number` | The column gap between the content. | `0` |
|
|
81
80
|
|
|
82
81
|
### `CardPressHandler` Props
|
|
83
82
|
|