@scripso-homepad/ui 0.4.2 → 0.4.3

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 (81) hide show
  1. package/dist/{chunk-WDSMJWKC.js → chunk-66WR57JJ.js} +35 -3
  2. package/dist/chunk-66WR57JJ.js.map +1 -0
  3. package/dist/index.cjs +2260 -385
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +133 -1
  6. package/dist/index.d.ts +133 -1
  7. package/dist/index.js +1960 -97
  8. package/dist/index.js.map +1 -1
  9. package/dist/web/index.cjs +30650 -1009
  10. package/dist/web/index.cjs.map +1 -1
  11. package/dist/web/index.css +78 -0
  12. package/dist/web/index.css.map +1 -0
  13. package/dist/web/index.d.cts +267 -6
  14. package/dist/web/index.d.ts +267 -6
  15. package/dist/web/index.js +30179 -601
  16. package/dist/web/index.js.map +1 -1
  17. package/package.json +5 -4
  18. package/src/components/Calendar.stories.tsx +337 -0
  19. package/src/components/Calendar.tsx +441 -0
  20. package/src/components/DatePicker.stories.tsx +219 -0
  21. package/src/components/DatePicker.tsx +366 -0
  22. package/src/components/Select.stories.tsx +134 -0
  23. package/src/components/Select.tsx +462 -0
  24. package/src/css.d.ts +1 -0
  25. package/src/icons/CalendarIcon.tsx +28 -0
  26. package/src/icons/CalendarIcon.web.tsx +34 -0
  27. package/src/icons/ChevronDownIcon.tsx +11 -4
  28. package/src/icons/ChevronDownIcon.web.tsx +6 -4
  29. package/src/icons/ChevronNavIcons.tsx +44 -0
  30. package/src/icons/ChevronNavIcons.web.tsx +57 -0
  31. package/src/icons/calendarIconPaths.ts +2 -0
  32. package/src/icons/chevronDownPath.ts +1 -0
  33. package/src/icons/chevronNavPaths.ts +2 -0
  34. package/src/index.ts +41 -0
  35. package/src/theme/calendar.ts +357 -0
  36. package/src/theme/select.ts +249 -0
  37. package/src/utils/calendarDays.ts +273 -0
  38. package/src/utils/calendarLocaleContext.tsx +95 -0
  39. package/src/utils/calendarLocalization.ts +187 -0
  40. package/src/utils/countryDropdownScrollStyles.ts +53 -13
  41. package/src/utils/formatMultiSelectDisplay.ts +56 -0
  42. package/src/web/components/Badge.stories.tsx +43 -0
  43. package/src/web/components/Badge.tsx +35 -0
  44. package/src/web/components/Pagination.stories.tsx +78 -0
  45. package/src/web/components/Pagination.tsx +153 -0
  46. package/src/web/components/StatusBadge.tsx +20 -0
  47. package/src/web/components/Table.stories.tsx +415 -0
  48. package/src/web/components/Table.tsx +3 -0
  49. package/src/web/components/TableActionButton.tsx +67 -0
  50. package/src/web/components/TableActionsMenu.tsx +208 -0
  51. package/src/web/components/TableIconText.tsx +23 -0
  52. package/src/web/components/TableRowStatusActions.tsx +33 -0
  53. package/src/web/components/TableStatusActionsCell.tsx +29 -0
  54. package/src/web/components/pagination-utils.ts +39 -0
  55. package/src/web/components/table/DataTable.tsx +123 -0
  56. package/src/web/components/table/TablePrimitives.tsx +214 -0
  57. package/src/web/components/table/TableScrollArea.tsx +51 -0
  58. package/src/web/components/table/constants.ts +59 -0
  59. package/src/web/components/table/data-table-class-names.ts +26 -0
  60. package/src/web/components/table/index.ts +44 -0
  61. package/src/web/components/table/table-row-context.tsx +19 -0
  62. package/src/web/components/table/use-horizontal-scroll-state.ts +72 -0
  63. package/src/web/components/table-scroll.css +81 -0
  64. package/src/web/hooks/useOnClickOutside.ts +20 -5
  65. package/src/web/icons/TableMenuActivateIcon.tsx +27 -0
  66. package/src/web/icons/TableMenuCheckIcon.tsx +27 -0
  67. package/src/web/icons/TableMenuCloseIcon.tsx +27 -0
  68. package/src/web/icons/TableMenuEditIcon.tsx +27 -0
  69. package/src/web/icons/TableMenuInfoIcon.tsx +27 -0
  70. package/src/web/icons/TableMenuRefreshIcon.tsx +27 -0
  71. package/src/web/icons/TableMenuSuspendIcon.tsx +34 -0
  72. package/src/web/icons/TableMenuTrashIcon.tsx +27 -0
  73. package/src/web/icons/TableMoreIcon.tsx +41 -0
  74. package/src/web/icons/TableSortIcon.tsx +37 -0
  75. package/src/web/index.ts +74 -0
  76. package/src/web/layout/AppHeader.stories.tsx +2 -0
  77. package/src/web/layout/AppHeader.tsx +18 -14
  78. package/src/web/layout/DashboardLayout.stories.tsx +1 -0
  79. package/src/web/layout/DashboardLayout.tsx +4 -4
  80. package/src/web/layout/story-helpers.tsx +10 -2
  81. package/dist/chunk-WDSMJWKC.js.map +0 -1
@@ -0,0 +1,462 @@
1
+ import {
2
+ cloneElement,
3
+ isValidElement,
4
+ useEffect,
5
+ useLayoutEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ type ComponentRef,
10
+ type ReactNode,
11
+ } from "react";
12
+ import {
13
+ Animated,
14
+ Easing,
15
+ Modal,
16
+ Pressable,
17
+ ScrollView,
18
+ StyleSheet,
19
+ Text,
20
+ View,
21
+ type StyleProp,
22
+ type ViewStyle,
23
+ } from "react-native";
24
+ import { ChevronDownIcon } from "../icons/ChevronDownIcon";
25
+ import {
26
+ getSelectFieldStyles,
27
+ SELECT_CHEVRON_COLOR,
28
+ SELECT_CHEVRON_ROTATION_MS,
29
+ SELECT_CHEVRON_SIZE,
30
+ SELECT_ICON_SIZE,
31
+ SELECT_LABEL_GAP,
32
+ SELECT_OPTION_GAP,
33
+ resolveInputVisualState,
34
+ resolveSelectDropdownHeight,
35
+ resolveSelectDropdownTop,
36
+ selectDropdownMetrics,
37
+ selectFieldMetrics,
38
+ selectLabelTypography,
39
+ } from "../theme/select";
40
+ import { colors } from "../theme/tokens";
41
+ import {
42
+ ensureSelectDropdownScrollStyles,
43
+ SELECT_DROPDOWN_SCROLL_CLASS,
44
+ } from "../utils/countryDropdownScrollStyles";
45
+ import {
46
+ formatMultiSelectDisplay,
47
+ resolveSelectedLabels,
48
+ } from "../utils/formatMultiSelectDisplay";
49
+ import { useApplyWebClassName } from "../utils/useApplyWebClassName";
50
+ import { Label } from "./Label";
51
+
52
+ type SelectIconProps = {
53
+ size?: number;
54
+ color?: string;
55
+ };
56
+
57
+ function renderSelectIcon(icon: ReactNode, color: string) {
58
+ if (!icon) return null;
59
+
60
+ if (isValidElement<SelectIconProps>(icon)) {
61
+ return cloneElement(icon, {
62
+ size: icon.props.size ?? SELECT_ICON_SIZE,
63
+ color: icon.props.color ?? color,
64
+ });
65
+ }
66
+
67
+ return icon;
68
+ }
69
+
70
+ export type SelectOption = {
71
+ value: string;
72
+ label: string;
73
+ disabled?: boolean;
74
+ };
75
+
76
+ type AnchorLayout = {
77
+ x: number;
78
+ y: number;
79
+ width: number;
80
+ height: number;
81
+ };
82
+
83
+ const useIsomorphicLayoutEffect =
84
+ typeof window === "undefined" ? useEffect : useLayoutEffect;
85
+
86
+ interface SelectBaseProps {
87
+ label?: string;
88
+ placeholder?: string;
89
+ options: SelectOption[];
90
+ leftIcon?: ReactNode;
91
+ disabled?: boolean;
92
+ error?: string;
93
+ invalid?: boolean;
94
+ hint?: string;
95
+ containerStyle?: StyleProp<ViewStyle>;
96
+ className?: string;
97
+ fieldClassName?: string;
98
+ menuClassName?: string;
99
+ labelClassName?: string;
100
+ errorClassName?: string;
101
+ hintClassName?: string;
102
+ }
103
+
104
+ export interface SingleSelectProps extends SelectBaseProps {
105
+ multiple?: false;
106
+ value?: string;
107
+ onValueChange?: (value: string) => void;
108
+ }
109
+
110
+ export interface MultipleSelectProps extends SelectBaseProps {
111
+ multiple: true;
112
+ value?: string[];
113
+ onValueChange?: (value: string[]) => void;
114
+ }
115
+
116
+ export type SelectProps = SingleSelectProps | MultipleSelectProps;
117
+
118
+ function normalizeSelectedValues(
119
+ multiple: boolean,
120
+ value: string | string[] | undefined,
121
+ ): string[] {
122
+ if (multiple) {
123
+ if (Array.isArray(value)) {
124
+ return value;
125
+ }
126
+
127
+ return value ? [value] : [];
128
+ }
129
+
130
+ return typeof value === "string" && value.length > 0 ? [value] : [];
131
+ }
132
+
133
+ export function Select({
134
+ label,
135
+ placeholder = "placeholder",
136
+ value,
137
+ onValueChange,
138
+ options,
139
+ leftIcon,
140
+ multiple = false,
141
+ disabled = false,
142
+ error,
143
+ invalid,
144
+ hint,
145
+ containerStyle,
146
+ className,
147
+ fieldClassName,
148
+ menuClassName,
149
+ labelClassName,
150
+ errorClassName,
151
+ hintClassName,
152
+ }: SelectProps) {
153
+ const wrapperRef = useRef<ComponentRef<typeof View>>(null);
154
+ const triggerRef = useRef<ComponentRef<typeof Pressable>>(null);
155
+ const menuRef = useRef<ComponentRef<typeof View>>(null);
156
+ const scrollRef = useRef<ComponentRef<typeof ScrollView>>(null);
157
+ const helperRef = useRef<ComponentRef<typeof Text>>(null);
158
+ const chevronRotation = useRef(new Animated.Value(0)).current;
159
+ const [open, setOpen] = useState(false);
160
+ const [anchor, setAnchor] = useState<AnchorLayout | null>(null);
161
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
162
+ const [valueContainerWidth, setValueContainerWidth] = useState(0);
163
+
164
+ useApplyWebClassName(wrapperRef, className);
165
+ useApplyWebClassName(triggerRef, fieldClassName);
166
+ useApplyWebClassName(menuRef, menuClassName);
167
+ useApplyWebClassName(scrollRef, SELECT_DROPDOWN_SCROLL_CLASS, open && Boolean(anchor));
168
+ useApplyWebClassName(helperRef, error ? errorClassName : hintClassName);
169
+
170
+ useIsomorphicLayoutEffect(() => {
171
+ ensureSelectDropdownScrollStyles();
172
+ }, []);
173
+
174
+ useEffect(() => {
175
+ Animated.timing(chevronRotation, {
176
+ toValue: open ? 1 : 0,
177
+ duration: SELECT_CHEVRON_ROTATION_MS,
178
+ easing: Easing.out(Easing.ease),
179
+ useNativeDriver: true,
180
+ }).start();
181
+ }, [chevronRotation, open]);
182
+
183
+ const chevronRotate = chevronRotation.interpolate({
184
+ inputRange: [0, 1],
185
+ outputRange: ["0deg", "180deg"],
186
+ });
187
+
188
+ const hasError = Boolean(error) || Boolean(invalid);
189
+ const visualState = resolveInputVisualState({
190
+ focused: open,
191
+ error: hasError,
192
+ disabled,
193
+ });
194
+ const fieldStyles = getSelectFieldStyles(visualState);
195
+
196
+ const selectedValues = useMemo(
197
+ () => normalizeSelectedValues(multiple, value),
198
+ [multiple, value],
199
+ );
200
+ const selectedValueSet = useMemo(() => new Set(selectedValues), [selectedValues]);
201
+ const selectedLabels = useMemo(
202
+ () => resolveSelectedLabels(options, selectedValues),
203
+ [options, selectedValues],
204
+ );
205
+ const selectableOptions = useMemo(
206
+ () => options.filter((option) => !option.disabled),
207
+ [options],
208
+ );
209
+ const selectableIndexByValue = useMemo(
210
+ () =>
211
+ new Map(
212
+ selectableOptions.map((option, index) => [option.value, index] as const),
213
+ ),
214
+ [selectableOptions],
215
+ );
216
+ const displayLabel = useMemo(() => {
217
+ if (selectedLabels.length === 0) {
218
+ return placeholder;
219
+ }
220
+
221
+ if (!multiple) {
222
+ return selectedLabels[0];
223
+ }
224
+
225
+ if (valueContainerWidth <= 0) {
226
+ return selectedLabels.join(", ");
227
+ }
228
+
229
+ return formatMultiSelectDisplay(selectedLabels, valueContainerWidth);
230
+ }, [multiple, placeholder, selectedLabels, valueContainerWidth]);
231
+
232
+ const isPlaceholder = selectedLabels.length === 0;
233
+ const dropdownHeight = resolveSelectDropdownHeight(options.length);
234
+ const helperMessage = error ?? hint;
235
+
236
+ function closeMenu() {
237
+ setOpen(false);
238
+ setAnchor(null);
239
+ setHighlightedIndex(-1);
240
+ }
241
+
242
+ function openMenu() {
243
+ if (disabled || selectableOptions.length === 0) {
244
+ return;
245
+ }
246
+
247
+ triggerRef.current?.measureInWindow((x, y, width, height) => {
248
+ const selectedIndex = selectableOptions.findIndex((option) =>
249
+ selectedValueSet.has(option.value),
250
+ );
251
+ setHighlightedIndex(selectedIndex >= 0 ? selectedIndex : 0);
252
+ setAnchor({ x, y, width, height });
253
+ setOpen(true);
254
+ });
255
+ }
256
+
257
+ function handleSelect(nextValue: string) {
258
+ if (multiple) {
259
+ const nextValues = selectedValues.includes(nextValue)
260
+ ? selectedValues.filter((item) => item !== nextValue)
261
+ : [...selectedValues, nextValue];
262
+
263
+ (onValueChange as MultipleSelectProps["onValueChange"] | undefined)?.(nextValues);
264
+ return;
265
+ }
266
+
267
+ (onValueChange as SingleSelectProps["onValueChange"] | undefined)?.(nextValue);
268
+ closeMenu();
269
+ }
270
+
271
+ const optionList = options.map((option, index) => {
272
+ const selectableIndex = selectableIndexByValue.get(option.value) ?? -1;
273
+ const isSelected = selectedValueSet.has(option.value);
274
+ const isHighlighted = selectableIndex === highlightedIndex;
275
+ const isLast = index === options.length - 1;
276
+
277
+ return (
278
+ <Pressable
279
+ key={option.value}
280
+ accessibilityRole="button"
281
+ accessibilityState={{ disabled: option.disabled, selected: isSelected }}
282
+ disabled={option.disabled}
283
+ onPress={() => {
284
+ if (!option.disabled) {
285
+ handleSelect(option.value);
286
+ }
287
+ }}
288
+ onHoverIn={() => {
289
+ if (!option.disabled && selectableIndex >= 0) {
290
+ setHighlightedIndex(selectableIndex);
291
+ }
292
+ }}
293
+ style={[
294
+ selectDropdownMetrics.option,
295
+ isSelected && selectDropdownMetrics.optionSelected,
296
+ isHighlighted && !isSelected && !option.disabled
297
+ ? selectDropdownMetrics.optionHighlighted
298
+ : null,
299
+ !isLast && styles.optionSpacing,
300
+ option.disabled && styles.optionDisabled,
301
+ ]}
302
+ >
303
+ <Text
304
+ style={[
305
+ selectDropdownMetrics.optionLabel,
306
+ isSelected && selectDropdownMetrics.optionLabelSelected,
307
+ option.disabled && styles.optionLabelDisabled,
308
+ ]}
309
+ numberOfLines={1}
310
+ >
311
+ {option.label}
312
+ </Text>
313
+ </Pressable>
314
+ );
315
+ });
316
+
317
+ const dropdownTop = anchor ? resolveSelectDropdownTop(anchor, dropdownHeight) : 0;
318
+
319
+ return (
320
+ <View ref={wrapperRef} style={[styles.wrapper, containerStyle]}>
321
+ {label ? (
322
+ <Label disabled={disabled} style={styles.label} className={labelClassName}>
323
+ {label}
324
+ </Label>
325
+ ) : null}
326
+
327
+ <View style={fieldStyles.outline}>
328
+ <Pressable
329
+ ref={triggerRef}
330
+ accessibilityRole="button"
331
+ accessibilityState={{ disabled, expanded: open }}
332
+ disabled={disabled}
333
+ onPress={open ? closeMenu : openMenu}
334
+ style={[selectFieldMetrics.container, fieldStyles.container]}
335
+ >
336
+ {leftIcon ? (
337
+ <View style={styles.iconSlot}>
338
+ {renderSelectIcon(leftIcon, fieldStyles.icon)}
339
+ </View>
340
+ ) : null}
341
+ <View
342
+ style={styles.valueContainer}
343
+ onLayout={(event) => {
344
+ setValueContainerWidth(event.nativeEvent.layout.width);
345
+ }}
346
+ >
347
+ <Text
348
+ style={[
349
+ selectFieldMetrics.value,
350
+ fieldStyles.value,
351
+ isPlaceholder && { color: fieldStyles.placeholder },
352
+ ]}
353
+ numberOfLines={1}
354
+ >
355
+ {displayLabel}
356
+ </Text>
357
+ </View>
358
+ <Animated.View
359
+ style={[styles.iconSlot, { transform: [{ rotate: chevronRotate }] }]}
360
+ >
361
+ <ChevronDownIcon
362
+ size={SELECT_CHEVRON_SIZE}
363
+ color={disabled ? colors.stormGray200 : SELECT_CHEVRON_COLOR}
364
+ />
365
+ </Animated.View>
366
+ </Pressable>
367
+ </View>
368
+
369
+ {helperMessage ? (
370
+ <Text ref={helperRef} style={error ? styles.error : styles.hint}>
371
+ {helperMessage}
372
+ </Text>
373
+ ) : null}
374
+
375
+ <Modal visible={open} transparent animationType="fade" onRequestClose={closeMenu}>
376
+ <Pressable style={styles.overlay} onPress={closeMenu}>
377
+ {anchor ? (
378
+ <View
379
+ ref={menuRef}
380
+ style={[
381
+ selectDropdownMetrics.menu,
382
+ styles.dropdown,
383
+ {
384
+ top: dropdownTop,
385
+ left: anchor.x,
386
+ width: anchor.width,
387
+ height: dropdownHeight,
388
+ },
389
+ ]}
390
+ >
391
+ <ScrollView
392
+ ref={scrollRef}
393
+ style={styles.dropdownScroll}
394
+ contentContainerStyle={styles.dropdownContent}
395
+ keyboardShouldPersistTaps="handled"
396
+ showsVerticalScrollIndicator
397
+ bounces={false}
398
+ nestedScrollEnabled
399
+ >
400
+ {optionList}
401
+ </ScrollView>
402
+ </View>
403
+ ) : null}
404
+ </Pressable>
405
+ </Modal>
406
+ </View>
407
+ );
408
+ }
409
+
410
+ const styles = StyleSheet.create({
411
+ wrapper: {
412
+ width: "100%",
413
+ gap: SELECT_LABEL_GAP,
414
+ },
415
+ label: {
416
+ ...selectLabelTypography,
417
+ },
418
+ iconSlot: {
419
+ width: SELECT_ICON_SIZE,
420
+ height: SELECT_ICON_SIZE,
421
+ alignItems: "center",
422
+ justifyContent: "center",
423
+ flexShrink: 0,
424
+ },
425
+ valueContainer: {
426
+ flex: 1,
427
+ minWidth: 0,
428
+ justifyContent: "center",
429
+ },
430
+ overlay: {
431
+ flex: 1,
432
+ backgroundColor: colors.transparent,
433
+ },
434
+ dropdown: {
435
+ position: "absolute",
436
+ },
437
+ dropdownScroll: {
438
+ flex: 1,
439
+ },
440
+ dropdownContent: {
441
+ flexGrow: 1,
442
+ },
443
+ optionSpacing: {
444
+ marginBottom: SELECT_OPTION_GAP,
445
+ },
446
+ optionDisabled: {
447
+ opacity: 0.5,
448
+ },
449
+ optionLabelDisabled: {
450
+ color: colors.stormGray300,
451
+ },
452
+ error: {
453
+ fontSize: 12,
454
+ lineHeight: 12,
455
+ color: colors.inputError,
456
+ },
457
+ hint: {
458
+ fontSize: 12,
459
+ lineHeight: 12,
460
+ color: colors.stormGray300,
461
+ },
462
+ });
package/src/css.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module '*.css';
@@ -0,0 +1,28 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { CALENDAR_ICON_BODY_PATH } from "./calendarIconPaths";
3
+
4
+ export { CALENDAR_ICON_BODY_PATH } from "./calendarIconPaths";
5
+
6
+ interface CalendarIconProps {
7
+ size?: number;
8
+ color?: string;
9
+ strokeWidth?: number;
10
+ }
11
+
12
+ export function CalendarIcon({
13
+ size = 20,
14
+ color = "#C7CDD1",
15
+ strokeWidth = 2,
16
+ }: CalendarIconProps) {
17
+ return (
18
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
19
+ <Path
20
+ d={CALENDAR_ICON_BODY_PATH}
21
+ stroke={color}
22
+ strokeWidth={strokeWidth}
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ />
26
+ </Svg>
27
+ );
28
+ }
@@ -0,0 +1,34 @@
1
+ import { CALENDAR_ICON_BODY_PATH } from "./calendarIconPaths";
2
+
3
+ interface CalendarIconProps {
4
+ size?: number;
5
+ color?: string;
6
+ strokeWidth?: number;
7
+ }
8
+
9
+ export function CalendarIcon({
10
+ size = 20,
11
+ color = "#C7CDD1",
12
+ strokeWidth = 2,
13
+ }: CalendarIconProps) {
14
+ return (
15
+ <svg
16
+ width={size}
17
+ height={size}
18
+ viewBox="0 0 20 20"
19
+ fill="none"
20
+ xmlns="http://www.w3.org/2000/svg"
21
+ aria-hidden
22
+ >
23
+ <path
24
+ d={CALENDAR_ICON_BODY_PATH}
25
+ stroke={color}
26
+ strokeWidth={strokeWidth}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ />
30
+ </svg>
31
+ );
32
+ }
33
+
34
+ export { CALENDAR_ICON_BODY_PATH } from "./calendarIconPaths";
@@ -1,7 +1,7 @@
1
1
  import Svg, { Path } from "react-native-svg";
2
- import { CHEVRON_DOWN_PATH } from "./chevronDownPath";
2
+ import { CHEVRON_DOWN_PATH, CHEVRON_DOWN_PATH_20 } from "./chevronDownPath";
3
3
 
4
- export { CHEVRON_DOWN_PATH } from "./chevronDownPath";
4
+ export { CHEVRON_DOWN_PATH, CHEVRON_DOWN_PATH_20 } from "./chevronDownPath";
5
5
 
6
6
  interface ChevronDownIconProps {
7
7
  size?: number;
@@ -15,10 +15,17 @@ export function ChevronDownIcon({
15
15
  color = "#dadde0",
16
16
  strokeWidth = 2,
17
17
  }: ChevronDownIconProps) {
18
+ const useLarge = size >= 20;
19
+
18
20
  return (
19
- <Svg width={size} height={size} viewBox="0 0 16 16" fill="none">
21
+ <Svg
22
+ width={size}
23
+ height={size}
24
+ viewBox={useLarge ? "0 0 20 20" : "0 0 16 16"}
25
+ fill="none"
26
+ >
20
27
  <Path
21
- d={CHEVRON_DOWN_PATH}
28
+ d={useLarge ? CHEVRON_DOWN_PATH_20 : CHEVRON_DOWN_PATH}
22
29
  stroke={color}
23
30
  strokeWidth={strokeWidth}
24
31
  strokeLinecap="round"
@@ -1,4 +1,4 @@
1
- import { CHEVRON_DOWN_PATH } from "./chevronDownPath";
1
+ import { CHEVRON_DOWN_PATH, CHEVRON_DOWN_PATH_20 } from "./chevronDownPath";
2
2
 
3
3
  interface ChevronDownIconProps {
4
4
  size?: number;
@@ -12,17 +12,19 @@ export function ChevronDownIcon({
12
12
  color = "#dadde0",
13
13
  strokeWidth = 2,
14
14
  }: ChevronDownIconProps) {
15
+ const useLarge = size >= 20;
16
+
15
17
  return (
16
18
  <svg
17
19
  width={size}
18
20
  height={size}
19
- viewBox="0 0 16 16"
21
+ viewBox={useLarge ? "0 0 20 20" : "0 0 16 16"}
20
22
  fill="none"
21
23
  xmlns="http://www.w3.org/2000/svg"
22
24
  aria-hidden
23
25
  >
24
26
  <path
25
- d={CHEVRON_DOWN_PATH}
27
+ d={useLarge ? CHEVRON_DOWN_PATH_20 : CHEVRON_DOWN_PATH}
26
28
  stroke={color}
27
29
  strokeWidth={strokeWidth}
28
30
  strokeLinecap="round"
@@ -32,4 +34,4 @@ export function ChevronDownIcon({
32
34
  );
33
35
  }
34
36
 
35
- export { CHEVRON_DOWN_PATH } from "./chevronDownPath";
37
+ export { CHEVRON_DOWN_PATH, CHEVRON_DOWN_PATH_20 } from "./chevronDownPath";
@@ -0,0 +1,44 @@
1
+ import Svg, { Path } from "react-native-svg";
2
+ import { CHEVRON_LEFT_PATH, CHEVRON_RIGHT_PATH } from "./chevronNavPaths";
3
+
4
+ interface ChevronNavIconProps {
5
+ size?: number;
6
+ color?: string;
7
+ strokeWidth?: number;
8
+ }
9
+
10
+ export function ChevronLeftIcon({
11
+ size = 20,
12
+ color = "#151a1e",
13
+ strokeWidth = 2,
14
+ }: ChevronNavIconProps) {
15
+ return (
16
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
17
+ <Path
18
+ d={CHEVRON_LEFT_PATH}
19
+ stroke={color}
20
+ strokeWidth={strokeWidth}
21
+ strokeLinecap="round"
22
+ strokeLinejoin="round"
23
+ />
24
+ </Svg>
25
+ );
26
+ }
27
+
28
+ export function ChevronRightIcon({
29
+ size = 20,
30
+ color = "#151a1e",
31
+ strokeWidth = 2,
32
+ }: ChevronNavIconProps) {
33
+ return (
34
+ <Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
35
+ <Path
36
+ d={CHEVRON_RIGHT_PATH}
37
+ stroke={color}
38
+ strokeWidth={strokeWidth}
39
+ strokeLinecap="round"
40
+ strokeLinejoin="round"
41
+ />
42
+ </Svg>
43
+ );
44
+ }
@@ -0,0 +1,57 @@
1
+ import { CHEVRON_LEFT_PATH, CHEVRON_RIGHT_PATH } from "./chevronNavPaths";
2
+
3
+ interface ChevronNavIconProps {
4
+ size?: number;
5
+ color?: string;
6
+ strokeWidth?: number;
7
+ }
8
+
9
+ export function ChevronLeftIcon({
10
+ size = 20,
11
+ color = "#151a1e",
12
+ strokeWidth = 2,
13
+ }: ChevronNavIconProps) {
14
+ return (
15
+ <svg
16
+ width={size}
17
+ height={size}
18
+ viewBox="0 0 20 20"
19
+ fill="none"
20
+ xmlns="http://www.w3.org/2000/svg"
21
+ aria-hidden
22
+ >
23
+ <path
24
+ d={CHEVRON_LEFT_PATH}
25
+ stroke={color}
26
+ strokeWidth={strokeWidth}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ />
30
+ </svg>
31
+ );
32
+ }
33
+
34
+ export function ChevronRightIcon({
35
+ size = 20,
36
+ color = "#151a1e",
37
+ strokeWidth = 2,
38
+ }: ChevronNavIconProps) {
39
+ return (
40
+ <svg
41
+ width={size}
42
+ height={size}
43
+ viewBox="0 0 20 20"
44
+ fill="none"
45
+ xmlns="http://www.w3.org/2000/svg"
46
+ aria-hidden
47
+ >
48
+ <path
49
+ d={CHEVRON_RIGHT_PATH}
50
+ stroke={color}
51
+ strokeWidth={strokeWidth}
52
+ strokeLinecap="round"
53
+ strokeLinejoin="round"
54
+ />
55
+ </svg>
56
+ );
57
+ }
@@ -0,0 +1,2 @@
1
+ export const CALENDAR_ICON_BODY_PATH =
2
+ "M6.66667 1.6665V4.99984M13.3333 1.6665V4.99984M2.5 8.33317H17.5M4.16667 3.33317H15.8333C16.7538 3.33317 17.5 4.07936 17.5 4.99984V16.6665C17.5 17.587 16.7538 18.3332 15.8333 18.3332H4.16667C3.24619 18.3332 2.5 17.587 2.5 16.6665V4.99984C2.5 4.07936 3.24619 3.33317 4.16667 3.33317Z";
@@ -1 +1,2 @@
1
1
  export const CHEVRON_DOWN_PATH = "M4 6L8 10L12 6";
2
+ export const CHEVRON_DOWN_PATH_20 = "M5 7.5L10 12.5L15 7.5";
@@ -0,0 +1,2 @@
1
+ export const CHEVRON_LEFT_PATH = "M12.5 15L7.5 10L12.5 5";
2
+ export const CHEVRON_RIGHT_PATH = "M7.5 15L12.5 10L7.5 5";