@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.
Files changed (118) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +18 -19
  3. package/CHANGELOG.md +110 -0
  4. package/build/components/Combobox/Combobox.context.d.ts +13 -0
  5. package/build/components/Combobox/Combobox.context.js +9 -0
  6. package/build/components/Combobox/Combobox.d.ts +6 -0
  7. package/build/components/Combobox/Combobox.js +246 -0
  8. package/build/components/Combobox/Combobox.props.d.ts +180 -0
  9. package/build/components/Combobox/Combobox.props.js +1 -0
  10. package/build/components/Combobox/ComboboxOption.d.ts +6 -0
  11. package/build/components/Combobox/ComboboxOption.js +56 -0
  12. package/build/components/Combobox/index.d.ts +4 -0
  13. package/build/components/Combobox/index.js +3 -0
  14. package/build/components/Modal/Modal.js +26 -42
  15. package/build/components/Modal/Modal.web.js +3 -3
  16. package/build/components/Pagination/Pagination.d.ts +6 -0
  17. package/build/components/Pagination/Pagination.js +125 -0
  18. package/build/components/Pagination/Pagination.props.d.ts +26 -0
  19. package/build/components/Pagination/Pagination.props.js +1 -0
  20. package/build/components/Pagination/Pagination.utils.d.ts +2 -0
  21. package/build/components/Pagination/Pagination.utils.js +20 -0
  22. package/build/components/Pagination/Pagination.utils.test.d.ts +1 -0
  23. package/build/components/Pagination/Pagination.utils.test.js +16 -0
  24. package/build/components/Pagination/index.d.ts +2 -0
  25. package/build/components/Pagination/index.js +1 -0
  26. package/build/components/SafeAreaView/SafeAreaView.d.ts +5 -0
  27. package/build/components/SafeAreaView/SafeAreaView.js +117 -0
  28. package/build/components/SafeAreaView/SafeAreaView.props.d.ts +17 -0
  29. package/build/components/SafeAreaView/SafeAreaView.props.js +1 -0
  30. package/build/components/SafeAreaView/index.d.ts +2 -0
  31. package/build/components/SafeAreaView/index.js +1 -0
  32. package/build/components/Select/Select.js +3 -2
  33. package/build/components/Table/Table.context.d.ts +12 -0
  34. package/build/components/Table/Table.context.js +9 -0
  35. package/build/components/Table/Table.d.ts +6 -0
  36. package/build/components/Table/Table.js +71 -0
  37. package/build/components/Table/Table.props.d.ts +56 -0
  38. package/build/components/Table/Table.props.js +1 -0
  39. package/build/components/Table/Table.utils.d.ts +5 -0
  40. package/build/components/Table/Table.utils.js +48 -0
  41. package/build/components/Table/Table.utils.test.d.ts +1 -0
  42. package/build/components/Table/Table.utils.test.js +71 -0
  43. package/build/components/Table/TableBody.d.ts +6 -0
  44. package/build/components/Table/TableBody.js +16 -0
  45. package/build/components/Table/TableCell.d.ts +10 -0
  46. package/build/components/Table/TableCell.js +44 -0
  47. package/build/components/Table/TableHeader.d.ts +6 -0
  48. package/build/components/Table/TableHeader.js +24 -0
  49. package/build/components/Table/TableHeaderCell.d.ts +10 -0
  50. package/build/components/Table/TableHeaderCell.js +97 -0
  51. package/build/components/Table/TablePagination.d.ts +6 -0
  52. package/build/components/Table/TablePagination.js +7 -0
  53. package/build/components/Table/TableRow.d.ts +8 -0
  54. package/build/components/Table/TableRow.js +25 -0
  55. package/build/components/Table/index.d.ts +8 -0
  56. package/build/components/Table/index.js +7 -0
  57. package/build/components/Timeline/Timeline.d.ts +6 -0
  58. package/build/components/Timeline/Timeline.js +34 -0
  59. package/build/components/Timeline/Timeline.props.d.ts +47 -0
  60. package/build/components/Timeline/Timeline.props.js +1 -0
  61. package/build/components/Timeline/TimelineItem.d.ts +6 -0
  62. package/build/components/Timeline/TimelineItem.js +235 -0
  63. package/build/components/Timeline/index.d.ts +3 -0
  64. package/build/components/Timeline/index.js +2 -0
  65. package/build/components/index.d.ts +5 -0
  66. package/build/components/index.js +5 -0
  67. package/build/tokens/components/dark/timeline.d.ts +2 -2
  68. package/build/tokens/components/dark/timeline.js +2 -2
  69. package/docs/components/AllComponents.web.tsx +106 -23
  70. package/docs/llm-docs/unistyles-llms-full.txt +1132 -534
  71. package/docs/llm-docs/unistyles-llms-small.txt +37 -37
  72. package/package.json +2 -2
  73. package/src/components/Combobox/Combobox.context.ts +26 -0
  74. package/src/components/Combobox/Combobox.docs.mdx +277 -0
  75. package/src/components/Combobox/Combobox.figma.tsx +60 -0
  76. package/src/components/Combobox/Combobox.props.ts +187 -0
  77. package/src/components/Combobox/Combobox.stories.tsx +233 -0
  78. package/src/components/Combobox/Combobox.tsx +446 -0
  79. package/src/components/Combobox/ComboboxOption.tsx +100 -0
  80. package/src/components/Combobox/index.ts +9 -0
  81. package/src/components/Modal/Modal.tsx +52 -74
  82. package/src/components/Modal/Modal.web.tsx +3 -3
  83. package/src/components/Pagination/Pagination.docs.mdx +99 -0
  84. package/src/components/Pagination/Pagination.figma.tsx +20 -0
  85. package/src/components/Pagination/Pagination.props.ts +28 -0
  86. package/src/components/Pagination/Pagination.stories.tsx +88 -0
  87. package/src/components/Pagination/Pagination.tsx +248 -0
  88. package/src/components/Pagination/Pagination.utils.test.ts +20 -0
  89. package/src/components/Pagination/Pagination.utils.ts +37 -0
  90. package/src/components/Pagination/index.ts +2 -0
  91. package/src/components/SafeAreaView/SafeAreaView.props.ts +20 -0
  92. package/src/components/SafeAreaView/SafeAreaView.tsx +173 -0
  93. package/src/components/SafeAreaView/index.ts +2 -0
  94. package/src/components/Select/Select.tsx +30 -27
  95. package/src/components/Table/Table.context.tsx +23 -0
  96. package/src/components/Table/Table.docs.mdx +239 -0
  97. package/src/components/Table/Table.figma.tsx +65 -0
  98. package/src/components/Table/Table.props.ts +65 -0
  99. package/src/components/Table/Table.stories.tsx +399 -0
  100. package/src/components/Table/Table.tsx +127 -0
  101. package/src/components/Table/Table.utils.test.ts +82 -0
  102. package/src/components/Table/Table.utils.ts +72 -0
  103. package/src/components/Table/TableBody.tsx +25 -0
  104. package/src/components/Table/TableCell.tsx +67 -0
  105. package/src/components/Table/TableHeader.tsx +41 -0
  106. package/src/components/Table/TableHeaderCell.tsx +136 -0
  107. package/src/components/Table/TablePagination.tsx +10 -0
  108. package/src/components/Table/TableRow.tsx +42 -0
  109. package/src/components/Table/index.ts +16 -0
  110. package/src/components/Timeline/Timeline.docs.mdx +177 -0
  111. package/src/components/Timeline/Timeline.figma.tsx +89 -0
  112. package/src/components/Timeline/Timeline.props.ts +51 -0
  113. package/src/components/Timeline/Timeline.stories.tsx +102 -0
  114. package/src/components/Timeline/Timeline.tsx +48 -0
  115. package/src/components/Timeline/TimelineItem.tsx +293 -0
  116. package/src/components/Timeline/index.ts +9 -0
  117. package/src/components/index.ts +5 -0
  118. package/src/tokens/components/dark/timeline.ts +2 -2
@@ -0,0 +1,248 @@
1
+ import {
2
+ ChevronLeftSmallIcon,
3
+ ChevronRightSmallIcon,
4
+ SkipFirstSmallIcon,
5
+ SkipLastSmallIcon,
6
+ } from '@utilitywarehouse/hearth-react-native-icons';
7
+ import { ComponentType, useState } from 'react';
8
+ import { Pressable, View } from 'react-native';
9
+ import { StyleSheet } from 'react-native-unistyles';
10
+ import { BodyText } from '../BodyText';
11
+ import { UnstyledIconButton } from '../UnstyledIconButton';
12
+ import type PaginationProps from './Pagination.props';
13
+ import { ELLIPSIS, generatePageNumbers } from './Pagination.utils';
14
+
15
+ interface PaginationItemProps {
16
+ label: number;
17
+ selected?: boolean;
18
+ onPress: () => void;
19
+ }
20
+
21
+ const PaginationItem = ({ label, selected = false, onPress }: PaginationItemProps) => {
22
+ const [isFocused, setIsFocused] = useState(false);
23
+
24
+ styles.useVariants({ selected });
25
+
26
+ return (
27
+ <Pressable
28
+ accessibilityRole="button"
29
+ accessibilityLabel={`Go to page ${label}`}
30
+ accessibilityState={{ selected }}
31
+ onBlur={() => setIsFocused(false)}
32
+ onFocus={() => setIsFocused(true)}
33
+ onPress={onPress}
34
+ style={({ pressed }) => [
35
+ styles.pageItem,
36
+ pressed && !selected && styles.pageItemPressed,
37
+ isFocused && styles.pageItemFocused,
38
+ ]}
39
+ >
40
+ <BodyText size="md" style={styles.pageItemText}>
41
+ {label}
42
+ </BodyText>
43
+ </Pressable>
44
+ );
45
+ };
46
+
47
+ const PaginationArrowButton = ({
48
+ accessibilityLabel,
49
+ disabled,
50
+ icon,
51
+ onPress,
52
+ }: {
53
+ accessibilityLabel: string;
54
+ disabled: boolean;
55
+ icon: ComponentType;
56
+ onPress: () => void;
57
+ }) => {
58
+ return (
59
+ <UnstyledIconButton
60
+ accessibilityLabel={accessibilityLabel}
61
+ disabled={disabled}
62
+ icon={icon}
63
+ onPress={onPress}
64
+ size="sm"
65
+ style={styles.arrowButton}
66
+ />
67
+ );
68
+ };
69
+
70
+ const Pagination = ({
71
+ currentPage,
72
+ totalPages,
73
+ onPageChange,
74
+ condensed = false,
75
+ hideSkipButtons = false,
76
+ style,
77
+ ...props
78
+ }: PaginationProps) => {
79
+ const pages = generatePageNumbers(currentPage, totalPages);
80
+
81
+ const handleFirst = () => {
82
+ if (currentPage !== 1) {
83
+ onPageChange(1);
84
+ }
85
+ };
86
+
87
+ const handlePrevious = () => {
88
+ if (currentPage > 1) {
89
+ onPageChange(currentPage - 1);
90
+ }
91
+ };
92
+
93
+ const handleNext = () => {
94
+ if (currentPage < totalPages) {
95
+ onPageChange(currentPage + 1);
96
+ }
97
+ };
98
+
99
+ const handleLast = () => {
100
+ if (currentPage !== totalPages) {
101
+ onPageChange(totalPages);
102
+ }
103
+ };
104
+
105
+ return (
106
+ <View accessibilityLabel="Pagination" style={[styles.container, style]} {...props}>
107
+ <View style={styles.controllers}>
108
+ {!hideSkipButtons ? (
109
+ <PaginationArrowButton
110
+ accessibilityLabel="Go to first page"
111
+ disabled={currentPage === 1}
112
+ icon={SkipFirstSmallIcon}
113
+ onPress={handleFirst}
114
+ />
115
+ ) : null}
116
+ <PaginationArrowButton
117
+ accessibilityLabel="Go to previous page"
118
+ disabled={currentPage === 1}
119
+ icon={ChevronLeftSmallIcon}
120
+ onPress={handlePrevious}
121
+ />
122
+ </View>
123
+
124
+ {condensed ? (
125
+ <BodyText size="md">
126
+ Page {currentPage} of {totalPages}
127
+ </BodyText>
128
+ ) : (
129
+ <View style={styles.pages}>
130
+ {pages.map((page, index) => {
131
+ if (page === ELLIPSIS) {
132
+ return (
133
+ <BodyText key={`ellipsis-${index}`} size="md" style={styles.ellipsis}>
134
+ {ELLIPSIS}
135
+ </BodyText>
136
+ );
137
+ }
138
+
139
+ return (
140
+ <PaginationItem
141
+ key={page}
142
+ label={page}
143
+ onPress={() => onPageChange(page)}
144
+ selected={page === currentPage}
145
+ />
146
+ );
147
+ })}
148
+ </View>
149
+ )}
150
+
151
+ <View style={styles.controllers}>
152
+ <PaginationArrowButton
153
+ accessibilityLabel="Go to next page"
154
+ disabled={currentPage === totalPages}
155
+ icon={ChevronRightSmallIcon}
156
+ onPress={handleNext}
157
+ />
158
+ {!hideSkipButtons ? (
159
+ <PaginationArrowButton
160
+ accessibilityLabel="Go to last page"
161
+ disabled={currentPage === totalPages}
162
+ icon={SkipLastSmallIcon}
163
+ onPress={handleLast}
164
+ />
165
+ ) : null}
166
+ </View>
167
+ </View>
168
+ );
169
+ };
170
+
171
+ PaginationItem.displayName = 'PaginationItem';
172
+ Pagination.displayName = 'Pagination';
173
+
174
+ const styles = StyleSheet.create(theme => ({
175
+ container: {
176
+ width: '100%',
177
+ minHeight: theme.components.pagination.item.height,
178
+ flexDirection: 'row',
179
+ alignItems: 'center',
180
+ justifyContent: 'space-between',
181
+ gap: theme.components.pagination.gap,
182
+ },
183
+ controllers: {
184
+ flexDirection: 'row',
185
+ alignItems: 'center',
186
+ gap: theme.components.pagination.gap,
187
+ },
188
+ arrowButton: {
189
+ width: theme.components.pagination.item.width,
190
+ height: theme.components.pagination.item.height,
191
+ },
192
+ pages: {
193
+ flex: 1,
194
+ flexDirection: 'row',
195
+ alignItems: 'center',
196
+ justifyContent: 'center',
197
+ flexWrap: 'wrap',
198
+ gap: theme.components.pagination.gap,
199
+ },
200
+ pageItem: {
201
+ width: theme.components.pagination.item.width,
202
+ height: theme.components.pagination.item.height,
203
+ borderRadius: theme.components.pagination.item.radius,
204
+ alignItems: 'center',
205
+ justifyContent: 'center',
206
+ backgroundColor: 'transparent',
207
+ _web: {
208
+ _hover: {
209
+ backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
210
+ },
211
+ },
212
+ variants: {
213
+ selected: {
214
+ true: {
215
+ backgroundColor: theme.color.interactive.brand.surface.strong.default,
216
+ },
217
+ },
218
+ },
219
+ },
220
+ pageItemPressed: {
221
+ backgroundColor: theme.color.interactive.functional.surface.subtle.active,
222
+ },
223
+ pageItemFocused: {
224
+ outlineWidth: 2,
225
+ outlineOffset: -2,
226
+ outlineColor: theme.color.border.strong,
227
+ outlineStyle: 'solid',
228
+ },
229
+ pageItemText: {
230
+ variants: {
231
+ selected: {
232
+ true: {
233
+ color: theme.color.text.inverted,
234
+ },
235
+ false: {
236
+ color: theme.color.text.primary,
237
+ },
238
+ },
239
+ },
240
+ },
241
+ ellipsis: {
242
+ minWidth: theme.components.pagination.item.width,
243
+ textAlign: 'center',
244
+ color: theme.color.text.primary,
245
+ },
246
+ }));
247
+
248
+ export default Pagination;
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ELLIPSIS, generatePageNumbers } from './Pagination.utils';
3
+
4
+ describe('generatePageNumbers', () => {
5
+ it('returns all pages when the total fits within the visible limit', () => {
6
+ expect(generatePageNumbers(1, 7)).toEqual([1, 2, 3, 4, 5, 6, 7]);
7
+ });
8
+
9
+ it('returns the leading window when the current page is near the start', () => {
10
+ expect(generatePageNumbers(2, 10)).toEqual([1, 2, 3, 4, 5, ELLIPSIS, 10]);
11
+ });
12
+
13
+ it('returns the centered window when the current page is in the middle', () => {
14
+ expect(generatePageNumbers(5, 10)).toEqual([1, ELLIPSIS, 4, 5, 6, ELLIPSIS, 10]);
15
+ });
16
+
17
+ it('returns the trailing window when the current page is near the end', () => {
18
+ expect(generatePageNumbers(9, 10)).toEqual([1, ELLIPSIS, 6, 7, 8, 9, 10]);
19
+ });
20
+ });
@@ -0,0 +1,37 @@
1
+ export const ELLIPSIS = '...';
2
+
3
+ const MAX_VISIBLE_ITEMS = 7;
4
+
5
+ export const generatePageNumbers = (
6
+ currentPage: number,
7
+ totalPages: number
8
+ ): Array<number | typeof ELLIPSIS> => {
9
+ if (totalPages <= MAX_VISIBLE_ITEMS) {
10
+ return Array.from({ length: totalPages }, (_, index) => index + 1);
11
+ }
12
+
13
+ const pages: Array<number | typeof ELLIPSIS> = [];
14
+ const isNearStart = currentPage <= 4;
15
+ const isNearEnd = currentPage > totalPages - 4;
16
+
17
+ if (isNearStart) {
18
+ pages.push(1, 2, 3, 4, 5, ELLIPSIS, totalPages);
19
+ return pages;
20
+ }
21
+
22
+ if (isNearEnd) {
23
+ pages.push(
24
+ 1,
25
+ ELLIPSIS,
26
+ totalPages - 4,
27
+ totalPages - 3,
28
+ totalPages - 2,
29
+ totalPages - 1,
30
+ totalPages
31
+ );
32
+ return pages;
33
+ }
34
+
35
+ pages.push(1, ELLIPSIS, currentPage - 1, currentPage, currentPage + 1, ELLIPSIS, totalPages);
36
+ return pages;
37
+ };
@@ -0,0 +1,2 @@
1
+ export { default as Pagination } from './Pagination';
2
+ export type { default as PaginationProps } from './Pagination.props';
@@ -0,0 +1,20 @@
1
+ import { ViewProps } from 'react-native';
2
+
3
+ export type SafeAreaEdge = 'top' | 'right' | 'bottom' | 'left';
4
+
5
+ interface SafeAreaViewProps extends ViewProps {
6
+ /**
7
+ * Which edges should receive safe area compensation.
8
+ *
9
+ * @default ['top', 'right', 'bottom', 'left']
10
+ */
11
+ edges?: SafeAreaEdge[];
12
+ /**
13
+ * Whether safe area compensation should be applied as padding or margin.
14
+ *
15
+ * @default 'padding'
16
+ */
17
+ mode?: 'padding' | 'margin';
18
+ }
19
+
20
+ export default SafeAreaViewProps;
@@ -0,0 +1,173 @@
1
+ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import {
3
+ LayoutChangeEvent,
4
+ StyleSheet as RNStyleSheet,
5
+ useWindowDimensions,
6
+ View,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+ import { UnistylesRuntime } from '../../core';
10
+ import SafeAreaViewProps, { SafeAreaEdge } from './SafeAreaView.props';
11
+
12
+ type EdgeInsets = Record<SafeAreaEdge, number>;
13
+
14
+ const DEFAULT_EDGES: SafeAreaEdge[] = ['top', 'right', 'bottom', 'left'];
15
+ const EMPTY_INSETS: EdgeInsets = { top: 0, right: 0, bottom: 0, left: 0 };
16
+ const EDGE_EPSILON = 1;
17
+
18
+ const getNumericStyleValue = (value: unknown): number => {
19
+ return typeof value === 'number' ? value : 0;
20
+ };
21
+
22
+ const getStyleInsetValue = (
23
+ style: ViewStyle | undefined,
24
+ mode: SafeAreaViewProps['mode'],
25
+ edge: SafeAreaEdge
26
+ ): number => {
27
+ const prefix = mode === 'margin' ? 'margin' : 'padding';
28
+
29
+ if (!style) {
30
+ // No style specified at all; treat as zero inset for safe-area calculations.
31
+ return 0;
32
+ }
33
+
34
+ if (edge === 'top') {
35
+ const raw = style[`${prefix}Top`] ?? style[`${prefix}Vertical`] ?? style[prefix];
36
+ if (raw == null) {
37
+ return 0;
38
+ }
39
+ return getNumericStyleValue(raw);
40
+ }
41
+
42
+ if (edge === 'bottom') {
43
+ const raw = style[`${prefix}Bottom`] ?? style[`${prefix}Vertical`] ?? style[prefix];
44
+ if (raw == null) {
45
+ return 0;
46
+ }
47
+ return getNumericStyleValue(raw);
48
+ }
49
+
50
+ if (edge === 'left') {
51
+ const raw = style[`${prefix}Left`] ?? style[`${prefix}Horizontal`] ?? style[prefix];
52
+ if (raw == null) {
53
+ return 0;
54
+ }
55
+ return getNumericStyleValue(raw);
56
+ }
57
+
58
+ const raw = style[`${prefix}Right`] ?? style[`${prefix}Horizontal`] ?? style[prefix];
59
+ if (raw == null) {
60
+ return 0;
61
+ }
62
+ return getNumericStyleValue(raw);
63
+ };
64
+
65
+ const SafeAreaView = forwardRef<View, SafeAreaViewProps>(
66
+ ({ children, edges = DEFAULT_EDGES, mode = 'padding', onLayout, style, ...props }, ref) => {
67
+ const viewRef = useRef<View>(null);
68
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
69
+ const [edgeInsets, setEdgeInsets] = useState<EdgeInsets>(EMPTY_INSETS);
70
+
71
+ const flattenedStyle = useMemo(
72
+ () => RNStyleSheet.flatten(style) as ViewStyle | undefined,
73
+ [style]
74
+ );
75
+
76
+ const updateEdgeInsets = useCallback(() => {
77
+ const currentView = viewRef.current;
78
+
79
+ if (!currentView) {
80
+ return;
81
+ }
82
+
83
+ currentView.measureInWindow((x, y, width, height) => {
84
+ const runtimeInsets = UnistylesRuntime.insets ?? EMPTY_INSETS;
85
+ const nextEdgeInsets: EdgeInsets = {
86
+ top: edges.includes('top') ? Math.max(runtimeInsets.top - Math.max(y, 0), 0) : 0,
87
+ right: edges.includes('right')
88
+ ? Math.max(runtimeInsets.right - Math.max(windowWidth - (x + width), 0), 0)
89
+ : 0,
90
+ bottom: edges.includes('bottom')
91
+ ? Math.max(runtimeInsets.bottom - Math.max(windowHeight - (y + height), 0), 0)
92
+ : 0,
93
+ left: edges.includes('left') ? Math.max(runtimeInsets.left - Math.max(x, 0), 0) : 0,
94
+ };
95
+
96
+ setEdgeInsets(previousInsets => {
97
+ const hasChanged = (Object.keys(nextEdgeInsets) as SafeAreaEdge[]).some(
98
+ edge => Math.abs(previousInsets[edge] - nextEdgeInsets[edge]) > EDGE_EPSILON
99
+ );
100
+
101
+ return hasChanged ? nextEdgeInsets : previousInsets;
102
+ });
103
+ });
104
+ }, [edges, windowHeight, windowWidth]);
105
+
106
+ useEffect(() => {
107
+ const frame = requestAnimationFrame(updateEdgeInsets);
108
+
109
+ return () => cancelAnimationFrame(frame);
110
+ }, [updateEdgeInsets]);
111
+
112
+ const handleRef = useCallback(
113
+ (node: View | null) => {
114
+ viewRef.current = node;
115
+
116
+ if (typeof ref === 'function') {
117
+ ref(node);
118
+ } else if (ref) {
119
+ ref.current = node;
120
+ }
121
+ },
122
+ [ref]
123
+ );
124
+
125
+ const handleLayout = useCallback(
126
+ (event: LayoutChangeEvent) => {
127
+ onLayout?.(event);
128
+ requestAnimationFrame(updateEdgeInsets);
129
+ },
130
+ [onLayout, updateEdgeInsets]
131
+ );
132
+
133
+ const safeAreaStyle = useMemo(() => {
134
+ const nextStyle: ViewStyle = {};
135
+
136
+ if (mode === 'padding') {
137
+ nextStyle.paddingTop = getStyleInsetValue(flattenedStyle, mode, 'top') + edgeInsets.top;
138
+ nextStyle.paddingRight =
139
+ getStyleInsetValue(flattenedStyle, mode, 'right') + edgeInsets.right;
140
+ nextStyle.paddingBottom =
141
+ getStyleInsetValue(flattenedStyle, mode, 'bottom') + edgeInsets.bottom;
142
+ nextStyle.paddingLeft = getStyleInsetValue(flattenedStyle, mode, 'left') + edgeInsets.left;
143
+
144
+ return nextStyle;
145
+ }
146
+
147
+ nextStyle.marginTop = getStyleInsetValue(flattenedStyle, mode, 'top') + edgeInsets.top;
148
+ nextStyle.marginRight = getStyleInsetValue(flattenedStyle, mode, 'right') + edgeInsets.right;
149
+ nextStyle.marginBottom =
150
+ getStyleInsetValue(flattenedStyle, mode, 'bottom') + edgeInsets.bottom;
151
+ nextStyle.marginLeft = getStyleInsetValue(flattenedStyle, mode, 'left') + edgeInsets.left;
152
+
153
+ return nextStyle;
154
+ }, [
155
+ edgeInsets.bottom,
156
+ edgeInsets.left,
157
+ edgeInsets.right,
158
+ edgeInsets.top,
159
+ flattenedStyle,
160
+ mode,
161
+ ]);
162
+
163
+ return (
164
+ <View ref={handleRef} onLayout={handleLayout} style={[style, safeAreaStyle]} {...props}>
165
+ {children}
166
+ </View>
167
+ );
168
+ }
169
+ );
170
+
171
+ SafeAreaView.displayName = 'SafeAreaView';
172
+
173
+ export default SafeAreaView;
@@ -0,0 +1,2 @@
1
+ export { default as SafeAreaView } from './SafeAreaView';
2
+ export type { default as SafeAreaViewProps, SafeAreaEdge } from './SafeAreaView.props';
@@ -16,6 +16,7 @@ import { Input } from '../Input';
16
16
  import { SelectContext } from './Select.context';
17
17
  import SelectProps, { SelectOptionItemProps } from './Select.props';
18
18
  import SelectOption from './SelectOption';
19
+ import { SafeAreaView } from '../SafeAreaView';
19
20
 
20
21
  const Select = ({
21
22
  options = [],
@@ -179,34 +180,36 @@ const Select = ({
179
180
  close: closeBottomSheet,
180
181
  }}
181
182
  >
182
- {menuHeading && (
183
- <View style={styles.headingContainer}>
184
- <DetailText size="lg">{menuHeading}</DetailText>
185
- </View>
186
- )}
187
- {searchable && (
188
- <View style={styles.searchContainer}>
189
- <Input
190
- placeholder={searchPlaceholder}
191
- value={search}
192
- inBottomSheet
193
- onChangeText={setSearch}
194
- type="search"
195
- />
196
- </View>
197
- )}
183
+ <SafeAreaView edges={['top']} style={{ flex: 1 }}>
184
+ {menuHeading && (
185
+ <View style={styles.headingContainer}>
186
+ <DetailText size="lg">{menuHeading}</DetailText>
187
+ </View>
188
+ )}
189
+ {searchable && (
190
+ <View style={styles.searchContainer}>
191
+ <Input
192
+ placeholder={searchPlaceholder}
193
+ value={search}
194
+ inBottomSheet
195
+ onChangeText={setSearch}
196
+ type="search"
197
+ />
198
+ </View>
199
+ )}
198
200
 
199
- {children ? (
200
- <BottomSheetScrollView>{children}</BottomSheetScrollView>
201
- ) : (
202
- <BottomSheetFlatList
203
- data={filteredOptions}
204
- keyExtractor={(option: any) => option.value}
205
- renderItem={renderSelectOption}
206
- ListEmptyComponent={renderEmptyComponent}
207
- {...listProps}
208
- />
209
- )}
201
+ {children ? (
202
+ <BottomSheetScrollView>{children}</BottomSheetScrollView>
203
+ ) : (
204
+ <BottomSheetFlatList
205
+ data={filteredOptions}
206
+ keyExtractor={(option: any) => option.value}
207
+ renderItem={renderSelectOption}
208
+ ListEmptyComponent={renderEmptyComponent}
209
+ {...listProps}
210
+ />
211
+ )}
212
+ </SafeAreaView>
210
213
  </SelectContext.Provider>
211
214
  </BottomSheetModal>
212
215
  </View>
@@ -0,0 +1,23 @@
1
+ import { createContext, useContext } from 'react';
2
+ import type { TableProps } from './Table.props';
3
+
4
+ type TableContainer = TableProps['container'];
5
+ type TableColumnWidths = NonNullable<TableProps['columnWidths']>;
6
+
7
+ interface TableContextValue {
8
+ columnWidths: TableColumnWidths;
9
+ container: TableContainer;
10
+ columnCount: number;
11
+ hasPagination: boolean;
12
+ }
13
+
14
+ const TableContext = createContext<TableContextValue>({
15
+ columnWidths: [],
16
+ container: 'none',
17
+ columnCount: 1,
18
+ hasPagination: false,
19
+ });
20
+
21
+ export const TableContextProvider = TableContext.Provider;
22
+
23
+ export const useTableContext = () => useContext(TableContext);