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.
Files changed (136) hide show
  1. package/README.md +80 -147
  2. package/components/modals/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/components/modals/AlertDialog/AlertDialog.js +14 -7
  4. package/components/modals/Snackbar/Snackbar.d.ts.map +1 -1
  5. package/components/modals/Snackbar/Snackbar.js +20 -17
  6. package/components/uis/Accordion/AccordionItem.d.ts.map +1 -1
  7. package/components/uis/Accordion/AccordionItem.js +41 -17
  8. package/components/uis/Button/Button.d.ts +2 -1
  9. package/components/uis/Button/Button.d.ts.map +1 -1
  10. package/components/uis/Button/Button.js +11 -11
  11. package/components/uis/Card/Card.d.ts +2 -1
  12. package/components/uis/Card/Card.d.ts.map +1 -1
  13. package/components/uis/Card/Card.js +12 -3
  14. package/components/uis/Checkbox/Checkbox.d.ts +2 -1
  15. package/components/uis/Checkbox/Checkbox.d.ts.map +1 -1
  16. package/components/uis/Checkbox/Checkbox.js +2 -2
  17. package/components/uis/CustomPressable/CustomPressable.d.ts.map +1 -1
  18. package/components/uis/CustomPressable/CustomPressable.js +6 -6
  19. package/components/uis/EditText/EditText.d.ts +1 -0
  20. package/components/uis/EditText/EditText.d.ts.map +1 -1
  21. package/components/uis/EditText/EditText.js +18 -13
  22. package/components/uis/Fab/Fab.d.ts +2 -2
  23. package/components/uis/Fab/Fab.d.ts.map +1 -1
  24. package/components/uis/Fab/Fab.js +22 -4
  25. package/components/uis/Icon/Icon.d.ts.map +1 -1
  26. package/components/uis/Icon/Icon.js +6 -3
  27. package/components/uis/IconButton/IconButton.d.ts +2 -1
  28. package/components/uis/IconButton/IconButton.d.ts.map +1 -1
  29. package/components/uis/IconButton/IconButton.js +44 -42
  30. package/components/uis/LoadingIndicator/LoadingIndicator.d.ts +2 -1
  31. package/components/uis/LoadingIndicator/LoadingIndicator.d.ts.map +1 -1
  32. package/components/uis/LoadingIndicator/LoadingIndicator.js +4 -4
  33. package/components/uis/RadioGroup/RadioButton.d.ts +2 -1
  34. package/components/uis/RadioGroup/RadioButton.d.ts.map +1 -1
  35. package/components/uis/RadioGroup/RadioButton.js +2 -2
  36. package/components/uis/RadioGroup/RadioGroup.d.ts.map +1 -1
  37. package/components/uis/RadioGroup/RadioGroup.js +16 -3
  38. package/components/uis/Rating/Rating.d.ts.map +1 -1
  39. package/components/uis/Rating/Rating.js +23 -19
  40. package/components/uis/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  41. package/components/uis/SegmentedControl/SegmentedControl.js +1 -1
  42. package/components/uis/StatusbarBrightness/StatusBarBrightness.d.ts +2 -2
  43. package/components/uis/StatusbarBrightness/StatusBarBrightness.d.ts.map +1 -1
  44. package/components/uis/StatusbarBrightness/StatusBarBrightness.js +1 -1
  45. package/components/uis/SwitchToggle/SwitchToggle.d.ts +2 -1
  46. package/components/uis/SwitchToggle/SwitchToggle.d.ts.map +1 -1
  47. package/components/uis/SwitchToggle/SwitchToggle.js +2 -2
  48. package/hooks/useHoverState.d.ts +8 -0
  49. package/hooks/useHoverState.d.ts.map +1 -0
  50. package/hooks/useHoverState.js +16 -0
  51. package/package.json +18 -6
  52. package/providers/ThemeProvider.d.ts.map +1 -1
  53. package/providers/ThemeProvider.js +0 -1
  54. package/providers/index.d.ts.map +1 -1
  55. package/providers/index.js +3 -2
  56. package/components/uis/Icon/selection.json +0 -1
  57. package/src/components/common/ErrorBoundary/ErrorBoundary.tsx +0 -75
  58. package/src/components/modals/AlertDialog/AlertDialog.stories.tsx +0 -216
  59. package/src/components/modals/AlertDialog/AlertDialog.test.tsx +0 -251
  60. package/src/components/modals/AlertDialog/AlertDialog.tsx +0 -356
  61. package/src/components/modals/Snackbar/Snackbar.stories.tsx +0 -182
  62. package/src/components/modals/Snackbar/Snackbar.test.tsx +0 -319
  63. package/src/components/modals/Snackbar/Snackbar.tsx +0 -260
  64. package/src/components/modals/Snackbar/const.ts +0 -4
  65. package/src/components/uis/Accordion/Accordion.stories.tsx +0 -167
  66. package/src/components/uis/Accordion/Accordion.test.tsx +0 -272
  67. package/src/components/uis/Accordion/Accordion.tsx +0 -109
  68. package/src/components/uis/Accordion/AccordionItem.tsx +0 -306
  69. package/src/components/uis/Button/Button.stories.tsx +0 -218
  70. package/src/components/uis/Button/Button.test.tsx +0 -620
  71. package/src/components/uis/Button/Button.tsx +0 -436
  72. package/src/components/uis/Card/Card.test.tsx +0 -146
  73. package/src/components/uis/Card/Card.tsx +0 -102
  74. package/src/components/uis/Checkbox/Checkbox.stories.tsx +0 -216
  75. package/src/components/uis/Checkbox/Checkbox.test.tsx +0 -173
  76. package/src/components/uis/Checkbox/Checkbox.tsx +0 -220
  77. package/src/components/uis/CustomPressable/CustomPressable.tsx +0 -48
  78. package/src/components/uis/EditText/EditText.stories.tsx +0 -124
  79. package/src/components/uis/EditText/EditText.test.tsx +0 -477
  80. package/src/components/uis/EditText/EditText.tsx +0 -590
  81. package/src/components/uis/Fab/Fab.stories.tsx +0 -133
  82. package/src/components/uis/Fab/Fab.test.tsx +0 -110
  83. package/src/components/uis/Fab/Fab.tsx +0 -168
  84. package/src/components/uis/Hr/Hr.test.tsx +0 -26
  85. package/src/components/uis/Hr/Hr.tsx +0 -19
  86. package/src/components/uis/Icon/Icon.stories.tsx +0 -98
  87. package/src/components/uis/Icon/Icon.test.tsx +0 -49
  88. package/src/components/uis/Icon/Icon.tsx +0 -3819
  89. package/src/components/uis/IconButton/IconButton.test.tsx +0 -165
  90. package/src/components/uis/IconButton/IconButton.tsx +0 -275
  91. package/src/components/uis/LoadingIndicator/LoadingIndicator.tsx +0 -126
  92. package/src/components/uis/LoadingIndicator/LodingIndicator.stories.tsx +0 -81
  93. package/src/components/uis/PinchZoom/PinchZoom.stories.tsx +0 -266
  94. package/src/components/uis/PinchZoom/PinchZoom.test.tsx +0 -113
  95. package/src/components/uis/PinchZoom/PinchZoom.tsx +0 -424
  96. package/src/components/uis/RadioGroup/RadioButton.tsx +0 -188
  97. package/src/components/uis/RadioGroup/RadioGroup.stories.tsx +0 -188
  98. package/src/components/uis/RadioGroup/RadioGroup.test.tsx +0 -287
  99. package/src/components/uis/RadioGroup/RadioGroup.tsx +0 -109
  100. package/src/components/uis/Rating/Rating.test.tsx +0 -98
  101. package/src/components/uis/Rating/Rating.tsx +0 -200
  102. package/src/components/uis/SegmentedControl/SegmentedControl.stories.tsx +0 -546
  103. package/src/components/uis/SegmentedControl/SegmentedControl.test.tsx +0 -274
  104. package/src/components/uis/SegmentedControl/SegmentedControl.tsx +0 -229
  105. package/src/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +0 -41
  106. package/src/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +0 -26
  107. package/src/components/uis/Styled/StyledComponents.tsx +0 -217
  108. package/src/components/uis/SwitchToggle/SwitchToggle.stories.tsx +0 -75
  109. package/src/components/uis/SwitchToggle/SwitchToggle.test.tsx +0 -105
  110. package/src/components/uis/SwitchToggle/SwitchToggle.tsx +0 -305
  111. package/src/components/uis/Typography/Typography.stories.tsx +0 -149
  112. package/src/components/uis/Typography/Typography.test.tsx +0 -58
  113. package/src/components/uis/Typography/Typography.tsx +0 -386
  114. package/src/hooks/index.ts +0 -2
  115. package/src/hooks/useAnimations.ts +0 -71
  116. package/src/hooks/useDebouncedColorScheme.tsx +0 -30
  117. package/src/hooks/useOptimizedStyles.ts +0 -22
  118. package/src/hooks/usePerformance.ts +0 -93
  119. package/src/index.tsx +0 -38
  120. package/src/providers/ThemeProvider.tsx +0 -177
  121. package/src/providers/index.tsx +0 -130
  122. package/src/test/testUtils.tsx +0 -101
  123. package/src/types/common.ts +0 -77
  124. package/src/types/index.ts +0 -1
  125. package/src/types/kstyled.d.ts +0 -6
  126. package/src/utils/accessibility.ts +0 -110
  127. package/src/utils/colors.ts +0 -151
  128. package/src/utils/createCtx.tsx +0 -26
  129. package/src/utils/guards.ts +0 -93
  130. package/src/utils/index.ts +0 -2
  131. package/src/utils/performanceTest.ts +0 -174
  132. package/src/utils/platform.ts +0 -69
  133. package/src/utils/theme.ts +0 -38
  134. package/test/testUtils.d.ts +0 -113
  135. package/test/testUtils.d.ts.map +0 -1
  136. 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
- };