cpk-ui 0.5.4 → 0.7.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/README.md +80 -147
- package/components/modals/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/components/modals/AlertDialog/AlertDialog.js +14 -7
- package/components/modals/Snackbar/Snackbar.d.ts.map +1 -1
- package/components/modals/Snackbar/Snackbar.js +20 -17
- package/components/uis/Accordion/AccordionItem.d.ts.map +1 -1
- package/components/uis/Accordion/AccordionItem.js +41 -17
- package/components/uis/Button/Button.d.ts +2 -1
- package/components/uis/Button/Button.d.ts.map +1 -1
- package/components/uis/Button/Button.js +11 -11
- package/components/uis/Card/Card.d.ts +2 -1
- package/components/uis/Card/Card.d.ts.map +1 -1
- package/components/uis/Card/Card.js +12 -3
- package/components/uis/Checkbox/Checkbox.d.ts +2 -1
- package/components/uis/Checkbox/Checkbox.d.ts.map +1 -1
- package/components/uis/Checkbox/Checkbox.js +2 -2
- package/components/uis/CustomPressable/CustomPressable.d.ts.map +1 -1
- package/components/uis/CustomPressable/CustomPressable.js +6 -6
- package/components/uis/EditText/EditText.d.ts +1 -0
- package/components/uis/EditText/EditText.d.ts.map +1 -1
- package/components/uis/EditText/EditText.js +18 -13
- package/components/uis/Fab/Fab.d.ts +2 -2
- package/components/uis/Fab/Fab.d.ts.map +1 -1
- package/components/uis/Fab/Fab.js +22 -4
- package/components/uis/Icon/Icon.d.ts.map +1 -1
- package/components/uis/Icon/Icon.js +6 -3
- package/components/uis/IconButton/IconButton.d.ts +2 -1
- package/components/uis/IconButton/IconButton.d.ts.map +1 -1
- package/components/uis/IconButton/IconButton.js +44 -42
- package/components/uis/LoadingIndicator/LoadingIndicator.d.ts +2 -1
- package/components/uis/LoadingIndicator/LoadingIndicator.d.ts.map +1 -1
- package/components/uis/LoadingIndicator/LoadingIndicator.js +4 -4
- package/components/uis/RadioGroup/RadioButton.d.ts +2 -1
- package/components/uis/RadioGroup/RadioButton.d.ts.map +1 -1
- package/components/uis/RadioGroup/RadioButton.js +2 -2
- package/components/uis/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/components/uis/RadioGroup/RadioGroup.js +16 -3
- package/components/uis/Rating/Rating.d.ts.map +1 -1
- package/components/uis/Rating/Rating.js +23 -19
- package/components/uis/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/components/uis/SegmentedControl/SegmentedControl.js +1 -1
- package/components/uis/StatusbarBrightness/StatusBarBrightness.d.ts +2 -2
- package/components/uis/StatusbarBrightness/StatusBarBrightness.d.ts.map +1 -1
- package/components/uis/StatusbarBrightness/StatusBarBrightness.js +1 -1
- package/components/uis/SwitchToggle/SwitchToggle.d.ts +2 -1
- package/components/uis/SwitchToggle/SwitchToggle.d.ts.map +1 -1
- package/components/uis/SwitchToggle/SwitchToggle.js +2 -2
- package/hooks/useHoverState.d.ts +8 -0
- package/hooks/useHoverState.d.ts.map +1 -0
- package/hooks/useHoverState.js +16 -0
- package/package.json +18 -6
- package/providers/ThemeProvider.d.ts.map +1 -1
- package/providers/ThemeProvider.js +0 -1
- package/providers/index.d.ts.map +1 -1
- package/providers/index.js +3 -2
- package/components/uis/Icon/selection.json +0 -1
- package/src/components/common/ErrorBoundary/ErrorBoundary.tsx +0 -75
- package/src/components/modals/AlertDialog/AlertDialog.stories.tsx +0 -216
- package/src/components/modals/AlertDialog/AlertDialog.test.tsx +0 -251
- package/src/components/modals/AlertDialog/AlertDialog.tsx +0 -356
- package/src/components/modals/Snackbar/Snackbar.stories.tsx +0 -182
- package/src/components/modals/Snackbar/Snackbar.test.tsx +0 -319
- package/src/components/modals/Snackbar/Snackbar.tsx +0 -260
- package/src/components/modals/Snackbar/const.ts +0 -4
- package/src/components/uis/Accordion/Accordion.stories.tsx +0 -167
- package/src/components/uis/Accordion/Accordion.test.tsx +0 -272
- package/src/components/uis/Accordion/Accordion.tsx +0 -109
- package/src/components/uis/Accordion/AccordionItem.tsx +0 -306
- package/src/components/uis/Button/Button.stories.tsx +0 -218
- package/src/components/uis/Button/Button.test.tsx +0 -620
- package/src/components/uis/Button/Button.tsx +0 -436
- package/src/components/uis/Card/Card.test.tsx +0 -146
- package/src/components/uis/Card/Card.tsx +0 -102
- package/src/components/uis/Checkbox/Checkbox.stories.tsx +0 -216
- package/src/components/uis/Checkbox/Checkbox.test.tsx +0 -173
- package/src/components/uis/Checkbox/Checkbox.tsx +0 -220
- package/src/components/uis/CustomPressable/CustomPressable.tsx +0 -48
- package/src/components/uis/EditText/EditText.stories.tsx +0 -124
- package/src/components/uis/EditText/EditText.test.tsx +0 -477
- package/src/components/uis/EditText/EditText.tsx +0 -590
- package/src/components/uis/Fab/Fab.stories.tsx +0 -133
- package/src/components/uis/Fab/Fab.test.tsx +0 -110
- package/src/components/uis/Fab/Fab.tsx +0 -168
- package/src/components/uis/Hr/Hr.test.tsx +0 -26
- package/src/components/uis/Hr/Hr.tsx +0 -19
- package/src/components/uis/Icon/Icon.stories.tsx +0 -98
- package/src/components/uis/Icon/Icon.test.tsx +0 -49
- package/src/components/uis/Icon/Icon.tsx +0 -3819
- package/src/components/uis/IconButton/IconButton.test.tsx +0 -165
- package/src/components/uis/IconButton/IconButton.tsx +0 -275
- package/src/components/uis/LoadingIndicator/LoadingIndicator.tsx +0 -126
- package/src/components/uis/LoadingIndicator/LodingIndicator.stories.tsx +0 -81
- package/src/components/uis/PinchZoom/PinchZoom.stories.tsx +0 -266
- package/src/components/uis/PinchZoom/PinchZoom.test.tsx +0 -113
- package/src/components/uis/PinchZoom/PinchZoom.tsx +0 -424
- package/src/components/uis/RadioGroup/RadioButton.tsx +0 -188
- package/src/components/uis/RadioGroup/RadioGroup.stories.tsx +0 -188
- package/src/components/uis/RadioGroup/RadioGroup.test.tsx +0 -287
- package/src/components/uis/RadioGroup/RadioGroup.tsx +0 -109
- package/src/components/uis/Rating/Rating.test.tsx +0 -98
- package/src/components/uis/Rating/Rating.tsx +0 -200
- package/src/components/uis/SegmentedControl/SegmentedControl.stories.tsx +0 -546
- package/src/components/uis/SegmentedControl/SegmentedControl.test.tsx +0 -274
- package/src/components/uis/SegmentedControl/SegmentedControl.tsx +0 -229
- package/src/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +0 -41
- package/src/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +0 -26
- package/src/components/uis/Styled/StyledComponents.tsx +0 -217
- package/src/components/uis/SwitchToggle/SwitchToggle.stories.tsx +0 -75
- package/src/components/uis/SwitchToggle/SwitchToggle.test.tsx +0 -105
- package/src/components/uis/SwitchToggle/SwitchToggle.tsx +0 -305
- package/src/components/uis/Typography/Typography.stories.tsx +0 -149
- package/src/components/uis/Typography/Typography.test.tsx +0 -58
- package/src/components/uis/Typography/Typography.tsx +0 -386
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useAnimations.ts +0 -71
- package/src/hooks/useDebouncedColorScheme.tsx +0 -30
- package/src/hooks/useOptimizedStyles.ts +0 -22
- package/src/hooks/usePerformance.ts +0 -93
- package/src/index.tsx +0 -38
- package/src/providers/ThemeProvider.tsx +0 -177
- package/src/providers/index.tsx +0 -130
- package/src/test/testUtils.tsx +0 -101
- package/src/types/common.ts +0 -77
- package/src/types/index.ts +0 -1
- package/src/types/kstyled.d.ts +0 -6
- package/src/utils/accessibility.ts +0 -110
- package/src/utils/colors.ts +0 -151
- package/src/utils/createCtx.tsx +0 -26
- package/src/utils/guards.ts +0 -93
- package/src/utils/index.ts +0 -2
- package/src/utils/performanceTest.ts +0 -174
- package/src/utils/platform.ts +0 -69
- package/src/utils/theme.ts +0 -38
- package/test/testUtils.d.ts +0 -113
- package/test/testUtils.d.ts.map +0 -1
- package/test/testUtils.js +0 -65
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import {Animated} from 'react-native';
|
|
2
|
-
import {styled, css} from 'kstyled';
|
|
3
|
-
import {getTheme} from '../../../utils/theme';
|
|
4
|
-
import {Typography} from '../Typography/Typography';
|
|
5
|
-
|
|
6
|
-
export type ButtonType =
|
|
7
|
-
| 'primary'
|
|
8
|
-
| 'secondary'
|
|
9
|
-
| 'success'
|
|
10
|
-
| 'danger'
|
|
11
|
-
| 'warning'
|
|
12
|
-
| 'info'
|
|
13
|
-
| 'light';
|
|
14
|
-
|
|
15
|
-
export type SnackbarType =
|
|
16
|
-
| 'primary'
|
|
17
|
-
| 'secondary'
|
|
18
|
-
| 'success'
|
|
19
|
-
| 'warning'
|
|
20
|
-
| 'info'
|
|
21
|
-
| 'danger';
|
|
22
|
-
|
|
23
|
-
// ButtonWrapper Component
|
|
24
|
-
export const ButtonWrapper = styled.View<{
|
|
25
|
-
$type?: ButtonType;
|
|
26
|
-
$outlined?: boolean;
|
|
27
|
-
$disabled?: boolean;
|
|
28
|
-
$loading?: boolean;
|
|
29
|
-
}>`
|
|
30
|
-
border-width: ${({$outlined}) => ($outlined ? '1px' : undefined)};
|
|
31
|
-
background-color: ${({theme, $type, $outlined, $loading, $disabled}) => {
|
|
32
|
-
const resolvedTheme = getTheme(theme);
|
|
33
|
-
|
|
34
|
-
if ($loading) {
|
|
35
|
-
return $outlined ? undefined : resolvedTheme.button[$type ?? 'primary'].bg;
|
|
36
|
-
}
|
|
37
|
-
if ($disabled) {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
return $outlined ? resolvedTheme.bg.basic : resolvedTheme.button[$type ?? 'primary'].bg;
|
|
41
|
-
}};
|
|
42
|
-
border-color: ${({theme, $type, $disabled}) => {
|
|
43
|
-
const resolvedTheme = getTheme(theme);
|
|
44
|
-
|
|
45
|
-
if ($disabled) {
|
|
46
|
-
return resolvedTheme.bg.basic;
|
|
47
|
-
}
|
|
48
|
-
return resolvedTheme.button[$type ?? 'primary'].bg;
|
|
49
|
-
}};
|
|
50
|
-
`;
|
|
51
|
-
|
|
52
|
-
// ButtonText Component
|
|
53
|
-
export const ButtonText = styled(Typography.Body2)<{
|
|
54
|
-
$outlined?: boolean;
|
|
55
|
-
$type?: ButtonType | SnackbarType;
|
|
56
|
-
$disabled?: boolean;
|
|
57
|
-
$loading?: boolean;
|
|
58
|
-
}>`
|
|
59
|
-
color: ${({theme, $outlined, $type, $disabled}) => {
|
|
60
|
-
const resolvedTheme = getTheme(theme);
|
|
61
|
-
|
|
62
|
-
if ($outlined) {
|
|
63
|
-
return resolvedTheme.text.basic;
|
|
64
|
-
}
|
|
65
|
-
if ($disabled) {
|
|
66
|
-
return resolvedTheme.text.disabled;
|
|
67
|
-
}
|
|
68
|
-
return resolvedTheme.button[$type ?? 'primary'].text;
|
|
69
|
-
}};
|
|
70
|
-
`;
|
|
71
|
-
|
|
72
|
-
// CheckboxWrapperOutlined Component
|
|
73
|
-
export const CheckboxWrapperOutlined = styled.View<{
|
|
74
|
-
$type: ButtonType;
|
|
75
|
-
$disabled?: boolean;
|
|
76
|
-
$checked?: boolean;
|
|
77
|
-
}>`
|
|
78
|
-
border-width: 1px;
|
|
79
|
-
border-color: ${({theme, $type, $disabled}) => {
|
|
80
|
-
const resolvedTheme = getTheme(theme);
|
|
81
|
-
|
|
82
|
-
if ($disabled) {
|
|
83
|
-
return resolvedTheme.bg.disabled;
|
|
84
|
-
}
|
|
85
|
-
return $type === 'light'
|
|
86
|
-
? resolvedTheme.role.primary
|
|
87
|
-
: resolvedTheme.button[$type ?? 'primary'].bg;
|
|
88
|
-
}};
|
|
89
|
-
`;
|
|
90
|
-
|
|
91
|
-
// CheckboxWrapper Component
|
|
92
|
-
export const CheckboxWrapper = styled.View<{
|
|
93
|
-
$type: ButtonType;
|
|
94
|
-
$disabled?: boolean;
|
|
95
|
-
$checked?: boolean;
|
|
96
|
-
}>`
|
|
97
|
-
background-color: ${({theme, $checked, $type, $disabled}) => {
|
|
98
|
-
const resolvedTheme = getTheme(theme);
|
|
99
|
-
|
|
100
|
-
if ($disabled) {
|
|
101
|
-
return undefined;
|
|
102
|
-
}
|
|
103
|
-
if (!$checked) {
|
|
104
|
-
return resolvedTheme.bg.disabled;
|
|
105
|
-
}
|
|
106
|
-
return $type === 'light'
|
|
107
|
-
? resolvedTheme.role.primary
|
|
108
|
-
: resolvedTheme.button[$type ?? 'primary'].bg;
|
|
109
|
-
}};
|
|
110
|
-
`;
|
|
111
|
-
|
|
112
|
-
// RadioButtonWrapper Component
|
|
113
|
-
export const RadioButtonWrapper = styled.View<{
|
|
114
|
-
$type: ButtonType;
|
|
115
|
-
$selected?: boolean;
|
|
116
|
-
$disabled?: boolean;
|
|
117
|
-
$size?: 'small' | 'medium' | 'large' | number;
|
|
118
|
-
}>`
|
|
119
|
-
width: ${({$size = 'medium'}) => {
|
|
120
|
-
if (typeof $size === 'number') return $size;
|
|
121
|
-
return $size === 'small' ? 16 : $size === 'large' ? 24 : 20;
|
|
122
|
-
}};
|
|
123
|
-
height: ${({$size = 'medium'}) => {
|
|
124
|
-
if (typeof $size === 'number') return $size;
|
|
125
|
-
return $size === 'small' ? 16 : $size === 'large' ? 24 : 20;
|
|
126
|
-
}};
|
|
127
|
-
border-radius: ${({$size = 'medium'}) => {
|
|
128
|
-
if (typeof $size === 'number') return $size / 2;
|
|
129
|
-
return $size === 'small' ? 8 : $size === 'large' ? 12 : 10;
|
|
130
|
-
}};
|
|
131
|
-
margin: 0px 6px;
|
|
132
|
-
justify-content: center;
|
|
133
|
-
align-items: center;
|
|
134
|
-
border-width: 1px;
|
|
135
|
-
border-color: ${({theme, $type, $selected, $disabled}) => {
|
|
136
|
-
const resolvedTheme = getTheme(theme);
|
|
137
|
-
|
|
138
|
-
if ($disabled) {
|
|
139
|
-
return resolvedTheme.bg.disabled;
|
|
140
|
-
}
|
|
141
|
-
const buttonColor = $type === 'light'
|
|
142
|
-
? resolvedTheme.role.primary
|
|
143
|
-
: resolvedTheme.button[$type ?? 'primary'].bg;
|
|
144
|
-
return $selected ? buttonColor : resolvedTheme.text.basic;
|
|
145
|
-
}};
|
|
146
|
-
background-color: transparent;
|
|
147
|
-
`;
|
|
148
|
-
|
|
149
|
-
// RadioWrapper Component
|
|
150
|
-
export const RadioWrapper = styled(Animated.View)<{
|
|
151
|
-
$type: ButtonType;
|
|
152
|
-
$disabled?: boolean;
|
|
153
|
-
$selected?: boolean;
|
|
154
|
-
}>`
|
|
155
|
-
flex: 1;
|
|
156
|
-
align-self: stretch;
|
|
157
|
-
background-color: ${({theme, $type, $disabled, $selected}) => {
|
|
158
|
-
const resolvedTheme = getTheme(theme);
|
|
159
|
-
|
|
160
|
-
if ($disabled) {
|
|
161
|
-
return resolvedTheme.bg.disabled;
|
|
162
|
-
}
|
|
163
|
-
if (!$selected) {
|
|
164
|
-
return 'transparent';
|
|
165
|
-
}
|
|
166
|
-
return $type === 'light'
|
|
167
|
-
? resolvedTheme.role.primary
|
|
168
|
-
: resolvedTheme.button[$type ?? 'primary'].bg;
|
|
169
|
-
}};
|
|
170
|
-
`;
|
|
171
|
-
|
|
172
|
-
// ColoredText Component
|
|
173
|
-
export const ColoredText = styled(Typography.Body2)<{
|
|
174
|
-
$type: ButtonType;
|
|
175
|
-
$disabled?: boolean;
|
|
176
|
-
$selected?: boolean;
|
|
177
|
-
$size?: 'small' | 'medium' | 'large' | number;
|
|
178
|
-
}>`
|
|
179
|
-
color: ${({theme, $selected, $type, $disabled}) => {
|
|
180
|
-
const resolvedTheme = getTheme(theme);
|
|
181
|
-
|
|
182
|
-
if ($disabled) {
|
|
183
|
-
return undefined;
|
|
184
|
-
}
|
|
185
|
-
if (!$selected) {
|
|
186
|
-
return resolvedTheme.text.basic;
|
|
187
|
-
}
|
|
188
|
-
return $type === 'light'
|
|
189
|
-
? resolvedTheme.role.primary
|
|
190
|
-
: resolvedTheme.button[$type ?? 'primary'].bg;
|
|
191
|
-
}};
|
|
192
|
-
font-size: ${({$size = 'medium'}) => {
|
|
193
|
-
if (typeof $size === 'number') return $size * 0.7;
|
|
194
|
-
return $size === 'small' ? 12 : $size === 'large' ? 16 : 14;
|
|
195
|
-
}}px;
|
|
196
|
-
`;
|
|
197
|
-
|
|
198
|
-
// SnackbarWrapper Component
|
|
199
|
-
export const SnackbarWrapper = styled.View<{
|
|
200
|
-
$type?: SnackbarType;
|
|
201
|
-
$checked?: boolean;
|
|
202
|
-
}>`
|
|
203
|
-
background-color: ${({theme, $type}) => {
|
|
204
|
-
const resolvedTheme = getTheme(theme);
|
|
205
|
-
|
|
206
|
-
return !$type ? resolvedTheme.bg.disabled : resolvedTheme.button[$type ?? 'light'].bg;
|
|
207
|
-
}};
|
|
208
|
-
flex-direction: row;
|
|
209
|
-
text-align: left;
|
|
210
|
-
align-items: center;
|
|
211
|
-
align-self: center;
|
|
212
|
-
position: absolute;
|
|
213
|
-
font-size: 16px;
|
|
214
|
-
padding: 10px 20px;
|
|
215
|
-
bottom: 20px;
|
|
216
|
-
border-radius: 20px;
|
|
217
|
-
`;
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type {ComponentProps} from 'react';
|
|
2
|
-
import type {Meta, StoryObj} from '@storybook/react';
|
|
3
|
-
import {SwitchToggle} from './SwitchToggle';
|
|
4
|
-
import {withThemeProvider} from '../../../../.storybook/decorators';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'SwitchToggle',
|
|
8
|
-
component: (props) => <SwitchToggle {...props} />,
|
|
9
|
-
parameters: {
|
|
10
|
-
docs: {
|
|
11
|
-
description: {
|
|
12
|
-
component: `
|
|
13
|
-
An animated SwitchToggle component with smooth transitions and proportional sizing.
|
|
14
|
-
|
|
15
|
-
## Features
|
|
16
|
-
- **Smooth Animations**: Slide and color transitions
|
|
17
|
-
- **Proportional Sizing**: Automatically calculates container and circle dimensions
|
|
18
|
-
- **Flexible Sizing**: Preset sizes and custom numeric values
|
|
19
|
-
- **Icon Elements**: Add elements before or after the switch
|
|
20
|
-
- **Custom Colors**: Support for custom active and inactive colors
|
|
21
|
-
|
|
22
|
-
## Size Options
|
|
23
|
-
- \`small\`: 24px height, 40px width
|
|
24
|
-
- \`medium\`: 30px height, 56px width (default)
|
|
25
|
-
- \`large\`: 40px height, 80px width
|
|
26
|
-
- Custom number: Height = size, Width = size × 2, automatically calculated
|
|
27
|
-
|
|
28
|
-
## Size Calculation
|
|
29
|
-
When using a custom number, dimensions are calculated as:
|
|
30
|
-
- **Container width**: \`size × 2\`
|
|
31
|
-
- **Container height**: \`size\`
|
|
32
|
-
- **Circle size**: \`size × 0.8\`
|
|
33
|
-
- **Padding**: \`size × 0.2\`
|
|
34
|
-
|
|
35
|
-
## Usage
|
|
36
|
-
\`\`\`tsx
|
|
37
|
-
<SwitchToggle
|
|
38
|
-
isOn={isToggled}
|
|
39
|
-
onPress={() => setIsToggled(!isToggled)}
|
|
40
|
-
size="medium"
|
|
41
|
-
/>
|
|
42
|
-
\`\`\`
|
|
43
|
-
`,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
argTypes: {
|
|
48
|
-
size: {
|
|
49
|
-
control: 'radio',
|
|
50
|
-
options: ['small', 'medium', 'large', 24, 30, 36, 42],
|
|
51
|
-
description: 'Switch size: "small" (24px), "medium" (30px), "large" (36px), or custom number with proportional scaling',
|
|
52
|
-
},
|
|
53
|
-
isOn: {
|
|
54
|
-
description: 'Whether the switch is in the on position',
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
decorators: [withThemeProvider],
|
|
58
|
-
} satisfies Meta<typeof SwitchToggle>;
|
|
59
|
-
|
|
60
|
-
export default meta;
|
|
61
|
-
|
|
62
|
-
type Story = StoryObj<typeof meta>;
|
|
63
|
-
|
|
64
|
-
export const Basic: Story = {
|
|
65
|
-
args: {
|
|
66
|
-
isOn: false,
|
|
67
|
-
size: 'medium',
|
|
68
|
-
},
|
|
69
|
-
argTypes: {
|
|
70
|
-
theme: {
|
|
71
|
-
control: 'select',
|
|
72
|
-
options: ['light', 'dark'],
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import React, {type ReactElement} from 'react';
|
|
2
|
-
import {Text} from 'react-native';
|
|
3
|
-
import type {RenderAPI} from '@testing-library/react-native';
|
|
4
|
-
import {fireEvent, render, waitFor} from '@testing-library/react-native';
|
|
5
|
-
import {createComponent} from '../../../../test/testUtils';
|
|
6
|
-
import {SwitchToggle, type SwitchToggleSizeType} from './SwitchToggle';
|
|
7
|
-
|
|
8
|
-
let testingLib: RenderAPI;
|
|
9
|
-
|
|
10
|
-
describe('[SwitchToggle]', () => {
|
|
11
|
-
const handlePress = jest.fn();
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
handlePress.mockClear();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('handles press event', async () => {
|
|
18
|
-
testingLib = render(
|
|
19
|
-
createComponent(<SwitchToggle isOn={false} onPress={handlePress} />),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
const baseElement = await waitFor(() => testingLib.toJSON());
|
|
23
|
-
expect(baseElement).toBeTruthy();
|
|
24
|
-
|
|
25
|
-
const {getByRole} = testingLib;
|
|
26
|
-
fireEvent.press(getByRole('switch'));
|
|
27
|
-
expect(handlePress).toBeCalled();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const getSwitchToggle = ({isOn, size}: {isOn: boolean; size?: SwitchToggleSizeType}): ReactElement =>
|
|
31
|
-
createComponent(
|
|
32
|
-
<SwitchToggle
|
|
33
|
-
isOn={isOn}
|
|
34
|
-
size={size}
|
|
35
|
-
offElement={<Text>off</Text>}
|
|
36
|
-
onElement={<Text>on</Text>}
|
|
37
|
-
onPress={handlePress}
|
|
38
|
-
style={{padding: 6}}
|
|
39
|
-
styles={{
|
|
40
|
-
container: {paddingLeft: 6, paddingRight: 5},
|
|
41
|
-
onElementContainer: {padding: 4},
|
|
42
|
-
offElementContainer: {padding: 3},
|
|
43
|
-
circle: {padding: 2},
|
|
44
|
-
button: {padding: 1},
|
|
45
|
-
circleColorOff: 'red',
|
|
46
|
-
circleColorOn: 'blue',
|
|
47
|
-
backgroundColorOn: 'green',
|
|
48
|
-
backgroundColorOff: 'grey',
|
|
49
|
-
}}
|
|
50
|
-
/>,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
context('when switch toggle is on', () => {
|
|
54
|
-
it('renders as on state', async () => {
|
|
55
|
-
testingLib = render(createComponent(getSwitchToggle({isOn: true})));
|
|
56
|
-
|
|
57
|
-
const baseElement = await waitFor(() => testingLib.toJSON());
|
|
58
|
-
expect(baseElement).toBeTruthy();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
context('when switch toggle is off', () => {
|
|
63
|
-
it('renders as off state', () => {
|
|
64
|
-
const component = createComponent(getSwitchToggle({isOn: false}));
|
|
65
|
-
testingLib = render(component);
|
|
66
|
-
|
|
67
|
-
const baseElement = testingLib.toJSON();
|
|
68
|
-
expect(baseElement).toBeTruthy();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('sizes', () => {
|
|
73
|
-
it('renders with small size', () => {
|
|
74
|
-
const component = createComponent(getSwitchToggle({size: 'small'}));
|
|
75
|
-
testingLib = render(component);
|
|
76
|
-
|
|
77
|
-
const baseElement = testingLib.toJSON();
|
|
78
|
-
expect(baseElement).toBeTruthy();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('renders with medium size', () => {
|
|
82
|
-
const component = createComponent(getSwitchToggle({size: 'medium'}));
|
|
83
|
-
testingLib = render(component);
|
|
84
|
-
|
|
85
|
-
const baseElement = testingLib.toJSON();
|
|
86
|
-
expect(baseElement).toBeTruthy();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('renders with large size', () => {
|
|
90
|
-
const component = createComponent(getSwitchToggle({size: 'large'}));
|
|
91
|
-
testingLib = render(component);
|
|
92
|
-
|
|
93
|
-
const baseElement = testingLib.toJSON();
|
|
94
|
-
expect(baseElement).toBeTruthy();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('renders with custom numeric size', () => {
|
|
98
|
-
const component = createComponent(getSwitchToggle({size: 28}));
|
|
99
|
-
testingLib = render(component);
|
|
100
|
-
|
|
101
|
-
const baseElement = testingLib.toJSON();
|
|
102
|
-
expect(baseElement).toBeTruthy();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import React, {useCallback, useEffect, useMemo, useState, type ReactElement} from 'react';
|
|
2
|
-
import type {StyleProp, ViewStyle} from 'react-native';
|
|
3
|
-
import {Animated, TouchableOpacity, View} from 'react-native';
|
|
4
|
-
import {styled} from 'kstyled';
|
|
5
|
-
import {useTheme} from '../../../providers/ThemeProvider';
|
|
6
|
-
|
|
7
|
-
// Metro cache workaround: Using explicit pattern until cache is cleared
|
|
8
|
-
const AnimatedView = Animated.createAnimatedComponent(View);
|
|
9
|
-
|
|
10
|
-
type Styles = {
|
|
11
|
-
container?: ViewStyle;
|
|
12
|
-
onElementContainer?: StyleProp<ViewStyle>;
|
|
13
|
-
offElementContainer?: StyleProp<ViewStyle>;
|
|
14
|
-
circle?: ViewStyle;
|
|
15
|
-
button?: StyleProp<ViewStyle>;
|
|
16
|
-
circleColorOff?: string;
|
|
17
|
-
circleColorOn?: string;
|
|
18
|
-
backgroundColorOn?: string;
|
|
19
|
-
backgroundColorOff?: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type SwitchToggleSizeType = 'small' | 'medium' | 'large' | number;
|
|
23
|
-
|
|
24
|
-
type Props = {
|
|
25
|
-
testID?: string;
|
|
26
|
-
size?: SwitchToggleSizeType;
|
|
27
|
-
isOn: boolean;
|
|
28
|
-
style?: StyleProp<ViewStyle>;
|
|
29
|
-
styles?: Styles;
|
|
30
|
-
duration?: number;
|
|
31
|
-
onElement?: any;
|
|
32
|
-
offElement?: any;
|
|
33
|
-
onPress?: () => void;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Using AnimatedView created above - functionally identical to styled(Animated.View)
|
|
37
|
-
const AnimatedContainer = styled(AnimatedView)`
|
|
38
|
-
flex-direction: row;
|
|
39
|
-
align-items: center;
|
|
40
|
-
`;
|
|
41
|
-
|
|
42
|
-
const smallContainer: ViewStyle = {
|
|
43
|
-
width: 40,
|
|
44
|
-
height: 24,
|
|
45
|
-
borderRadius: 25,
|
|
46
|
-
padding: 5,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const smallCircle: ViewStyle = {
|
|
50
|
-
width: 16,
|
|
51
|
-
height: 16,
|
|
52
|
-
borderRadius: 8,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const mediumContainer: ViewStyle = {
|
|
56
|
-
width: 56,
|
|
57
|
-
height: 30,
|
|
58
|
-
borderRadius: 25,
|
|
59
|
-
padding: 5,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const mediumCircle: ViewStyle = {
|
|
63
|
-
width: 20,
|
|
64
|
-
height: 20,
|
|
65
|
-
borderRadius: 10,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const largeContainer: ViewStyle = {
|
|
69
|
-
width: 80,
|
|
70
|
-
height: 40,
|
|
71
|
-
borderRadius: 25,
|
|
72
|
-
padding: 5,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const largeCircle: ViewStyle = {
|
|
76
|
-
width: 32,
|
|
77
|
-
height: 32,
|
|
78
|
-
borderRadius: 16,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export function SwitchToggle({
|
|
82
|
-
testID,
|
|
83
|
-
isOn,
|
|
84
|
-
style,
|
|
85
|
-
styles,
|
|
86
|
-
duration = 300,
|
|
87
|
-
onElement,
|
|
88
|
-
size = 'medium',
|
|
89
|
-
offElement,
|
|
90
|
-
onPress,
|
|
91
|
-
}: Props): ReactElement {
|
|
92
|
-
const {theme} = useTheme();
|
|
93
|
-
|
|
94
|
-
// Memoize size-based configurations
|
|
95
|
-
const sizeConfig = useMemo(() => {
|
|
96
|
-
if (typeof size === 'number') {
|
|
97
|
-
const containerConfig: ViewStyle = {
|
|
98
|
-
width: size * 1.87,
|
|
99
|
-
height: size,
|
|
100
|
-
borderRadius: size / 2,
|
|
101
|
-
padding: size * 0.17,
|
|
102
|
-
};
|
|
103
|
-
const circleConfig: ViewStyle = {
|
|
104
|
-
width: size * 0.67,
|
|
105
|
-
height: size * 0.67,
|
|
106
|
-
borderRadius: size / 3,
|
|
107
|
-
};
|
|
108
|
-
return {containerConfig, circleConfig};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const containerConfig =
|
|
112
|
-
size === 'large'
|
|
113
|
-
? largeContainer
|
|
114
|
-
: size === 'small'
|
|
115
|
-
? smallContainer
|
|
116
|
-
: mediumContainer;
|
|
117
|
-
|
|
118
|
-
const circleConfig =
|
|
119
|
-
size === 'large'
|
|
120
|
-
? largeCircle
|
|
121
|
-
: size === 'small'
|
|
122
|
-
? smallCircle
|
|
123
|
-
: mediumCircle;
|
|
124
|
-
|
|
125
|
-
return {containerConfig, circleConfig};
|
|
126
|
-
}, [size]);
|
|
127
|
-
|
|
128
|
-
// Memoize styles with defaults
|
|
129
|
-
const switchStyles = useMemo(() => {
|
|
130
|
-
const {
|
|
131
|
-
backgroundColorOn = theme.role.primary,
|
|
132
|
-
backgroundColorOff = theme.bg.disabled,
|
|
133
|
-
circleColorOn = theme.text.contrast,
|
|
134
|
-
circleColorOff = theme.text.basic,
|
|
135
|
-
container = sizeConfig.containerConfig,
|
|
136
|
-
circle = sizeConfig.circleConfig,
|
|
137
|
-
button,
|
|
138
|
-
onElementContainer,
|
|
139
|
-
offElementContainer,
|
|
140
|
-
} = styles ?? {};
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
backgroundColorOn,
|
|
144
|
-
backgroundColorOff,
|
|
145
|
-
circleColorOn,
|
|
146
|
-
circleColorOff,
|
|
147
|
-
container,
|
|
148
|
-
circle,
|
|
149
|
-
button,
|
|
150
|
-
onElementContainer,
|
|
151
|
-
offElementContainer,
|
|
152
|
-
};
|
|
153
|
-
}, [styles, theme, sizeConfig]);
|
|
154
|
-
|
|
155
|
-
// Memoize padding calculations
|
|
156
|
-
const padding = useMemo(() => {
|
|
157
|
-
const paddingLeft =
|
|
158
|
-
(switchStyles.container.padding as number) ||
|
|
159
|
-
(switchStyles.container.paddingLeft as number) ||
|
|
160
|
-
0;
|
|
161
|
-
const paddingRight =
|
|
162
|
-
(switchStyles.container.padding as number) ||
|
|
163
|
-
(switchStyles.container.paddingRight as number) ||
|
|
164
|
-
0;
|
|
165
|
-
return {paddingLeft, paddingRight};
|
|
166
|
-
}, [switchStyles.container]);
|
|
167
|
-
|
|
168
|
-
// Memoize position calculations
|
|
169
|
-
const positions = useMemo(() => {
|
|
170
|
-
const circlePosXStart = 0;
|
|
171
|
-
const circlePosXEnd =
|
|
172
|
-
((switchStyles.container.width ?? mediumContainer.width) as number) -
|
|
173
|
-
((switchStyles.circle.width ?? mediumCircle.width) as number) -
|
|
174
|
-
(padding.paddingRight + padding.paddingLeft);
|
|
175
|
-
|
|
176
|
-
return {circlePosXStart, circlePosXEnd};
|
|
177
|
-
}, [
|
|
178
|
-
switchStyles.container.width,
|
|
179
|
-
switchStyles.circle.width,
|
|
180
|
-
padding,
|
|
181
|
-
mediumContainer,
|
|
182
|
-
smallContainer,
|
|
183
|
-
largeContainer,
|
|
184
|
-
]);
|
|
185
|
-
|
|
186
|
-
const [animXValue] = useState(new Animated.Value(isOn ? 1 : 0));
|
|
187
|
-
|
|
188
|
-
// Memoize animation function
|
|
189
|
-
const runAnimation = useCallback(() => {
|
|
190
|
-
const option = {
|
|
191
|
-
fromValue: isOn ? 0 : 1,
|
|
192
|
-
toValue: isOn ? 1 : 0,
|
|
193
|
-
duration,
|
|
194
|
-
useNativeDriver: false,
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
Animated.timing(animXValue, option).start();
|
|
198
|
-
}, [animXValue, isOn, duration]);
|
|
199
|
-
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
runAnimation();
|
|
202
|
-
}, [runAnimation]);
|
|
203
|
-
|
|
204
|
-
// Memoize animated components
|
|
205
|
-
const CircleButton = useMemo(
|
|
206
|
-
() => (
|
|
207
|
-
<Animated.View
|
|
208
|
-
style={[
|
|
209
|
-
switchStyles.circle,
|
|
210
|
-
{
|
|
211
|
-
backgroundColor: animXValue.interpolate({
|
|
212
|
-
inputRange: [0.5, 1],
|
|
213
|
-
outputRange: [
|
|
214
|
-
switchStyles.circleColorOff as string | number,
|
|
215
|
-
switchStyles.circleColorOn as string | number,
|
|
216
|
-
] as string[] | number[],
|
|
217
|
-
}),
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
transform: [
|
|
221
|
-
{
|
|
222
|
-
translateX: animXValue.interpolate({
|
|
223
|
-
inputRange: [0, 1],
|
|
224
|
-
outputRange: [
|
|
225
|
-
positions.circlePosXStart as string | number,
|
|
226
|
-
positions.circlePosXEnd as string | number,
|
|
227
|
-
] as string[] | number[],
|
|
228
|
-
}),
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
},
|
|
232
|
-
switchStyles.button,
|
|
233
|
-
]}
|
|
234
|
-
/>
|
|
235
|
-
),
|
|
236
|
-
[switchStyles, animXValue, positions]
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
const OnElement = useMemo(
|
|
240
|
-
() => (
|
|
241
|
-
<Animated.View style={[{opacity: animXValue}, switchStyles.onElementContainer]}>
|
|
242
|
-
{onElement}
|
|
243
|
-
</Animated.View>
|
|
244
|
-
),
|
|
245
|
-
[animXValue, switchStyles.onElementContainer, onElement]
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
const OffElement = useMemo(
|
|
249
|
-
() => (
|
|
250
|
-
<Animated.View
|
|
251
|
-
style={[
|
|
252
|
-
{
|
|
253
|
-
opacity: animXValue.interpolate({
|
|
254
|
-
inputRange: [0, 1],
|
|
255
|
-
outputRange: [1, 0],
|
|
256
|
-
}),
|
|
257
|
-
},
|
|
258
|
-
switchStyles.offElementContainer,
|
|
259
|
-
]}
|
|
260
|
-
>
|
|
261
|
-
{offElement}
|
|
262
|
-
</Animated.View>
|
|
263
|
-
),
|
|
264
|
-
[animXValue, switchStyles.offElementContainer, offElement]
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
// Memoize container styles
|
|
268
|
-
const containerStyles = useMemo(
|
|
269
|
-
() => [
|
|
270
|
-
switchStyles.container,
|
|
271
|
-
{
|
|
272
|
-
paddingLeft: padding.paddingLeft,
|
|
273
|
-
paddingRight: padding.paddingRight,
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
backgroundColor: animXValue.interpolate({
|
|
277
|
-
inputRange: [0, 1],
|
|
278
|
-
outputRange: [
|
|
279
|
-
switchStyles.backgroundColorOff as string | number,
|
|
280
|
-
switchStyles.backgroundColorOn as string | number,
|
|
281
|
-
] as string[] | number[],
|
|
282
|
-
}),
|
|
283
|
-
},
|
|
284
|
-
],
|
|
285
|
-
[switchStyles, padding, animXValue]
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
return (
|
|
289
|
-
<TouchableOpacity
|
|
290
|
-
accessibilityRole="switch"
|
|
291
|
-
activeOpacity={0.8}
|
|
292
|
-
onPress={onPress}
|
|
293
|
-
style={style}
|
|
294
|
-
testID={testID}
|
|
295
|
-
>
|
|
296
|
-
<AnimatedContainer style={containerStyles}>
|
|
297
|
-
{isOn ? OnElement : OffElement}
|
|
298
|
-
{CircleButton}
|
|
299
|
-
</AnimatedContainer>
|
|
300
|
-
</TouchableOpacity>
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Export memoized component for better performance
|
|
305
|
-
export default React.memo(SwitchToggle) as typeof SwitchToggle;
|