cpk-ui 0.0.1

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 (69) hide show
  1. package/README.md +27 -0
  2. package/components/modals/AlertDialog/AlertDialog.js +80 -0
  3. package/components/modals/AlertDialog/AlertDialog.stories.tsx +80 -0
  4. package/components/modals/AlertDialog/AlertDialog.tsx +194 -0
  5. package/components/modals/Snackbar/Snackbar.js +94 -0
  6. package/components/modals/Snackbar/Snackbar.stories.tsx +98 -0
  7. package/components/modals/Snackbar/Snackbar.tsx +174 -0
  8. package/components/modals/Snackbar/const.js +5 -0
  9. package/components/modals/Snackbar/const.ts +4 -0
  10. package/components/uis/Accordion/Accordion.js +11 -0
  11. package/components/uis/Accordion/Accordion.stories.tsx +38 -0
  12. package/components/uis/Accordion/Accordion.test.tsx +128 -0
  13. package/components/uis/Accordion/Accordion.tsx +58 -0
  14. package/components/uis/Accordion/AccordionItem.js +102 -0
  15. package/components/uis/Accordion/AccordionItem.tsx +213 -0
  16. package/components/uis/Button/Button.js +161 -0
  17. package/components/uis/Button/Button.stories.tsx +81 -0
  18. package/components/uis/Button/Button.test.tsx +560 -0
  19. package/components/uis/Button/Button.tsx +335 -0
  20. package/components/uis/Checkbox/Checkbox.js +70 -0
  21. package/components/uis/Checkbox/Checkbox.stories.tsx +46 -0
  22. package/components/uis/Checkbox/Checkbox.test.tsx +139 -0
  23. package/components/uis/Checkbox/Checkbox.tsx +150 -0
  24. package/components/uis/Icon/Icon.js +3780 -0
  25. package/components/uis/Icon/Icon.stories.tsx +45 -0
  26. package/components/uis/Icon/Icon.test.tsx +19 -0
  27. package/components/uis/Icon/Icon.tsx +3800 -0
  28. package/components/uis/Icon/Pretendard-Bold.otf +0 -0
  29. package/components/uis/Icon/Pretendard-Regular.otf +0 -0
  30. package/components/uis/Icon/Pretendard-Thin.otf +0 -0
  31. package/components/uis/Icon/cpk.ttf +0 -0
  32. package/components/uis/Icon/selection.json +1 -0
  33. package/components/uis/IconButton/IconButton.js +120 -0
  34. package/components/uis/IconButton/IconButton.test.tsx +165 -0
  35. package/components/uis/IconButton/IconButton.tsx +252 -0
  36. package/components/uis/LoadingIndicator/LoadingIndicator.js +24 -0
  37. package/components/uis/LoadingIndicator/LoadingIndicator.tsx +79 -0
  38. package/components/uis/Rating/Rating.js +53 -0
  39. package/components/uis/Rating/Rating.test.tsx +72 -0
  40. package/components/uis/Rating/Rating.tsx +155 -0
  41. package/components/uis/StatusbarBrightness/StatusBarBrightness.js +13 -0
  42. package/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +41 -0
  43. package/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +17 -0
  44. package/components/uis/Styled/StyledComponents.js +130 -0
  45. package/components/uis/Styled/StyledComponents.tsx +200 -0
  46. package/components/uis/SwitchToggle/SwitchToggle.js +126 -0
  47. package/components/uis/SwitchToggle/SwitchToggle.test.tsx +70 -0
  48. package/components/uis/SwitchToggle/SwitchToggle.tsx +224 -0
  49. package/components/uis/Typography/Typography.js +90 -0
  50. package/components/uis/Typography/Typography.test.tsx +58 -0
  51. package/components/uis/Typography/Typography.tsx +132 -0
  52. package/hooks/useDebouncedColorScheme.js +21 -0
  53. package/hooks/useDebouncedColorScheme.tsx +30 -0
  54. package/index.js +16 -0
  55. package/index.tsx +18 -0
  56. package/package.json +35 -0
  57. package/providers/ThemeProvider.js +106 -0
  58. package/providers/ThemeProvider.tsx +180 -0
  59. package/providers/index.js +62 -0
  60. package/providers/index.tsx +125 -0
  61. package/react-native.config.cjs +5 -0
  62. package/utils/colors.js +124 -0
  63. package/utils/colors.ts +127 -0
  64. package/utils/createCtx.js +15 -0
  65. package/utils/createCtx.tsx +26 -0
  66. package/utils/guards.js +44 -0
  67. package/utils/guards.ts +93 -0
  68. package/utils/theme.js +5 -0
  69. package/utils/theme.ts +33 -0
@@ -0,0 +1,335 @@
1
+ import React, {useCallback, useRef} from 'react';
2
+ import type {
3
+ Insets,
4
+ StyleProp,
5
+ TextStyle,
6
+ TouchableHighlightProps,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+ import {Platform, Text, TouchableHighlight, View} from 'react-native';
10
+ import {useHover} from 'react-native-web-hooks';
11
+ import styled, {css} from '@emotion/native';
12
+
13
+ import {useTheme} from '../../../providers/ThemeProvider';
14
+ import {cloneElemWithDefaultColors} from '../../../utils/guards';
15
+ import type {CpkTheme} from '../../../utils/theme';
16
+ import {LoadingIndicator} from '../LoadingIndicator/LoadingIndicator';
17
+
18
+ export type ButtonType = 'text' | 'solid' | 'outlined';
19
+ export type ButtonColorType =
20
+ | 'primary'
21
+ | 'secondary'
22
+ | 'success'
23
+ | 'danger'
24
+ | 'warning'
25
+ | 'info'
26
+ | 'light';
27
+ export type ButtonSizeType = 'small' | 'medium' | 'large';
28
+
29
+ export type Props = {
30
+ testID?: string;
31
+ type?: ButtonType;
32
+ color?: ButtonColorType;
33
+ size?: ButtonSizeType;
34
+ disabled?: boolean;
35
+ loading?: boolean;
36
+ loadingColor?: string;
37
+ loadingElement?: JSX.Element;
38
+ text?: string | JSX.Element;
39
+ borderRadius?: number;
40
+ startElement?: JSX.Element;
41
+ endElement?: JSX.Element;
42
+ style?: StyleProp<Omit<ViewStyle, 'borderRadius' | 'padding'>>;
43
+ styles?: Styles;
44
+ onPress?: TouchableHighlightProps['onPress'];
45
+ activeOpacity?: TouchableHighlightProps['activeOpacity'];
46
+ touchableHighlightProps?: Omit<
47
+ TouchableHighlightProps,
48
+ 'onPress' | 'activeOpacity' | 'style'
49
+ >;
50
+ hitSlop?: null | Insets | number | undefined;
51
+ };
52
+
53
+ type Styles = {
54
+ container?: StyleProp<ViewStyle>;
55
+ content?: StyleProp<ViewStyle>;
56
+ loading?: StyleProp<ViewStyle>;
57
+ text?: StyleProp<TextStyle>;
58
+ disabled?: StyleProp<ViewStyle>;
59
+ disabledText?: StyleProp<TextStyle>;
60
+ hovered?: StyleProp<ViewStyle>;
61
+ };
62
+
63
+ type ButtonContainerStyleProps = {
64
+ type: ButtonType;
65
+ size?: ButtonSizeType;
66
+ outlined?: boolean;
67
+ disabled?: boolean;
68
+ loading?: boolean;
69
+ };
70
+
71
+ const ButtonContainer = styled.View<ButtonContainerStyleProps>`
72
+ align-self: stretch;
73
+ flex-direction: row;
74
+ align-items: center;
75
+ justify-content: center;
76
+ `;
77
+
78
+ const calculateStyles = ({
79
+ theme,
80
+ type,
81
+ color,
82
+ size,
83
+ loading,
84
+ disabled,
85
+ borderRadius,
86
+ hovered,
87
+ styles,
88
+ }: {
89
+ theme: CpkTheme;
90
+ type: ButtonType;
91
+ color: ButtonColorType;
92
+ size: ButtonSizeType;
93
+ loading: boolean;
94
+ disabled: boolean;
95
+ borderRadius: number;
96
+ hovered: boolean;
97
+ styles?: Styles;
98
+ }): Styles => {
99
+ const isDisabled = disabled || loading;
100
+ const padding =
101
+ type === 'text'
102
+ ? '8px'
103
+ : size === 'large'
104
+ ? '16px 32px'
105
+ : size === 'small'
106
+ ? '8px 16px'
107
+ : '12px 24px';
108
+
109
+ return {
110
+ container: [
111
+ css`
112
+ border-radius: ${borderRadius + 'px'};
113
+ padding: ${padding};
114
+ background-color: ${isDisabled
115
+ ? theme.button.disabled.bg
116
+ : ['text', 'outlined'].includes(type)
117
+ ? theme.bg.basic
118
+ : theme.button[color].bg};
119
+ border-color: ${isDisabled
120
+ ? theme.bg.disabled
121
+ : type === 'text'
122
+ ? 'transparent'
123
+ : theme.button[color].bg};
124
+ border-width: ${type !== 'text' ? '1px' : 0};
125
+ `,
126
+ styles?.container,
127
+ ],
128
+ text: [
129
+ css`
130
+ font-family: 'Pretendard';
131
+ color: ${isDisabled
132
+ ? theme.button.disabled.text
133
+ : type === 'solid' || color === 'light'
134
+ ? theme.button[color].text
135
+ : theme.button[color].bg};
136
+ `,
137
+ styles?.text,
138
+ ],
139
+ content: [
140
+ css`
141
+ flex-direction: row;
142
+ align-items: center;
143
+ opacity: ${loading ? '0' : '1'};
144
+ gap: 8px;
145
+ `,
146
+ styles?.content,
147
+ ],
148
+ loading: [
149
+ css`
150
+ position: absolute;
151
+ opacity: ${loading ? '1' : '0'};
152
+ `,
153
+ styles?.loading,
154
+ ],
155
+ hovered: hovered
156
+ ? [
157
+ {
158
+ shadowColor: 'black',
159
+ shadowOpacity: 0.24,
160
+ shadowRadius: 16,
161
+ elevation: 10,
162
+ },
163
+ css`
164
+ border-radius: ${borderRadius + 'px'};
165
+ `,
166
+ styles?.hovered,
167
+ ]
168
+ : undefined,
169
+ disabled: isDisabled
170
+ ? [
171
+ css`
172
+ background-color: ${theme.button.disabled.bg};
173
+ border-color: ${theme.bg.disabled};
174
+ `,
175
+ styles?.disabled,
176
+ ]
177
+ : undefined,
178
+ };
179
+ };
180
+
181
+ const useButtonState = ({
182
+ disabled,
183
+ onPress,
184
+ loading,
185
+ }: {
186
+ disabled?: boolean;
187
+ onPress?: TouchableHighlightProps['onPress'];
188
+ loading: boolean;
189
+ }): {
190
+ innerDisabled: boolean;
191
+ isLoading: boolean;
192
+ } => {
193
+ return {
194
+ innerDisabled: disabled || !onPress,
195
+ isLoading: loading,
196
+ };
197
+ };
198
+
199
+ export function Button({
200
+ testID,
201
+ type = 'solid',
202
+ color = 'primary',
203
+ size = 'medium',
204
+ disabled,
205
+ loading = false,
206
+ loadingElement,
207
+ text,
208
+ startElement,
209
+ endElement,
210
+ style,
211
+ styles,
212
+ onPress,
213
+ activeOpacity = 0.8,
214
+ touchableHighlightProps,
215
+ borderRadius = 4,
216
+ loadingColor,
217
+ hitSlop = {top: 8, bottom: 8, left: 8, right: 8},
218
+ }: Props): JSX.Element {
219
+ const ref = useRef<React.ElementRef<typeof TouchableHighlight>>(null);
220
+ const hovered = useHover(ref);
221
+ const {theme} = useTheme();
222
+
223
+ const {innerDisabled} = useButtonState({disabled, onPress, loading});
224
+ const compositeStyles: Styles = calculateStyles({
225
+ theme,
226
+ type,
227
+ color,
228
+ size,
229
+ loading,
230
+ disabled: innerDisabled,
231
+ borderRadius,
232
+ hovered,
233
+ styles,
234
+ });
235
+
236
+ const LoadingView = loadingElement ?? (
237
+ <LoadingIndicator
238
+ color={
239
+ loadingColor || type === 'solid'
240
+ ? theme.text.contrast
241
+ : theme.text.basic
242
+ }
243
+ size="small"
244
+ />
245
+ );
246
+
247
+ const renderContainer = useCallback(
248
+ ({
249
+ children,
250
+ loadingView,
251
+ }: {
252
+ children: JSX.Element;
253
+ loadingView: JSX.Element;
254
+ }): JSX.Element => (
255
+ <ButtonContainer
256
+ disabled={innerDisabled}
257
+ size={size}
258
+ style={[
259
+ hovered && !innerDisabled && compositeStyles.hovered,
260
+ compositeStyles.container,
261
+ type === 'text' &&
262
+ css`
263
+ background-color: transparent;
264
+ `,
265
+ innerDisabled && compositeStyles.disabled,
266
+ ]}
267
+ testID={loading ? 'loading-view' : 'button-container'}
268
+ type={type}
269
+ >
270
+ <View style={compositeStyles.content}>{children}</View>
271
+ <View style={compositeStyles.loading}>{loadingView}</View>
272
+ </ButtonContainer>
273
+ ),
274
+ [
275
+ innerDisabled,
276
+ size,
277
+ compositeStyles.container,
278
+ compositeStyles.hovered,
279
+ compositeStyles.disabled,
280
+ compositeStyles.content,
281
+ compositeStyles.loading,
282
+ hovered,
283
+ type,
284
+ loading,
285
+ ],
286
+ );
287
+
288
+ const ChildView = (
289
+ <>
290
+ {cloneElemWithDefaultColors({
291
+ element: startElement,
292
+ backgroundColor: compositeStyles.container?.[0]?.backgroundColor,
293
+ color: compositeStyles.text?.[0]?.color,
294
+ })}
295
+ {!text || typeof text === 'string' ? (
296
+ <Text style={compositeStyles.text}>{text}</Text>
297
+ ) : (
298
+ text
299
+ )}
300
+ {cloneElemWithDefaultColors({
301
+ element: endElement,
302
+ backgroundColor: compositeStyles.container?.[0]?.backgroundColor,
303
+ color: compositeStyles.text?.[0]?.color,
304
+ })}
305
+ </>
306
+ );
307
+
308
+ return (
309
+ <TouchableHighlight
310
+ activeOpacity={activeOpacity}
311
+ delayPressIn={30}
312
+ disabled={innerDisabled || loading || !onPress}
313
+ hitSlop={hitSlop}
314
+ onPress={onPress}
315
+ ref={Platform.select({
316
+ web: ref,
317
+ default: undefined,
318
+ })}
319
+ style={[
320
+ style,
321
+ css`
322
+ border-radius: ${borderRadius + 'px'};
323
+ `,
324
+ ]}
325
+ testID={testID}
326
+ underlayColor={type === 'text' ? 'transparent' : theme.role.underlay}
327
+ {...touchableHighlightProps}
328
+ >
329
+ {renderContainer({
330
+ children: ChildView,
331
+ loadingView: LoadingView,
332
+ })}
333
+ </TouchableHighlight>
334
+ );
335
+ }
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { Animated, Platform, View } from 'react-native';
4
+ import styled, { css } from '@emotion/native';
5
+ import { Icon } from '../Icon/Icon';
6
+ import { CheckboxWrapper, CheckboxWrapperOutlined, } from '../Styled/StyledComponents';
7
+ import { useTheme } from '../../../providers/ThemeProvider';
8
+ const Container = styled.TouchableOpacity `
9
+ flex-direction: row;
10
+ align-items: center;
11
+ `;
12
+ const StyledCheckboxOutlined = styled(CheckboxWrapperOutlined) `
13
+ width: 20px;
14
+ height: 20px;
15
+ border-width: 1px;
16
+ margin: 0 6px;
17
+
18
+ justify-content: center;
19
+ align-items: center;
20
+ `;
21
+ const StyledCheckbox = styled(CheckboxWrapper) `
22
+ width: 20px;
23
+ height: 20px;
24
+ margin: 0 6px;
25
+
26
+ justify-content: center;
27
+ align-items: center;
28
+ `;
29
+ const StyledCheck = styled(Icon) `
30
+ font-weight: bold;
31
+ color: ${({ theme, checked }) => (checked ? theme.bg.basic : 'transparent')};
32
+ `;
33
+ export function Checkbox({ style, styles, endElement, startElement, color = 'primary', disabled = false, checked = false, onPress, }) {
34
+ const animatedValue = new Animated.Value(0);
35
+ const fadeAnim = useRef(animatedValue).current;
36
+ const scaleAnim = useRef(animatedValue).current;
37
+ const { theme } = useTheme();
38
+ useEffect(() => {
39
+ Animated.sequence([
40
+ Animated.spring(fadeAnim, {
41
+ toValue: !checked ? 0 : 1,
42
+ useNativeDriver: Platform.select({
43
+ web: false,
44
+ default: true,
45
+ }),
46
+ }),
47
+ Animated.spring(scaleAnim, {
48
+ toValue: !checked ? 0 : 1,
49
+ useNativeDriver: Platform.select({
50
+ web: false,
51
+ default: true,
52
+ }),
53
+ }),
54
+ ]).start();
55
+ }, [fadeAnim, scaleAnim, checked]);
56
+ return (_jsx(Container, { activeOpacity: 0.9, disabled: disabled, onPress: onPress, style: style, children: _jsxs(View, { style: [
57
+ css `
58
+ flex: 1;
59
+ padding: 6px 0;
60
+
61
+ flex-direction: row;
62
+ column-gap: 2px;
63
+ align-items: center;
64
+ `,
65
+ styles?.container,
66
+ ], children: [startElement, _jsx(StyledCheckboxOutlined, { checked: checked, disabled: disabled, style: styles?.checkbox, testID: "cpk-ui-checkbox", type: color, children: _jsx(StyledCheckbox, { checked: checked, disabled: disabled, style: [
67
+ styles?.checkbox,
68
+ { opacity: fadeAnim, transform: [{ scale: scaleAnim }] },
69
+ ], type: color, children: _jsx(StyledCheck, { checked: checked, name: "Check", style: styles?.check, theme: theme }) }) }), endElement] }) }));
70
+ }
@@ -0,0 +1,46 @@
1
+ import {type ComponentProps} from 'react';
2
+ import {Text} from 'react-native';
3
+ import {css} from '@emotion/native';
4
+ import type {Meta, StoryObj} from '@storybook/react';
5
+
6
+ import {withThemeProvider} from '../../../../.storybook/decorators';
7
+ import {Checkbox, CheckboxColor} from './Checkbox';
8
+ import {Typography} from '../Typography/Typography';
9
+
10
+ const colors: CheckboxColor[] = [
11
+ 'primary',
12
+ 'success',
13
+ 'info',
14
+ 'warning',
15
+ 'danger',
16
+ 'secondary',
17
+ ];
18
+
19
+ const meta = {
20
+ title: 'Checkbox',
21
+ component: (props) => <Checkbox {...props} />,
22
+ argTypes: {
23
+ color: {
24
+ control: 'select',
25
+ options: colors,
26
+ },
27
+ checked: {
28
+ defaultValue: false,
29
+ type: 'boolean',
30
+ },
31
+ },
32
+ decorators: [withThemeProvider],
33
+ } satisfies Meta<typeof Checkbox>;
34
+
35
+ export default meta;
36
+
37
+ type Story = StoryObj<typeof meta>;
38
+
39
+ export const Basic: Story = {
40
+ args: {
41
+ color: 'primary',
42
+ endElement: <Typography.Body2>Click Checkbox!</Typography.Body2>,
43
+ checked: false,
44
+ onPress: () => {},
45
+ },
46
+ };
@@ -0,0 +1,139 @@
1
+ import React from 'react';
2
+ import {View} from 'react-native';
3
+ import type {RenderAPI} from '@testing-library/react-native';
4
+ import {render} from '@testing-library/react-native';
5
+
6
+ import {createComponent} from '../../../../test/testUtils';
7
+ import {Checkbox, type CheckboxProps} from './Checkbox';
8
+ import {light} from '../../../utils/colors';
9
+
10
+ let testingLib: RenderAPI;
11
+
12
+ const Component = (props?: CheckboxProps): JSX.Element =>
13
+ createComponent(<Checkbox {...props} />);
14
+
15
+ describe('[Checkbox]', () => {
16
+ it('should render without crashing', () => {
17
+ testingLib = render(Component());
18
+
19
+ const json = testingLib.toJSON();
20
+
21
+ expect(json).toBeTruthy();
22
+ });
23
+
24
+ it('should render `disabled` button', () => {
25
+ testingLib = render(
26
+ Component({
27
+ disabled: true,
28
+ }),
29
+ );
30
+
31
+ const json = testingLib.toJSON();
32
+
33
+ expect(json).toBeTruthy();
34
+ });
35
+
36
+ it('should render `startElement`', () => {
37
+ testingLib = render(
38
+ Component({
39
+ startElement: <View />,
40
+ }),
41
+ );
42
+
43
+ const json = testingLib.toJSON();
44
+
45
+ expect(json).toBeTruthy();
46
+ });
47
+
48
+ it('should render `endElement`', () => {
49
+ testingLib = render(
50
+ Component({
51
+ endElement: <View />,
52
+ }),
53
+ );
54
+
55
+ const json = testingLib.toJSON();
56
+
57
+ expect(json).toBeTruthy();
58
+ });
59
+
60
+ describe('type', () => {
61
+ it('should render type==="info"', () => {
62
+ testingLib = render(
63
+ Component({
64
+ color: 'info',
65
+ checked: true,
66
+ }),
67
+ );
68
+
69
+ const checkbox = testingLib.getByTestId('cpk-ui-checkbox');
70
+
71
+ expect(checkbox.props.style.borderTopColor).toEqual(light.role.info);
72
+ });
73
+
74
+ it('should render type==="primary"', () => {
75
+ testingLib = render(
76
+ Component({
77
+ color: 'primary',
78
+ checked: true,
79
+ }),
80
+ );
81
+
82
+ const checkbox = testingLib.getByTestId('cpk-ui-checkbox');
83
+
84
+ expect(checkbox.props.style.borderTopColor).toEqual(light.role.primary);
85
+ });
86
+
87
+ it('should render type==="secondary"', () => {
88
+ testingLib = render(
89
+ Component({
90
+ color: 'secondary',
91
+ checked: true,
92
+ }),
93
+ );
94
+
95
+ const checkbox = testingLib.getByTestId('cpk-ui-checkbox');
96
+
97
+ expect(checkbox.props.style.borderTopColor).toEqual(light.role.secondary);
98
+ });
99
+
100
+ it('should render type==="success"', () => {
101
+ testingLib = render(
102
+ Component({
103
+ color: 'success',
104
+ checked: true,
105
+ }),
106
+ );
107
+
108
+ const checkbox = testingLib.getByTestId('cpk-ui-checkbox');
109
+
110
+ expect(checkbox.props.style.borderTopColor).toEqual(light.role.success);
111
+ });
112
+
113
+ it('should render type==="danger"', () => {
114
+ testingLib = render(
115
+ Component({
116
+ color: 'danger',
117
+ checked: true,
118
+ }),
119
+ );
120
+
121
+ const checkbox = testingLib.getByTestId('cpk-ui-checkbox');
122
+
123
+ expect(checkbox.props.style.borderTopColor).toEqual(light.role.danger);
124
+ });
125
+
126
+ it('should render type==="warning"', () => {
127
+ testingLib = render(
128
+ Component({
129
+ color: 'warning',
130
+ checked: true,
131
+ }),
132
+ );
133
+
134
+ const checkbox = testingLib.getByTestId('cpk-ui-checkbox');
135
+
136
+ expect(checkbox.props.style.borderTopColor).toEqual(light.role.warning);
137
+ });
138
+ });
139
+ });