@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.
- package/LICENSE +20 -0
- package/README.md +148 -0
- package/lib/module/components/Accordion.js +242 -0
- package/lib/module/components/Accordion.js.map +1 -0
- package/lib/module/components/BottomSheet.js +183 -0
- package/lib/module/components/BottomSheet.js.map +1 -0
- package/lib/module/components/Button.js +64 -0
- package/lib/module/components/Button.js.map +1 -0
- package/lib/module/components/ButtonContainer.js +118 -0
- package/lib/module/components/ButtonContainer.js.map +1 -0
- package/lib/module/components/CalendarPicker.js +374 -0
- package/lib/module/components/CalendarPicker.js.map +1 -0
- package/lib/module/components/Card.js +43 -0
- package/lib/module/components/Card.js.map +1 -0
- package/lib/module/components/Checkbox.js +122 -0
- package/lib/module/components/Checkbox.js.map +1 -0
- package/lib/module/components/Chip.js +50 -0
- package/lib/module/components/Chip.js.map +1 -0
- package/lib/module/components/CopyToClipboard.js +98 -0
- package/lib/module/components/CopyToClipboard.js.map +1 -0
- package/lib/module/components/Dialog.js +232 -0
- package/lib/module/components/Dialog.js.map +1 -0
- package/lib/module/components/Header.js +94 -0
- package/lib/module/components/Header.js.map +1 -0
- package/lib/module/components/Icon.js +22 -0
- package/lib/module/components/Icon.js.map +1 -0
- package/lib/module/components/IconButton.js +57 -0
- package/lib/module/components/IconButton.js.map +1 -0
- package/lib/module/components/Input.js +111 -0
- package/lib/module/components/Input.js.map +1 -0
- package/lib/module/components/InputContainer.js +104 -0
- package/lib/module/components/InputContainer.js.map +1 -0
- package/lib/module/components/LoadingIndicator.js +62 -0
- package/lib/module/components/LoadingIndicator.js.map +1 -0
- package/lib/module/components/OtpInput.js +85 -0
- package/lib/module/components/OtpInput.js.map +1 -0
- package/lib/module/components/OtpInputContainer.js +148 -0
- package/lib/module/components/OtpInputContainer.js.map +1 -0
- package/lib/module/components/Select.js +189 -0
- package/lib/module/components/Select.js.map +1 -0
- package/lib/module/components/Switch.js +74 -0
- package/lib/module/components/Switch.js.map +1 -0
- package/lib/module/components/Tabs.js +99 -0
- package/lib/module/components/Tabs.js.map +1 -0
- package/lib/module/components/Text.js +66 -0
- package/lib/module/components/Text.js.map +1 -0
- package/lib/module/components/TextArea.js +106 -0
- package/lib/module/components/TextArea.js.map +1 -0
- package/lib/module/hooks/useTheme.js +20 -0
- package/lib/module/hooks/useTheme.js.map +1 -0
- package/lib/module/index.js +27 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/store/themeStore.js +50 -0
- package/lib/module/store/themeStore.js.map +1 -0
- package/lib/module/theme/colors.js +25 -0
- package/lib/module/theme/colors.js.map +1 -0
- package/lib/module/theme/meassures.js +10 -0
- package/lib/module/theme/meassures.js.map +1 -0
- package/lib/module/utils/stringUtils.js +12 -0
- package/lib/module/utils/stringUtils.js.map +1 -0
- package/lib/module/utils/uiUtils.js +63 -0
- package/lib/module/utils/uiUtils.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/components/Accordion.d.ts +22 -0
- package/lib/typescript/src/components/Accordion.d.ts.map +1 -0
- package/lib/typescript/src/components/BottomSheet.d.ts +13 -0
- package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -0
- package/lib/typescript/src/components/Button.d.ts +16 -0
- package/lib/typescript/src/components/Button.d.ts.map +1 -0
- package/lib/typescript/src/components/ButtonContainer.d.ts +30 -0
- package/lib/typescript/src/components/ButtonContainer.d.ts.map +1 -0
- package/lib/typescript/src/components/CalendarPicker.d.ts +19 -0
- package/lib/typescript/src/components/CalendarPicker.d.ts.map +1 -0
- package/lib/typescript/src/components/Card.d.ts +7 -0
- package/lib/typescript/src/components/Card.d.ts.map +1 -0
- package/lib/typescript/src/components/Checkbox.d.ts +11 -0
- package/lib/typescript/src/components/Checkbox.d.ts.map +1 -0
- package/lib/typescript/src/components/Chip.d.ts +9 -0
- package/lib/typescript/src/components/Chip.d.ts.map +1 -0
- package/lib/typescript/src/components/CopyToClipboard.d.ts +12 -0
- package/lib/typescript/src/components/CopyToClipboard.d.ts.map +1 -0
- package/lib/typescript/src/components/Dialog.d.ts +40 -0
- package/lib/typescript/src/components/Dialog.d.ts.map +1 -0
- package/lib/typescript/src/components/Header.d.ts +18 -0
- package/lib/typescript/src/components/Header.d.ts.map +1 -0
- package/lib/typescript/src/components/Icon.d.ts +12 -0
- package/lib/typescript/src/components/Icon.d.ts.map +1 -0
- package/lib/typescript/src/components/IconButton.d.ts +13 -0
- package/lib/typescript/src/components/IconButton.d.ts.map +1 -0
- package/lib/typescript/src/components/Input.d.ts +17 -0
- package/lib/typescript/src/components/Input.d.ts.map +1 -0
- package/lib/typescript/src/components/InputContainer.d.ts +22 -0
- package/lib/typescript/src/components/InputContainer.d.ts.map +1 -0
- package/lib/typescript/src/components/LoadingIndicator.d.ts +9 -0
- package/lib/typescript/src/components/LoadingIndicator.d.ts.map +1 -0
- package/lib/typescript/src/components/OtpInput.d.ts +3 -0
- package/lib/typescript/src/components/OtpInput.d.ts.map +1 -0
- package/lib/typescript/src/components/OtpInputContainer.d.ts +17 -0
- package/lib/typescript/src/components/OtpInputContainer.d.ts.map +1 -0
- package/lib/typescript/src/components/Select.d.ts +20 -0
- package/lib/typescript/src/components/Select.d.ts.map +1 -0
- package/lib/typescript/src/components/Switch.d.ts +10 -0
- package/lib/typescript/src/components/Switch.d.ts.map +1 -0
- package/lib/typescript/src/components/Tabs.d.ts +14 -0
- package/lib/typescript/src/components/Tabs.d.ts.map +1 -0
- package/lib/typescript/src/components/Text.d.ts +7 -0
- package/lib/typescript/src/components/Text.d.ts.map +1 -0
- package/lib/typescript/src/components/TextArea.d.ts +16 -0
- package/lib/typescript/src/components/TextArea.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useTheme.d.ts +9 -0
- package/lib/typescript/src/hooks/useTheme.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +27 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/store/themeStore.d.ts +32 -0
- package/lib/typescript/src/store/themeStore.d.ts.map +1 -0
- package/lib/typescript/src/theme/colors.d.ts +14 -0
- package/lib/typescript/src/theme/colors.d.ts.map +1 -0
- package/lib/typescript/src/theme/meassures.d.ts +9 -0
- package/lib/typescript/src/theme/meassures.d.ts.map +1 -0
- package/lib/typescript/src/utils/stringUtils.d.ts +7 -0
- package/lib/typescript/src/utils/stringUtils.d.ts.map +1 -0
- package/lib/typescript/src/utils/uiUtils.d.ts +21 -0
- package/lib/typescript/src/utils/uiUtils.d.ts.map +1 -0
- package/package.json +173 -0
- package/src/components/Accordion.tsx +284 -0
- package/src/components/BottomSheet.tsx +259 -0
- package/src/components/Button.tsx +85 -0
- package/src/components/ButtonContainer.tsx +161 -0
- package/src/components/CalendarPicker.tsx +428 -0
- package/src/components/Card.tsx +55 -0
- package/src/components/Checkbox.tsx +160 -0
- package/src/components/Chip.tsx +58 -0
- package/src/components/CopyToClipboard.tsx +108 -0
- package/src/components/Dialog.tsx +263 -0
- package/src/components/Header.tsx +100 -0
- package/src/components/Icon.tsx +27 -0
- package/src/components/IconButton.tsx +71 -0
- package/src/components/Input.tsx +144 -0
- package/src/components/InputContainer.tsx +134 -0
- package/src/components/LoadingIndicator.tsx +78 -0
- package/src/components/OtpInput.tsx +109 -0
- package/src/components/OtpInputContainer.tsx +196 -0
- package/src/components/Select.tsx +219 -0
- package/src/components/Switch.tsx +104 -0
- package/src/components/Tabs.tsx +117 -0
- package/src/components/Text.tsx +64 -0
- package/src/components/TextArea.tsx +141 -0
- package/src/hooks/useTheme.tsx +23 -0
- package/src/index.tsx +38 -0
- package/src/store/themeStore.ts +57 -0
- package/src/theme/colors.ts +35 -0
- package/src/theme/meassures.ts +7 -0
- package/src/utils/stringUtils.ts +16 -0
- 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,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
|
+
};
|