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,165 +0,0 @@
|
|
|
1
|
-
import React, {type ReactElement} 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): ReactElement =>
|
|
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
|
-
});
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import React, {useCallback, useMemo, useRef, type ReactElement} 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 'kstyled';
|
|
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
|
-
import * as Haptics from 'expo-haptics';
|
|
13
|
-
|
|
14
|
-
type Styles = {
|
|
15
|
-
container?: StyleProp<ViewStyle>;
|
|
16
|
-
icon?: StyleProp<ViewStyle>;
|
|
17
|
-
disabled?: StyleProp<ViewStyle>;
|
|
18
|
-
hovered?: StyleProp<ViewStyle>;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
type ButtonType = 'text' | 'solid' | 'outlined';
|
|
22
|
-
|
|
23
|
-
type ButtonColorType =
|
|
24
|
-
| 'primary'
|
|
25
|
-
| 'secondary'
|
|
26
|
-
| 'success'
|
|
27
|
-
| 'danger'
|
|
28
|
-
| 'warning'
|
|
29
|
-
| 'info'
|
|
30
|
-
| 'light';
|
|
31
|
-
|
|
32
|
-
type ButtonSizeType = 'small' | 'medium' | 'large' | number;
|
|
33
|
-
|
|
34
|
-
// Memoized styles calculation function
|
|
35
|
-
const ButtonStyles = ({
|
|
36
|
-
theme,
|
|
37
|
-
type,
|
|
38
|
-
color,
|
|
39
|
-
loading,
|
|
40
|
-
disabled,
|
|
41
|
-
}: {
|
|
42
|
-
theme: CpkTheme;
|
|
43
|
-
type: ButtonType;
|
|
44
|
-
color: ButtonColorType;
|
|
45
|
-
loading?: boolean;
|
|
46
|
-
disabled?: boolean;
|
|
47
|
-
}) => {
|
|
48
|
-
const isDisabled = disabled || loading;
|
|
49
|
-
|
|
50
|
-
let backgroundColor = theme.bg.basic;
|
|
51
|
-
let borderColor = 'transparent';
|
|
52
|
-
let iconColor = theme.text.basic;
|
|
53
|
-
|
|
54
|
-
if (type === 'solid') {
|
|
55
|
-
backgroundColor = isDisabled
|
|
56
|
-
? theme.button.disabled.bg
|
|
57
|
-
: theme.button[color].bg;
|
|
58
|
-
borderColor = backgroundColor;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (type === 'outlined') {
|
|
62
|
-
backgroundColor = isDisabled
|
|
63
|
-
? theme.bg.disabled
|
|
64
|
-
: theme.bg.basic;
|
|
65
|
-
borderColor = isDisabled
|
|
66
|
-
? theme.bg.disabled
|
|
67
|
-
: theme.button[color].bg;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (type === 'solid' || color === 'light') {
|
|
71
|
-
iconColor = theme.button[color].text;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
backgroundColor,
|
|
76
|
-
borderColor,
|
|
77
|
-
borderWidth: type === 'outlined' ? 1 : 0,
|
|
78
|
-
iconColor,
|
|
79
|
-
disabledBackgroundColor:
|
|
80
|
-
type === 'solid' && !loading ? theme.button.disabled.bg : theme.bg.basic,
|
|
81
|
-
disabledBorderColor: theme.bg.disabled,
|
|
82
|
-
disabledTextColor: theme.button.disabled.text,
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export type IconButtonProps = {
|
|
87
|
-
testID?: string;
|
|
88
|
-
type?: ButtonType;
|
|
89
|
-
color?: ButtonColorType;
|
|
90
|
-
size?: ButtonSizeType | number;
|
|
91
|
-
disabled?: boolean;
|
|
92
|
-
loading?: boolean;
|
|
93
|
-
loadingElement?: ReactElement;
|
|
94
|
-
icon?: IconName;
|
|
95
|
-
iconElement?: ReactElement;
|
|
96
|
-
style?: StyleProp<Omit<ViewStyle, 'borderRadius' | 'padding'>>;
|
|
97
|
-
styles?: Styles;
|
|
98
|
-
onPress?: TouchableHighlightProps['onPress'];
|
|
99
|
-
activeOpacity?: number;
|
|
100
|
-
touchableHighlightProps?: Omit<TouchableHighlightProps, 'onPress' | 'style'>;
|
|
101
|
-
hapticFeedback?: Haptics.ImpactFeedbackStyle;
|
|
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
|
-
hapticFeedback,
|
|
120
|
-
}: IconButtonProps): ReactElement {
|
|
121
|
-
const ref = useRef<React.ElementRef<typeof TouchableHighlight>>(null);
|
|
122
|
-
const hovered = useHover(ref);
|
|
123
|
-
const {theme} = useTheme();
|
|
124
|
-
|
|
125
|
-
// Memoize button styles
|
|
126
|
-
const buttonStylesConfig = useMemo(() => ButtonStyles({
|
|
127
|
-
theme,
|
|
128
|
-
type,
|
|
129
|
-
color,
|
|
130
|
-
loading,
|
|
131
|
-
disabled,
|
|
132
|
-
}), [theme, type, color, loading, disabled]);
|
|
133
|
-
|
|
134
|
-
// Memoize icon size calculation
|
|
135
|
-
const iconSize = useMemo(() => {
|
|
136
|
-
return size === 'large'
|
|
137
|
-
? 32
|
|
138
|
-
: size === 'medium'
|
|
139
|
-
? 24
|
|
140
|
-
: size === 'small'
|
|
141
|
-
? 16
|
|
142
|
-
: size;
|
|
143
|
-
}, [size]);
|
|
144
|
-
|
|
145
|
-
// Memoize style strings
|
|
146
|
-
const borderRadiusStr = useMemo(() => '99px', []);
|
|
147
|
-
const borderWidthStr = useMemo(() => `${buttonStylesConfig.borderWidth}px`, [buttonStylesConfig.borderWidth]);
|
|
148
|
-
|
|
149
|
-
// Memoize composite styles
|
|
150
|
-
const compositeStyles: Styles = useMemo(() => ({
|
|
151
|
-
container: [
|
|
152
|
-
css`
|
|
153
|
-
background-color: ${buttonStylesConfig.backgroundColor};
|
|
154
|
-
border-color: ${buttonStylesConfig.borderColor};
|
|
155
|
-
border-width: ${borderWidthStr};
|
|
156
|
-
padding: 12px;
|
|
157
|
-
`,
|
|
158
|
-
styles?.container,
|
|
159
|
-
],
|
|
160
|
-
icon: [
|
|
161
|
-
css`
|
|
162
|
-
color: ${buttonStylesConfig.iconColor};
|
|
163
|
-
`,
|
|
164
|
-
styles?.icon,
|
|
165
|
-
],
|
|
166
|
-
disabled: [
|
|
167
|
-
css`
|
|
168
|
-
background-color: ${buttonStylesConfig.disabledBackgroundColor};
|
|
169
|
-
border-color: ${buttonStylesConfig.disabledBorderColor};
|
|
170
|
-
`,
|
|
171
|
-
styles?.disabled,
|
|
172
|
-
],
|
|
173
|
-
hovered: [
|
|
174
|
-
{
|
|
175
|
-
shadowColor: theme.text.basic,
|
|
176
|
-
shadowOpacity: 0.24,
|
|
177
|
-
shadowRadius: 16,
|
|
178
|
-
elevation: 10,
|
|
179
|
-
},
|
|
180
|
-
styles?.hovered,
|
|
181
|
-
],
|
|
182
|
-
}), [buttonStylesConfig, borderWidthStr, theme.text.basic, styles]);
|
|
183
|
-
|
|
184
|
-
// Memoize press handler
|
|
185
|
-
const handlePress = useCallback((e: any) => {
|
|
186
|
-
onPress?.(e);
|
|
187
|
-
if (hapticFeedback) {
|
|
188
|
-
Haptics.impactAsync(hapticFeedback);
|
|
189
|
-
}
|
|
190
|
-
}, [onPress, hapticFeedback]);
|
|
191
|
-
|
|
192
|
-
// Memoize loading view
|
|
193
|
-
const LoadingView = useMemo(() =>
|
|
194
|
-
loadingElement || (
|
|
195
|
-
<LoadingIndicator
|
|
196
|
-
color={theme.text.disabled}
|
|
197
|
-
size="small"
|
|
198
|
-
style={css`
|
|
199
|
-
justify-content: center;
|
|
200
|
-
align-items: center;
|
|
201
|
-
height: ${iconSize}px;
|
|
202
|
-
width: ${iconSize}px;
|
|
203
|
-
`}
|
|
204
|
-
/>
|
|
205
|
-
),
|
|
206
|
-
[loadingElement, theme.text.disabled, iconSize]
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
// Memoize icon view
|
|
210
|
-
const IconView = useMemo(() =>
|
|
211
|
-
iconElement || (
|
|
212
|
-
<Icon
|
|
213
|
-
color={buttonStylesConfig.iconColor}
|
|
214
|
-
name={icon || 'QuestBoxFill'}
|
|
215
|
-
size={iconSize}
|
|
216
|
-
style={compositeStyles?.icon}
|
|
217
|
-
/>
|
|
218
|
-
),
|
|
219
|
-
[iconElement, buttonStylesConfig.iconColor, icon, iconSize, compositeStyles?.icon]
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
// Memoize container styles
|
|
223
|
-
const containerStyles = useMemo(() => [
|
|
224
|
-
css`
|
|
225
|
-
flex-direction: row;
|
|
226
|
-
border-radius: ${borderRadiusStr};
|
|
227
|
-
`,
|
|
228
|
-
style,
|
|
229
|
-
], [borderRadiusStr, style]);
|
|
230
|
-
|
|
231
|
-
// Memoize inner container styles
|
|
232
|
-
const innerContainerStyles = useMemo(() => [
|
|
233
|
-
css`
|
|
234
|
-
padding: 4px;
|
|
235
|
-
border-radius: ${borderRadiusStr};
|
|
236
|
-
flex-direction: row;
|
|
237
|
-
justify-content: center;
|
|
238
|
-
align-items: center;
|
|
239
|
-
`,
|
|
240
|
-
compositeStyles.container,
|
|
241
|
-
hovered && !disabled && compositeStyles.hovered,
|
|
242
|
-
disabled && compositeStyles.disabled,
|
|
243
|
-
], [borderRadiusStr, compositeStyles, hovered, disabled]);
|
|
244
|
-
|
|
245
|
-
return (
|
|
246
|
-
<View style={containerStyles}>
|
|
247
|
-
<TouchableHighlight
|
|
248
|
-
activeOpacity={activeOpacity}
|
|
249
|
-
delayPressIn={50}
|
|
250
|
-
disabled={disabled || loading}
|
|
251
|
-
onPress={handlePress}
|
|
252
|
-
ref={Platform.select({
|
|
253
|
-
web: ref,
|
|
254
|
-
default: undefined,
|
|
255
|
-
})}
|
|
256
|
-
style={css`
|
|
257
|
-
border-radius: ${borderRadiusStr};
|
|
258
|
-
`}
|
|
259
|
-
testID={testID}
|
|
260
|
-
underlayColor={theme.role.underlay}
|
|
261
|
-
{...touchableHighlightProps}
|
|
262
|
-
>
|
|
263
|
-
<View
|
|
264
|
-
style={innerContainerStyles}
|
|
265
|
-
testID={loading ? 'loading-view' : 'button-container'}
|
|
266
|
-
>
|
|
267
|
-
{loading ? LoadingView : IconView}
|
|
268
|
-
</View>
|
|
269
|
-
</TouchableHighlight>
|
|
270
|
-
</View>
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Export memoized component for better performance
|
|
275
|
-
export default React.memo(IconButton) as typeof IconButton;
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import React, {useMemo, useCallback, type ReactElement} from "react";
|
|
2
|
-
import type {
|
|
3
|
-
ImageSourcePropType,
|
|
4
|
-
ImageStyle,
|
|
5
|
-
StyleProp,
|
|
6
|
-
ViewStyle,
|
|
7
|
-
} from "react-native";
|
|
8
|
-
import { ActivityIndicator, Image, Platform, View } from "react-native";
|
|
9
|
-
import {styled} from "kstyled";
|
|
10
|
-
import { useTheme } from "../../../providers/ThemeProvider";
|
|
11
|
-
import type {BaseComponentProps} from "../../../types/common";
|
|
12
|
-
|
|
13
|
-
type Styles = {
|
|
14
|
-
activityIndicator?: ViewStyle;
|
|
15
|
-
image?: ImageStyle;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type LoadingIndicatorSizeType = 'small' | 'medium' | 'large' | number;
|
|
19
|
-
|
|
20
|
-
export interface LoadingIndicatorProps extends BaseComponentProps {
|
|
21
|
-
style?: StyleProp<ViewStyle>;
|
|
22
|
-
styles?: Styles;
|
|
23
|
-
color?: string;
|
|
24
|
-
size?: LoadingIndicatorSizeType;
|
|
25
|
-
imgSource?: string | ImageSourcePropType;
|
|
26
|
-
customElement?: ReactElement | (() => ReactElement);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const Container = styled(View)`
|
|
30
|
-
justify-content: center;
|
|
31
|
-
align-items: center;
|
|
32
|
-
`;
|
|
33
|
-
|
|
34
|
-
function LoadingIndicator({
|
|
35
|
-
customElement,
|
|
36
|
-
style,
|
|
37
|
-
styles,
|
|
38
|
-
size = "large",
|
|
39
|
-
color,
|
|
40
|
-
imgSource,
|
|
41
|
-
testID,
|
|
42
|
-
}: LoadingIndicatorProps): ReactElement {
|
|
43
|
-
const { theme } = useTheme();
|
|
44
|
-
|
|
45
|
-
// Convert size to ActivityIndicator compatible format
|
|
46
|
-
// Note: iOS only supports 'small' | 'large', Android also supports numbers
|
|
47
|
-
const activityIndicatorSize: 'small' | 'large' | number = useMemo(() => {
|
|
48
|
-
if (typeof size === 'number') {
|
|
49
|
-
// On iOS, convert numeric sizes to 'large' since numeric sizes aren't supported
|
|
50
|
-
if (Platform.OS === 'ios') return 'large';
|
|
51
|
-
return size;
|
|
52
|
-
}
|
|
53
|
-
if (size === 'medium') return 'large';
|
|
54
|
-
return size;
|
|
55
|
-
}, [size]);
|
|
56
|
-
|
|
57
|
-
// Memoize image source processing
|
|
58
|
-
const imageSource = useMemo((): ImageSourcePropType | undefined => {
|
|
59
|
-
if (!imgSource) return undefined;
|
|
60
|
-
|
|
61
|
-
if (typeof imgSource === "string") {
|
|
62
|
-
return { uri: imgSource };
|
|
63
|
-
}
|
|
64
|
-
return imgSource;
|
|
65
|
-
}, [imgSource]);
|
|
66
|
-
|
|
67
|
-
// Memoize image style calculation
|
|
68
|
-
const imageStyle = useMemo(() => {
|
|
69
|
-
if (typeof size === 'number') {
|
|
70
|
-
return [{ width: size, height: size }, styles?.image];
|
|
71
|
-
}
|
|
72
|
-
return [
|
|
73
|
-
size === "large"
|
|
74
|
-
? { width: 50, height: 50 }
|
|
75
|
-
: size === "medium"
|
|
76
|
-
? { width: 40, height: 40 }
|
|
77
|
-
: size === "small"
|
|
78
|
-
? { width: 30, height: 30 }
|
|
79
|
-
: undefined,
|
|
80
|
-
styles?.image,
|
|
81
|
-
];
|
|
82
|
-
}, [size, styles?.image]);
|
|
83
|
-
|
|
84
|
-
// Memoize activity indicator color
|
|
85
|
-
const activityIndicatorColor = useMemo(() => {
|
|
86
|
-
return color || theme.role.secondary;
|
|
87
|
-
}, [color, theme.role.secondary]);
|
|
88
|
-
|
|
89
|
-
// Memoize content renderer
|
|
90
|
-
const renderContent = useCallback(() => {
|
|
91
|
-
if (customElement) {
|
|
92
|
-
return typeof customElement === "function" ? customElement() : customElement;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (imageSource) {
|
|
96
|
-
return (
|
|
97
|
-
<Image
|
|
98
|
-
source={imageSource}
|
|
99
|
-
style={imageStyle}
|
|
100
|
-
testID={`${testID}-image`}
|
|
101
|
-
/>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<ActivityIndicator
|
|
107
|
-
color={activityIndicatorColor}
|
|
108
|
-
size={activityIndicatorSize}
|
|
109
|
-
style={styles?.activityIndicator}
|
|
110
|
-
testID={`${testID}-activity-indicator`}
|
|
111
|
-
/>
|
|
112
|
-
);
|
|
113
|
-
}, [customElement, imageSource, imageStyle, testID, activityIndicatorColor, activityIndicatorSize, styles?.activityIndicator]);
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<Container style={style} testID={testID}>
|
|
117
|
-
{renderContent()}
|
|
118
|
-
</Container>
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Export memoized component for better performance
|
|
123
|
-
export default React.memo(LoadingIndicator) as typeof LoadingIndicator;
|
|
124
|
-
|
|
125
|
-
// Also export the non-memoized version for cases where memoization is not needed
|
|
126
|
-
export {LoadingIndicator};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import type {ComponentProps} from 'react';
|
|
2
|
-
import type {Meta, StoryObj} from '@storybook/react';
|
|
3
|
-
import {withThemeProvider} from '../../../../.storybook/decorators';
|
|
4
|
-
import {LoadingIndicator} from './LoadingIndicator';
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'LoadingIndicator',
|
|
8
|
-
component: (props) => <LoadingIndicator {...props} />,
|
|
9
|
-
parameters: {
|
|
10
|
-
docs: {
|
|
11
|
-
description: {
|
|
12
|
-
component: `
|
|
13
|
-
A loading indicator component that displays a spinner or custom loading animation.
|
|
14
|
-
|
|
15
|
-
## Features
|
|
16
|
-
- **Flexible Sizing**: Preset sizes and custom numeric values
|
|
17
|
-
- **Platform Adaptive**: Uses native ActivityIndicator on mobile platforms
|
|
18
|
-
- **Custom Colors**: Support for custom loading indicator colors
|
|
19
|
-
- **Custom Elements**: Can replace with custom loading animation
|
|
20
|
-
|
|
21
|
-
## Size Options
|
|
22
|
-
- \`small\`: 30px indicator
|
|
23
|
-
- \`medium\`: 40px indicator
|
|
24
|
-
- \`large\`: 50px indicator (default on native)
|
|
25
|
-
- Custom number: Any pixel value (e.g., \`size={45}\`)
|
|
26
|
-
|
|
27
|
-
## Platform Behavior
|
|
28
|
-
- **iOS/Android**: Uses native ActivityIndicator with "small" or "large" sizes
|
|
29
|
-
- **Web**: Custom implementation with flexible sizing
|
|
30
|
-
- **Medium size**: Maps to "large" on native platforms
|
|
31
|
-
|
|
32
|
-
## Usage
|
|
33
|
-
\`\`\`tsx
|
|
34
|
-
<LoadingIndicator
|
|
35
|
-
size="medium"
|
|
36
|
-
color="#0000FF"
|
|
37
|
-
/>
|
|
38
|
-
\`\`\`
|
|
39
|
-
|
|
40
|
-
### With Custom Element
|
|
41
|
-
\`\`\`tsx
|
|
42
|
-
<LoadingIndicator
|
|
43
|
-
size={50}
|
|
44
|
-
customElement={<CustomSpinner />}
|
|
45
|
-
/>
|
|
46
|
-
\`\`\`
|
|
47
|
-
`,
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
argTypes: {
|
|
52
|
-
size: {
|
|
53
|
-
control: 'radio',
|
|
54
|
-
options: ['small', 'medium', 'large', 20, 30, 40, 50],
|
|
55
|
-
description: 'Indicator size: "small" (30px), "medium" (40px), "large" (50px), or custom number in pixels',
|
|
56
|
-
},
|
|
57
|
-
color: {
|
|
58
|
-
description: 'Custom color for the loading indicator',
|
|
59
|
-
},
|
|
60
|
-
customElement: {
|
|
61
|
-
description: 'Custom loading element to replace the default spinner',
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
decorators: [withThemeProvider],
|
|
65
|
-
} satisfies Meta<typeof LoadingIndicator>;
|
|
66
|
-
|
|
67
|
-
export default meta;
|
|
68
|
-
|
|
69
|
-
type Story = StoryObj<typeof meta>;
|
|
70
|
-
|
|
71
|
-
export const Basic: Story = {
|
|
72
|
-
args: {
|
|
73
|
-
size: 'medium',
|
|
74
|
-
},
|
|
75
|
-
argTypes: {
|
|
76
|
-
theme: {
|
|
77
|
-
control: 'select',
|
|
78
|
-
options: ['light', 'dark'],
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
};
|