@himakarinv/nativex 1.0.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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/images.d.ts +14 -0
  4. package/index.js +8 -0
  5. package/nativewind-preset.js +18 -0
  6. package/package.json +120 -0
  7. package/src/assets/branding/logo.jpg +0 -0
  8. package/src/assets/branding/softech-logo.png +0 -0
  9. package/src/assets/icons/2-layers.svg +3 -0
  10. package/src/assets/icons/Forbidden.svg +211 -0
  11. package/src/assets/icons/Internal-server-error.svg +119 -0
  12. package/src/assets/icons/Log-out-nav.svg +4 -0
  13. package/src/assets/icons/Not-Found.svg +282 -0
  14. package/src/assets/icons/Unauthorized.svg +314 -0
  15. package/src/assets/icons/User-Frame.svg +5 -0
  16. package/src/assets/icons/alert.svg +4 -0
  17. package/src/assets/icons/arrow-right.svg +3 -0
  18. package/src/assets/icons/avatar-female.svg +10 -0
  19. package/src/assets/icons/avatar-male.svg +10 -0
  20. package/src/assets/icons/bell.svg +3 -0
  21. package/src/assets/icons/calendar-event.svg +11 -0
  22. package/src/assets/icons/calendar.svg +6 -0
  23. package/src/assets/icons/card.svg +4 -0
  24. package/src/assets/icons/caret-right.svg +3 -0
  25. package/src/assets/icons/chat.svg +11 -0
  26. package/src/assets/icons/check.svg +3 -0
  27. package/src/assets/icons/checkbox-minus.svg +3 -0
  28. package/src/assets/icons/checkmark.svg +3 -0
  29. package/src/assets/icons/chevron-down.svg +3 -0
  30. package/src/assets/icons/chevron-left.svg +9 -0
  31. package/src/assets/icons/chevron-right.svg +9 -0
  32. package/src/assets/icons/circle-radio-active.svg +10 -0
  33. package/src/assets/icons/circle-radio.svg +10 -0
  34. package/src/assets/icons/clock.svg +3 -0
  35. package/src/assets/icons/close.svg +3 -0
  36. package/src/assets/icons/code.svg +3 -0
  37. package/src/assets/icons/country.svg +20 -0
  38. package/src/assets/icons/dot.svg +3 -0
  39. package/src/assets/icons/download.svg +1 -0
  40. package/src/assets/icons/dropdown-arrow.svg +3 -0
  41. package/src/assets/icons/dropdown-avatar.svg +3 -0
  42. package/src/assets/icons/email.svg +3 -0
  43. package/src/assets/icons/error.svg +3 -0
  44. package/src/assets/icons/eye-off.svg +1 -0
  45. package/src/assets/icons/eye.svg +1 -0
  46. package/src/assets/icons/hashtag-icon.svg +3 -0
  47. package/src/assets/icons/help-circle.svg +10 -0
  48. package/src/assets/icons/help.svg +4 -0
  49. package/src/assets/icons/hide-checkbox.svg +3 -0
  50. package/src/assets/icons/home-smile.svg +3 -0
  51. package/src/assets/icons/home.svg +3 -0
  52. package/src/assets/icons/info.svg +5 -0
  53. package/src/assets/icons/left-arrow.svg +3 -0
  54. package/src/assets/icons/log-out.svg +3 -0
  55. package/src/assets/icons/notification-box.svg +4 -0
  56. package/src/assets/icons/pencil.svg +1 -0
  57. package/src/assets/icons/plus.svg +3 -0
  58. package/src/assets/icons/radio-dot.svg +3 -0
  59. package/src/assets/icons/right-arrow.svg +3 -0
  60. package/src/assets/icons/search.svg +3 -0
  61. package/src/assets/icons/settings.svg +11 -0
  62. package/src/assets/icons/slack.svg +17 -0
  63. package/src/assets/icons/slash.svg +3 -0
  64. package/src/assets/icons/success.svg +3 -0
  65. package/src/assets/icons/successicon.svg +5 -0
  66. package/src/assets/icons/three-dot-vertical.svg +5 -0
  67. package/src/assets/icons/trash.svg +1 -0
  68. package/src/assets/icons/trend-down-01.svg +3 -0
  69. package/src/assets/icons/trend-up-01.svg +3 -0
  70. package/src/assets/icons/up-arrow.svg +3 -0
  71. package/src/assets/icons/user-plus.svg +10 -0
  72. package/src/assets/icons/user.svg +3 -0
  73. package/src/assets/icons/users.svg +3 -0
  74. package/src/assets/icons/vertical-dots-true-icon.svg +5 -0
  75. package/src/assets/icons/vertical-line.svg +3 -0
  76. package/src/assets/icons/vertical-three-dots-icon.svg +5 -0
  77. package/src/assets/icons/warning.svg +3 -0
  78. package/src/assets/icons/zap.svg +3 -0
  79. package/src/components/ActionsMenu/ActionsMenu.tsx +314 -0
  80. package/src/components/ActionsMenu/index.ts +2 -0
  81. package/src/components/Alert/Alert.tsx +291 -0
  82. package/src/components/Alert/index.ts +2 -0
  83. package/src/components/Badge/Badge.tsx +230 -0
  84. package/src/components/Badge/index.ts +2 -0
  85. package/src/components/Breadcrumb/Breadcrumb.tsx +364 -0
  86. package/src/components/Breadcrumb/index.ts +7 -0
  87. package/src/components/Button/Button.tsx +584 -0
  88. package/src/components/Button/index.ts +9 -0
  89. package/src/components/Checkbox/Checkbox.tsx +336 -0
  90. package/src/components/Checkbox/index.ts +2 -0
  91. package/src/components/CreatePassword/CreatePassword.tsx +161 -0
  92. package/src/components/CreatePassword/index.ts +2 -0
  93. package/src/components/DataTable/DataTable.tsx +822 -0
  94. package/src/components/DataTable/index.ts +10 -0
  95. package/src/components/Datepicker/Datepicker.tsx +797 -0
  96. package/src/components/Datepicker/index.ts +2 -0
  97. package/src/components/Dropdown/Dropdown.tsx +277 -0
  98. package/src/components/Dropdown/index.ts +7 -0
  99. package/src/components/DropdownMenu/DropdownMenu.tsx +471 -0
  100. package/src/components/DropdownMenu/index.ts +11 -0
  101. package/src/components/DynamicForm/DynamicForm.tsx +741 -0
  102. package/src/components/DynamicForm/index.ts +13 -0
  103. package/src/components/ErrorPages/ErrorPages.tsx +135 -0
  104. package/src/components/ErrorPages/index.ts +2 -0
  105. package/src/components/ForgotPassword/ForgotPassword.tsx +121 -0
  106. package/src/components/ForgotPassword/index.ts +2 -0
  107. package/src/components/HintError/HintError.tsx +37 -0
  108. package/src/components/HintError/index.ts +2 -0
  109. package/src/components/Icon/Icon.tsx +43 -0
  110. package/src/components/Icon/icon-registry.ts +227 -0
  111. package/src/components/Icon/index.ts +3 -0
  112. package/src/components/InputDropdown/InputDropdown.tsx +662 -0
  113. package/src/components/InputDropdown/index.ts +9 -0
  114. package/src/components/InputField/InputField.tsx +527 -0
  115. package/src/components/InputField/index.ts +2 -0
  116. package/src/components/Label/Label.tsx +88 -0
  117. package/src/components/Label/index.ts +2 -0
  118. package/src/components/LoginForm/LoginForm.tsx +158 -0
  119. package/src/components/LoginForm/index.ts +2 -0
  120. package/src/components/LoginShell/LoginShell.tsx +97 -0
  121. package/src/components/LoginShell/RoleCard.tsx +24 -0
  122. package/src/components/LoginShell/SoftechLogo.tsx +24 -0
  123. package/src/components/LoginShell/branding-assets.ts +2 -0
  124. package/src/components/LoginShell/index.ts +20 -0
  125. package/src/components/LoginShell/login-copy.ts +45 -0
  126. package/src/components/LoginShell/loginPageStyles.ts +210 -0
  127. package/src/components/Modals/Modals.tsx +313 -0
  128. package/src/components/Modals/index.ts +2 -0
  129. package/src/components/Offcanvas/Offcanvas.tsx +404 -0
  130. package/src/components/Offcanvas/index.ts +13 -0
  131. package/src/components/PageHeader/PageHeader.tsx +128 -0
  132. package/src/components/PageHeader/index.ts +2 -0
  133. package/src/components/Pagination/Pagination.tsx +540 -0
  134. package/src/components/Pagination/index.ts +8 -0
  135. package/src/components/ResetPassword/ResetPassword.tsx +172 -0
  136. package/src/components/ResetPassword/index.ts +2 -0
  137. package/src/components/RoleSelection/RoleSelection.tsx +111 -0
  138. package/src/components/RoleSelection/index.ts +2 -0
  139. package/src/components/SendOtp/SendOtp.tsx +149 -0
  140. package/src/components/SendOtp/index.ts +2 -0
  141. package/src/components/Sidenavbar/Sidenavbar.tsx +358 -0
  142. package/src/components/Sidenavbar/index.ts +2 -0
  143. package/src/components/StatCard/StatCard.tsx +393 -0
  144. package/src/components/StatCard/index.ts +6 -0
  145. package/src/components/Tabs/Tabs.tsx +239 -0
  146. package/src/components/Tabs/index.ts +2 -0
  147. package/src/components/Textarea/Textarea.tsx +217 -0
  148. package/src/components/Textarea/index.ts +2 -0
  149. package/src/components/Timepicker/Timepicker.tsx +384 -0
  150. package/src/components/Timepicker/index.ts +2 -0
  151. package/src/components/Toast/Toast.tsx +278 -0
  152. package/src/components/Toast/index.ts +2 -0
  153. package/src/components/Toggle/Toggle.tsx +296 -0
  154. package/src/components/Toggle/index.ts +2 -0
  155. package/src/components/Typeahead/Typeahead.tsx +521 -0
  156. package/src/components/Typeahead/index.ts +2 -0
  157. package/src/components/Verification/Verification.tsx +374 -0
  158. package/src/components/Verification/index.ts +2 -0
  159. package/src/index.ts +39 -0
  160. package/src/theme/borders.ts +19 -0
  161. package/src/theme/button.ts +41 -0
  162. package/src/theme/colors.ts +273 -0
  163. package/src/theme/index.ts +14 -0
  164. package/src/theme/nativewind-theme.js +182 -0
  165. package/src/theme/radius.ts +12 -0
  166. package/src/theme/shadows.ts +40 -0
  167. package/src/theme/spacing.ts +18 -0
  168. package/src/theme/typography.ts +35 -0
  169. package/src/utils/a11y.ts +21 -0
  170. package/svg.d.ts +7 -0
@@ -0,0 +1,13 @@
1
+ export {
2
+ Offcanvas,
3
+ OffcanvasSize,
4
+ OffcanvasPosition,
5
+ DEFAULT_OFFCANVAS_SIZE,
6
+ offcanvasOpenOptions,
7
+ } from './Offcanvas';
8
+ export type {
9
+ OffcanvasProps,
10
+ OffcanvasFooterButton,
11
+ OffcanvasOpenConfig,
12
+ OffcanvasBackdrop,
13
+ } from './Offcanvas';
@@ -0,0 +1,128 @@
1
+ import type { ReactNode } from 'react';
2
+ import { StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';
3
+
4
+ import { borders, colors, spacing, typography } from '../../theme';
5
+
6
+ export const PageHeaderSize = {
7
+ Small: 'sm',
8
+ Medium: 'md',
9
+ Large: 'lg',
10
+ } as const;
11
+
12
+ export type PageHeaderSize = (typeof PageHeaderSize)[keyof typeof PageHeaderSize];
13
+
14
+ export type PageHeaderProps = {
15
+ title?: string;
16
+ subtitle?: string;
17
+ showDivider?: boolean;
18
+ size?: PageHeaderSize;
19
+ sticky?: boolean;
20
+ actions?: ReactNode;
21
+ style?: StyleProp<ViewStyle>;
22
+ };
23
+
24
+ const SIZE_PAD: Record<PageHeaderSize, { paddingVertical: number; paddingHorizontal: number; marginBottom: number }> =
25
+ {
26
+ sm: {
27
+ paddingVertical: spacing.sm,
28
+ paddingHorizontal: spacing.lg,
29
+ marginBottom: spacing.xl,
30
+ },
31
+ md: {
32
+ paddingVertical: spacing.md,
33
+ paddingHorizontal: spacing.lg,
34
+ marginBottom: spacing['3xl'],
35
+ },
36
+ lg: {
37
+ paddingVertical: spacing.lg,
38
+ paddingHorizontal: spacing.lg,
39
+ marginBottom: spacing['4xl'],
40
+ },
41
+ };
42
+
43
+ export function PageHeader({
44
+ title,
45
+ subtitle,
46
+ showDivider = true,
47
+ size = PageHeaderSize.Medium,
48
+ sticky = false,
49
+ actions,
50
+ style,
51
+ }: PageHeaderProps) {
52
+ const pad = SIZE_PAD[size];
53
+ const hasActions = Boolean(actions);
54
+
55
+ return (
56
+ <View
57
+ accessibilityRole="header"
58
+ style={[
59
+ styles.host,
60
+ { paddingVertical: pad.paddingVertical, paddingHorizontal: pad.paddingHorizontal, marginBottom: pad.marginBottom },
61
+ showDivider && styles.divider,
62
+ sticky && styles.sticky,
63
+ style,
64
+ ]}>
65
+ <View style={styles.row}>
66
+ {title ? (
67
+ <View style={styles.titleWrap}>
68
+ <Text style={styles.title}>{title}</Text>
69
+ {subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : null}
70
+ </View>
71
+ ) : (
72
+ <View style={styles.titleWrap} />
73
+ )}
74
+
75
+ {hasActions ? <View style={styles.actions}>{actions}</View> : null}
76
+ </View>
77
+ </View>
78
+ );
79
+ }
80
+
81
+ const styles = StyleSheet.create({
82
+ host: {
83
+ width: '100%',
84
+ backgroundColor: colors.white,
85
+ },
86
+ row: {
87
+ width: '100%',
88
+ flexDirection: 'row',
89
+ alignItems: 'center',
90
+ justifyContent: 'space-between',
91
+ gap: spacing.md,
92
+ },
93
+ divider: {
94
+ borderBottomWidth: borders.width.thin,
95
+ borderBottomColor: colors.borderSubtle,
96
+ borderStyle: borders.style.solid,
97
+ },
98
+ sticky: {
99
+ zIndex: 100,
100
+ },
101
+ titleWrap: {
102
+ flex: 1,
103
+ minWidth: 0,
104
+ gap: spacing.xs,
105
+ },
106
+ title: {
107
+ fontFamily: typography.fontFamily.base,
108
+ fontSize: typography.fontSize.lg,
109
+ fontWeight: typography.fontWeight.bold,
110
+ lineHeight: Math.round(typography.fontSize.lg * typography.lineHeight.tight),
111
+ color: colors.text.strong,
112
+ },
113
+ subtitle: {
114
+ fontFamily: typography.fontFamily.base,
115
+ fontSize: typography.fontSize.sm,
116
+ fontWeight: typography.fontWeight.regular,
117
+ lineHeight: Math.round(typography.fontSize.sm * typography.lineHeight.normal),
118
+ color: colors.text.muted,
119
+ },
120
+ actions: {
121
+ flexDirection: 'row',
122
+ flexShrink: 0,
123
+ flexWrap: 'nowrap',
124
+ alignItems: 'center',
125
+ justifyContent: 'flex-end',
126
+ gap: spacing.sm,
127
+ },
128
+ });
@@ -0,0 +1,2 @@
1
+ export { PageHeader, PageHeaderSize } from './PageHeader';
2
+ export type { PageHeaderProps } from './PageHeader';
@@ -0,0 +1,540 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { Pressable, ScrollView, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';
3
+
4
+ import { borders, button as buttonTokens, colors, radius, spacing, typography } from '../../theme';
5
+ import { Button, ButtonSize, ButtonVariant } from '../Button';
6
+ import { Icon, IconName } from '../Icon';
7
+
8
+ export const PaginationVariant = {
9
+ NumberedLink: 'numbered-link',
10
+ NumberedButton: 'numbered-button',
11
+ Segmented: 'segmented',
12
+ Simple: 'simple',
13
+ SimpleIcon: 'simple-icon',
14
+ } as const;
15
+
16
+ export type PaginationVariant = (typeof PaginationVariant)[keyof typeof PaginationVariant];
17
+
18
+ export const SimpleLayout = {
19
+ InfoLeft: 'info-left',
20
+ InfoRight: 'info-right',
21
+ InfoCenter: 'info-center',
22
+ } as const;
23
+
24
+ export type SimpleLayout = (typeof SimpleLayout)[keyof typeof SimpleLayout];
25
+
26
+ export const PageNumberShape = {
27
+ Circle: 'circle',
28
+ Square: 'square',
29
+ } as const;
30
+
31
+ export type PageNumberShape = (typeof PageNumberShape)[keyof typeof PageNumberShape];
32
+
33
+ export const ArrowStyle = {
34
+ Plain: 'plain',
35
+ Outlined: 'outlined',
36
+ } as const;
37
+
38
+ export type ArrowStyle = (typeof ArrowStyle)[keyof typeof ArrowStyle];
39
+
40
+ export type PaginationProps = {
41
+ page?: number;
42
+ pageSize?: number;
43
+ collectionSize?: number;
44
+ maxPages?: number;
45
+ variant?: PaginationVariant;
46
+ simpleLayout?: SimpleLayout;
47
+ pageNumberShape?: PageNumberShape;
48
+ arrowStyle?: ArrowStyle;
49
+ segmentedNavIconOnly?: boolean;
50
+ onPageChange?: (page: number) => void;
51
+ style?: StyleProp<ViewStyle>;
52
+ };
53
+
54
+ const PREVIOUS_LABEL = 'Previous';
55
+ const NEXT_LABEL = 'Next';
56
+ const NAV_ICON_SIZE = 14;
57
+ const PAGE_HIT = buttonTokens.minTouch;
58
+
59
+ type PageSlot = { kind: 'page'; page: number } | { kind: 'ellipsis'; key: string };
60
+
61
+ function totalPages(collectionSize: number, pageSize: number) {
62
+ return Math.max(1, Math.ceil(Math.max(1, collectionSize) / Math.max(1, pageSize)));
63
+ }
64
+
65
+ function clampPage(page: number, last: number) {
66
+ return Math.min(last, Math.max(1, Math.floor(page)));
67
+ }
68
+
69
+ function buildSlots(current: number, last: number, maxPages: number): PageSlot[] {
70
+ const windowSize = Math.max(1, Math.floor(maxPages));
71
+ if (last <= windowSize) {
72
+ return Array.from({ length: last }, (_, index) => ({ kind: 'page' as const, page: index + 1 }));
73
+ }
74
+ const half = Math.floor((windowSize - 1) / 2);
75
+ let start = Math.max(1, current - half);
76
+ let end = Math.min(last, start + windowSize - 1);
77
+ start = Math.max(1, end - windowSize + 1);
78
+ const slots: PageSlot[] = [];
79
+ if (start > 1) {
80
+ slots.push({ kind: 'ellipsis', key: 'start' });
81
+ }
82
+ for (let page = start; page <= end; page += 1) {
83
+ slots.push({ kind: 'page', page });
84
+ }
85
+ if (end < last) {
86
+ slots.push({ kind: 'ellipsis', key: 'end' });
87
+ }
88
+ return slots;
89
+ }
90
+
91
+ function barJustify(layout: SimpleLayout): ViewStyle['justifyContent'] {
92
+ if (layout === SimpleLayout.InfoLeft) {
93
+ return 'flex-start';
94
+ }
95
+ if (layout === SimpleLayout.InfoRight) {
96
+ return 'flex-end';
97
+ }
98
+ return 'center';
99
+ }
100
+
101
+ export function Pagination({
102
+ page = 1,
103
+ pageSize = 10,
104
+ collectionSize = 100,
105
+ maxPages = 5,
106
+ variant = PaginationVariant.NumberedLink,
107
+ simpleLayout = SimpleLayout.InfoCenter,
108
+ pageNumberShape = PageNumberShape.Circle,
109
+ arrowStyle = ArrowStyle.Plain,
110
+ segmentedNavIconOnly = false,
111
+ onPageChange,
112
+ style,
113
+ }: PaginationProps) {
114
+ const last = totalPages(collectionSize, pageSize);
115
+ const [current, setCurrent] = useState(() => clampPage(page, last));
116
+
117
+ useEffect(() => {
118
+ setCurrent(clampPage(page, last));
119
+ }, [page, last]);
120
+
121
+ const slots = useMemo(() => buildSlots(current, last, maxPages), [current, last, maxPages]);
122
+ const isFirst = current <= 1;
123
+ const isLast = current >= last;
124
+ const pageInfo = `Page ${current} of ${last}`;
125
+ const showNavLabels = variant !== PaginationVariant.Segmented || !segmentedNavIconOnly;
126
+ const isSegmented = variant === PaginationVariant.Segmented;
127
+ const isNumbered =
128
+ variant === PaginationVariant.NumberedLink || variant === PaginationVariant.NumberedButton;
129
+
130
+ function goTo(next: number) {
131
+ const safe = clampPage(next, last);
132
+ if (safe === current) {
133
+ return;
134
+ }
135
+ setCurrent(safe);
136
+ onPageChange?.(safe);
137
+ }
138
+
139
+ const numbered = isNumbered || isSegmented ? (
140
+ <View
141
+ style={[
142
+ isSegmented && styles.segmentedAlign,
143
+ isSegmented && { justifyContent: barJustify(simpleLayout) },
144
+ ]}>
145
+ <View style={[styles.numberedRow, isSegmented && styles.segmentedFrame]}>
146
+ <NavControl
147
+ direction="prev"
148
+ disabled={isFirst}
149
+ showLabel={showNavLabels}
150
+ numberedButton={variant === PaginationVariant.NumberedButton}
151
+ segmented={isSegmented}
152
+ onPress={() => goTo(current - 1)}
153
+ />
154
+ {isSegmented ? (
155
+ <View style={styles.segmentedPages}>
156
+ {slots.map((slot) =>
157
+ slot.kind === 'ellipsis' ? (
158
+ <SegmentCell key={slot.key} label="…" disabled />
159
+ ) : (
160
+ <SegmentCell
161
+ key={slot.page}
162
+ label={String(slot.page)}
163
+ active={slot.page === current}
164
+ onPress={() => goTo(slot.page)}
165
+ />
166
+ ),
167
+ )}
168
+ </View>
169
+ ) : (
170
+ <ScrollView
171
+ horizontal
172
+ bounces={false}
173
+ showsHorizontalScrollIndicator={false}
174
+ style={styles.pagesScroller}
175
+ contentContainerStyle={[styles.pagesContent, { justifyContent: barJustify(simpleLayout) }]}>
176
+ {slots.map((slot) =>
177
+ slot.kind === 'ellipsis' ? (
178
+ <Text key={slot.key} style={styles.ellipsis}>
179
+
180
+ </Text>
181
+ ) : (
182
+ <PageNumber
183
+ key={slot.page}
184
+ page={slot.page}
185
+ active={slot.page === current}
186
+ shape={pageNumberShape}
187
+ onPress={() => goTo(slot.page)}
188
+ />
189
+ ),
190
+ )}
191
+ </ScrollView>
192
+ )}
193
+ <NavControl
194
+ direction="next"
195
+ disabled={isLast}
196
+ showLabel={showNavLabels}
197
+ numberedButton={variant === PaginationVariant.NumberedButton}
198
+ segmented={isSegmented}
199
+ onPress={() => goTo(current + 1)}
200
+ />
201
+ </View>
202
+ </View>
203
+ ) : null;
204
+
205
+ return (
206
+ <View accessibilityLabel="Pagination" style={[styles.host, style]}>
207
+ {numbered}
208
+
209
+ {variant === PaginationVariant.Simple ? (
210
+ <View
211
+ style={[
212
+ styles.simpleRow,
213
+ simpleLayout === SimpleLayout.InfoCenter && styles.simpleCenter,
214
+ ]}>
215
+ {simpleLayout === SimpleLayout.InfoLeft ? <Text style={styles.info}>{pageInfo}</Text> : null}
216
+ {simpleLayout === SimpleLayout.InfoCenter ? (
217
+ <>
218
+ <Button
219
+ label={PREVIOUS_LABEL}
220
+ variant={ButtonVariant.SecondaryGray}
221
+ size={ButtonSize.Medium}
222
+ disabled={isFirst}
223
+ onPress={() => goTo(current - 1)}
224
+ />
225
+ <Text style={[styles.info, styles.infoFlex]}>{pageInfo}</Text>
226
+ <Button
227
+ label={NEXT_LABEL}
228
+ variant={ButtonVariant.SecondaryGray}
229
+ size={ButtonSize.Medium}
230
+ disabled={isLast}
231
+ onPress={() => goTo(current + 1)}
232
+ />
233
+ </>
234
+ ) : (
235
+ <View style={styles.simpleControls}>
236
+ <Button
237
+ label={PREVIOUS_LABEL}
238
+ variant={ButtonVariant.SecondaryGray}
239
+ size={ButtonSize.Medium}
240
+ disabled={isFirst}
241
+ onPress={() => goTo(current - 1)}
242
+ />
243
+ <Button
244
+ label={NEXT_LABEL}
245
+ variant={ButtonVariant.SecondaryGray}
246
+ size={ButtonSize.Medium}
247
+ disabled={isLast}
248
+ onPress={() => goTo(current + 1)}
249
+ />
250
+ </View>
251
+ )}
252
+ {simpleLayout === SimpleLayout.InfoRight ? <Text style={styles.info}>{pageInfo}</Text> : null}
253
+ </View>
254
+ ) : null}
255
+
256
+ {variant === PaginationVariant.SimpleIcon ? (
257
+ <View style={[styles.iconNavHost, { justifyContent: barJustify(simpleLayout) }]}>
258
+ <View style={styles.iconNav}>
259
+ <Button
260
+ variant={arrowStyle === ArrowStyle.Outlined ? ButtonVariant.SecondaryGray : ButtonVariant.TertiaryGray}
261
+ size={arrowStyle === ArrowStyle.Outlined ? ButtonSize.Medium : ButtonSize.Small}
262
+ iconName={IconName.LeftArrow}
263
+ disabled={isFirst}
264
+ accessibilityLabel="Previous page"
265
+ onPress={() => goTo(current - 1)}
266
+ />
267
+ <Text style={styles.info}>{pageInfo}</Text>
268
+ <Button
269
+ variant={arrowStyle === ArrowStyle.Outlined ? ButtonVariant.SecondaryGray : ButtonVariant.TertiaryGray}
270
+ size={arrowStyle === ArrowStyle.Outlined ? ButtonSize.Medium : ButtonSize.Small}
271
+ iconName={IconName.RightArrow}
272
+ disabled={isLast}
273
+ accessibilityLabel="Next page"
274
+ onPress={() => goTo(current + 1)}
275
+ />
276
+ </View>
277
+ </View>
278
+ ) : null}
279
+ </View>
280
+ );
281
+ }
282
+
283
+ function NavControl({
284
+ direction,
285
+ disabled,
286
+ showLabel,
287
+ numberedButton,
288
+ segmented,
289
+ onPress,
290
+ }: {
291
+ direction: 'prev' | 'next';
292
+ disabled: boolean;
293
+ showLabel: boolean;
294
+ numberedButton: boolean;
295
+ segmented: boolean;
296
+ onPress: () => void;
297
+ }) {
298
+ const isPrev = direction === 'prev';
299
+ const label = isPrev ? PREVIOUS_LABEL : NEXT_LABEL;
300
+ const icon = isPrev ? IconName.LeftArrow : IconName.RightArrow;
301
+ const color = disabled ? colors.pagination.disabled : colors.pagination.navText;
302
+
303
+ return (
304
+ <Pressable
305
+ accessibilityRole="button"
306
+ accessibilityLabel={isPrev ? 'Previous page' : 'Next page'}
307
+ accessibilityState={{ disabled }}
308
+ disabled={disabled}
309
+ hitSlop={spacing.sm}
310
+ onPress={onPress}
311
+ style={[
312
+ styles.navLink,
313
+ numberedButton && styles.navLinkButton,
314
+ segmented && styles.navLinkSegmented,
315
+ segmented && direction === 'next' && styles.navLinkSegmentedLast,
316
+ !showLabel && styles.navLinkIconOnly,
317
+ ]}>
318
+ {isPrev ? <Icon name={icon} size={NAV_ICON_SIZE} color={color} decorative /> : null}
319
+ {showLabel ? <Text style={[styles.navLabel, { color }]}>{label}</Text> : null}
320
+ {!isPrev ? <Icon name={icon} size={NAV_ICON_SIZE} color={color} decorative /> : null}
321
+ </Pressable>
322
+ );
323
+ }
324
+
325
+ function PageNumber({
326
+ page,
327
+ active,
328
+ shape,
329
+ onPress,
330
+ }: {
331
+ page: number;
332
+ active: boolean;
333
+ shape: PageNumberShape;
334
+ onPress: () => void;
335
+ }) {
336
+ return (
337
+ <Pressable
338
+ accessibilityRole="button"
339
+ accessibilityLabel={`Page ${page}`}
340
+ accessibilityState={{ selected: active }}
341
+ onPress={onPress}
342
+ hitSlop={spacing.sm}
343
+ style={[
344
+ styles.pageHit,
345
+ shape === PageNumberShape.Circle ? styles.pageCircle : styles.pageSquare,
346
+ active && styles.pageActive,
347
+ ]}>
348
+ <Text style={[styles.pageLabel, active && styles.pageLabelActive]}>{page}</Text>
349
+ </Pressable>
350
+ );
351
+ }
352
+
353
+ function SegmentCell({
354
+ label,
355
+ active = false,
356
+ disabled = false,
357
+ onPress,
358
+ }: {
359
+ label: string;
360
+ active?: boolean;
361
+ disabled?: boolean;
362
+ onPress?: () => void;
363
+ }) {
364
+ return (
365
+ <Pressable
366
+ accessibilityRole="button"
367
+ accessibilityLabel={label === '…' ? 'More pages' : `Page ${label}`}
368
+ accessibilityState={{ selected: active, disabled }}
369
+ disabled={disabled}
370
+ onPress={onPress}
371
+ style={[styles.segmentCell, active && styles.segmentCellActive]}>
372
+ <Text style={[styles.segmentLabel, active && styles.segmentLabelActive]}>{label}</Text>
373
+ </Pressable>
374
+ );
375
+ }
376
+
377
+ const styles = StyleSheet.create({
378
+ host: {
379
+ width: '100%',
380
+ paddingVertical: spacing.md,
381
+ },
382
+ numberedRow: {
383
+ width: '100%',
384
+ flexDirection: 'row',
385
+ alignItems: 'center',
386
+ gap: spacing.sm,
387
+ },
388
+ pagesScroller: {
389
+ flexGrow: 1,
390
+ flexShrink: 1,
391
+ },
392
+ pagesContent: {
393
+ flexGrow: 1,
394
+ flexDirection: 'row',
395
+ alignItems: 'center',
396
+ justifyContent: 'center',
397
+ gap: spacing.sm,
398
+ },
399
+ segmentedAlign: {
400
+ width: '100%',
401
+ flexDirection: 'row',
402
+ },
403
+ navLink: {
404
+ flexDirection: 'row',
405
+ alignItems: 'center',
406
+ flexShrink: 0,
407
+ gap: spacing.sm,
408
+ minHeight: PAGE_HIT,
409
+ paddingHorizontal: spacing.xs,
410
+ },
411
+ navLinkButton: {
412
+ paddingHorizontal: spacing.base,
413
+ backgroundColor: colors.pagination.background,
414
+ borderWidth: borders.width.thin,
415
+ borderColor: colors.pagination.border,
416
+ borderRadius: radius.sm,
417
+ },
418
+ navLinkSegmented: {
419
+ paddingHorizontal: spacing.md,
420
+ minHeight: spacing['4xl'] + spacing.sm,
421
+ borderRightWidth: borders.width.thin,
422
+ borderRightColor: colors.pagination.segmentedDivider,
423
+ },
424
+ navLinkSegmentedLast: {
425
+ borderRightWidth: borders.width.none,
426
+ },
427
+ navLinkIconOnly: {
428
+ paddingHorizontal: spacing.sm,
429
+ },
430
+ navLabel: {
431
+ fontFamily: typography.fontFamily.base,
432
+ fontSize: typography.fontSize.sm,
433
+ fontWeight: typography.fontWeight.large,
434
+ },
435
+ pageHit: {
436
+ width: spacing['5xl'],
437
+ height: spacing['5xl'],
438
+ alignItems: 'center',
439
+ justifyContent: 'center',
440
+ },
441
+ pageCircle: {
442
+ borderRadius: radius.full,
443
+ },
444
+ pageSquare: {
445
+ borderRadius: radius.md,
446
+ },
447
+ pageActive: {
448
+ backgroundColor: colors.pagination.activeBg,
449
+ },
450
+ pageLabel: {
451
+ fontFamily: typography.fontFamily.base,
452
+ fontSize: typography.fontSize.sm,
453
+ fontWeight: typography.fontWeight.large,
454
+ color: colors.pagination.inactiveNumber,
455
+ },
456
+ pageLabelActive: {
457
+ color: colors.primary.darker,
458
+ },
459
+ ellipsis: {
460
+ fontFamily: typography.fontFamily.base,
461
+ fontSize: typography.fontSize.sm,
462
+ fontWeight: typography.fontWeight.large,
463
+ color: colors.pagination.inactiveNumber,
464
+ paddingHorizontal: spacing.xs,
465
+ },
466
+ segmentedFrame: {
467
+ alignSelf: 'center',
468
+ alignItems: 'stretch',
469
+ width: 'auto',
470
+ maxWidth: '100%',
471
+ gap: spacing.none,
472
+ borderWidth: borders.width.thin,
473
+ borderColor: colors.pagination.segmentedDivider,
474
+ borderRadius: radius.md,
475
+ overflow: 'hidden',
476
+ backgroundColor: colors.pagination.background,
477
+ },
478
+ segmentedPages: {
479
+ flexDirection: 'row',
480
+ alignItems: 'stretch',
481
+ },
482
+ segmentCell: {
483
+ minWidth: spacing['4xl'] + spacing.sm,
484
+ minHeight: spacing['4xl'] + spacing.sm,
485
+ paddingHorizontal: spacing.sm,
486
+ alignItems: 'center',
487
+ justifyContent: 'center',
488
+ borderRightWidth: borders.width.thin,
489
+ borderRightColor: colors.pagination.segmentedDivider,
490
+ backgroundColor: colors.pagination.background,
491
+ },
492
+ segmentCellActive: {
493
+ backgroundColor: colors.pagination.segmentedActiveBg,
494
+ },
495
+ segmentLabel: {
496
+ fontFamily: typography.fontFamily.base,
497
+ fontSize: typography.fontSize.sm,
498
+ fontWeight: typography.fontWeight.large,
499
+ color: colors.pagination.segmentedNumber,
500
+ },
501
+ segmentLabelActive: {
502
+ color: colors.pagination.segmentedActiveText,
503
+ },
504
+ simpleRow: {
505
+ width: '100%',
506
+ flexDirection: 'row',
507
+ alignItems: 'center',
508
+ justifyContent: 'space-between',
509
+ gap: spacing.base,
510
+ },
511
+ simpleCenter: {
512
+ justifyContent: 'space-between',
513
+ },
514
+ simpleControls: {
515
+ flexDirection: 'row',
516
+ alignItems: 'center',
517
+ gap: spacing.sm,
518
+ },
519
+ iconNavHost: {
520
+ width: '100%',
521
+ flexDirection: 'row',
522
+ },
523
+ iconNav: {
524
+ flexDirection: 'row',
525
+ alignItems: 'center',
526
+ gap: spacing.base,
527
+ maxWidth: '100%',
528
+ },
529
+ info: {
530
+ fontFamily: typography.fontFamily.base,
531
+ fontSize: typography.fontSize.sm,
532
+ fontWeight: typography.fontWeight.large,
533
+ color: colors.pagination.button,
534
+ paddingHorizontal: spacing.md,
535
+ },
536
+ infoFlex: {
537
+ flex: 1,
538
+ textAlign: 'center',
539
+ },
540
+ });
@@ -0,0 +1,8 @@
1
+ export {
2
+ Pagination,
3
+ PaginationVariant,
4
+ SimpleLayout,
5
+ PageNumberShape,
6
+ ArrowStyle,
7
+ } from './Pagination';
8
+ export type { PaginationProps } from './Pagination';