@utilitywarehouse/hearth-react-native 0.27.3 → 0.28.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 +18 -19
- package/CHANGELOG.md +110 -0
- package/build/components/Combobox/Combobox.context.d.ts +13 -0
- package/build/components/Combobox/Combobox.context.js +9 -0
- package/build/components/Combobox/Combobox.d.ts +6 -0
- package/build/components/Combobox/Combobox.js +246 -0
- package/build/components/Combobox/Combobox.props.d.ts +180 -0
- package/build/components/Combobox/Combobox.props.js +1 -0
- package/build/components/Combobox/ComboboxOption.d.ts +6 -0
- package/build/components/Combobox/ComboboxOption.js +56 -0
- package/build/components/Combobox/index.d.ts +4 -0
- package/build/components/Combobox/index.js +3 -0
- package/build/components/Modal/Modal.js +26 -42
- package/build/components/Modal/Modal.web.js +3 -3
- package/build/components/Pagination/Pagination.d.ts +6 -0
- package/build/components/Pagination/Pagination.js +125 -0
- package/build/components/Pagination/Pagination.props.d.ts +26 -0
- package/build/components/Pagination/Pagination.props.js +1 -0
- package/build/components/Pagination/Pagination.utils.d.ts +2 -0
- package/build/components/Pagination/Pagination.utils.js +20 -0
- package/build/components/Pagination/Pagination.utils.test.d.ts +1 -0
- package/build/components/Pagination/Pagination.utils.test.js +16 -0
- package/build/components/Pagination/index.d.ts +2 -0
- package/build/components/Pagination/index.js +1 -0
- package/build/components/SafeAreaView/SafeAreaView.d.ts +5 -0
- package/build/components/SafeAreaView/SafeAreaView.js +117 -0
- package/build/components/SafeAreaView/SafeAreaView.props.d.ts +17 -0
- package/build/components/SafeAreaView/SafeAreaView.props.js +1 -0
- package/build/components/SafeAreaView/index.d.ts +2 -0
- package/build/components/SafeAreaView/index.js +1 -0
- package/build/components/Select/Select.js +3 -2
- package/build/components/Table/Table.context.d.ts +12 -0
- package/build/components/Table/Table.context.js +9 -0
- package/build/components/Table/Table.d.ts +6 -0
- package/build/components/Table/Table.js +71 -0
- package/build/components/Table/Table.props.d.ts +56 -0
- package/build/components/Table/Table.props.js +1 -0
- package/build/components/Table/Table.utils.d.ts +5 -0
- package/build/components/Table/Table.utils.js +48 -0
- package/build/components/Table/Table.utils.test.d.ts +1 -0
- package/build/components/Table/Table.utils.test.js +71 -0
- package/build/components/Table/TableBody.d.ts +6 -0
- package/build/components/Table/TableBody.js +16 -0
- package/build/components/Table/TableCell.d.ts +10 -0
- package/build/components/Table/TableCell.js +44 -0
- package/build/components/Table/TableHeader.d.ts +6 -0
- package/build/components/Table/TableHeader.js +24 -0
- package/build/components/Table/TableHeaderCell.d.ts +10 -0
- package/build/components/Table/TableHeaderCell.js +97 -0
- package/build/components/Table/TablePagination.d.ts +6 -0
- package/build/components/Table/TablePagination.js +7 -0
- package/build/components/Table/TableRow.d.ts +8 -0
- package/build/components/Table/TableRow.js +25 -0
- package/build/components/Table/index.d.ts +8 -0
- package/build/components/Table/index.js +7 -0
- package/build/components/Timeline/Timeline.d.ts +6 -0
- package/build/components/Timeline/Timeline.js +34 -0
- package/build/components/Timeline/Timeline.props.d.ts +47 -0
- package/build/components/Timeline/Timeline.props.js +1 -0
- package/build/components/Timeline/TimelineItem.d.ts +6 -0
- package/build/components/Timeline/TimelineItem.js +235 -0
- package/build/components/Timeline/index.d.ts +3 -0
- package/build/components/Timeline/index.js +2 -0
- package/build/components/index.d.ts +5 -0
- package/build/components/index.js +5 -0
- package/build/tokens/components/dark/timeline.d.ts +2 -2
- package/build/tokens/components/dark/timeline.js +2 -2
- package/docs/components/AllComponents.web.tsx +106 -23
- package/docs/llm-docs/unistyles-llms-full.txt +1132 -534
- package/docs/llm-docs/unistyles-llms-small.txt +37 -37
- package/package.json +2 -2
- package/src/components/Combobox/Combobox.context.ts +26 -0
- package/src/components/Combobox/Combobox.docs.mdx +277 -0
- package/src/components/Combobox/Combobox.figma.tsx +60 -0
- package/src/components/Combobox/Combobox.props.ts +187 -0
- package/src/components/Combobox/Combobox.stories.tsx +233 -0
- package/src/components/Combobox/Combobox.tsx +446 -0
- package/src/components/Combobox/ComboboxOption.tsx +100 -0
- package/src/components/Combobox/index.ts +9 -0
- package/src/components/Modal/Modal.tsx +52 -74
- package/src/components/Modal/Modal.web.tsx +3 -3
- package/src/components/Pagination/Pagination.docs.mdx +99 -0
- package/src/components/Pagination/Pagination.figma.tsx +20 -0
- package/src/components/Pagination/Pagination.props.ts +28 -0
- package/src/components/Pagination/Pagination.stories.tsx +88 -0
- package/src/components/Pagination/Pagination.tsx +248 -0
- package/src/components/Pagination/Pagination.utils.test.ts +20 -0
- package/src/components/Pagination/Pagination.utils.ts +37 -0
- package/src/components/Pagination/index.ts +2 -0
- package/src/components/SafeAreaView/SafeAreaView.props.ts +20 -0
- package/src/components/SafeAreaView/SafeAreaView.tsx +173 -0
- package/src/components/SafeAreaView/index.ts +2 -0
- package/src/components/Select/Select.tsx +30 -27
- package/src/components/Table/Table.context.tsx +23 -0
- package/src/components/Table/Table.docs.mdx +239 -0
- package/src/components/Table/Table.figma.tsx +65 -0
- package/src/components/Table/Table.props.ts +65 -0
- package/src/components/Table/Table.stories.tsx +399 -0
- package/src/components/Table/Table.tsx +127 -0
- package/src/components/Table/Table.utils.test.ts +82 -0
- package/src/components/Table/Table.utils.ts +72 -0
- package/src/components/Table/TableBody.tsx +25 -0
- package/src/components/Table/TableCell.tsx +67 -0
- package/src/components/Table/TableHeader.tsx +41 -0
- package/src/components/Table/TableHeaderCell.tsx +136 -0
- package/src/components/Table/TablePagination.tsx +10 -0
- package/src/components/Table/TableRow.tsx +42 -0
- package/src/components/Table/index.ts +16 -0
- package/src/components/Timeline/Timeline.docs.mdx +177 -0
- package/src/components/Timeline/Timeline.figma.tsx +89 -0
- package/src/components/Timeline/Timeline.props.ts +51 -0
- package/src/components/Timeline/Timeline.stories.tsx +102 -0
- package/src/components/Timeline/Timeline.tsx +48 -0
- package/src/components/Timeline/TimelineItem.tsx +293 -0
- package/src/components/Timeline/index.ts +9 -0
- package/src/components/index.ts +5 -0
- package/src/tokens/components/dark/timeline.ts +2 -2
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { TickSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
2
|
+
import { Pressable, View } from 'react-native';
|
|
3
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
4
|
+
import { BodyText } from '../BodyText';
|
|
5
|
+
import { Icon } from '../Icon';
|
|
6
|
+
import { useComboboxContext } from './Combobox.context';
|
|
7
|
+
import { ComboboxOptionProps } from './Combobox.props';
|
|
8
|
+
|
|
9
|
+
const ComboboxOption = ({
|
|
10
|
+
label,
|
|
11
|
+
value,
|
|
12
|
+
leadingIcon: LeftIcon,
|
|
13
|
+
trailingIcon: RightIcon,
|
|
14
|
+
selected,
|
|
15
|
+
disabled,
|
|
16
|
+
onPress,
|
|
17
|
+
}: ComboboxOptionProps) => {
|
|
18
|
+
const { selectedValue, selectOption } = useComboboxContext();
|
|
19
|
+
const isSelected = selected !== undefined ? selected : selectedValue === value;
|
|
20
|
+
|
|
21
|
+
styles.useVariants({ disabled });
|
|
22
|
+
|
|
23
|
+
const handlePress = () => {
|
|
24
|
+
if (disabled) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (onPress) {
|
|
29
|
+
onPress(value);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
selectOption({ label, value });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Pressable
|
|
38
|
+
onPress={handlePress}
|
|
39
|
+
disabled={disabled}
|
|
40
|
+
style={({ pressed }) => [styles.container, pressed && styles.pressed]}
|
|
41
|
+
>
|
|
42
|
+
{!!LeftIcon && (
|
|
43
|
+
<View>
|
|
44
|
+
<Icon as={LeftIcon} style={styles.icon} />
|
|
45
|
+
</View>
|
|
46
|
+
)}
|
|
47
|
+
|
|
48
|
+
<View style={styles.labelContainer}>
|
|
49
|
+
<BodyText>{label}</BodyText>
|
|
50
|
+
</View>
|
|
51
|
+
|
|
52
|
+
{isSelected && (
|
|
53
|
+
<View>
|
|
54
|
+
<Icon as={TickSmallIcon} style={styles.icon} />
|
|
55
|
+
</View>
|
|
56
|
+
)}
|
|
57
|
+
{!!RightIcon && !isSelected && (
|
|
58
|
+
<View>
|
|
59
|
+
<Icon as={RightIcon} style={styles.icon} />
|
|
60
|
+
</View>
|
|
61
|
+
)}
|
|
62
|
+
</Pressable>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const styles = StyleSheet.create(theme => ({
|
|
67
|
+
container: {
|
|
68
|
+
flexDirection: 'row',
|
|
69
|
+
alignItems: 'center',
|
|
70
|
+
gap: theme.components.select.dropdown.item.gap,
|
|
71
|
+
borderRadius: theme.components.select.dropdown.item.borderRadius,
|
|
72
|
+
paddingVertical: theme.components.select.dropdown.item.padding,
|
|
73
|
+
paddingHorizontal: theme.components.select.dropdown.padding,
|
|
74
|
+
variants: {
|
|
75
|
+
disabled: {
|
|
76
|
+
true: {
|
|
77
|
+
opacity: theme.opacity.disabled,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
_web: {
|
|
82
|
+
_hover: {
|
|
83
|
+
backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
icon: {
|
|
88
|
+
color: theme.color.interactive.functional.foreground.subtle,
|
|
89
|
+
},
|
|
90
|
+
pressed: {
|
|
91
|
+
backgroundColor: theme.color.interactive.functional.surface.subtle.active,
|
|
92
|
+
},
|
|
93
|
+
labelContainer: {
|
|
94
|
+
flex: 1,
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
ComboboxOption.displayName = 'ComboboxOption';
|
|
99
|
+
|
|
100
|
+
export default ComboboxOption;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as Combobox } from './Combobox';
|
|
2
|
+
export { ComboboxContext, useComboboxContext } from './Combobox.context';
|
|
3
|
+
export { default as ComboboxOption } from './ComboboxOption';
|
|
4
|
+
export type {
|
|
5
|
+
default as ComboboxProps,
|
|
6
|
+
ComboboxOptionItemProps,
|
|
7
|
+
ComboboxOptionProps,
|
|
8
|
+
ComboboxRenderContentProps,
|
|
9
|
+
} from './Combobox.props';
|
|
@@ -6,15 +6,8 @@ import {
|
|
|
6
6
|
} from '@gorhom/bottom-sheet';
|
|
7
7
|
import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
|
|
8
8
|
import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
9
|
-
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef
|
|
10
|
-
import {
|
|
11
|
-
AccessibilityInfo,
|
|
12
|
-
Dimensions,
|
|
13
|
-
Platform,
|
|
14
|
-
ScrollView,
|
|
15
|
-
View,
|
|
16
|
-
findNodeHandle,
|
|
17
|
-
} from 'react-native';
|
|
9
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
|
|
10
|
+
import { AccessibilityInfo, Platform, ScrollView, View, findNodeHandle } from 'react-native';
|
|
18
11
|
import Animated, {
|
|
19
12
|
Easing,
|
|
20
13
|
useAnimatedStyle,
|
|
@@ -29,6 +22,7 @@ import { BodyText } from '../BodyText';
|
|
|
29
22
|
import { BottomSheetModal, BottomSheetScrollView } from '../BottomSheet';
|
|
30
23
|
import { Button } from '../Button';
|
|
31
24
|
import { Heading } from '../Heading';
|
|
25
|
+
import { SafeAreaView } from '../SafeAreaView';
|
|
32
26
|
import { Spinner } from '../Spinner';
|
|
33
27
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
34
28
|
import ModalProps from './Modal.props';
|
|
@@ -69,16 +63,6 @@ const Modal = ({
|
|
|
69
63
|
const pretendContentTranslateY = useSharedValue(20);
|
|
70
64
|
const isBrandBackground = background === 'brand';
|
|
71
65
|
|
|
72
|
-
const [inNavModalHeight, setInNavModalHeight] = useState<number>();
|
|
73
|
-
|
|
74
|
-
const isNavModalFullScreen = useMemo(() => {
|
|
75
|
-
if (!inNavModalHeight || !inNavModal) return false;
|
|
76
|
-
|
|
77
|
-
const screenHeight = Dimensions.get('window').height;
|
|
78
|
-
|
|
79
|
-
return inNavModalHeight >= screenHeight;
|
|
80
|
-
}, [inNavModalHeight, inNavModal]);
|
|
81
|
-
|
|
82
66
|
const triggerCloseAnimation = useCallback(() => {
|
|
83
67
|
if (Platform.OS === 'android' && inNavModal) {
|
|
84
68
|
pretendContentTranslateY.value = withTiming(20, {
|
|
@@ -90,7 +74,7 @@ const Modal = ({
|
|
|
90
74
|
easing: Easing.in(Easing.quad),
|
|
91
75
|
});
|
|
92
76
|
}
|
|
93
|
-
}, [
|
|
77
|
+
}, [inNavModal, pretendContentTranslateY, backgroundOpacity]);
|
|
94
78
|
|
|
95
79
|
useImperativeHandle(ref, () => ({
|
|
96
80
|
...(bottomSheetModalRef.current as BottomSheetModal),
|
|
@@ -157,30 +141,30 @@ const Modal = ({
|
|
|
157
141
|
props.onChange?.(index, position, type);
|
|
158
142
|
};
|
|
159
143
|
|
|
160
|
-
const handleCloseButtonPress = () => {
|
|
144
|
+
const handleCloseButtonPress = useCallback(() => {
|
|
161
145
|
bottomSheetModalRef.current?.dismiss();
|
|
162
146
|
if (onPressCloseButton) {
|
|
163
147
|
onPressCloseButton();
|
|
164
148
|
}
|
|
165
|
-
};
|
|
149
|
+
}, [onPressCloseButton]);
|
|
166
150
|
|
|
167
|
-
const handlePrimaryButtonPress = () => {
|
|
151
|
+
const handlePrimaryButtonPress = useCallback(() => {
|
|
168
152
|
if (onPressPrimaryButton) {
|
|
169
153
|
onPressPrimaryButton();
|
|
170
154
|
}
|
|
171
155
|
if (closeOnPrimaryButtonPress) {
|
|
172
156
|
bottomSheetModalRef.current?.dismiss();
|
|
173
157
|
}
|
|
174
|
-
};
|
|
158
|
+
}, [closeOnPrimaryButtonPress, onPressPrimaryButton]);
|
|
175
159
|
|
|
176
|
-
const handleSecondaryButtonPress = () => {
|
|
160
|
+
const handleSecondaryButtonPress = useCallback(() => {
|
|
177
161
|
if (onPressSecondaryButton) {
|
|
178
162
|
onPressSecondaryButton();
|
|
179
163
|
}
|
|
180
164
|
if (closeOnSecondaryButtonPress) {
|
|
181
165
|
bottomSheetModalRef.current?.dismiss();
|
|
182
166
|
}
|
|
183
|
-
};
|
|
167
|
+
}, [closeOnSecondaryButtonPress, onPressSecondaryButton]);
|
|
184
168
|
|
|
185
169
|
const noButtons = !onPressPrimaryButton && !onPressSecondaryButton;
|
|
186
170
|
|
|
@@ -191,34 +175,45 @@ const Modal = ({
|
|
|
191
175
|
stickyFooter,
|
|
192
176
|
showHandle: props.showHandle,
|
|
193
177
|
background: isBrandBackground ? 'brand' : 'primary',
|
|
194
|
-
...(inNavModal && {
|
|
195
|
-
fullscreen: isNavModalFullScreen,
|
|
196
|
-
}),
|
|
197
178
|
});
|
|
198
179
|
|
|
199
|
-
const footer = (
|
|
200
|
-
|
|
201
|
-
{
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
180
|
+
const footer = useMemo(
|
|
181
|
+
() => (
|
|
182
|
+
<View style={styles.footer}>
|
|
183
|
+
{onPressPrimaryButton && primaryButtonText ? (
|
|
184
|
+
<Button
|
|
185
|
+
onPress={handlePrimaryButtonPress}
|
|
186
|
+
text={primaryButtonText}
|
|
187
|
+
inverted={isBrandBackground && inNavModal}
|
|
188
|
+
{...primaryButtonProps}
|
|
189
|
+
variant={(primaryButtonProps?.variant as 'solid') ?? 'solid'}
|
|
190
|
+
colorScheme={(primaryButtonProps?.colorScheme as 'highlight') ?? 'highlight'}
|
|
191
|
+
/>
|
|
192
|
+
) : null}
|
|
193
|
+
{onPressSecondaryButton && secondaryButtonText ? (
|
|
194
|
+
<Button
|
|
195
|
+
onPress={handleSecondaryButtonPress}
|
|
196
|
+
text={secondaryButtonText}
|
|
197
|
+
inverted={isBrandBackground && inNavModal}
|
|
198
|
+
{...secondaryButtonProps}
|
|
199
|
+
variant={(secondaryButtonProps?.variant as 'outline') ?? 'outline'}
|
|
200
|
+
colorScheme={(secondaryButtonProps?.colorScheme as 'functional') ?? 'functional'}
|
|
201
|
+
/>
|
|
202
|
+
) : null}
|
|
203
|
+
</View>
|
|
204
|
+
),
|
|
205
|
+
[
|
|
206
|
+
handlePrimaryButtonPress,
|
|
207
|
+
handleSecondaryButtonPress,
|
|
208
|
+
inNavModal,
|
|
209
|
+
isBrandBackground,
|
|
210
|
+
onPressPrimaryButton,
|
|
211
|
+
onPressSecondaryButton,
|
|
212
|
+
primaryButtonProps,
|
|
213
|
+
primaryButtonText,
|
|
214
|
+
secondaryButtonProps,
|
|
215
|
+
secondaryButtonText,
|
|
216
|
+
]
|
|
222
217
|
);
|
|
223
218
|
|
|
224
219
|
const InNavModalContainer = scrollable ? ScrollView : View;
|
|
@@ -327,21 +322,11 @@ const Modal = ({
|
|
|
327
322
|
<View style={styles.footerWrap}>{footer}</View>
|
|
328
323
|
</BottomSheetFooter>
|
|
329
324
|
),
|
|
330
|
-
[
|
|
331
|
-
onPressPrimaryButton,
|
|
332
|
-
primaryButtonText,
|
|
333
|
-
onPressSecondaryButton,
|
|
334
|
-
secondaryButtonText,
|
|
335
|
-
primaryButtonProps,
|
|
336
|
-
secondaryButtonProps,
|
|
337
|
-
]
|
|
325
|
+
[footer]
|
|
338
326
|
);
|
|
339
327
|
|
|
340
328
|
return inNavModal ? (
|
|
341
329
|
<View
|
|
342
|
-
onLayout={e => {
|
|
343
|
-
setInNavModalHeight(e.nativeEvent.layout.height);
|
|
344
|
-
}}
|
|
345
330
|
style={{
|
|
346
331
|
flex: 1,
|
|
347
332
|
backgroundColor: theme.color.background[isBrandBackground ? 'brand' : 'primary'],
|
|
@@ -355,7 +340,9 @@ const Modal = ({
|
|
|
355
340
|
<Animated.View
|
|
356
341
|
style={[styles.inNavModalContainer, Platform.OS === 'android' && animatedInNavModalStyle]}
|
|
357
342
|
>
|
|
358
|
-
<
|
|
343
|
+
<SafeAreaView edges={['top', 'bottom']} style={styles.inNavModalContent}>
|
|
344
|
+
{content}
|
|
345
|
+
</SafeAreaView>
|
|
359
346
|
</Animated.View>
|
|
360
347
|
</View>
|
|
361
348
|
) : (
|
|
@@ -486,7 +473,7 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
486
473
|
borderTopLeftRadius: theme.components.modal.borderRadius,
|
|
487
474
|
borderTopRightRadius: theme.components.modal.borderRadius,
|
|
488
475
|
backgroundColor: theme.color.surface.neutral.strong,
|
|
489
|
-
|
|
476
|
+
padding: theme.components.bottomSheet.padding,
|
|
490
477
|
variants: {
|
|
491
478
|
background: {
|
|
492
479
|
primary: {},
|
|
@@ -494,15 +481,6 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
494
481
|
backgroundColor: theme.color.background.brand,
|
|
495
482
|
},
|
|
496
483
|
},
|
|
497
|
-
fullscreen: {
|
|
498
|
-
true: {
|
|
499
|
-
padding: theme.components.bottomSheet.padding,
|
|
500
|
-
paddingTop: rt.insets.top,
|
|
501
|
-
},
|
|
502
|
-
false: {
|
|
503
|
-
padding: theme.components.bottomSheet.padding,
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
484
|
},
|
|
507
485
|
},
|
|
508
486
|
inNavModalFooterContainer: {
|
|
@@ -310,13 +310,13 @@ const styles = StyleSheet.create((theme, rt) => ({
|
|
|
310
310
|
borderTopRightRadius: theme.components.modal.borderRadius,
|
|
311
311
|
backgroundColor: theme.color.surface.neutral.strong,
|
|
312
312
|
gap: theme.components.modal.gap,
|
|
313
|
-
padding: theme.components.
|
|
313
|
+
padding: theme.components.bottomSheet.padding,
|
|
314
314
|
paddingBottom: theme.components.modal.padding + rt.insets.bottom,
|
|
315
315
|
},
|
|
316
316
|
androidContainer: {
|
|
317
317
|
height: rt.insets.top + 18,
|
|
318
|
-
paddingLeft: theme.components.
|
|
319
|
-
paddingRight: theme.components.
|
|
318
|
+
paddingLeft: theme.components.bottomSheet.padding,
|
|
319
|
+
paddingRight: theme.components.bottomSheet.padding,
|
|
320
320
|
justifyContent: 'flex-end',
|
|
321
321
|
},
|
|
322
322
|
pretendContent: {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Canvas, Controls, Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import { Box, Center, Pagination } from '../..';
|
|
3
|
+
import { BackToTopButton, UsageWrap, ViewFigmaButton } from '../../../docs/components';
|
|
4
|
+
import * as Stories from './Pagination.stories';
|
|
5
|
+
|
|
6
|
+
<Meta title="Components / Pagination" />
|
|
7
|
+
|
|
8
|
+
<BackToTopButton />
|
|
9
|
+
|
|
10
|
+
<ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=6416-5836&t=pZwKJYFo1y1QRQD1-4" />
|
|
11
|
+
|
|
12
|
+
# Pagination
|
|
13
|
+
|
|
14
|
+
Pagination helps users move between pages of content while keeping the current position visible. It supports both a full page list and a condensed “Page X of Y” layout.
|
|
15
|
+
|
|
16
|
+
- [Playground](#playground)
|
|
17
|
+
- [Usage](#usage)
|
|
18
|
+
- [Props](#props)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
|
|
21
|
+
## Playground
|
|
22
|
+
|
|
23
|
+
<Canvas of={Stories.Playground} />
|
|
24
|
+
|
|
25
|
+
<Controls of={Stories.Playground} />
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
<UsageWrap>
|
|
30
|
+
<Center>
|
|
31
|
+
<Box style={{ width: 520, maxWidth: '100%' }}>
|
|
32
|
+
<Pagination currentPage={3} totalPages={10} onPageChange={() => {}} />
|
|
33
|
+
</Box>
|
|
34
|
+
</Center>
|
|
35
|
+
</UsageWrap>
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { Pagination } from '@utilitywarehouse/hearth-react-native';
|
|
39
|
+
import { useState } from 'react';
|
|
40
|
+
|
|
41
|
+
const MyComponent = () => {
|
|
42
|
+
const [page, setPage] = useState(3);
|
|
43
|
+
|
|
44
|
+
return <Pagination currentPage={page} totalPages={10} onPageChange={setPage} />;
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Props
|
|
49
|
+
|
|
50
|
+
| Property | Type | Description | Default |
|
|
51
|
+
| ----------------- | ------------------------ | ------------------------------------------------- | -------- |
|
|
52
|
+
| `currentPage` | `number` | The current active page number. | Required |
|
|
53
|
+
| `totalPages` | `number` | Total number of pages available. | Required |
|
|
54
|
+
| `onPageChange` | `(page: number) => void` | Called when the user selects a different page. | Required |
|
|
55
|
+
| `condensed` | `boolean` | Displays “Page X of Y” instead of numbered items. | `false` |
|
|
56
|
+
| `hideSkipButtons` | `boolean` | Hides the first and last page controls. | `false` |
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
### Condensed
|
|
61
|
+
|
|
62
|
+
<Canvas of={Stories.Condensed} />
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
66
|
+
|
|
67
|
+
<Pagination condensed currentPage={currentPage} onPageChange={setCurrentPage} totalPages={10} />;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Without skip buttons
|
|
71
|
+
|
|
72
|
+
<Canvas of={Stories.WithoutSkipButtons} />
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
const [currentPage, setCurrentPage] = useState(3);
|
|
76
|
+
|
|
77
|
+
<Pagination
|
|
78
|
+
currentPage={currentPage}
|
|
79
|
+
hideSkipButtons
|
|
80
|
+
onPageChange={setCurrentPage}
|
|
81
|
+
totalPages={10}
|
|
82
|
+
/>;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Edge cases
|
|
86
|
+
|
|
87
|
+
<Canvas of={Stories.EdgeCases} />
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
const [nearStartPage, setNearStartPage] = useState(2);
|
|
91
|
+
const [middlePage, setMiddlePage] = useState(5);
|
|
92
|
+
const [nearEndPage, setNearEndPage] = useState(9);
|
|
93
|
+
|
|
94
|
+
<Flex direction="column" spacing="lg" style={{ width: '100%', maxWidth: 520 }}>
|
|
95
|
+
<Pagination currentPage={nearStartPage} onPageChange={setNearStartPage} totalPages={10} />
|
|
96
|
+
<Pagination currentPage={middlePage} onPageChange={setMiddlePage} totalPages={10} />
|
|
97
|
+
<Pagination currentPage={nearEndPage} onPageChange={setNearEndPage} totalPages={10} />
|
|
98
|
+
</Flex>;
|
|
99
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import figma from '@figma/code-connect';
|
|
2
|
+
import { Pagination } from './';
|
|
3
|
+
|
|
4
|
+
figma.connect(
|
|
5
|
+
Pagination,
|
|
6
|
+
'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=6416-5836&t=pZwKJYFo1y1QRQD1-4',
|
|
7
|
+
{
|
|
8
|
+
props: {
|
|
9
|
+
condensed: figma.boolean('Condensed?'),
|
|
10
|
+
},
|
|
11
|
+
example: props => (
|
|
12
|
+
<Pagination
|
|
13
|
+
currentPage={1}
|
|
14
|
+
totalPages={10}
|
|
15
|
+
onPageChange={() => {}}
|
|
16
|
+
condensed={props.condensed}
|
|
17
|
+
/>
|
|
18
|
+
),
|
|
19
|
+
}
|
|
20
|
+
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ViewProps } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface PaginationProps extends ViewProps {
|
|
4
|
+
/**
|
|
5
|
+
* The current active page number (1-indexed).
|
|
6
|
+
*/
|
|
7
|
+
currentPage: number;
|
|
8
|
+
/**
|
|
9
|
+
* The total number of pages.
|
|
10
|
+
*/
|
|
11
|
+
totalPages: number;
|
|
12
|
+
/**
|
|
13
|
+
* Callback fired when the page changes.
|
|
14
|
+
*/
|
|
15
|
+
onPageChange: (page: number) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Whether to show condensed copy instead of individual page items.
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
condensed?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether to hide the first and last page controls.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
hideSkipButtons?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default PaginationProps;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-native';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { BodyText } from '../BodyText';
|
|
4
|
+
import { Flex } from '../Flex';
|
|
5
|
+
import { Pagination } from './';
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: 'Stories / Pagination',
|
|
9
|
+
component: Pagination,
|
|
10
|
+
args: {
|
|
11
|
+
currentPage: 1,
|
|
12
|
+
totalPages: 10,
|
|
13
|
+
condensed: false,
|
|
14
|
+
hideSkipButtons: false,
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
currentPage: { control: { type: 'number', min: 1 } },
|
|
18
|
+
totalPages: { control: { type: 'number', min: 1 } },
|
|
19
|
+
condensed: { control: 'boolean' },
|
|
20
|
+
hideSkipButtons: { control: 'boolean' },
|
|
21
|
+
},
|
|
22
|
+
} satisfies Meta<typeof Pagination>;
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
type Story = StoryObj<typeof meta>;
|
|
26
|
+
|
|
27
|
+
export const Playground: Story = {
|
|
28
|
+
render: (args: StoryObj<typeof meta.args>) => {
|
|
29
|
+
const [currentPage, setCurrentPage] = useState(args.currentPage);
|
|
30
|
+
|
|
31
|
+
return <Pagination {...args} currentPage={currentPage} onPageChange={setCurrentPage} />;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const Condensed: Story = {
|
|
36
|
+
render: () => {
|
|
37
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Pagination
|
|
41
|
+
condensed
|
|
42
|
+
currentPage={currentPage}
|
|
43
|
+
onPageChange={setCurrentPage}
|
|
44
|
+
totalPages={10}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const WithoutSkipButtons: Story = {
|
|
51
|
+
render: () => {
|
|
52
|
+
const [currentPage, setCurrentPage] = useState(3);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Pagination
|
|
56
|
+
currentPage={currentPage}
|
|
57
|
+
hideSkipButtons
|
|
58
|
+
onPageChange={setCurrentPage}
|
|
59
|
+
totalPages={10}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const EdgeCases: Story = {
|
|
66
|
+
render: () => {
|
|
67
|
+
const [nearStartPage, setNearStartPage] = useState(2);
|
|
68
|
+
const [middlePage, setMiddlePage] = useState(5);
|
|
69
|
+
const [nearEndPage, setNearEndPage] = useState(9);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Flex direction="column" spacing="lg" style={{ width: '100%', maxWidth: 520 }}>
|
|
73
|
+
<Flex direction="column" spacing="xs">
|
|
74
|
+
<BodyText size="sm">Near start</BodyText>
|
|
75
|
+
<Pagination currentPage={nearStartPage} onPageChange={setNearStartPage} totalPages={10} />
|
|
76
|
+
</Flex>
|
|
77
|
+
<Flex direction="column" spacing="xs">
|
|
78
|
+
<BodyText size="sm">Middle</BodyText>
|
|
79
|
+
<Pagination currentPage={middlePage} onPageChange={setMiddlePage} totalPages={10} />
|
|
80
|
+
</Flex>
|
|
81
|
+
<Flex direction="column" spacing="xs">
|
|
82
|
+
<BodyText size="sm">Near end</BodyText>
|
|
83
|
+
<Pagination currentPage={nearEndPage} onPageChange={setNearEndPage} totalPages={10} />
|
|
84
|
+
</Flex>
|
|
85
|
+
</Flex>
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
};
|