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.
- package/README.md +27 -0
- package/components/modals/AlertDialog/AlertDialog.js +80 -0
- package/components/modals/AlertDialog/AlertDialog.stories.tsx +80 -0
- package/components/modals/AlertDialog/AlertDialog.tsx +194 -0
- package/components/modals/Snackbar/Snackbar.js +94 -0
- package/components/modals/Snackbar/Snackbar.stories.tsx +98 -0
- package/components/modals/Snackbar/Snackbar.tsx +174 -0
- package/components/modals/Snackbar/const.js +5 -0
- package/components/modals/Snackbar/const.ts +4 -0
- package/components/uis/Accordion/Accordion.js +11 -0
- package/components/uis/Accordion/Accordion.stories.tsx +38 -0
- package/components/uis/Accordion/Accordion.test.tsx +128 -0
- package/components/uis/Accordion/Accordion.tsx +58 -0
- package/components/uis/Accordion/AccordionItem.js +102 -0
- package/components/uis/Accordion/AccordionItem.tsx +213 -0
- package/components/uis/Button/Button.js +161 -0
- package/components/uis/Button/Button.stories.tsx +81 -0
- package/components/uis/Button/Button.test.tsx +560 -0
- package/components/uis/Button/Button.tsx +335 -0
- package/components/uis/Checkbox/Checkbox.js +70 -0
- package/components/uis/Checkbox/Checkbox.stories.tsx +46 -0
- package/components/uis/Checkbox/Checkbox.test.tsx +139 -0
- package/components/uis/Checkbox/Checkbox.tsx +150 -0
- package/components/uis/Icon/Icon.js +3780 -0
- package/components/uis/Icon/Icon.stories.tsx +45 -0
- package/components/uis/Icon/Icon.test.tsx +19 -0
- package/components/uis/Icon/Icon.tsx +3800 -0
- package/components/uis/Icon/Pretendard-Bold.otf +0 -0
- package/components/uis/Icon/Pretendard-Regular.otf +0 -0
- package/components/uis/Icon/Pretendard-Thin.otf +0 -0
- package/components/uis/Icon/cpk.ttf +0 -0
- package/components/uis/Icon/selection.json +1 -0
- package/components/uis/IconButton/IconButton.js +120 -0
- package/components/uis/IconButton/IconButton.test.tsx +165 -0
- package/components/uis/IconButton/IconButton.tsx +252 -0
- package/components/uis/LoadingIndicator/LoadingIndicator.js +24 -0
- package/components/uis/LoadingIndicator/LoadingIndicator.tsx +79 -0
- package/components/uis/Rating/Rating.js +53 -0
- package/components/uis/Rating/Rating.test.tsx +72 -0
- package/components/uis/Rating/Rating.tsx +155 -0
- package/components/uis/StatusbarBrightness/StatusBarBrightness.js +13 -0
- package/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +41 -0
- package/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +17 -0
- package/components/uis/Styled/StyledComponents.js +130 -0
- package/components/uis/Styled/StyledComponents.tsx +200 -0
- package/components/uis/SwitchToggle/SwitchToggle.js +126 -0
- package/components/uis/SwitchToggle/SwitchToggle.test.tsx +70 -0
- package/components/uis/SwitchToggle/SwitchToggle.tsx +224 -0
- package/components/uis/Typography/Typography.js +90 -0
- package/components/uis/Typography/Typography.test.tsx +58 -0
- package/components/uis/Typography/Typography.tsx +132 -0
- package/hooks/useDebouncedColorScheme.js +21 -0
- package/hooks/useDebouncedColorScheme.tsx +30 -0
- package/index.js +16 -0
- package/index.tsx +18 -0
- package/package.json +35 -0
- package/providers/ThemeProvider.js +106 -0
- package/providers/ThemeProvider.tsx +180 -0
- package/providers/index.js +62 -0
- package/providers/index.tsx +125 -0
- package/react-native.config.cjs +5 -0
- package/utils/colors.js +124 -0
- package/utils/colors.ts +127 -0
- package/utils/createCtx.js +15 -0
- package/utils/createCtx.tsx +26 -0
- package/utils/guards.js +44 -0
- package/utils/guards.ts +93 -0
- package/utils/theme.js +5 -0
- package/utils/theme.ts +33 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { Platform, TouchableHighlight, View } from 'react-native';
|
|
4
|
+
import { useHover } from 'react-native-web-hooks';
|
|
5
|
+
import { css } from '@emotion/native';
|
|
6
|
+
import { Icon } from '../Icon/Icon';
|
|
7
|
+
import { LoadingIndicator } from '../LoadingIndicator/LoadingIndicator';
|
|
8
|
+
import { getTheme } from '../../../utils/theme';
|
|
9
|
+
import { useTheme } from '../../../providers/ThemeProvider';
|
|
10
|
+
export const ButtonStyles = ({ theme, type = 'solid', color = 'primary', loading, disabled, }) => {
|
|
11
|
+
theme = getTheme(theme);
|
|
12
|
+
let backgroundColor = theme.button[color].bg;
|
|
13
|
+
let borderColor = theme.button[color].bg;
|
|
14
|
+
let iconColor = theme.button[color].bg;
|
|
15
|
+
if (disabled) {
|
|
16
|
+
backgroundColor = theme.button.disabled.bg;
|
|
17
|
+
borderColor = theme.button.disabled.text;
|
|
18
|
+
iconColor = theme.button.disabled.text;
|
|
19
|
+
}
|
|
20
|
+
if (['text', 'outlined'].includes(type)) {
|
|
21
|
+
backgroundColor = theme.bg.basic;
|
|
22
|
+
}
|
|
23
|
+
if (type === 'solid' || color === 'light') {
|
|
24
|
+
iconColor = theme.button[color].text;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
backgroundColor,
|
|
28
|
+
borderColor,
|
|
29
|
+
borderWidth: type === 'outlined' ? 1 : 0,
|
|
30
|
+
iconColor,
|
|
31
|
+
disabledBackgroundColor: type === 'solid' && !loading ? theme.button.disabled.bg : theme.bg.basic,
|
|
32
|
+
disabledBorderColor: theme.bg.disabled,
|
|
33
|
+
disabledTextColor: theme.button.disabled.text,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export function IconButton({ testID, type = 'solid', color = 'primary', size = 'medium', disabled, loading = false, loadingElement, icon, iconElement, style, styles, onPress, activeOpacity = 0.95, touchableHighlightProps, }) {
|
|
37
|
+
const ref = useRef(null);
|
|
38
|
+
const hovered = useHover(ref);
|
|
39
|
+
const { theme } = useTheme();
|
|
40
|
+
const { backgroundColor, borderColor, borderWidth, iconColor, disabledBackgroundColor, disabledBorderColor, } = ButtonStyles({
|
|
41
|
+
theme,
|
|
42
|
+
type,
|
|
43
|
+
color,
|
|
44
|
+
loading,
|
|
45
|
+
disabled,
|
|
46
|
+
});
|
|
47
|
+
const iconSize = size === 'large'
|
|
48
|
+
? 32
|
|
49
|
+
: size === 'medium'
|
|
50
|
+
? 24
|
|
51
|
+
: size === 'small'
|
|
52
|
+
? 16
|
|
53
|
+
: size;
|
|
54
|
+
const borderWidthStr = `${borderWidth + 'px'}`;
|
|
55
|
+
const borderRadiusStr = `99px`;
|
|
56
|
+
const compositeStyles = {
|
|
57
|
+
container: [
|
|
58
|
+
css `
|
|
59
|
+
background-color: ${backgroundColor};
|
|
60
|
+
border-color: ${borderColor};
|
|
61
|
+
border-width: ${borderWidthStr};
|
|
62
|
+
padding: 12px;
|
|
63
|
+
`,
|
|
64
|
+
styles?.container,
|
|
65
|
+
],
|
|
66
|
+
icon: [
|
|
67
|
+
css `
|
|
68
|
+
color: ${iconColor};
|
|
69
|
+
`,
|
|
70
|
+
styles?.icon,
|
|
71
|
+
],
|
|
72
|
+
disabled: [
|
|
73
|
+
css `
|
|
74
|
+
background-color: ${disabledBackgroundColor};
|
|
75
|
+
border-color: ${disabledBorderColor};
|
|
76
|
+
`,
|
|
77
|
+
styles?.disabled,
|
|
78
|
+
],
|
|
79
|
+
hovered: [
|
|
80
|
+
{
|
|
81
|
+
shadowColor: theme.text.basic,
|
|
82
|
+
shadowOpacity: 0.24,
|
|
83
|
+
shadowRadius: 16,
|
|
84
|
+
elevation: 10,
|
|
85
|
+
},
|
|
86
|
+
styles?.hovered,
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
return (_jsx(View, { style: [
|
|
90
|
+
css `
|
|
91
|
+
flex-direction: row;
|
|
92
|
+
border-radius: ${borderRadiusStr};
|
|
93
|
+
`,
|
|
94
|
+
style,
|
|
95
|
+
], children: _jsx(TouchableHighlight, { activeOpacity: activeOpacity, delayPressIn: 50, disabled: disabled || loading, onPress: onPress, ref: Platform.select({
|
|
96
|
+
web: ref,
|
|
97
|
+
default: undefined,
|
|
98
|
+
}), style: css `
|
|
99
|
+
border-radius: ${borderRadiusStr};
|
|
100
|
+
`, testID: testID, underlayColor: theme.role.underlay, ...touchableHighlightProps, children: _jsx(View, { style: [
|
|
101
|
+
css `
|
|
102
|
+
padding: 4px;
|
|
103
|
+
border-radius: ${borderRadiusStr};
|
|
104
|
+
|
|
105
|
+
flex-direction: row;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
align-items: center;
|
|
108
|
+
`,
|
|
109
|
+
compositeStyles.container,
|
|
110
|
+
hovered && !disabled && compositeStyles.hovered,
|
|
111
|
+
disabled && compositeStyles.disabled,
|
|
112
|
+
], testID: loading ? 'loading-view' : 'button-container', children: loading
|
|
113
|
+
? loadingElement || (_jsx(LoadingIndicator, { color: theme.text.disabled, size: "small", style: css `
|
|
114
|
+
justify-content: center;
|
|
115
|
+
align-items: center;
|
|
116
|
+
height: ${iconSize + 'px'};
|
|
117
|
+
width: ${iconSize + 'px'};
|
|
118
|
+
` }))
|
|
119
|
+
: iconElement || (_jsx(Icon, { color: iconColor, name: icon || 'QuestBoxFill', size: iconSize, style: compositeStyles?.icon })) }) }) }));
|
|
120
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type {RenderAPI} from '@testing-library/react-native';
|
|
3
|
+
import {render} from '@testing-library/react-native';
|
|
4
|
+
|
|
5
|
+
import {createComponent} from '../../../../test/testUtils';
|
|
6
|
+
import type {IconButtonProps} from './IconButton';
|
|
7
|
+
import {IconButton} from './IconButton';
|
|
8
|
+
|
|
9
|
+
let testingLib: RenderAPI;
|
|
10
|
+
|
|
11
|
+
jest.mock('react-native-web-hooks', () => ({
|
|
12
|
+
useHover: () => true,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const Component = (props?: IconButtonProps): JSX.Element =>
|
|
16
|
+
createComponent(<IconButton {...props} />);
|
|
17
|
+
|
|
18
|
+
describe('[IconButton]', () => {
|
|
19
|
+
it('should render without crashing', () => {
|
|
20
|
+
testingLib = render(Component());
|
|
21
|
+
|
|
22
|
+
const json = testingLib.toJSON();
|
|
23
|
+
|
|
24
|
+
expect(json).toBeTruthy();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should render `outlined` button', () => {
|
|
28
|
+
testingLib = render(
|
|
29
|
+
Component({
|
|
30
|
+
type: 'outlined',
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const json = testingLib.toJSON();
|
|
35
|
+
|
|
36
|
+
expect(json).toBeTruthy();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('loading', () => {
|
|
40
|
+
it('should render `loading` button', () => {
|
|
41
|
+
testingLib = render(
|
|
42
|
+
Component({
|
|
43
|
+
loading: true,
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const json = testingLib.toJSON();
|
|
48
|
+
|
|
49
|
+
expect(json).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should render `disabled` button', () => {
|
|
53
|
+
testingLib = render(
|
|
54
|
+
Component({
|
|
55
|
+
loading: true,
|
|
56
|
+
disabled: true,
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const json = testingLib.toJSON();
|
|
61
|
+
|
|
62
|
+
expect(json).toBeTruthy();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('size', () => {
|
|
67
|
+
it('should renders large size', () => {
|
|
68
|
+
testingLib = render(
|
|
69
|
+
Component({
|
|
70
|
+
size: 'large',
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const json = testingLib.toJSON();
|
|
75
|
+
|
|
76
|
+
expect(json).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should renders small size', () => {
|
|
80
|
+
testingLib = render(
|
|
81
|
+
Component({
|
|
82
|
+
size: 'small',
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const json = testingLib.toJSON();
|
|
87
|
+
|
|
88
|
+
expect(json).toBeTruthy();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('type', () => {
|
|
93
|
+
it('should render color==="info"', () => {
|
|
94
|
+
testingLib = render(
|
|
95
|
+
Component({
|
|
96
|
+
color: 'info',
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const json = testingLib.toJSON();
|
|
101
|
+
|
|
102
|
+
expect(json).toBeTruthy();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should renders primary color', () => {
|
|
106
|
+
testingLib = render(
|
|
107
|
+
Component({
|
|
108
|
+
color: 'primary',
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const json = testingLib.toJSON();
|
|
113
|
+
|
|
114
|
+
expect(json).toBeTruthy();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should renders secondary color', () => {
|
|
118
|
+
testingLib = render(
|
|
119
|
+
Component({
|
|
120
|
+
color: 'secondary',
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const json = testingLib.toJSON();
|
|
125
|
+
|
|
126
|
+
expect(json).toBeTruthy();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should renders danger color', () => {
|
|
130
|
+
testingLib = render(
|
|
131
|
+
Component({
|
|
132
|
+
color: 'danger',
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const json = testingLib.toJSON();
|
|
137
|
+
|
|
138
|
+
expect(json).toBeTruthy();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should renders warning color', () => {
|
|
142
|
+
testingLib = render(
|
|
143
|
+
Component({
|
|
144
|
+
color: 'warning',
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const json = testingLib.toJSON();
|
|
149
|
+
|
|
150
|
+
expect(json).toBeTruthy();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should render `disabled` button', () => {
|
|
155
|
+
testingLib = render(
|
|
156
|
+
Component({
|
|
157
|
+
disabled: true,
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const json = testingLib.toJSON();
|
|
162
|
+
|
|
163
|
+
expect(json).toBeTruthy();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import React, {useRef} from 'react';
|
|
2
|
+
import type {StyleProp, TouchableHighlightProps, ViewStyle} from 'react-native';
|
|
3
|
+
import {Platform, TouchableHighlight, View} from 'react-native';
|
|
4
|
+
import {useHover} from 'react-native-web-hooks';
|
|
5
|
+
import {css} from '@emotion/native';
|
|
6
|
+
|
|
7
|
+
import type {IconName} from '../Icon/Icon';
|
|
8
|
+
import {Icon} from '../Icon/Icon';
|
|
9
|
+
import {LoadingIndicator} from '../LoadingIndicator/LoadingIndicator';
|
|
10
|
+
import {CpkTheme, getTheme} from '../../../utils/theme';
|
|
11
|
+
import {useTheme} from '../../../providers/ThemeProvider';
|
|
12
|
+
|
|
13
|
+
type Styles = {
|
|
14
|
+
container?: StyleProp<ViewStyle>;
|
|
15
|
+
icon?: StyleProp<ViewStyle>;
|
|
16
|
+
disabled?: StyleProp<ViewStyle>;
|
|
17
|
+
hovered?: StyleProp<ViewStyle>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type ButtonType = 'text' | 'solid' | 'outlined';
|
|
21
|
+
|
|
22
|
+
type ButtonColorType =
|
|
23
|
+
| 'primary'
|
|
24
|
+
| 'secondary'
|
|
25
|
+
| 'success'
|
|
26
|
+
| 'danger'
|
|
27
|
+
| 'warning'
|
|
28
|
+
| 'info'
|
|
29
|
+
| 'light';
|
|
30
|
+
|
|
31
|
+
type ButtonSizeType = 'small' | 'medium' | 'large' | number;
|
|
32
|
+
|
|
33
|
+
export const ButtonStyles = ({
|
|
34
|
+
theme,
|
|
35
|
+
type = 'solid',
|
|
36
|
+
color = 'primary',
|
|
37
|
+
loading,
|
|
38
|
+
disabled,
|
|
39
|
+
}: {
|
|
40
|
+
theme?: CpkTheme;
|
|
41
|
+
type?: ButtonType;
|
|
42
|
+
color?: ButtonColorType;
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
loading?: boolean;
|
|
45
|
+
}): {
|
|
46
|
+
padding?: number;
|
|
47
|
+
backgroundColor?: string;
|
|
48
|
+
borderColor?: string;
|
|
49
|
+
borderWidth?: number;
|
|
50
|
+
iconColor?: string;
|
|
51
|
+
disabledBackgroundColor: string;
|
|
52
|
+
disabledBorderColor: string;
|
|
53
|
+
disabledTextColor: string;
|
|
54
|
+
} => {
|
|
55
|
+
theme = getTheme(theme);
|
|
56
|
+
|
|
57
|
+
let backgroundColor = theme.button[color].bg;
|
|
58
|
+
let borderColor = theme.button[color].bg;
|
|
59
|
+
let iconColor = theme.button[color].bg;
|
|
60
|
+
|
|
61
|
+
if (disabled) {
|
|
62
|
+
backgroundColor = theme.button.disabled.bg;
|
|
63
|
+
borderColor = theme.button.disabled.text;
|
|
64
|
+
iconColor = theme.button.disabled.text;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (['text', 'outlined'].includes(type)) {
|
|
68
|
+
backgroundColor = theme.bg.basic;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (type === 'solid' || color === 'light') {
|
|
72
|
+
iconColor = theme.button[color].text;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
backgroundColor,
|
|
77
|
+
borderColor,
|
|
78
|
+
borderWidth: type === 'outlined' ? 1 : 0,
|
|
79
|
+
iconColor,
|
|
80
|
+
disabledBackgroundColor:
|
|
81
|
+
type === 'solid' && !loading ? theme.button.disabled.bg : theme.bg.basic,
|
|
82
|
+
disabledBorderColor: theme.bg.disabled,
|
|
83
|
+
disabledTextColor: theme.button.disabled.text,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type IconButtonProps = {
|
|
88
|
+
testID?: string;
|
|
89
|
+
type?: ButtonType;
|
|
90
|
+
color?: ButtonColorType;
|
|
91
|
+
size?: ButtonSizeType | number;
|
|
92
|
+
disabled?: boolean;
|
|
93
|
+
loading?: boolean;
|
|
94
|
+
loadingElement?: JSX.Element;
|
|
95
|
+
icon?: IconName;
|
|
96
|
+
iconElement?: JSX.Element;
|
|
97
|
+
style?: StyleProp<Omit<ViewStyle, 'borderRadius' | 'padding'>>;
|
|
98
|
+
styles?: Styles;
|
|
99
|
+
onPress?: TouchableHighlightProps['onPress'];
|
|
100
|
+
activeOpacity?: number;
|
|
101
|
+
touchableHighlightProps?: Omit<TouchableHighlightProps, 'onPress' | 'style'>;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export function IconButton({
|
|
105
|
+
testID,
|
|
106
|
+
type = 'solid',
|
|
107
|
+
color = 'primary',
|
|
108
|
+
size = 'medium',
|
|
109
|
+
disabled,
|
|
110
|
+
loading = false,
|
|
111
|
+
loadingElement,
|
|
112
|
+
icon,
|
|
113
|
+
iconElement,
|
|
114
|
+
style,
|
|
115
|
+
styles,
|
|
116
|
+
onPress,
|
|
117
|
+
activeOpacity = 0.95,
|
|
118
|
+
touchableHighlightProps,
|
|
119
|
+
}: IconButtonProps): JSX.Element {
|
|
120
|
+
const ref = useRef<React.ElementRef<typeof TouchableHighlight>>(null);
|
|
121
|
+
const hovered = useHover(ref);
|
|
122
|
+
const {theme} = useTheme();
|
|
123
|
+
|
|
124
|
+
const {
|
|
125
|
+
backgroundColor,
|
|
126
|
+
borderColor,
|
|
127
|
+
borderWidth,
|
|
128
|
+
iconColor,
|
|
129
|
+
disabledBackgroundColor,
|
|
130
|
+
disabledBorderColor,
|
|
131
|
+
} = ButtonStyles({
|
|
132
|
+
theme,
|
|
133
|
+
type,
|
|
134
|
+
color,
|
|
135
|
+
loading,
|
|
136
|
+
disabled,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const iconSize =
|
|
140
|
+
size === 'large'
|
|
141
|
+
? 32
|
|
142
|
+
: size === 'medium'
|
|
143
|
+
? 24
|
|
144
|
+
: size === 'small'
|
|
145
|
+
? 16
|
|
146
|
+
: size;
|
|
147
|
+
|
|
148
|
+
const borderWidthStr = `${borderWidth + 'px'}`;
|
|
149
|
+
const borderRadiusStr = `99px`;
|
|
150
|
+
|
|
151
|
+
const compositeStyles: Styles = {
|
|
152
|
+
container: [
|
|
153
|
+
css`
|
|
154
|
+
background-color: ${backgroundColor};
|
|
155
|
+
border-color: ${borderColor};
|
|
156
|
+
border-width: ${borderWidthStr};
|
|
157
|
+
padding: 12px;
|
|
158
|
+
`,
|
|
159
|
+
styles?.container,
|
|
160
|
+
],
|
|
161
|
+
icon: [
|
|
162
|
+
css`
|
|
163
|
+
color: ${iconColor};
|
|
164
|
+
`,
|
|
165
|
+
styles?.icon,
|
|
166
|
+
],
|
|
167
|
+
disabled: [
|
|
168
|
+
css`
|
|
169
|
+
background-color: ${disabledBackgroundColor};
|
|
170
|
+
border-color: ${disabledBorderColor};
|
|
171
|
+
`,
|
|
172
|
+
styles?.disabled,
|
|
173
|
+
],
|
|
174
|
+
hovered: [
|
|
175
|
+
{
|
|
176
|
+
shadowColor: theme.text.basic,
|
|
177
|
+
shadowOpacity: 0.24,
|
|
178
|
+
shadowRadius: 16,
|
|
179
|
+
elevation: 10,
|
|
180
|
+
},
|
|
181
|
+
styles?.hovered,
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<View
|
|
187
|
+
style={[
|
|
188
|
+
css`
|
|
189
|
+
flex-direction: row;
|
|
190
|
+
border-radius: ${borderRadiusStr};
|
|
191
|
+
`,
|
|
192
|
+
style,
|
|
193
|
+
]}
|
|
194
|
+
>
|
|
195
|
+
<TouchableHighlight
|
|
196
|
+
activeOpacity={activeOpacity}
|
|
197
|
+
delayPressIn={50}
|
|
198
|
+
disabled={disabled || loading}
|
|
199
|
+
onPress={onPress}
|
|
200
|
+
ref={Platform.select({
|
|
201
|
+
web: ref,
|
|
202
|
+
default: undefined,
|
|
203
|
+
})}
|
|
204
|
+
style={css`
|
|
205
|
+
border-radius: ${borderRadiusStr};
|
|
206
|
+
`}
|
|
207
|
+
testID={testID}
|
|
208
|
+
underlayColor={theme.role.underlay}
|
|
209
|
+
{...touchableHighlightProps}
|
|
210
|
+
>
|
|
211
|
+
<View
|
|
212
|
+
style={[
|
|
213
|
+
css`
|
|
214
|
+
padding: 4px;
|
|
215
|
+
border-radius: ${borderRadiusStr};
|
|
216
|
+
|
|
217
|
+
flex-direction: row;
|
|
218
|
+
justify-content: center;
|
|
219
|
+
align-items: center;
|
|
220
|
+
`,
|
|
221
|
+
compositeStyles.container,
|
|
222
|
+
hovered && !disabled && compositeStyles.hovered,
|
|
223
|
+
disabled && compositeStyles.disabled,
|
|
224
|
+
]}
|
|
225
|
+
testID={loading ? 'loading-view' : 'button-container'}
|
|
226
|
+
>
|
|
227
|
+
{loading
|
|
228
|
+
? loadingElement || (
|
|
229
|
+
<LoadingIndicator
|
|
230
|
+
color={theme.text.disabled}
|
|
231
|
+
size="small"
|
|
232
|
+
style={css`
|
|
233
|
+
justify-content: center;
|
|
234
|
+
align-items: center;
|
|
235
|
+
height: ${iconSize + 'px'};
|
|
236
|
+
width: ${iconSize + 'px'};
|
|
237
|
+
`}
|
|
238
|
+
/>
|
|
239
|
+
)
|
|
240
|
+
: iconElement || (
|
|
241
|
+
<Icon
|
|
242
|
+
color={iconColor}
|
|
243
|
+
name={icon || 'QuestBoxFill'}
|
|
244
|
+
size={iconSize}
|
|
245
|
+
style={compositeStyles?.icon}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
</View>
|
|
249
|
+
</TouchableHighlight>
|
|
250
|
+
</View>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ActivityIndicator, Image } from "react-native";
|
|
3
|
+
import styled from "@emotion/native";
|
|
4
|
+
import { useTheme } from "../../../providers/ThemeProvider";
|
|
5
|
+
const Container = styled.View ``;
|
|
6
|
+
export function LoadingIndicator({ customElement, style, styles, size = "large", color, imgSource, }) {
|
|
7
|
+
const { theme } = useTheme();
|
|
8
|
+
const handleImgSourceType = (src) => {
|
|
9
|
+
if (typeof src === "string") {
|
|
10
|
+
return {
|
|
11
|
+
uri: src,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return src;
|
|
15
|
+
};
|
|
16
|
+
return (_jsx(Container, { style: style, children: customElement ? (typeof customElement === "function" ? (customElement()) : (customElement)) : !imgSource ? (_jsx(ActivityIndicator, { color: color || theme.role.secondary, size: size, style: styles?.activityIndicator })) : (_jsx(Image, { source: handleImgSourceType(imgSource), style: [
|
|
17
|
+
size === "large"
|
|
18
|
+
? { width: 50, height: 50 }
|
|
19
|
+
: size === "small"
|
|
20
|
+
? { width: 30, height: 30 }
|
|
21
|
+
: undefined,
|
|
22
|
+
styles?.image,
|
|
23
|
+
] })) }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type {
|
|
3
|
+
ImageSourcePropType,
|
|
4
|
+
ImageStyle,
|
|
5
|
+
StyleProp,
|
|
6
|
+
ViewStyle,
|
|
7
|
+
} from "react-native";
|
|
8
|
+
import { ActivityIndicator, Image } from "react-native";
|
|
9
|
+
import styled from "@emotion/native";
|
|
10
|
+
import { useTheme } from "../../../providers/ThemeProvider";
|
|
11
|
+
|
|
12
|
+
type Styles = {
|
|
13
|
+
activityIndicator?: ViewStyle;
|
|
14
|
+
image?: ImageStyle;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type Props = {
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
styles?: Styles;
|
|
20
|
+
color?: string;
|
|
21
|
+
size?: ActivityIndicator["props"]["size"];
|
|
22
|
+
imgSource?: string | ImageSourcePropType;
|
|
23
|
+
customElement?: JSX.Element | (() => JSX.Element);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const Container = styled.View``;
|
|
27
|
+
|
|
28
|
+
export function LoadingIndicator({
|
|
29
|
+
customElement,
|
|
30
|
+
style,
|
|
31
|
+
styles,
|
|
32
|
+
size = "large",
|
|
33
|
+
color,
|
|
34
|
+
imgSource,
|
|
35
|
+
}: Props): JSX.Element {
|
|
36
|
+
const { theme } = useTheme();
|
|
37
|
+
|
|
38
|
+
const handleImgSourceType = (
|
|
39
|
+
src: string | ImageSourcePropType
|
|
40
|
+
): ImageSourcePropType => {
|
|
41
|
+
if (typeof src === "string") {
|
|
42
|
+
return {
|
|
43
|
+
uri: src,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return src;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Container style={style}>
|
|
52
|
+
{customElement ? (
|
|
53
|
+
typeof customElement === "function" ? (
|
|
54
|
+
customElement()
|
|
55
|
+
) : (
|
|
56
|
+
customElement
|
|
57
|
+
)
|
|
58
|
+
) : !imgSource ? (
|
|
59
|
+
<ActivityIndicator
|
|
60
|
+
color={color || theme.role.secondary}
|
|
61
|
+
size={size}
|
|
62
|
+
style={styles?.activityIndicator}
|
|
63
|
+
/>
|
|
64
|
+
) : (
|
|
65
|
+
<Image
|
|
66
|
+
source={handleImgSourceType(imgSource)}
|
|
67
|
+
style={[
|
|
68
|
+
size === "large"
|
|
69
|
+
? { width: 50, height: 50 }
|
|
70
|
+
: size === "small"
|
|
71
|
+
? { width: 30, height: 30 }
|
|
72
|
+
: undefined,
|
|
73
|
+
styles?.image,
|
|
74
|
+
]}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</Container>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// React import is needed for expo-web
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Pressable, View } from 'react-native';
|
|
5
|
+
import styled, { css } from '@emotion/native';
|
|
6
|
+
import { Icon } from '../Icon/Icon';
|
|
7
|
+
const Container = styled.View `
|
|
8
|
+
flex-direction: row;
|
|
9
|
+
`;
|
|
10
|
+
const StarContainer = styled.View `
|
|
11
|
+
padding: 1px;
|
|
12
|
+
|
|
13
|
+
flex-direction: row;
|
|
14
|
+
`;
|
|
15
|
+
export function Rating({ testID, style, styles, initialRating = 0, size = 24, iconType = 'star', onRatingUpdate, direction = 'horizontal', allowHalfRating = true, disabled = false, color, }) {
|
|
16
|
+
const [rating, setRating] = useState(initialRating);
|
|
17
|
+
const iconPrefix = iconType === 'star' ? 'Star' : 'QuestBox';
|
|
18
|
+
const handlePress = (newRating, halfPressed) => {
|
|
19
|
+
const convertedRating = newRating + (!halfPressed ? 0.5 : 0);
|
|
20
|
+
setRating(convertedRating);
|
|
21
|
+
if (onRatingUpdate) {
|
|
22
|
+
onRatingUpdate(convertedRating);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const renderStarIcon = ({ key, position, }) => {
|
|
26
|
+
const filled = rating >= position + (allowHalfRating ? 0.5 : 0);
|
|
27
|
+
const iconName = filled ? `${iconPrefix}Fill` : `${iconPrefix}`;
|
|
28
|
+
const halfFilled = rating >= position && rating < position + (allowHalfRating ? 0.5 : 0);
|
|
29
|
+
return (_jsxs(StarContainer, { style: [
|
|
30
|
+
css `
|
|
31
|
+
width: ${size + 'px'};
|
|
32
|
+
`,
|
|
33
|
+
styles?.starContainer,
|
|
34
|
+
], testID: testID, children: [halfFilled && allowHalfRating ? (_jsxs(View, { style: { position: 'absolute' }, children: [_jsx(Icon, { color: color, name: `${iconPrefix}`, size: size, style: { position: 'absolute' } }), _jsx(Icon, { color: color, name: `${iconPrefix}HalfFill`, size: size })] })) : (_jsx(Icon, { color: color, name: iconName, size: size, style: { position: 'absolute' } })), _jsx(Pressable, { accessibilityRole: "button", disabled: disabled, onPress: () => handlePress(position, true), children: _jsx(View, { style: {
|
|
35
|
+
width: size / 2,
|
|
36
|
+
height: size,
|
|
37
|
+
backgroundColor: 'transparent',
|
|
38
|
+
} }) }), _jsx(Pressable, { accessibilityRole: "button", disabled: disabled, onPress: () => handlePress(position), children: _jsx(View, { style: {
|
|
39
|
+
width: size / 2,
|
|
40
|
+
height: size,
|
|
41
|
+
backgroundColor: 'transparent',
|
|
42
|
+
} }) })] }, key));
|
|
43
|
+
};
|
|
44
|
+
return (_jsx(Container, { style: [
|
|
45
|
+
css `
|
|
46
|
+
flex-direction: ${direction === 'horizontal' ? 'row' : 'column'};
|
|
47
|
+
`,
|
|
48
|
+
style,
|
|
49
|
+
], children: [...Array(5)].map((_, index) => {
|
|
50
|
+
const position = index + (allowHalfRating ? 0.5 : 1);
|
|
51
|
+
return renderStarIcon({ key: `${_}-${index}`, position });
|
|
52
|
+
}) }));
|
|
53
|
+
}
|