@space-uy/pulsar-ui 0.2.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 (155) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +148 -0
  3. package/lib/module/components/Accordion.js +242 -0
  4. package/lib/module/components/Accordion.js.map +1 -0
  5. package/lib/module/components/BottomSheet.js +183 -0
  6. package/lib/module/components/BottomSheet.js.map +1 -0
  7. package/lib/module/components/Button.js +64 -0
  8. package/lib/module/components/Button.js.map +1 -0
  9. package/lib/module/components/ButtonContainer.js +118 -0
  10. package/lib/module/components/ButtonContainer.js.map +1 -0
  11. package/lib/module/components/CalendarPicker.js +374 -0
  12. package/lib/module/components/CalendarPicker.js.map +1 -0
  13. package/lib/module/components/Card.js +43 -0
  14. package/lib/module/components/Card.js.map +1 -0
  15. package/lib/module/components/Checkbox.js +122 -0
  16. package/lib/module/components/Checkbox.js.map +1 -0
  17. package/lib/module/components/Chip.js +50 -0
  18. package/lib/module/components/Chip.js.map +1 -0
  19. package/lib/module/components/CopyToClipboard.js +98 -0
  20. package/lib/module/components/CopyToClipboard.js.map +1 -0
  21. package/lib/module/components/Dialog.js +232 -0
  22. package/lib/module/components/Dialog.js.map +1 -0
  23. package/lib/module/components/Header.js +94 -0
  24. package/lib/module/components/Header.js.map +1 -0
  25. package/lib/module/components/Icon.js +22 -0
  26. package/lib/module/components/Icon.js.map +1 -0
  27. package/lib/module/components/IconButton.js +57 -0
  28. package/lib/module/components/IconButton.js.map +1 -0
  29. package/lib/module/components/Input.js +111 -0
  30. package/lib/module/components/Input.js.map +1 -0
  31. package/lib/module/components/InputContainer.js +104 -0
  32. package/lib/module/components/InputContainer.js.map +1 -0
  33. package/lib/module/components/LoadingIndicator.js +62 -0
  34. package/lib/module/components/LoadingIndicator.js.map +1 -0
  35. package/lib/module/components/OtpInput.js +85 -0
  36. package/lib/module/components/OtpInput.js.map +1 -0
  37. package/lib/module/components/OtpInputContainer.js +148 -0
  38. package/lib/module/components/OtpInputContainer.js.map +1 -0
  39. package/lib/module/components/Select.js +189 -0
  40. package/lib/module/components/Select.js.map +1 -0
  41. package/lib/module/components/Switch.js +74 -0
  42. package/lib/module/components/Switch.js.map +1 -0
  43. package/lib/module/components/Tabs.js +99 -0
  44. package/lib/module/components/Tabs.js.map +1 -0
  45. package/lib/module/components/Text.js +66 -0
  46. package/lib/module/components/Text.js.map +1 -0
  47. package/lib/module/components/TextArea.js +106 -0
  48. package/lib/module/components/TextArea.js.map +1 -0
  49. package/lib/module/hooks/useTheme.js +20 -0
  50. package/lib/module/hooks/useTheme.js.map +1 -0
  51. package/lib/module/index.js +27 -0
  52. package/lib/module/index.js.map +1 -0
  53. package/lib/module/package.json +1 -0
  54. package/lib/module/store/themeStore.js +50 -0
  55. package/lib/module/store/themeStore.js.map +1 -0
  56. package/lib/module/theme/colors.js +25 -0
  57. package/lib/module/theme/colors.js.map +1 -0
  58. package/lib/module/theme/meassures.js +10 -0
  59. package/lib/module/theme/meassures.js.map +1 -0
  60. package/lib/module/utils/stringUtils.js +12 -0
  61. package/lib/module/utils/stringUtils.js.map +1 -0
  62. package/lib/module/utils/uiUtils.js +63 -0
  63. package/lib/module/utils/uiUtils.js.map +1 -0
  64. package/lib/typescript/package.json +1 -0
  65. package/lib/typescript/src/components/Accordion.d.ts +22 -0
  66. package/lib/typescript/src/components/Accordion.d.ts.map +1 -0
  67. package/lib/typescript/src/components/BottomSheet.d.ts +13 -0
  68. package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -0
  69. package/lib/typescript/src/components/Button.d.ts +16 -0
  70. package/lib/typescript/src/components/Button.d.ts.map +1 -0
  71. package/lib/typescript/src/components/ButtonContainer.d.ts +30 -0
  72. package/lib/typescript/src/components/ButtonContainer.d.ts.map +1 -0
  73. package/lib/typescript/src/components/CalendarPicker.d.ts +19 -0
  74. package/lib/typescript/src/components/CalendarPicker.d.ts.map +1 -0
  75. package/lib/typescript/src/components/Card.d.ts +7 -0
  76. package/lib/typescript/src/components/Card.d.ts.map +1 -0
  77. package/lib/typescript/src/components/Checkbox.d.ts +11 -0
  78. package/lib/typescript/src/components/Checkbox.d.ts.map +1 -0
  79. package/lib/typescript/src/components/Chip.d.ts +9 -0
  80. package/lib/typescript/src/components/Chip.d.ts.map +1 -0
  81. package/lib/typescript/src/components/CopyToClipboard.d.ts +12 -0
  82. package/lib/typescript/src/components/CopyToClipboard.d.ts.map +1 -0
  83. package/lib/typescript/src/components/Dialog.d.ts +40 -0
  84. package/lib/typescript/src/components/Dialog.d.ts.map +1 -0
  85. package/lib/typescript/src/components/Header.d.ts +18 -0
  86. package/lib/typescript/src/components/Header.d.ts.map +1 -0
  87. package/lib/typescript/src/components/Icon.d.ts +12 -0
  88. package/lib/typescript/src/components/Icon.d.ts.map +1 -0
  89. package/lib/typescript/src/components/IconButton.d.ts +13 -0
  90. package/lib/typescript/src/components/IconButton.d.ts.map +1 -0
  91. package/lib/typescript/src/components/Input.d.ts +17 -0
  92. package/lib/typescript/src/components/Input.d.ts.map +1 -0
  93. package/lib/typescript/src/components/InputContainer.d.ts +22 -0
  94. package/lib/typescript/src/components/InputContainer.d.ts.map +1 -0
  95. package/lib/typescript/src/components/LoadingIndicator.d.ts +9 -0
  96. package/lib/typescript/src/components/LoadingIndicator.d.ts.map +1 -0
  97. package/lib/typescript/src/components/OtpInput.d.ts +3 -0
  98. package/lib/typescript/src/components/OtpInput.d.ts.map +1 -0
  99. package/lib/typescript/src/components/OtpInputContainer.d.ts +17 -0
  100. package/lib/typescript/src/components/OtpInputContainer.d.ts.map +1 -0
  101. package/lib/typescript/src/components/Select.d.ts +20 -0
  102. package/lib/typescript/src/components/Select.d.ts.map +1 -0
  103. package/lib/typescript/src/components/Switch.d.ts +10 -0
  104. package/lib/typescript/src/components/Switch.d.ts.map +1 -0
  105. package/lib/typescript/src/components/Tabs.d.ts +14 -0
  106. package/lib/typescript/src/components/Tabs.d.ts.map +1 -0
  107. package/lib/typescript/src/components/Text.d.ts +7 -0
  108. package/lib/typescript/src/components/Text.d.ts.map +1 -0
  109. package/lib/typescript/src/components/TextArea.d.ts +16 -0
  110. package/lib/typescript/src/components/TextArea.d.ts.map +1 -0
  111. package/lib/typescript/src/hooks/useTheme.d.ts +9 -0
  112. package/lib/typescript/src/hooks/useTheme.d.ts.map +1 -0
  113. package/lib/typescript/src/index.d.ts +27 -0
  114. package/lib/typescript/src/index.d.ts.map +1 -0
  115. package/lib/typescript/src/store/themeStore.d.ts +32 -0
  116. package/lib/typescript/src/store/themeStore.d.ts.map +1 -0
  117. package/lib/typescript/src/theme/colors.d.ts +14 -0
  118. package/lib/typescript/src/theme/colors.d.ts.map +1 -0
  119. package/lib/typescript/src/theme/meassures.d.ts +9 -0
  120. package/lib/typescript/src/theme/meassures.d.ts.map +1 -0
  121. package/lib/typescript/src/utils/stringUtils.d.ts +7 -0
  122. package/lib/typescript/src/utils/stringUtils.d.ts.map +1 -0
  123. package/lib/typescript/src/utils/uiUtils.d.ts +21 -0
  124. package/lib/typescript/src/utils/uiUtils.d.ts.map +1 -0
  125. package/package.json +173 -0
  126. package/src/components/Accordion.tsx +284 -0
  127. package/src/components/BottomSheet.tsx +259 -0
  128. package/src/components/Button.tsx +85 -0
  129. package/src/components/ButtonContainer.tsx +161 -0
  130. package/src/components/CalendarPicker.tsx +428 -0
  131. package/src/components/Card.tsx +55 -0
  132. package/src/components/Checkbox.tsx +160 -0
  133. package/src/components/Chip.tsx +58 -0
  134. package/src/components/CopyToClipboard.tsx +108 -0
  135. package/src/components/Dialog.tsx +263 -0
  136. package/src/components/Header.tsx +100 -0
  137. package/src/components/Icon.tsx +27 -0
  138. package/src/components/IconButton.tsx +71 -0
  139. package/src/components/Input.tsx +144 -0
  140. package/src/components/InputContainer.tsx +134 -0
  141. package/src/components/LoadingIndicator.tsx +78 -0
  142. package/src/components/OtpInput.tsx +109 -0
  143. package/src/components/OtpInputContainer.tsx +196 -0
  144. package/src/components/Select.tsx +219 -0
  145. package/src/components/Switch.tsx +104 -0
  146. package/src/components/Tabs.tsx +117 -0
  147. package/src/components/Text.tsx +64 -0
  148. package/src/components/TextArea.tsx +141 -0
  149. package/src/hooks/useTheme.tsx +23 -0
  150. package/src/index.tsx +38 -0
  151. package/src/store/themeStore.ts +57 -0
  152. package/src/theme/colors.ts +35 -0
  153. package/src/theme/meassures.ts +7 -0
  154. package/src/utils/stringUtils.ts +16 -0
  155. package/src/utils/uiUtils.ts +70 -0
@@ -0,0 +1,141 @@
1
+ import {
2
+ forwardRef,
3
+ useImperativeHandle,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import {
9
+ TextInput,
10
+ StyleSheet,
11
+ type StyleProp,
12
+ type ViewStyle,
13
+ type TextInputProps,
14
+ type TextInputFocusEventData,
15
+ type NativeSyntheticEvent,
16
+ Platform,
17
+ } from 'react-native';
18
+
19
+ import InputContainer from './InputContainer';
20
+
21
+ import useTheme from '../hooks/useTheme';
22
+
23
+ import { convertHexToRgba } from '../utils/uiUtils';
24
+ import Text from './Text';
25
+
26
+ type Props = TextInputProps & {
27
+ style?: StyleProp<ViewStyle>;
28
+ error?: boolean;
29
+ label?: string;
30
+ hint?: string;
31
+ numberOfLines?: number;
32
+ maxLength?: number;
33
+ onChangeText?: (text: string) => void;
34
+ };
35
+
36
+ export type InputRef = { focus: () => void; blur: () => void };
37
+
38
+ export const TextArea = forwardRef<InputRef, Props>(
39
+ (
40
+ {
41
+ style,
42
+ editable = true,
43
+ error,
44
+ label,
45
+ hint,
46
+ onBlur,
47
+ onFocus,
48
+ numberOfLines = 4,
49
+ maxLength = 1200,
50
+ onChangeText,
51
+ ...rest
52
+ },
53
+ ref
54
+ ) => {
55
+ const [focused, setFocused] = useState(false);
56
+ const { colors, theme } = useTheme();
57
+ const inputRef = useRef<TextInput>(null);
58
+
59
+ const value = useMemo(() => rest.value ?? '', [rest.value]);
60
+
61
+ useImperativeHandle(ref, () => ({
62
+ focus: () => inputRef.current?.focus(),
63
+ blur: () => inputRef.current?.blur(),
64
+ }));
65
+
66
+ const handleFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
67
+ setFocused(true);
68
+ onFocus?.(e);
69
+ };
70
+
71
+ const handleBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
72
+ setFocused(false);
73
+ onBlur?.(e);
74
+ };
75
+
76
+ const handleChangeText = (text: string) => {
77
+ if (maxLength && text.length > maxLength) return;
78
+ onChangeText?.(text);
79
+ };
80
+
81
+ return (
82
+ <InputContainer
83
+ style={style}
84
+ label={label}
85
+ hint={hint}
86
+ error={error}
87
+ onPress={() => inputRef.current?.focus()}
88
+ contentContainerStyle={styles.container}
89
+ focused={focused}
90
+ disabled={!editable}
91
+ height={numberOfLines * 22}
92
+ >
93
+ <TextInput
94
+ {...rest}
95
+ ref={inputRef}
96
+ style={[
97
+ styles.input,
98
+ {
99
+ fontFamily: theme.fonts.regular,
100
+ color: colors.foreground,
101
+ height: numberOfLines * 22,
102
+ // @ts-ignore
103
+ caretColor: colors.primary, // This to make the cursor color match the primary color on web
104
+ },
105
+ Platform.OS === 'web' && styles.webInput,
106
+ ]}
107
+ onFocus={handleFocus}
108
+ onBlur={handleBlur}
109
+ placeholderTextColor={convertHexToRgba(colors.foreground, 0.5)}
110
+ editable={editable}
111
+ numberOfLines={numberOfLines}
112
+ maxLength={maxLength}
113
+ textAlignVertical="top"
114
+ onChangeText={handleChangeText}
115
+ multiline
116
+ cursorColor={colors.primary}
117
+ selectionColor={convertHexToRgba(
118
+ colors.primary,
119
+ Platform.OS === 'android' ? 0.15 : 1
120
+ )}
121
+ selectionHandleColor={colors.primary}
122
+ />
123
+ <Text
124
+ variant="caption"
125
+ style={[styles.caption, { color: colors.foreground }]}
126
+ >
127
+ {`${value.length ?? 0} / ${maxLength}`}
128
+ </Text>
129
+ </InputContainer>
130
+ );
131
+ }
132
+ );
133
+
134
+ const styles = StyleSheet.create({
135
+ container: { flexDirection: 'column' },
136
+ input: { flex: 1, fontSize: 14, width: '100%', marginVertical: 8 },
137
+ caption: { marginBottom: 6, alignSelf: 'flex-end' },
138
+ webInput: { outlineWidth: 0 } as ViewStyle, // To remove native focus outline on web
139
+ });
140
+
141
+ export default TextArea;
@@ -0,0 +1,23 @@
1
+ import { useMemo } from 'react';
2
+ import themeStore from '../store/themeStore';
3
+
4
+ const useUIKitTheme = () => {
5
+ const theme = themeStore((state) => state.theme);
6
+ const setTheme = themeStore((state) => state.setTheme);
7
+ const colorScheme = themeStore((state) => state.colorScheme);
8
+ const setColorScheme = themeStore((state) => state.setColorScheme);
9
+ const colors = themeStore((state) => state.colors);
10
+
11
+ return useMemo(
12
+ () => ({
13
+ theme,
14
+ setTheme,
15
+ colorScheme,
16
+ setColorScheme,
17
+ colors,
18
+ }),
19
+ [theme, setTheme, colorScheme, setColorScheme, colors]
20
+ );
21
+ };
22
+
23
+ export default useUIKitTheme;
package/src/index.tsx ADDED
@@ -0,0 +1,38 @@
1
+ export { default as Button } from './components/Button';
2
+ export { default as Tabs, type Tab } from './components/Tabs';
3
+ export { default as Input } from './components/Input';
4
+ export { default as Text } from './components/Text';
5
+ export { default as Card } from './components/Card';
6
+ export { default as Select, type SelectOption } from './components/Select';
7
+ export { default as useUIKitTheme } from './hooks/useTheme';
8
+ export type { Theme } from './store/themeStore';
9
+ export { default as LoadingIndicator } from './components/LoadingIndicator';
10
+ export { default as Checkbox } from './components/Checkbox';
11
+ export { default as Chip } from './components/Chip';
12
+ export { default as Icon, type IconName } from './components/Icon';
13
+ export { default as IconButton } from './components/IconButton';
14
+ export { default as Header } from './components/Header';
15
+ export { default as Switch } from './components/Switch';
16
+ export {
17
+ default as ButtonContainer,
18
+ ButtonVariant,
19
+ ButtonSize,
20
+ } from './components/ButtonContainer';
21
+ export { convertHexToRgba } from './utils/uiUtils';
22
+ export { default as BottomSheet } from './components/BottomSheet';
23
+ export { default as TextArea } from './components/TextArea';
24
+ export { OtpInputContainer } from './components/OtpInputContainer';
25
+ export { OtpInput } from './components/OtpInput';
26
+ export { type OtpInputContainerRef } from './components/OtpInputContainer';
27
+ export { default as CalendarPicker } from './components/CalendarPicker';
28
+ export { default as Accordion, AccordionItem } from './components/Accordion';
29
+ export { CopyToClipboard } from './components/CopyToClipboard';
30
+ export {
31
+ default as Dialog,
32
+ DialogHeader,
33
+ DialogTitle,
34
+ DialogDescription,
35
+ DialogFooter,
36
+ DialogAction,
37
+ DialogCancel,
38
+ } from './components/Dialog';
@@ -0,0 +1,57 @@
1
+ import { create } from 'zustand';
2
+ import type { ColorSchemeName } from 'react-native';
3
+
4
+ import { darkColors, lightColors, type ColorPalette } from '../theme/colors';
5
+
6
+ type Theme = {
7
+ colors: { dark: ColorPalette; light: ColorPalette };
8
+ fonts: { light: string; regular: string; medium: string; bold: string };
9
+ roundness: number;
10
+ insets: { top: number; left: number; right: number; bottom: number };
11
+ };
12
+
13
+ const defaultTheme: Theme = {
14
+ colors: { dark: darkColors, light: lightColors },
15
+ fonts: {
16
+ light: 'Inter_300Light',
17
+ regular: 'Inter_400Regular',
18
+ medium: 'Inter_500Medium',
19
+ bold: 'Inter_700Bold',
20
+ },
21
+ roundness: 6,
22
+ insets: {
23
+ top: 0,
24
+ left: 0,
25
+ right: 0,
26
+ bottom: 0,
27
+ },
28
+ };
29
+
30
+ type ThemeStore = {
31
+ theme: Theme;
32
+ colorScheme: ColorSchemeName;
33
+ colors: ColorPalette;
34
+ setTheme: (theme: Theme) => void;
35
+ setColorScheme: (colorScheme: ColorSchemeName) => void;
36
+ };
37
+
38
+ const themeStore = create<ThemeStore>((set, get) => ({
39
+ theme: defaultTheme,
40
+ colorScheme: 'light',
41
+ colors: lightColors,
42
+ setTheme: (theme: Theme) => {
43
+ const { colorScheme } = get();
44
+ const colors =
45
+ colorScheme === 'dark' ? theme.colors.dark : theme.colors.light;
46
+ set({ theme, colors });
47
+ },
48
+ setColorScheme: (colorScheme: ColorSchemeName) => {
49
+ const { theme } = get();
50
+ const colors =
51
+ colorScheme === 'dark' ? theme.colors.dark : theme.colors.light;
52
+ set({ colorScheme, colors });
53
+ },
54
+ }));
55
+
56
+ export type { Theme, ThemeStore };
57
+ export default themeStore;
@@ -0,0 +1,35 @@
1
+ export type ColorPalette = {
2
+ primary: string;
3
+ foregroundOnPrimary: string;
4
+ background: string;
5
+ altBackground: string;
6
+ foreground: string;
7
+ altForeground: string;
8
+ destructive: string;
9
+ border: string;
10
+ foregroundOnDestructive: string;
11
+ };
12
+
13
+ export const darkColors: ColorPalette = {
14
+ primary: '#FAFAFA',
15
+ foregroundOnPrimary: '#09090B',
16
+ background: '#09090B',
17
+ altBackground: '#09090B',
18
+ foreground: '#FAFAFA',
19
+ altForeground: '#18181B',
20
+ border: '#27272A',
21
+ destructive: '#DC2626',
22
+ foregroundOnDestructive: '#FFFFFF',
23
+ };
24
+
25
+ export const lightColors: ColorPalette = {
26
+ primary: '#09090B',
27
+ foregroundOnPrimary: '#FFFFFF',
28
+ background: '#FFFFFF',
29
+ altBackground: '#FFFFFF',
30
+ foreground: '#09090B',
31
+ altForeground: '#FAFAFA',
32
+ border: '#E4E4E7',
33
+ destructive: '#DC2626',
34
+ foregroundOnDestructive: '#FFFFFF',
35
+ };
@@ -0,0 +1,7 @@
1
+ export default {
2
+ button: {
3
+ small: 24,
4
+ medium: 32,
5
+ large: 40,
6
+ },
7
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Converts a date string in "yyyy-MM-dd" format to an ISO string
3
+ * @param dateString - Date string in "yyyy-MM-dd" format
4
+ * @returns ISO string representation of the date with time set to midnight
5
+ */
6
+ export const convertDateToISOString = (dateString: string) => {
7
+ const dateArray = dateString.split('-').map(Number);
8
+ return new Date(
9
+ dateArray[0]!,
10
+ (dateArray[1]! - 1)!,
11
+ dateArray[2]!,
12
+ 0,
13
+ 0,
14
+ 0
15
+ ).toISOString();
16
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Converts a hex color code to an rgba color string with the specified opacity
3
+ * @param hex - The hex color code (e.g. '#FF0000')
4
+ * @param opacity - The opacity value between 0 and 1
5
+ * @returns An rgba color string (e.g. 'rgba(255,0,0,0.5)')
6
+ */
7
+ export const convertHexToRgba = (hex: string, opacity: number): string => {
8
+ const tempHex = hex.replace('#', '');
9
+ const red = parseInt(tempHex.substring(0, 2), 16);
10
+ const green = parseInt(tempHex.substring(2, 4), 16);
11
+ const blue = parseInt(tempHex.substring(4, 6), 16);
12
+
13
+ return `rgba(${red},${green},${blue},${opacity})`;
14
+ };
15
+
16
+ /**
17
+ * Calculates the contrast ratio between two colors according to WCAG guidelines
18
+ * @param color1 - The first color in hex format (e.g. '#FF0000')
19
+ * @param color2 - The second color in hex format (e.g. '#FFFFFF')
20
+ * @returns The contrast ratio as a number (higher values indicate better contrast)
21
+ */
22
+ export const getColorContrastRatio = (
23
+ color1: string,
24
+ color2: string
25
+ ): number => {
26
+ // Convert hex colors to RGB
27
+ const getRGB = (color: string): [number, number, number] => {
28
+ const hex = color.replace('#', '');
29
+ const r = parseInt(hex.substring(0, 2), 16);
30
+ const g = parseInt(hex.substring(2, 4), 16);
31
+ const b = parseInt(hex.substring(4, 6), 16);
32
+ return [r, g, b];
33
+ };
34
+
35
+ // Calculate relative luminance
36
+ const getRelativeLuminance = (r: number, g: number, b: number): number => {
37
+ const mappedValues = [r, g, b].map((c) => {
38
+ const s = c / 255;
39
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
40
+ });
41
+ const [rs, gs, bs] = mappedValues as [number, number, number];
42
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
43
+ };
44
+
45
+ const [r1, g1, b1] = getRGB(color1);
46
+ const [r2, g2, b2] = getRGB(color2);
47
+
48
+ const l1 = getRelativeLuminance(r1, g1, b1);
49
+ const l2 = getRelativeLuminance(r2, g2, b2);
50
+
51
+ const lighter = Math.max(l1, l2);
52
+ const darker = Math.min(l1, l2);
53
+
54
+ return (lighter + 0.05) / (darker + 0.05);
55
+ };
56
+
57
+ /**
58
+ * Determines whether black or white text would be more readable on a given background color
59
+ * @param backgroundColor - The background color in hex format (e.g. '#FF0000')
60
+ * @returns Either 'black' or 'white' depending on which provides better contrast
61
+ */
62
+ export const getAccessibleTextColor = (
63
+ backgroundColor: string
64
+ ): 'black' | 'white' => {
65
+ const blackContrast = getColorContrastRatio(backgroundColor, '#000000');
66
+ const whiteContrast = getColorContrastRatio(backgroundColor, '#FFFFFF');
67
+
68
+ // Return the color with higher contrast ratio
69
+ return blackContrast > whiteContrast ? 'black' : 'white';
70
+ };