@utilitywarehouse/hearth-react-native 0.5.0 → 0.6.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/.storybook/main.ts +21 -1
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +8 -0
- package/build/components/Container/Container.d.ts +6 -0
- package/build/components/Container/Container.js +40 -0
- package/build/components/Container/Container.props.d.ts +85 -0
- package/build/components/Container/Container.props.js +1 -0
- package/build/components/Container/index.d.ts +2 -0
- package/build/components/Container/index.js +1 -0
- package/build/components/ProgressStepper/ProgressStep.d.ts +1 -1
- package/build/components/ProgressStepper/ProgressStep.js +6 -6
- package/build/components/ProgressStepper/ProgressStepper.props.d.ts +3 -3
- package/build/components/ProgressStepper/index.d.ts +1 -1
- package/build/components/ThemedImage/ThemedImage.d.ts +12 -0
- package/build/components/ThemedImage/ThemedImage.js +27 -0
- package/build/components/ThemedImage/ThemedImage.props.d.ts +13 -0
- package/build/components/ThemedImage/ThemedImage.props.js +1 -0
- package/build/components/ThemedImage/index.d.ts +2 -0
- package/build/components/ThemedImage/index.js +1 -0
- package/build/components/index.d.ts +2 -0
- package/build/components/index.js +2 -0
- package/build/hooks/useStyleProps.js +1 -1
- package/docs/components/AllComponents.web.tsx +38 -3
- package/docs/layout-components.docs.mdx +30 -0
- package/package.json +3 -1
- package/src/components/Container/Container.docs.mdx +168 -0
- package/src/components/Container/Container.props.ts +89 -0
- package/src/components/Container/Container.stories.tsx +274 -0
- package/src/components/Container/Container.tsx +52 -0
- package/src/components/Container/index.tsx +2 -0
- package/src/components/ProgressStepper/ProgressStep.tsx +8 -8
- package/src/components/ProgressStepper/ProgressStepper.docs.mdx +11 -11
- package/src/components/ProgressStepper/ProgressStepper.props.ts +3 -3
- package/src/components/ProgressStepper/ProgressStepper.stories.tsx +27 -27
- package/src/components/ProgressStepper/index.ts +1 -1
- package/src/components/ThemedImage/ThemedImage.docs.mdx +208 -0
- package/src/components/ThemedImage/ThemedImage.props.ts +15 -0
- package/src/components/ThemedImage/ThemedImage.stories.tsx +175 -0
- package/src/components/ThemedImage/ThemedImage.tsx +34 -0
- package/src/components/ThemedImage/index.tsx +2 -0
- package/src/components/index.ts +2 -0
- package/src/hooks/useStyleProps.ts +1 -1
- package/src/vite-env.d.ts +6 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ViewProps } from 'react-native';
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type StepStatus = 'complete' | 'active' | 'incomplete';
|
|
4
4
|
|
|
5
5
|
export interface ProgressStepperProps extends ViewProps {
|
|
6
6
|
/**
|
|
@@ -15,9 +15,9 @@ export interface ProgressStepProps extends ViewProps {
|
|
|
15
15
|
*/
|
|
16
16
|
id: string;
|
|
17
17
|
/**
|
|
18
|
-
* Current
|
|
18
|
+
* Current status of the step
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
status: StepStatus;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface ProgressStepperRootProps extends ViewProps {
|
|
@@ -22,26 +22,26 @@ export const Playground: Story = {
|
|
|
22
22
|
children: (
|
|
23
23
|
<Flex space="xl" direction="column" align="center" style={{ flex: 1, minWidth: 200 }}>
|
|
24
24
|
<ProgressStepper>
|
|
25
|
-
<ProgressStep id={'1'}
|
|
26
|
-
<ProgressStep id={'2'}
|
|
27
|
-
<ProgressStep id={'3'}
|
|
28
|
-
<ProgressStep id={'4'}
|
|
25
|
+
<ProgressStep id={'1'} status="complete" />
|
|
26
|
+
<ProgressStep id={'2'} status="complete" />
|
|
27
|
+
<ProgressStep id={'3'} status="active" />
|
|
28
|
+
<ProgressStep id={'4'} status="incomplete" />
|
|
29
29
|
</ProgressStepper>
|
|
30
30
|
<Flex direction="row" space="lg" style={{ width: '100%' }}>
|
|
31
|
-
<ProgressStep id={'1'}
|
|
31
|
+
<ProgressStep id={'1'} status="complete" />
|
|
32
32
|
</Flex>
|
|
33
33
|
<Flex direction="row" space="lg" style={{ width: '100%' }}>
|
|
34
|
-
<ProgressStep id={'3'}
|
|
34
|
+
<ProgressStep id={'3'} status="active" />
|
|
35
35
|
</Flex>
|
|
36
36
|
<Flex direction="row" space="lg" style={{ width: '100%' }}>
|
|
37
|
-
<ProgressStep id={'5'}
|
|
37
|
+
<ProgressStep id={'5'} status="incomplete" />
|
|
38
38
|
</Flex>
|
|
39
39
|
</Flex>
|
|
40
40
|
),
|
|
41
41
|
},
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
export const
|
|
44
|
+
export const StepStatuses: Story = {
|
|
45
45
|
args: {
|
|
46
46
|
children: <></>,
|
|
47
47
|
},
|
|
@@ -53,31 +53,31 @@ export const StepStates: Story = {
|
|
|
53
53
|
<Flex space="xl" direction="column" align="center">
|
|
54
54
|
<VariantTitle title="All Uncompleted Steps">
|
|
55
55
|
<ProgressStepper {...props}>
|
|
56
|
-
<ProgressStep id={'1'}
|
|
57
|
-
<ProgressStep id={'2'}
|
|
58
|
-
<ProgressStep id={'3'}
|
|
56
|
+
<ProgressStep id={'1'} status="incomplete" />
|
|
57
|
+
<ProgressStep id={'2'} status="incomplete" />
|
|
58
|
+
<ProgressStep id={'3'} status="incomplete" />
|
|
59
59
|
</ProgressStepper>
|
|
60
60
|
</VariantTitle>
|
|
61
61
|
<VariantTitle title="One Active Step">
|
|
62
62
|
<ProgressStepper {...props}>
|
|
63
|
-
<ProgressStep id={'1'}
|
|
64
|
-
<ProgressStep id={'2'}
|
|
65
|
-
<ProgressStep id={'3'}
|
|
63
|
+
<ProgressStep id={'1'} status="active" />
|
|
64
|
+
<ProgressStep id={'2'} status="incomplete" />
|
|
65
|
+
<ProgressStep id={'3'} status="incomplete" />
|
|
66
66
|
</ProgressStepper>
|
|
67
67
|
</VariantTitle>
|
|
68
|
-
<VariantTitle title="Mixed
|
|
68
|
+
<VariantTitle title="Mixed Statuses">
|
|
69
69
|
<ProgressStepper {...props}>
|
|
70
|
-
<ProgressStep id={'1'}
|
|
71
|
-
<ProgressStep id={'2'}
|
|
72
|
-
<ProgressStep id={'3'}
|
|
73
|
-
<ProgressStep id={'4'}
|
|
70
|
+
<ProgressStep id={'1'} status="complete" />
|
|
71
|
+
<ProgressStep id={'2'} status="complete" />
|
|
72
|
+
<ProgressStep id={'3'} status="active" />
|
|
73
|
+
<ProgressStep id={'4'} status="incomplete" />
|
|
74
74
|
</ProgressStepper>
|
|
75
75
|
</VariantTitle>
|
|
76
76
|
<VariantTitle title="All Completed">
|
|
77
77
|
<ProgressStepper {...props}>
|
|
78
|
-
<ProgressStep id={'1'}
|
|
79
|
-
<ProgressStep id={'2'}
|
|
80
|
-
<ProgressStep id={'3'}
|
|
78
|
+
<ProgressStep id={'1'} status="complete" />
|
|
79
|
+
<ProgressStep id={'2'} status="complete" />
|
|
80
|
+
<ProgressStep id={'3'} status="complete" />
|
|
81
81
|
</ProgressStepper>
|
|
82
82
|
</VariantTitle>
|
|
83
83
|
</Flex>
|
|
@@ -95,11 +95,11 @@ export const BasicExample: Story = {
|
|
|
95
95
|
<Heading size="md">Progress Stepper</Heading>
|
|
96
96
|
<BodyText>Shows progress through multi-step processes</BodyText>
|
|
97
97
|
<ProgressStepper {...props}>
|
|
98
|
-
<ProgressStep id="services-data"
|
|
99
|
-
<ProgressStep id="customer-data"
|
|
100
|
-
<ProgressStep id="shipping-data"
|
|
101
|
-
<ProgressStep id="payment-data"
|
|
102
|
-
<ProgressStep id="summary"
|
|
98
|
+
<ProgressStep id="services-data" status="complete" />
|
|
99
|
+
<ProgressStep id="customer-data" status="complete" />
|
|
100
|
+
<ProgressStep id="shipping-data" status="active" />
|
|
101
|
+
<ProgressStep id="payment-data" status="incomplete" />
|
|
102
|
+
<ProgressStep id="summary" status="incomplete" />
|
|
103
103
|
</ProgressStepper>
|
|
104
104
|
<BodyText>Step 3 of 5</BodyText>
|
|
105
105
|
</Flex>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default as ProgressStepper } from './ProgressStepper';
|
|
2
2
|
export { default as ProgressStep } from './ProgressStep';
|
|
3
|
-
export type { ProgressStepperProps, ProgressStepProps,
|
|
3
|
+
export type { ProgressStepperProps, ProgressStepProps, StepStatus } from './ProgressStepper.props';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Canvas, Controls, Meta, Story } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as ThemedImageStories from './ThemedImage.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Utility Components / Themed Image" />
|
|
5
|
+
|
|
6
|
+
# Themed Image
|
|
7
|
+
|
|
8
|
+
The `ThemedImage` component automatically switches between light and dark mode images or SVG components based on the current theme. It's perfect for illustrations, logos, or any visual assets that need different appearances for light and dark modes.
|
|
9
|
+
|
|
10
|
+
- [Features](#features)
|
|
11
|
+
- [Basic Usage](#basic-usage)
|
|
12
|
+
- [Props](#props)
|
|
13
|
+
- [Examples](#examples)
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Automatic theme switching** - Responds to color mode changes
|
|
18
|
+
- **Flexible sources** - Accepts both regular image sources and React components (like SVGs)
|
|
19
|
+
- **Accessible** - Supports all standard Image accessibility props
|
|
20
|
+
- **Works with svg-assets** - Seamlessly integrates with the `@utilitywarehouse/hearth-svg-assets` package
|
|
21
|
+
|
|
22
|
+
## Basic Usage
|
|
23
|
+
|
|
24
|
+
<Canvas of={ThemedImageStories.Playground} />
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
| Prop | Type | Required | Description |
|
|
29
|
+
| ------- | ------------------------------------- | -------- | -------------------------------------------------- |
|
|
30
|
+
| `light` | `ImageSourcePropType \| ReactElement` | Yes | Image source or component to display in light mode |
|
|
31
|
+
| `dark` | `ImageSourcePropType \| ReactElement` | Yes | Image source or component to display in dark mode |
|
|
32
|
+
| ...rest | `ImageProps` | No | All other standard React Native Image props |
|
|
33
|
+
|
|
34
|
+
### Additional Props
|
|
35
|
+
|
|
36
|
+
`ThemedImage` extends React Native's `Image` component, so it accepts all standard `ImageProps` including:
|
|
37
|
+
|
|
38
|
+
- `style` - Custom styling for the image container
|
|
39
|
+
- `resizeMode` - How to resize the image when the frame doesn't match the raw image dimensions
|
|
40
|
+
- `accessible` - Whether the element is an accessibility element
|
|
41
|
+
- `accessibilityLabel` - Text to be announced by screen readers
|
|
42
|
+
- `testID` - Used to locate this view in end-to-end tests
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
### With SVG Assets
|
|
47
|
+
|
|
48
|
+
The most common use case is with SVG components from the `@utilitywarehouse/hearth-svg-assets` package:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { View } from 'react-native';
|
|
52
|
+
import { ThemedImage } from '@utilitywarehouse/hearth-react-native';
|
|
53
|
+
import SpotBillingDark from '@utilitywarehouse/hearth-svg-assets/lib/spot-billing-dark.svg';
|
|
54
|
+
import SpotBillingLight from '@utilitywarehouse/hearth-svg-assets/lib/spot-billing-light.svg';
|
|
55
|
+
|
|
56
|
+
function MyComponent() {
|
|
57
|
+
return <ThemedImage light={SpotBillingLight} dark={SpotBillingDark} width={200} height={200} />;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
<Canvas of={ThemedImageStories.WithSpotIllustrations} />
|
|
62
|
+
|
|
63
|
+
### With Regular Images
|
|
64
|
+
|
|
65
|
+
You can also use regular image sources (not just SVG components):
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { ThemedImage } from '@utilitywarehouse/hearth-react-native';
|
|
69
|
+
import logoLight from './assets/logo-light.png';
|
|
70
|
+
import logoDark from './assets/logo-dark.png';
|
|
71
|
+
|
|
72
|
+
function MyComponent() {
|
|
73
|
+
return <ThemedImage light={logoLight} dark={logoDark} />;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<Canvas of={ThemedImageStories.WithRegularImages} />
|
|
78
|
+
|
|
79
|
+
### With Custom Sizes
|
|
80
|
+
|
|
81
|
+
When using SVG components, control the size by passing a `style` prop with `width` and `height` to the component:
|
|
82
|
+
|
|
83
|
+
<Canvas of={ThemedImageStories.WithCustomSize} />
|
|
84
|
+
|
|
85
|
+
### With Accessibility
|
|
86
|
+
|
|
87
|
+
Support screen readers and assistive technologies:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { View } from 'react-native';
|
|
91
|
+
import { ThemedImage } from '@utilitywarehouse/hearth-react-native';
|
|
92
|
+
import MascotEnergyDark from '@utilitywarehouse/hearth-svg-assets/lib/mascot-energy-dark.svg';
|
|
93
|
+
import MascotEnergyLight from '@utilitywarehouse/hearth-svg-assets/lib/mascot-energy-light.svg';
|
|
94
|
+
|
|
95
|
+
function MyComponent() {
|
|
96
|
+
return (
|
|
97
|
+
<ThemedImage
|
|
98
|
+
light={MascotEnergyLight}
|
|
99
|
+
dark={MascotEnergyDark}
|
|
100
|
+
width={150}
|
|
101
|
+
height={150}
|
|
102
|
+
accessible={true}
|
|
103
|
+
accessibilityLabel="Energy service mascot illustration"
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
<Canvas of={ThemedImageStories.WithAccessibility} />
|
|
110
|
+
|
|
111
|
+
## Usage with svg-assets Package
|
|
112
|
+
|
|
113
|
+
The `@utilitywarehouse/hearth-svg-assets` package provides themed illustrations in three categories:
|
|
114
|
+
|
|
115
|
+
### Spot Illustrations
|
|
116
|
+
|
|
117
|
+
Small, focused illustrations for specific concepts:
|
|
118
|
+
|
|
119
|
+
- `spot-billing` - Billing and payments
|
|
120
|
+
- `spot-celebratory` - Success and celebration
|
|
121
|
+
- `spot-error` - Error states
|
|
122
|
+
- `spot-help` - Help and support
|
|
123
|
+
- `spot-innovation` - New features
|
|
124
|
+
- `spot-savings` - Cost savings
|
|
125
|
+
- And more...
|
|
126
|
+
|
|
127
|
+
### Mascots
|
|
128
|
+
|
|
129
|
+
Service-specific character illustrations:
|
|
130
|
+
|
|
131
|
+
- `mascot-energy` - Energy service
|
|
132
|
+
- `mascot-broadband` - Broadband service
|
|
133
|
+
- `mascot-mobile` - Mobile service
|
|
134
|
+
- `mascot-insurance` - Insurance service
|
|
135
|
+
- `mascot-cashback` - Cashback rewards
|
|
136
|
+
|
|
137
|
+
### Scene Illustrations
|
|
138
|
+
|
|
139
|
+
Large, detailed illustrations for landing pages:
|
|
140
|
+
|
|
141
|
+
- `scene-energy` - Energy service scenes
|
|
142
|
+
- `scene-broadband` - Broadband service scenes
|
|
143
|
+
- `scene-mobile` - Mobile service scenes
|
|
144
|
+
- `scene-insurance` - Insurance service scenes
|
|
145
|
+
- `scene-bundle` - Service bundle scenes
|
|
146
|
+
|
|
147
|
+
All assets come in both `-light.svg` and `-dark.svg` variants.
|
|
148
|
+
|
|
149
|
+
## Integration with react-native-svg-transformer
|
|
150
|
+
|
|
151
|
+
To use SVG files as React components in React Native, you'll need `react-native-svg-transformer`:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Install dependencies
|
|
155
|
+
npm install react-native-svg react-native-svg-transformer --save-dev
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Configure your `metro.config.js`:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
|
162
|
+
|
|
163
|
+
const config = {
|
|
164
|
+
transformer: {
|
|
165
|
+
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
|
166
|
+
},
|
|
167
|
+
resolver: {
|
|
168
|
+
assetExts: getDefaultConfig(__dirname).resolver.assetExts.filter(ext => ext !== 'svg'),
|
|
169
|
+
sourceExts: [...getDefaultConfig(__dirname).resolver.sourceExts, 'svg'],
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Add TypeScript declarations (optional):
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// svg.d.ts
|
|
180
|
+
declare module '*.svg' {
|
|
181
|
+
import React from 'react';
|
|
182
|
+
import { SvgProps } from 'react-native-svg';
|
|
183
|
+
const content: React.FC<SvgProps>;
|
|
184
|
+
export default content;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Best Practices
|
|
189
|
+
|
|
190
|
+
1. **Always provide both variants** - Ensure you have both light and dark versions of your assets
|
|
191
|
+
2. **Use appropriate sizes** - Match illustration sizes to your design specifications
|
|
192
|
+
3. **Add accessibility labels** - Make your images accessible to screen readers
|
|
193
|
+
4. **Consider performance** - Large SVG files may impact performance on older devices
|
|
194
|
+
5. **Test in both modes** - Always test your themed images in both light and dark modes
|
|
195
|
+
|
|
196
|
+
## Troubleshooting
|
|
197
|
+
|
|
198
|
+
### SVG not displaying
|
|
199
|
+
|
|
200
|
+
Make sure you have configured `react-native-svg-transformer` correctly and that your SVG files are valid.
|
|
201
|
+
|
|
202
|
+
### Theme not switching
|
|
203
|
+
|
|
204
|
+
The component uses `useColorMode` hook which relies on the Unistyles theme system. Ensure your app is properly configured with Unistyles.
|
|
205
|
+
|
|
206
|
+
### TypeScript errors with SVG imports
|
|
207
|
+
|
|
208
|
+
Add the TypeScript declaration file for `.svg` modules as shown in the integration section above.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ComponentType, ReactElement } from 'react';
|
|
2
|
+
import type { ImageProps, ImageSourcePropType } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface ThemedImageProps extends Omit<ImageProps, 'source'> {
|
|
5
|
+
/**
|
|
6
|
+
* Image source or component to display in light mode
|
|
7
|
+
*/
|
|
8
|
+
light: ImageSourcePropType | ReactElement | ComponentType<any>;
|
|
9
|
+
/**
|
|
10
|
+
* Image source or component to display in dark mode
|
|
11
|
+
*/
|
|
12
|
+
dark: ImageSourcePropType | ReactElement | ComponentType<any>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default ThemedImageProps;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import MascotEnergyDark from '@utilitywarehouse/hearth-svg-assets/lib/mascot-energy-dark.svg';
|
|
3
|
+
import MascotEnergyLight from '@utilitywarehouse/hearth-svg-assets/lib/mascot-energy-light.svg';
|
|
4
|
+
import SceneBroadbandDark from '@utilitywarehouse/hearth-svg-assets/lib/scene-broadband-dark.svg';
|
|
5
|
+
import SceneBroadbandLight from '@utilitywarehouse/hearth-svg-assets/lib/scene-broadband-light.svg';
|
|
6
|
+
import SpotBillingDark from '@utilitywarehouse/hearth-svg-assets/lib/spot-billing-dark.svg';
|
|
7
|
+
import SpotBillingLight from '@utilitywarehouse/hearth-svg-assets/lib/spot-billing-light.svg';
|
|
8
|
+
import { SvgProps } from 'react-native-svg';
|
|
9
|
+
import pig from '../../../docs/assets/pigs.png';
|
|
10
|
+
import { Box } from '../Box';
|
|
11
|
+
import { ThemedImage } from './';
|
|
12
|
+
|
|
13
|
+
const meta: Meta<typeof ThemedImage> = {
|
|
14
|
+
title: 'Stories / ThemedImage',
|
|
15
|
+
component: ThemedImage,
|
|
16
|
+
argTypes: {},
|
|
17
|
+
parameters: {
|
|
18
|
+
docs: {
|
|
19
|
+
description: {
|
|
20
|
+
component:
|
|
21
|
+
'ThemedImage component that automatically switches between light and dark mode images or SVG components based on the current theme.',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
|
|
29
|
+
type Story = StoryObj<typeof ThemedImage>;
|
|
30
|
+
|
|
31
|
+
export const Playground: Story = {
|
|
32
|
+
args: {},
|
|
33
|
+
render: () => (
|
|
34
|
+
<Box gap="200">
|
|
35
|
+
<ThemedImage
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
light={<SpotBillingLight width={200} height={200} />}
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
dark={<SpotBillingDark width={200} height={200} />}
|
|
40
|
+
/>
|
|
41
|
+
</Box>
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const WithSpotIllustrations: Story = {
|
|
46
|
+
parameters: {
|
|
47
|
+
controls: { include: [] },
|
|
48
|
+
},
|
|
49
|
+
render: () => (
|
|
50
|
+
<Box flexDirection="row" flexWrap="wrap" gap="200">
|
|
51
|
+
<ThemedImage
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
light={<SpotBillingLight width={120} height={120} />}
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
dark={<SpotBillingDark width={120} height={120} />}
|
|
56
|
+
/>
|
|
57
|
+
</Box>
|
|
58
|
+
),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const WithMascots: Story = {
|
|
62
|
+
parameters: {
|
|
63
|
+
controls: { include: [] },
|
|
64
|
+
},
|
|
65
|
+
render: () => (
|
|
66
|
+
<Box flexDirection="row" flexWrap="wrap" gap="200" alignItems="center">
|
|
67
|
+
<ThemedImage
|
|
68
|
+
light={MascotEnergyLight as unknown as React.FC<SvgProps>}
|
|
69
|
+
dark={MascotEnergyDark as unknown as React.FC<SvgProps>}
|
|
70
|
+
width={120}
|
|
71
|
+
height={120}
|
|
72
|
+
/>
|
|
73
|
+
</Box>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const WithSceneIllustrations: Story = {
|
|
78
|
+
parameters: {
|
|
79
|
+
controls: { include: [] },
|
|
80
|
+
},
|
|
81
|
+
render: () => (
|
|
82
|
+
<Box gap="200">
|
|
83
|
+
<ThemedImage
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
light={<SceneBroadbandLight width={300} height={200} />}
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
dark={<SceneBroadbandDark width={300} height={200} />}
|
|
88
|
+
/>
|
|
89
|
+
</Box>
|
|
90
|
+
),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const WithRegularImages: Story = {
|
|
94
|
+
parameters: {
|
|
95
|
+
controls: { include: [] },
|
|
96
|
+
docs: {
|
|
97
|
+
description: {
|
|
98
|
+
story:
|
|
99
|
+
'ThemedImage also works with regular image sources (using `require` or `uri`), not just SVG components.',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
render: () => (
|
|
104
|
+
<Box gap="200">
|
|
105
|
+
<ThemedImage
|
|
106
|
+
light={{
|
|
107
|
+
uri: pig,
|
|
108
|
+
}}
|
|
109
|
+
dark={{
|
|
110
|
+
uri: pig,
|
|
111
|
+
}}
|
|
112
|
+
width={200}
|
|
113
|
+
height={200}
|
|
114
|
+
style={{ width: 200, height: 200, borderRadius: 8 }}
|
|
115
|
+
/>
|
|
116
|
+
</Box>
|
|
117
|
+
),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const WithCustomSize: Story = {
|
|
121
|
+
parameters: {
|
|
122
|
+
controls: { include: [] },
|
|
123
|
+
docs: {
|
|
124
|
+
description: {
|
|
125
|
+
story: 'When using SVG components, control their size with width and height props.',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
render: () => (
|
|
130
|
+
<Box flexDirection="row" flexWrap="wrap" gap="200" alignItems="center">
|
|
131
|
+
<ThemedImage
|
|
132
|
+
// @ts-ignore
|
|
133
|
+
light={<SpotBillingLight width={80} height={80} />}
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
dark={<SpotBillingDark width={80} height={80} />}
|
|
136
|
+
/>
|
|
137
|
+
<ThemedImage
|
|
138
|
+
// @ts-ignore
|
|
139
|
+
light={<SpotBillingLight width={120} height={120} />}
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
dark={<SpotBillingDark width={120} height={120} />}
|
|
142
|
+
/>
|
|
143
|
+
<ThemedImage
|
|
144
|
+
// @ts-ignore
|
|
145
|
+
light={<SpotBillingLight width={160} height={160} />}
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
dark={<SpotBillingDark width={160} height={160} />}
|
|
148
|
+
/>
|
|
149
|
+
</Box>
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const WithAccessibility: Story = {
|
|
154
|
+
parameters: {
|
|
155
|
+
controls: { include: [] },
|
|
156
|
+
docs: {
|
|
157
|
+
description: {
|
|
158
|
+
story:
|
|
159
|
+
'ThemedImage supports all standard Image accessibility props like `accessibilityLabel` and `accessible`.',
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
render: () => (
|
|
164
|
+
<Box gap="200">
|
|
165
|
+
<ThemedImage
|
|
166
|
+
// @ts-ignore
|
|
167
|
+
light={<MascotEnergyLight width={150} height={150} />}
|
|
168
|
+
// @ts-ignore
|
|
169
|
+
dark={<MascotEnergyDark width={150} height={150} />}
|
|
170
|
+
accessible={true}
|
|
171
|
+
accessibilityLabel="Energy service mascot illustration"
|
|
172
|
+
/>
|
|
173
|
+
</Box>
|
|
174
|
+
),
|
|
175
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isValidElement } from 'react';
|
|
2
|
+
import { Image } from 'react-native';
|
|
3
|
+
import useColorMode from '../../hooks/useColorMode';
|
|
4
|
+
import type ThemedImageProps from './ThemedImage.props';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ThemedImage component that displays different images or components based on the current theme
|
|
8
|
+
* @param light - Image source or SVG component to display in light mode
|
|
9
|
+
* @param dark - Image source or SVG component to display in dark mode
|
|
10
|
+
* @param ...rest - All other Image props including width/height for SVG components
|
|
11
|
+
*/
|
|
12
|
+
const ThemedImage = ({ light, dark, ...props }: ThemedImageProps) => {
|
|
13
|
+
const [colorMode] = useColorMode();
|
|
14
|
+
|
|
15
|
+
const source = colorMode === 'light' ? light : dark;
|
|
16
|
+
|
|
17
|
+
// If the source is a React element (like an SVG component), render it directly
|
|
18
|
+
if (isValidElement(source)) {
|
|
19
|
+
return source;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// If the source is a component type (function/class), instantiate it with props
|
|
23
|
+
if (typeof source === 'function') {
|
|
24
|
+
const Source = source as React.ComponentType<any>;
|
|
25
|
+
return <Source {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Otherwise, render as a regular Image with the source
|
|
29
|
+
return <Image source={source} {...props} />;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
ThemedImage.displayName = 'ThemedImage';
|
|
33
|
+
|
|
34
|
+
export default ThemedImage;
|
package/src/components/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './Card';
|
|
|
10
10
|
export * from './Carousel';
|
|
11
11
|
export * from './Center';
|
|
12
12
|
export * from './Checkbox';
|
|
13
|
+
export * from './Container';
|
|
13
14
|
export * from './CurrencyInput';
|
|
14
15
|
export * from './DatePicker';
|
|
15
16
|
export * from './DatePickerInput';
|
|
@@ -42,6 +43,7 @@ export * from './Spinner';
|
|
|
42
43
|
export * from './Switch';
|
|
43
44
|
export * from './Tabs';
|
|
44
45
|
export * from './Textarea';
|
|
46
|
+
export * from './ThemedImage';
|
|
45
47
|
export * from './ToggleButtonCard';
|
|
46
48
|
|
|
47
49
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { ViewStyle } from 'react-native';
|
|
3
|
-
import useTheme from './useTheme';
|
|
4
3
|
import { propStyleMapping, resolveThemeValue, themeStyleMapping, viewStyleProps } from '../utils';
|
|
4
|
+
import useTheme from './useTheme';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Type for the return value from useStyleProps
|
package/src/vite-env.d.ts
CHANGED