@utilitywarehouse/hearth-react-native 0.8.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +16 -0
- package/build/components/Banner/Banner.js +25 -6
- package/build/components/Banner/Banner.props.d.ts +2 -2
- package/build/components/BottomSheet/BottomSheetHandle.js +8 -0
- package/build/components/Menu/Menu.context.d.ts +5 -0
- package/build/components/Menu/Menu.context.js +9 -0
- package/build/components/Menu/Menu.d.ts +4 -0
- package/build/components/Menu/Menu.js +25 -0
- package/build/components/Menu/Menu.props.d.ts +21 -0
- package/build/components/Menu/Menu.props.js +1 -0
- package/build/components/Menu/MenuItem.d.ts +18 -0
- package/build/components/Menu/MenuItem.js +115 -0
- package/build/components/Menu/MenuItem.props.d.ts +27 -0
- package/build/components/Menu/MenuItem.props.js +1 -0
- package/build/components/Menu/MenuTrigger.d.ts +9 -0
- package/build/components/Menu/MenuTrigger.js +11 -0
- package/build/components/Menu/MenuTrigger.props.d.ts +12 -0
- package/build/components/Menu/MenuTrigger.props.js +1 -0
- package/build/components/Menu/index.d.ts +7 -0
- package/build/components/Menu/index.js +4 -0
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +32 -30
- package/build/components/Modal/Modal.props.d.ts +1 -0
- package/build/components/Modal/Modal.web.d.ts +1 -1
- package/build/components/Modal/Modal.web.js +25 -25
- package/build/components/PillGroup/Pill.d.ts +16 -0
- package/build/components/PillGroup/Pill.js +94 -0
- package/build/components/PillGroup/Pill.props.d.ts +10 -0
- package/build/components/PillGroup/Pill.props.js +1 -0
- package/build/components/PillGroup/PillGroup.context.d.ts +6 -0
- package/build/components/PillGroup/PillGroup.context.js +5 -0
- package/build/components/PillGroup/PillGroup.d.ts +5 -0
- package/build/components/PillGroup/PillGroup.js +34 -0
- package/build/components/PillGroup/PillGroup.props.d.ts +15 -0
- package/build/components/PillGroup/PillGroup.props.js +1 -0
- package/build/components/PillGroup/index.d.ts +4 -0
- package/build/components/PillGroup/index.js +2 -0
- package/build/components/Select/Select.js +2 -1
- package/build/components/Toast/Toast.context.d.ts +9 -0
- package/build/components/Toast/Toast.context.js +90 -0
- package/build/components/Toast/Toast.props.d.ts +29 -0
- package/build/components/Toast/Toast.props.js +1 -0
- package/build/components/Toast/ToastItem.d.ts +10 -0
- package/build/components/Toast/ToastItem.js +129 -0
- package/build/components/Toast/index.d.ts +3 -0
- package/build/components/Toast/index.js +2 -0
- package/build/components/index.d.ts +3 -0
- package/build/components/index.js +3 -0
- package/build/tokens/components/dark/checkbox.d.ts +3 -0
- package/build/tokens/components/dark/checkbox.js +3 -0
- package/build/tokens/components/dark/index.d.ts +3 -1
- package/build/tokens/components/dark/index.js +3 -1
- package/build/tokens/components/dark/input.d.ts +9 -0
- package/build/tokens/components/dark/input.js +9 -0
- package/build/tokens/components/dark/modal.d.ts +7 -4
- package/build/tokens/components/dark/modal.js +7 -4
- package/build/tokens/components/dark/radio.d.ts +3 -0
- package/build/tokens/components/dark/radio.js +3 -0
- package/build/tokens/components/dark/rating.d.ts +8 -0
- package/build/tokens/components/dark/rating.js +7 -0
- package/build/tokens/components/dark/table.d.ts +2 -3
- package/build/tokens/components/dark/table.js +2 -3
- package/build/tokens/components/dark/time-picker.d.ts +29 -0
- package/build/tokens/components/dark/time-picker.js +28 -0
- package/build/tokens/components/dark/timeline.d.ts +27 -0
- package/build/tokens/components/dark/timeline.js +26 -0
- package/build/tokens/components/dark/toast.d.ts +6 -2
- package/build/tokens/components/dark/toast.js +6 -2
- package/build/tokens/components/light/checkbox.d.ts +3 -0
- package/build/tokens/components/light/checkbox.js +3 -0
- package/build/tokens/components/light/index.d.ts +3 -1
- package/build/tokens/components/light/index.js +3 -1
- package/build/tokens/components/light/input.d.ts +9 -0
- package/build/tokens/components/light/input.js +9 -0
- package/build/tokens/components/light/modal.d.ts +7 -4
- package/build/tokens/components/light/modal.js +7 -4
- package/build/tokens/components/light/radio.d.ts +3 -0
- package/build/tokens/components/light/radio.js +3 -0
- package/build/tokens/components/light/rating.d.ts +8 -0
- package/build/tokens/components/light/rating.js +7 -0
- package/build/tokens/components/light/table.d.ts +2 -3
- package/build/tokens/components/light/table.js +2 -3
- package/build/tokens/components/light/time-picker.d.ts +29 -0
- package/build/tokens/components/light/time-picker.js +28 -0
- package/build/tokens/components/light/timeline.d.ts +27 -0
- package/build/tokens/components/light/timeline.js +26 -0
- package/build/tokens/components/light/toast.d.ts +6 -2
- package/build/tokens/components/light/toast.js +6 -2
- package/docs/assets/toast-ios.MP4 +0 -0
- package/docs/components/AllComponents.web.tsx +59 -0
- package/docs/components/BackToTopButton.tsx +1 -1
- package/package.json +4 -4
- package/src/components/Banner/Banner.docs.mdx +19 -10
- package/src/components/Banner/Banner.props.ts +2 -2
- package/src/components/Banner/Banner.stories.tsx +1 -4
- package/src/components/Banner/Banner.tsx +47 -7
- package/src/components/BottomSheet/BottomSheetHandle.tsx +12 -0
- package/src/components/DatePickerInput/DatePickerInput.docs.mdx +1 -1
- package/src/components/Menu/Menu.context.ts +15 -0
- package/src/components/Menu/Menu.docs.mdx +158 -0
- package/src/components/Menu/Menu.props.ts +24 -0
- package/src/components/Menu/Menu.stories.tsx +292 -0
- package/src/components/Menu/Menu.tsx +54 -0
- package/src/components/Menu/MenuItem.props.ts +29 -0
- package/src/components/Menu/MenuItem.tsx +145 -0
- package/src/components/Menu/MenuTrigger.props.ts +14 -0
- package/src/components/Menu/MenuTrigger.tsx +20 -0
- package/src/components/Menu/index.ts +7 -0
- package/src/components/Modal/Modal.docs.mdx +34 -5
- package/src/components/Modal/Modal.props.ts +1 -0
- package/src/components/Modal/Modal.stories.tsx +46 -0
- package/src/components/Modal/Modal.tsx +37 -33
- package/src/components/Modal/Modal.web.tsx +27 -27
- package/src/components/PillGroup/Pill.props.ts +13 -0
- package/src/components/PillGroup/Pill.tsx +120 -0
- package/src/components/PillGroup/PillGroup.context.tsx +12 -0
- package/src/components/PillGroup/PillGroup.docs.mdx +96 -0
- package/src/components/PillGroup/PillGroup.props.ts +22 -0
- package/src/components/PillGroup/PillGroup.stories.tsx +159 -0
- package/src/components/PillGroup/PillGroup.tsx +66 -0
- package/src/components/PillGroup/index.ts +4 -0
- package/src/components/Select/Select.tsx +2 -0
- package/src/components/Toast/Toast.context.tsx +118 -0
- package/src/components/Toast/Toast.docs.mdx +164 -0
- package/src/components/Toast/Toast.props.ts +33 -0
- package/src/components/Toast/Toast.stories.tsx +356 -0
- package/src/components/Toast/ToastItem.tsx +200 -0
- package/src/components/Toast/index.ts +3 -0
- package/src/components/index.ts +3 -0
- package/src/tokens/components/dark/checkbox.ts +3 -0
- package/src/tokens/components/dark/index.ts +3 -1
- package/src/tokens/components/dark/input.ts +9 -0
- package/src/tokens/components/dark/modal.ts +7 -4
- package/src/tokens/components/dark/radio.ts +3 -0
- package/src/tokens/components/dark/rating.ts +8 -0
- package/src/tokens/components/dark/table.ts +2 -3
- package/src/tokens/components/dark/time-picker.ts +29 -0
- package/src/tokens/components/dark/timeline.ts +27 -0
- package/src/tokens/components/dark/toast.ts +6 -2
- package/src/tokens/components/light/checkbox.ts +3 -0
- package/src/tokens/components/light/index.ts +3 -1
- package/src/tokens/components/light/input.ts +9 -0
- package/src/tokens/components/light/modal.ts +7 -4
- package/src/tokens/components/light/radio.ts +3 -0
- package/src/tokens/components/light/rating.ts +8 -0
- package/src/tokens/components/light/table.ts +2 -3
- package/src/tokens/components/light/time-picker.ts +29 -0
- package/src/tokens/components/light/timeline.ts +27 -0
- package/src/tokens/components/light/toast.ts +6 -2
- package/build/tokens/components/dark/dialog.d.ts +0 -25
- package/build/tokens/components/dark/dialog.js +0 -24
- package/build/tokens/components/light/dialog.d.ts +0 -25
- package/build/tokens/components/light/dialog.js +0 -24
- package/src/tokens/components/dark/dialog.ts +0 -25
- package/src/tokens/components/light/dialog.ts +0 -25
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-native';
|
|
2
|
+
import {
|
|
3
|
+
InfoSmallIcon,
|
|
4
|
+
TickSmallIcon,
|
|
5
|
+
TrashSmallIcon,
|
|
6
|
+
WarningSmallIcon,
|
|
7
|
+
} from '@utilitywarehouse/hearth-react-native-icons';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { Platform, View } from 'react-native';
|
|
10
|
+
import { Button } from '../Button';
|
|
11
|
+
import { ToastProvider, useToast } from './';
|
|
12
|
+
|
|
13
|
+
const meta: Meta = {
|
|
14
|
+
title: 'Stories / Toast',
|
|
15
|
+
argTypes: {},
|
|
16
|
+
};
|
|
17
|
+
export default meta;
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
const ViewWrap = ({ children }: { children: React.ReactNode }) => (
|
|
21
|
+
<View
|
|
22
|
+
style={
|
|
23
|
+
Platform.OS === 'web'
|
|
24
|
+
? { width: 400, height: 400, position: 'relative', overflow: 'visible' }
|
|
25
|
+
: { flex: 1 }
|
|
26
|
+
}
|
|
27
|
+
>
|
|
28
|
+
<ToastProvider>{children}</ToastProvider>
|
|
29
|
+
</View>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const Playground: Story = {
|
|
33
|
+
render: () => {
|
|
34
|
+
return (
|
|
35
|
+
<ViewWrap>
|
|
36
|
+
<PlaygroundDemo />
|
|
37
|
+
</ViewWrap>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const PlaygroundDemo = () => {
|
|
43
|
+
const { addToast } = useToast();
|
|
44
|
+
|
|
45
|
+
const showBasic = () => {
|
|
46
|
+
addToast({ text: 'This is a simple toast message' });
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
51
|
+
<Button onPress={showBasic}>Show Toast</Button>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const BasicToastDemo = () => {
|
|
57
|
+
const { addToast } = useToast();
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
61
|
+
<Button onPress={() => addToast({ text: 'Settings saved successfully' })}>
|
|
62
|
+
Show Basic Toast
|
|
63
|
+
</Button>
|
|
64
|
+
</View>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const BasicToast: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<ViewWrap>
|
|
71
|
+
<BasicToastDemo />
|
|
72
|
+
</ViewWrap>
|
|
73
|
+
),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const WithIconDemo = () => {
|
|
77
|
+
const { addToast } = useToast();
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
81
|
+
<Button
|
|
82
|
+
onPress={() =>
|
|
83
|
+
addToast({
|
|
84
|
+
text: 'Changes saved',
|
|
85
|
+
icon: TickSmallIcon,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
>
|
|
89
|
+
Show Success Toast
|
|
90
|
+
</Button>
|
|
91
|
+
<Button
|
|
92
|
+
onPress={() =>
|
|
93
|
+
addToast({
|
|
94
|
+
text: 'Important information',
|
|
95
|
+
icon: InfoSmallIcon,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
>
|
|
99
|
+
Show Info Toast
|
|
100
|
+
</Button>
|
|
101
|
+
<Button
|
|
102
|
+
onPress={() =>
|
|
103
|
+
addToast({
|
|
104
|
+
text: 'Please review your input',
|
|
105
|
+
icon: WarningSmallIcon,
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
>
|
|
109
|
+
Show Warning Toast
|
|
110
|
+
</Button>
|
|
111
|
+
<Button
|
|
112
|
+
onPress={() =>
|
|
113
|
+
addToast({
|
|
114
|
+
text: 'Item deleted',
|
|
115
|
+
icon: TrashSmallIcon,
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
>
|
|
119
|
+
Show Destructive Toast
|
|
120
|
+
</Button>
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const WithIcon: Story = {
|
|
126
|
+
render: () => (
|
|
127
|
+
<ViewWrap>
|
|
128
|
+
<WithIconDemo />
|
|
129
|
+
</ViewWrap>
|
|
130
|
+
),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const WithActionDemo = () => {
|
|
134
|
+
const { addToast } = useToast();
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<View style={{ gap: 12, padding: 16, flex: 1 }}>
|
|
138
|
+
<Button
|
|
139
|
+
onPress={() =>
|
|
140
|
+
addToast({
|
|
141
|
+
text: 'File uploaded successfully',
|
|
142
|
+
actionText: 'View file',
|
|
143
|
+
onPress: () => console.log('View clicked'),
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
>
|
|
147
|
+
Show Toast With Action
|
|
148
|
+
</Button>
|
|
149
|
+
<Button
|
|
150
|
+
onPress={() =>
|
|
151
|
+
addToast({
|
|
152
|
+
text: 'Changes saved',
|
|
153
|
+
icon: TickSmallIcon,
|
|
154
|
+
actionText: 'Undo',
|
|
155
|
+
onPress: () => console.log('Undo clicked'),
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
>
|
|
159
|
+
Show With Icon & Action
|
|
160
|
+
</Button>
|
|
161
|
+
</View>
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export const WithAction: Story = {
|
|
166
|
+
render: () => (
|
|
167
|
+
<ViewWrap>
|
|
168
|
+
<WithActionDemo />
|
|
169
|
+
</ViewWrap>
|
|
170
|
+
),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const CustomDurationDemo = () => {
|
|
174
|
+
const { addToast } = useToast();
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
178
|
+
<Button
|
|
179
|
+
onPress={() =>
|
|
180
|
+
addToast({
|
|
181
|
+
text: 'Quick message (2 seconds)',
|
|
182
|
+
duration: 2000,
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
>
|
|
186
|
+
2 Second Toast
|
|
187
|
+
</Button>
|
|
188
|
+
<Button
|
|
189
|
+
onPress={() =>
|
|
190
|
+
addToast({
|
|
191
|
+
text: 'Default duration (6 seconds)',
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
>
|
|
195
|
+
Default Duration
|
|
196
|
+
</Button>
|
|
197
|
+
<Button
|
|
198
|
+
onPress={() =>
|
|
199
|
+
addToast({
|
|
200
|
+
text: 'Long message (10 seconds)',
|
|
201
|
+
duration: 10000,
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
>
|
|
205
|
+
10 Second Toast
|
|
206
|
+
</Button>
|
|
207
|
+
<Button
|
|
208
|
+
onPress={() =>
|
|
209
|
+
addToast({
|
|
210
|
+
text: 'Persistent (no auto-dismiss)',
|
|
211
|
+
duration: 0,
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
>
|
|
215
|
+
No Auto-Dismiss
|
|
216
|
+
</Button>
|
|
217
|
+
</View>
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export const CustomDuration: Story = {
|
|
222
|
+
render: () => (
|
|
223
|
+
<ViewWrap>
|
|
224
|
+
<CustomDurationDemo />
|
|
225
|
+
</ViewWrap>
|
|
226
|
+
),
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const StackedToastsDemo = () => {
|
|
230
|
+
const { addToast } = useToast();
|
|
231
|
+
|
|
232
|
+
const showMultiple = () => {
|
|
233
|
+
addToast({ text: 'First toast', icon: TickSmallIcon });
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
addToast({ text: 'Second toast', icon: InfoSmallIcon });
|
|
236
|
+
}, 500);
|
|
237
|
+
setTimeout(() => {
|
|
238
|
+
addToast({ text: 'Third toast', icon: TickSmallIcon });
|
|
239
|
+
}, 1000);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
244
|
+
<Button onPress={showMultiple}>Show Multiple Toasts</Button>
|
|
245
|
+
<Button
|
|
246
|
+
onPress={() =>
|
|
247
|
+
addToast({
|
|
248
|
+
text: 'Another toast message',
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
>
|
|
252
|
+
Add Another
|
|
253
|
+
</Button>
|
|
254
|
+
</View>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const StackedToasts: Story = {
|
|
259
|
+
render: () => (
|
|
260
|
+
<ViewWrap>
|
|
261
|
+
<StackedToastsDemo />
|
|
262
|
+
</ViewWrap>
|
|
263
|
+
),
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const ProgrammaticDismissDemo = () => {
|
|
267
|
+
const { addToast, removeToast } = useToast();
|
|
268
|
+
const [lastId, setLastId] = React.useState<string | null>(null);
|
|
269
|
+
|
|
270
|
+
const showToast = () => {
|
|
271
|
+
const id = addToast({
|
|
272
|
+
text: 'This toast can be dismissed programmatically',
|
|
273
|
+
duration: 0, // Don't auto-dismiss
|
|
274
|
+
});
|
|
275
|
+
setLastId(id);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const dismissLast = () => {
|
|
279
|
+
if (lastId) {
|
|
280
|
+
removeToast(lastId);
|
|
281
|
+
setLastId(null);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
287
|
+
<Button onPress={showToast}>Show Persistent Toast</Button>
|
|
288
|
+
<Button onPress={dismissLast} disabled={!lastId}>
|
|
289
|
+
Dismiss Last Toast
|
|
290
|
+
</Button>
|
|
291
|
+
</View>
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const ProgrammaticDismiss: Story = {
|
|
296
|
+
render: () => (
|
|
297
|
+
<ViewWrap>
|
|
298
|
+
<ProgrammaticDismissDemo />
|
|
299
|
+
</ViewWrap>
|
|
300
|
+
),
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const DismissOptionsDemo = () => {
|
|
304
|
+
const { addToast } = useToast();
|
|
305
|
+
|
|
306
|
+
return (
|
|
307
|
+
<View style={{ gap: 12, padding: 16 }}>
|
|
308
|
+
<Button
|
|
309
|
+
onPress={() =>
|
|
310
|
+
addToast({
|
|
311
|
+
text: 'Toast without dismiss button',
|
|
312
|
+
showDismissIcon: false,
|
|
313
|
+
duration: 3000,
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
>
|
|
317
|
+
Hide Dismiss Icon
|
|
318
|
+
</Button>
|
|
319
|
+
<Button
|
|
320
|
+
onPress={() =>
|
|
321
|
+
addToast({
|
|
322
|
+
text: 'Tap anywhere to dismiss',
|
|
323
|
+
dismissOnPress: true,
|
|
324
|
+
onPress: () => console.log('Toast pressed'),
|
|
325
|
+
actionText: 'Action',
|
|
326
|
+
duration: 0,
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
>
|
|
330
|
+
Dismiss On Press
|
|
331
|
+
</Button>
|
|
332
|
+
<Button
|
|
333
|
+
onPress={() =>
|
|
334
|
+
addToast({
|
|
335
|
+
text: 'No dismiss button, press to dismiss',
|
|
336
|
+
showDismissIcon: false,
|
|
337
|
+
dismissOnPress: true,
|
|
338
|
+
onPress: () => console.log('Toast pressed'),
|
|
339
|
+
actionText: 'Action',
|
|
340
|
+
duration: 0,
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
>
|
|
344
|
+
Both Options
|
|
345
|
+
</Button>
|
|
346
|
+
</View>
|
|
347
|
+
);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
export const DismissOptions: Story = {
|
|
351
|
+
render: () => (
|
|
352
|
+
<ViewWrap>
|
|
353
|
+
<DismissOptionsDemo />
|
|
354
|
+
</ViewWrap>
|
|
355
|
+
),
|
|
356
|
+
};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { CloseSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle } from 'react';
|
|
3
|
+
import { AccessibilityInfo, Platform, Pressable, View } from 'react-native';
|
|
4
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
5
|
+
import Animated, {
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withSpring,
|
|
9
|
+
withTiming,
|
|
10
|
+
} from 'react-native-reanimated';
|
|
11
|
+
import { StyleSheet, withUnistyles } from 'react-native-unistyles';
|
|
12
|
+
import { scheduleOnRN } from 'react-native-worklets';
|
|
13
|
+
import { BodyText } from '../BodyText';
|
|
14
|
+
import { Icon } from '../Icon';
|
|
15
|
+
import { Link } from '../Link';
|
|
16
|
+
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
17
|
+
import type { ToastInstance } from './Toast.props';
|
|
18
|
+
|
|
19
|
+
const AnimatedView = Platform.OS === 'web' ? withUnistyles(Animated.View) : Animated.View;
|
|
20
|
+
|
|
21
|
+
export interface ToastItemHandle {
|
|
22
|
+
dismiss: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
toast: ToastInstance;
|
|
27
|
+
onClose: (id: string) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ToastItem = forwardRef<ToastItemHandle, Props>(({ toast, onClose }, ref) => {
|
|
31
|
+
const translateY = useSharedValue(30);
|
|
32
|
+
const opacity = useSharedValue(0);
|
|
33
|
+
const gestureTranslateY = useSharedValue(0);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
translateY.value = withTiming(0, { duration: 300 });
|
|
37
|
+
opacity.value = withTiming(1, { duration: 300 });
|
|
38
|
+
|
|
39
|
+
// Announce toast content to screen readers
|
|
40
|
+
// Use a slight delay to ensure iOS VoiceOver picks it up
|
|
41
|
+
const timer = setTimeout(() => {
|
|
42
|
+
const message = typeof toast.text === 'string' ? toast.text : 'Toast notification';
|
|
43
|
+
const announcement = toast.actionText ? `${message}, ${toast.actionText}` : message;
|
|
44
|
+
AccessibilityInfo.announceForAccessibility(announcement);
|
|
45
|
+
}, 100);
|
|
46
|
+
|
|
47
|
+
return () => clearTimeout(timer);
|
|
48
|
+
}, [toast.text, toast.actionText]);
|
|
49
|
+
|
|
50
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
51
|
+
transform: [{ translateY: translateY.value + gestureTranslateY.value }],
|
|
52
|
+
opacity: opacity.value,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
const handleDismiss = (fromGesture = false) => {
|
|
56
|
+
'worklet';
|
|
57
|
+
// Call onDismiss callback if provided
|
|
58
|
+
if (toast.onDismiss) {
|
|
59
|
+
scheduleOnRN(toast.onDismiss);
|
|
60
|
+
}
|
|
61
|
+
// animate out then call onClose
|
|
62
|
+
if (!fromGesture) {
|
|
63
|
+
gestureTranslateY.value = 0;
|
|
64
|
+
}
|
|
65
|
+
// Continue from current position and animate further down
|
|
66
|
+
translateY.value = withTiming(100, { duration: 250 });
|
|
67
|
+
opacity.value = withTiming(0, { duration: 250 }, finished => {
|
|
68
|
+
if (finished) scheduleOnRN(onClose, toast.id);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
useImperativeHandle(ref, () => ({
|
|
73
|
+
dismiss: handleDismiss,
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
const panGesture = Gesture.Pan()
|
|
77
|
+
.onUpdate(event => {
|
|
78
|
+
// only allow downward drag
|
|
79
|
+
if (event.translationY > 0) {
|
|
80
|
+
gestureTranslateY.value = event.translationY;
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.onEnd(event => {
|
|
84
|
+
if (event.translationY > 30 || event.velocityY > 800) {
|
|
85
|
+
handleDismiss(true);
|
|
86
|
+
} else {
|
|
87
|
+
// spring back to original position
|
|
88
|
+
gestureTranslateY.value = withSpring(0, {
|
|
89
|
+
damping: 20,
|
|
90
|
+
stiffness: 300,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const IconComp = toast.icon;
|
|
96
|
+
const showDismissIcon = toast.showDismissIcon !== false; // default true
|
|
97
|
+
const dismissOnPress = toast.dismissOnPress === true; // default false
|
|
98
|
+
|
|
99
|
+
const handlePress = () => {
|
|
100
|
+
if (toast.onPress) {
|
|
101
|
+
toast.onPress();
|
|
102
|
+
if (dismissOnPress) {
|
|
103
|
+
handleDismiss(false);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const toastContent = (
|
|
109
|
+
<View style={styles.inner}>
|
|
110
|
+
<View style={styles.content}>
|
|
111
|
+
{IconComp ? (
|
|
112
|
+
<View style={styles.iconWrap}>
|
|
113
|
+
<Icon as={IconComp} style={styles.icon} />
|
|
114
|
+
</View>
|
|
115
|
+
) : null}
|
|
116
|
+
<BodyText inverted>{toast.text}</BodyText>
|
|
117
|
+
</View>
|
|
118
|
+
{toast.actionText ? (
|
|
119
|
+
<Link onPress={handlePress} showIcon={false} inverted>
|
|
120
|
+
{toast.actionText}
|
|
121
|
+
</Link>
|
|
122
|
+
) : null}
|
|
123
|
+
|
|
124
|
+
{showDismissIcon ? (
|
|
125
|
+
<View style={styles.actions}>
|
|
126
|
+
<UnstyledIconButton
|
|
127
|
+
icon={CloseSmallIcon}
|
|
128
|
+
accessibilityLabel="Dismiss"
|
|
129
|
+
inverted
|
|
130
|
+
onPress={() => handleDismiss(false)}
|
|
131
|
+
/>
|
|
132
|
+
</View>
|
|
133
|
+
) : null}
|
|
134
|
+
</View>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<GestureDetector gesture={panGesture}>
|
|
139
|
+
<AnimatedView
|
|
140
|
+
style={[styles.toast, animatedStyle]}
|
|
141
|
+
{...(Platform.OS === 'ios' && {
|
|
142
|
+
accessible: true,
|
|
143
|
+
accessibilityRole: 'alert',
|
|
144
|
+
accessibilityLiveRegion: 'polite',
|
|
145
|
+
})}
|
|
146
|
+
importantForAccessibility={Platform.OS === 'android' ? 'no-hide-descendants' : undefined}
|
|
147
|
+
>
|
|
148
|
+
{toast.onPress ? (
|
|
149
|
+
<Pressable onPress={handlePress} style={styles.pressable}>
|
|
150
|
+
{toastContent}
|
|
151
|
+
</Pressable>
|
|
152
|
+
) : (
|
|
153
|
+
toastContent
|
|
154
|
+
)}
|
|
155
|
+
</AnimatedView>
|
|
156
|
+
</GestureDetector>
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
ToastItem.displayName = 'ToastItem';
|
|
161
|
+
|
|
162
|
+
const styles = StyleSheet.create(theme => ({
|
|
163
|
+
toast: {
|
|
164
|
+
backgroundColor: theme.components.toast.backgroundColor,
|
|
165
|
+
borderRadius: theme.components.toast.borderRadius,
|
|
166
|
+
padding: theme.components.toast.padding,
|
|
167
|
+
width: '95%',
|
|
168
|
+
},
|
|
169
|
+
pressable: {
|
|
170
|
+
width: '100%',
|
|
171
|
+
},
|
|
172
|
+
inner: {
|
|
173
|
+
flexDirection: 'row',
|
|
174
|
+
alignItems: 'center',
|
|
175
|
+
width: '100%',
|
|
176
|
+
gap: theme.components.toast.gap,
|
|
177
|
+
},
|
|
178
|
+
iconWrap: {
|
|
179
|
+
width: 24,
|
|
180
|
+
height: 24,
|
|
181
|
+
justifyContent: 'center',
|
|
182
|
+
alignItems: 'center',
|
|
183
|
+
flexShrink: 0,
|
|
184
|
+
},
|
|
185
|
+
icon: {
|
|
186
|
+
color: theme.color.icon.inverted,
|
|
187
|
+
},
|
|
188
|
+
content: {
|
|
189
|
+
flex: 1,
|
|
190
|
+
gap: theme.components.toast.text.gap,
|
|
191
|
+
flexDirection: 'row',
|
|
192
|
+
alignItems: 'center',
|
|
193
|
+
minWidth: 0,
|
|
194
|
+
},
|
|
195
|
+
actions: {
|
|
196
|
+
flexShrink: 0,
|
|
197
|
+
},
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
export default ToastItem;
|
package/src/components/index.ts
CHANGED
|
@@ -36,7 +36,9 @@ export * from './Input';
|
|
|
36
36
|
export * from './Label';
|
|
37
37
|
export * from './Link';
|
|
38
38
|
export * from './List';
|
|
39
|
+
export * from './Menu';
|
|
39
40
|
export * from './Modal';
|
|
41
|
+
export * from './PillGroup';
|
|
40
42
|
export * from './ProgressStepper';
|
|
41
43
|
export * from './Radio';
|
|
42
44
|
export * from './RadioCard';
|
|
@@ -48,6 +50,7 @@ export * from './Switch';
|
|
|
48
50
|
export * from './Tabs';
|
|
49
51
|
export * from './Textarea';
|
|
50
52
|
export * from './ThemedImage';
|
|
53
|
+
export * from './Toast';
|
|
51
54
|
export * from './ToggleButtonCard';
|
|
52
55
|
|
|
53
56
|
export { FlatList, Image, KeyboardAvoidingView, ScrollView, SectionList, View } from 'react-native';
|
|
@@ -18,7 +18,6 @@ export { default as carouselControl } from './carousel-control';
|
|
|
18
18
|
export { default as checkbox } from './checkbox';
|
|
19
19
|
export { default as datePicker } from './date-picker';
|
|
20
20
|
export { default as descriptionList } from './description-list';
|
|
21
|
-
export { default as dialog } from './dialog';
|
|
22
21
|
export { default as divider } from './divider';
|
|
23
22
|
export { default as drawer } from './drawer';
|
|
24
23
|
export { default as expandableCard } from './expandable-card';
|
|
@@ -41,6 +40,7 @@ export { default as pill } from './pill';
|
|
|
41
40
|
export { default as progressBar } from './progress-bar';
|
|
42
41
|
export { default as progressStepper } from './progress-stepper';
|
|
43
42
|
export { default as radio } from './radio';
|
|
43
|
+
export { default as rating } from './rating';
|
|
44
44
|
export { default as sectionHeader } from './section-header';
|
|
45
45
|
export { default as segmentedControl } from './segmented-control';
|
|
46
46
|
export { default as select } from './select';
|
|
@@ -49,6 +49,8 @@ export { default as spinner } from './spinner';
|
|
|
49
49
|
export { default as switch } from './switch';
|
|
50
50
|
export { default as table } from './table';
|
|
51
51
|
export { default as tabs } from './tabs';
|
|
52
|
+
export { default as timePicker } from './time-picker';
|
|
53
|
+
export { default as timeline } from './timeline';
|
|
52
54
|
export { default as toast } from './toast';
|
|
53
55
|
export { default as toggleButton } from './toggle-button';
|
|
54
56
|
export { default as tooltip } from './tooltip';
|
|
@@ -14,6 +14,12 @@ export default {
|
|
|
14
14
|
gap: 8,
|
|
15
15
|
},
|
|
16
16
|
gap: 6,
|
|
17
|
+
heading: {
|
|
18
|
+
gap: 12,
|
|
19
|
+
text: {
|
|
20
|
+
gap: 0,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
17
23
|
height: 48,
|
|
18
24
|
label: {
|
|
19
25
|
gap: 6,
|
|
@@ -22,6 +28,9 @@ export default {
|
|
|
22
28
|
minWidth: 152,
|
|
23
29
|
paddingHorizontal: 16,
|
|
24
30
|
paddingVertical: 12,
|
|
31
|
+
stepper: {
|
|
32
|
+
gap: 4,
|
|
33
|
+
},
|
|
25
34
|
textArea: {
|
|
26
35
|
height: 96,
|
|
27
36
|
},
|
|
@@ -11,17 +11,20 @@ export default {
|
|
|
11
11
|
gap: 12,
|
|
12
12
|
},
|
|
13
13
|
gap: 24,
|
|
14
|
-
|
|
14
|
+
heading: {
|
|
15
|
+
gap: 24,
|
|
16
|
+
},
|
|
17
|
+
illustration: {
|
|
18
|
+
padding: 48,
|
|
19
|
+
},
|
|
20
|
+
padding: 24,
|
|
15
21
|
mobile: {
|
|
16
|
-
padding: 16,
|
|
17
22
|
width: 360,
|
|
18
23
|
},
|
|
19
24
|
tablet: {
|
|
20
|
-
padding: 48,
|
|
21
25
|
width: 504,
|
|
22
26
|
},
|
|
23
27
|
desktop: {
|
|
24
|
-
padding: 48,
|
|
25
28
|
width: 680,
|
|
26
29
|
},
|
|
27
30
|
} as const;
|
|
@@ -6,6 +6,7 @@ export default {
|
|
|
6
6
|
borderRadius: 16,
|
|
7
7
|
cell: {
|
|
8
8
|
borderWidth: 1,
|
|
9
|
+
minHeight: 48,
|
|
9
10
|
padding: 12,
|
|
10
11
|
},
|
|
11
12
|
emphasis: {
|
|
@@ -14,9 +15,7 @@ export default {
|
|
|
14
15
|
headerCell: {
|
|
15
16
|
borderWidth: 2,
|
|
16
17
|
gap: 8,
|
|
17
|
-
|
|
18
|
-
backgroundColor: '#30302c',
|
|
19
|
-
},
|
|
18
|
+
height: 56,
|
|
20
19
|
paddingHorizontal: 12,
|
|
21
20
|
paddingVertical: 16,
|
|
22
21
|
},
|