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,590 +0,0 @@
1
- import type {MutableRefObject, ReactNode, RefObject} from 'react';
2
- import React, {
3
- forwardRef,
4
- isValidElement,
5
- useCallback,
6
- useMemo,
7
- useRef,
8
- useState,
9
- type ReactElement,
10
- } from 'react';
11
- import type {
12
- StyleProp,
13
- TextInputProps,
14
- TextStyle,
15
- ViewStyle,
16
- } from 'react-native';
17
- import {
18
- Platform,
19
- Text,
20
- TextInput,
21
- TouchableWithoutFeedback,
22
- View,
23
- } from 'react-native';
24
- import {useHover} from 'react-native-web-hooks';
25
- import {css} from 'kstyled';
26
-
27
- import {Icon} from '../Icon/Icon';
28
- import {cloneElemWithDefaultColors} from '../../../utils/guards';
29
- import {useTheme} from '../../../providers/ThemeProvider';
30
- import {Typography} from '../Typography/Typography';
31
- import {isWeb, safePlatformSelect} from '../../../utils/platform';
32
-
33
- export type EditTextStyles = {
34
- container?: StyleProp<ViewStyle>;
35
- labelContainer?: StyleProp<ViewStyle>;
36
- label?: StyleProp<TextStyle>;
37
- inputContainer?: StyleProp<TextStyle>;
38
- input?: StyleProp<TextStyle>;
39
- error?: StyleProp<TextStyle>;
40
- counter?: StyleProp<TextStyle>;
41
- };
42
-
43
- export type EditTextSizeType = 'small' | 'medium' | 'large' | number;
44
-
45
- export type EditTextStatus =
46
- | 'disabled'
47
- | 'error'
48
- | 'focused'
49
- | 'hovered'
50
- | 'basic';
51
-
52
- type RenderType = (stats: EditTextStatus) => ReactElement;
53
-
54
- type CustomRenderType =
55
- | (({color, status}: {color: string; status: EditTextStatus}) => ReactElement)
56
- | null;
57
-
58
- export type EditTextProps = {
59
- testID?: TextInputProps['testID'];
60
- inputRef?: MutableRefObject<TextInput | undefined> | RefObject<TextInput>;
61
- style?: StyleProp<ViewStyle>;
62
- styles?: EditTextStyles;
63
- size?: EditTextSizeType;
64
-
65
- // Component
66
- startElement?: ReactElement | CustomRenderType;
67
- endElement?: ReactElement | CustomRenderType;
68
- required?: boolean;
69
- label?: string | RenderType;
70
- error?: string | RenderType;
71
- direction?: 'row' | 'column';
72
- decoration?: 'underline' | 'boxed';
73
- value?: TextInputProps['value'];
74
- multiline?: TextInputProps['multiline'];
75
- onChange?: TextInputProps['onChange'];
76
- onChangeText?: TextInputProps['onChangeText'];
77
- placeholder?: TextInputProps['placeholder'];
78
- placeholderColor?: TextInputProps['placeholderTextColor'];
79
- onFocus?: TextInputProps['onFocus'] | undefined;
80
- onBlur?: TextInputProps['onBlur'] | undefined;
81
- editable?: TextInputProps['editable'];
82
- autoComplete?: TextInputProps['autoComplete'];
83
- autoCapitalize?: TextInputProps['autoCapitalize'];
84
- secureTextEntry?: TextInputProps['secureTextEntry'];
85
- onSubmitEditing?: TextInputProps['onSubmitEditing'];
86
- numberOfLines?: TextInputProps['numberOfLines'];
87
- maxLength?: TextInputProps['maxLength'];
88
- hideCounter?: boolean;
89
-
90
- textInputProps?: Omit<
91
- TextInputProps,
92
- | 'value'
93
- | 'onChange'
94
- | 'numberOfLines'
95
- | 'multiline'
96
- | 'onChange'
97
- | 'onChangeText'
98
- | 'placeholder'
99
- | 'placeholderTextColor'
100
- | 'onFocus'
101
- | 'onBlur'
102
- | 'editable'
103
- | 'autoComplete'
104
- | 'autoCapitalize'
105
- | 'secureTextEntry'
106
- | 'onSubmitEditing'
107
- | 'maxLength'
108
- >;
109
-
110
- colors?: {
111
- basic?: string;
112
- disabled?: string;
113
- error?: string;
114
- focused?: string;
115
- hovered?: string;
116
- placeholder?: string;
117
- };
118
- };
119
-
120
- export const EditText = forwardRef<TextInput, EditTextProps>(
121
- (
122
- {
123
- testID,
124
- textInputProps,
125
- style,
126
- styles,
127
- size = 'medium',
128
- label,
129
- error,
130
- startElement,
131
- endElement,
132
- multiline = false,
133
- value = '',
134
- placeholder,
135
- placeholderColor,
136
- onChange,
137
- onChangeText,
138
- onFocus,
139
- onBlur,
140
- onSubmitEditing,
141
- numberOfLines,
142
- maxLength,
143
- hideCounter = false,
144
- autoComplete,
145
- autoCapitalize = 'none',
146
- secureTextEntry = false,
147
- editable = true,
148
- direction = 'column',
149
- decoration = 'underline',
150
- colors = {},
151
- required = false,
152
- }: EditTextProps,
153
- ref,
154
- ): ReactElement => {
155
- EditText.displayName = 'EditText';
156
-
157
- const {theme} = useTheme();
158
-
159
- // Calculate sizes based on size prop
160
- const sizeConfig = useMemo(() => {
161
- if (typeof size === 'number') {
162
- return {
163
- fontSize: size,
164
- labelFontSize: size,
165
- iconSize: size * 0.875,
166
- padding: size * 0.625,
167
- labelMargin: size * 0.875,
168
- errorFontSize: size * 0.75,
169
- counterFontSize: size * 0.75,
170
- };
171
- }
172
-
173
- switch (size) {
174
- case 'small':
175
- return {
176
- fontSize: 14,
177
- labelFontSize: 14,
178
- iconSize: 12,
179
- padding: 8,
180
- labelMargin: 12,
181
- errorFontSize: 10,
182
- counterFontSize: 10,
183
- };
184
- case 'large':
185
- return {
186
- fontSize: 18,
187
- labelFontSize: 18,
188
- iconSize: 16,
189
- padding: 12,
190
- labelMargin: 16,
191
- errorFontSize: 14,
192
- counterFontSize: 14,
193
- };
194
- default: // 'medium'
195
- return {
196
- fontSize: 16,
197
- labelFontSize: 16,
198
- iconSize: 14,
199
- padding: 10,
200
- labelMargin: 14,
201
- errorFontSize: 12,
202
- counterFontSize: 12,
203
- };
204
- }
205
- }, [size]);
206
- const webRef = useRef<View>(null);
207
- const [focused, setFocused] = useState(false);
208
- const defaultInputRef = useRef(null);
209
- const inputRef = (ref as MutableRefObject<TextInput>) || defaultInputRef;
210
- const hovered = useHover(webRef);
211
-
212
- // Memoize default container style
213
- const defaultContainerStyle = useMemo(() => css`
214
- flex-direction: ${direction};
215
- `, [direction]);
216
-
217
- // Memoize default color calculation
218
- const defaultColor = useMemo(() => {
219
- if (!editable) return colors.disabled || theme.text.disabled;
220
- if (error) return colors.error || theme.text.validation;
221
- if (focused) return colors.focused || theme.text.basic;
222
- if (hovered) return colors.hovered || theme.text.basic;
223
- return colors.placeholder || theme.text.placeholder;
224
- }, [
225
- editable,
226
- error,
227
- focused,
228
- hovered,
229
- colors.disabled,
230
- colors.error,
231
- colors.focused,
232
- colors.hovered,
233
- colors.placeholder,
234
- theme.text.disabled,
235
- theme.text.validation,
236
- theme.text.basic,
237
- theme.text.placeholder,
238
- ]);
239
-
240
- // Memoize label placeholder color
241
- const labelPlaceholderColor = useMemo(
242
- () =>
243
- defaultColor === (colors.placeholder || theme.text.placeholder) && {
244
- color: colors.placeholder || theme.text.disabled,
245
- },
246
- [
247
- colors.placeholder,
248
- defaultColor,
249
- theme.text.disabled,
250
- theme.text.placeholder,
251
- ],
252
- );
253
-
254
- // Memoize status calculation
255
- const status: EditTextStatus = useMemo(() => {
256
- if (!editable) return 'disabled';
257
- if (error) return 'error';
258
- if (hovered) return 'hovered';
259
- if (focused) return 'focused';
260
- return 'basic';
261
- }, [editable, error, hovered, focused]);
262
-
263
- // Memoize render label function
264
- const renderLabel = useCallback((): ReactElement | null => {
265
- // eslint-disable-next-line react/no-unstable-nested-components
266
- function Wrapper({children}: {children: ReactNode}): ReactElement {
267
- return (
268
- <View
269
- style={[
270
- css`
271
- margin-bottom: ${decoration === 'boxed' ? sizeConfig.labelMargin : 0}px;
272
-
273
- flex-direction: row;
274
- align-items: center;
275
- `,
276
- styles?.labelContainer,
277
- ]}
278
- >
279
- {children}
280
- {required ? (
281
- <Icon
282
- name="AsteriskBold"
283
- size={sizeConfig.iconSize}
284
- style={css`
285
- color: ${theme.role.danger};
286
- opacity: ${focused ? '1' : '0.5'};
287
- `}
288
- />
289
- ) : null}
290
- </View>
291
- );
292
- }
293
-
294
- return typeof label === 'string' ? (
295
- <Wrapper>
296
- <Typography.Heading5
297
- style={[
298
- css`
299
- color: ${defaultColor};
300
- margin-right: 4px;
301
- font-size: ${sizeConfig.labelFontSize}px;
302
- `,
303
- labelPlaceholderColor,
304
- styles?.label,
305
- ]}
306
- >
307
- {label}
308
- </Typography.Heading5>
309
- </Wrapper>
310
- ) : label ? (
311
- <Wrapper>{label(status)}</Wrapper>
312
- ) : null;
313
- }, [
314
- decoration,
315
- defaultColor,
316
- focused,
317
- label,
318
- labelPlaceholderColor,
319
- required,
320
- sizeConfig,
321
- status,
322
- styles?.label,
323
- styles?.labelContainer,
324
- theme.role.danger,
325
- ]);
326
-
327
- // Memoize render container function
328
- const renderContainer = useCallback(
329
- (children: ReactNode): ReactElement => {
330
- return (
331
- <TouchableWithoutFeedback
332
- onPress={() => inputRef.current?.focus()}
333
- testID="container-touch"
334
- >
335
- <View
336
- style={[
337
- defaultContainerStyle,
338
- css`
339
- flex-direction: ${direction};
340
- align-items: ${direction === 'row' ? 'center' : 'flex-start'};
341
- justify-content: ${direction === 'row'
342
- ? 'flex-start'
343
- : 'space-between'};
344
- border-color: ${labelPlaceholderColor
345
- ? labelPlaceholderColor.color
346
- : defaultColor};
347
- `,
348
- decoration === 'boxed'
349
- ? css`
350
- border-radius: 4px;
351
- border-width: 1px;
352
- padding-left: ${sizeConfig.padding}px;
353
- padding-right: ${sizeConfig.padding}px;
354
- `
355
- : css`
356
- border-bottom-width: 1px;
357
- `,
358
- styles?.container,
359
- ]}
360
- testID="container"
361
- >
362
- {children}
363
- </View>
364
- </TouchableWithoutFeedback>
365
- );
366
- },
367
- [
368
- decoration,
369
- defaultColor,
370
- defaultContainerStyle,
371
- direction,
372
- inputRef,
373
- labelPlaceholderColor,
374
- sizeConfig,
375
- styles?.container,
376
- ],
377
- );
378
-
379
- // Memoize render input function
380
- const renderInput = useCallback((): ReactElement | null => {
381
- return (
382
- <View
383
- style={[
384
- direction === 'row'
385
- ? css`
386
- flex: 1;
387
- `
388
- : css`
389
- align-self: stretch;
390
- `,
391
- css`
392
- padding: ${decoration === 'boxed' ? `${sizeConfig.padding * 0.4}px 0` : `${sizeConfig.padding * 0.2}px 0`};
393
-
394
- flex-direction: row;
395
- align-items: center;
396
- justify-content: space-between;
397
- `,
398
- styles?.inputContainer,
399
- ]}
400
- >
401
- <>
402
- {isValidElement(startElement)
403
- ? cloneElemWithDefaultColors({
404
- element: startElement,
405
- color: defaultColor,
406
- style: css`
407
- margin-left: -4px;
408
- margin-right: 4px;
409
- `,
410
- })
411
- : startElement}
412
- <TextInput
413
- autoCapitalize={autoCapitalize}
414
- autoComplete={autoComplete}
415
- editable={editable}
416
- maxLength={maxLength}
417
- multiline={multiline}
418
- numberOfLines={numberOfLines}
419
- onBlur={(e) => {
420
- setFocused(false);
421
- onBlur?.(e);
422
- }}
423
- onChange={onChange}
424
- onChangeText={onChangeText}
425
- onFocus={(e) => {
426
- setFocused(true);
427
- onFocus?.(e);
428
- }}
429
- onSubmitEditing={onSubmitEditing}
430
- placeholder={placeholder}
431
- placeholderTextColor={placeholderColor || theme.text.placeholder}
432
- ref={inputRef}
433
- secureTextEntry={secureTextEntry}
434
- selectionColor={theme.role.underlay}
435
- style={[
436
- // Stretch input in order to make remaining space clickable
437
- css`
438
- font-family: Pretendard;
439
- flex: 1;
440
- font-size: ${sizeConfig.fontSize}px;
441
- text-align-vertical: ${multiline ? 'top' : 'center'};
442
- `,
443
- isWeb() &&
444
- css`
445
- outline-width: 0;
446
- `,
447
- direction === 'column'
448
- ? css`
449
- padding-top: ${sizeConfig.padding}px;
450
- `
451
- : css`
452
- padding-left: ${sizeConfig.padding}px;
453
- `,
454
- css`
455
- color: ${defaultColor};
456
- padding: ${sizeConfig.padding}px 0 ${sizeConfig.padding * 1.2}px 0;
457
- `,
458
- styles?.input,
459
- ]}
460
- testID={testID}
461
- value={value}
462
- {...textInputProps}
463
- />
464
- {isValidElement(endElement)
465
- ? cloneElemWithDefaultColors({
466
- element: endElement,
467
- color: defaultColor,
468
- style: css`
469
- margin-left: 4px;
470
- margin-right: ${decoration === 'boxed' ? '-8px' : '-4px'};
471
- `,
472
- })
473
- : endElement}
474
- </>
475
- </View>
476
- );
477
- }, [
478
- autoCapitalize,
479
- autoComplete,
480
- decoration,
481
- defaultColor,
482
- direction,
483
- editable,
484
- endElement,
485
- inputRef,
486
- maxLength,
487
- multiline,
488
- numberOfLines,
489
- onBlur,
490
- onChange,
491
- onChangeText,
492
- onFocus,
493
- onSubmitEditing,
494
- placeholder,
495
- placeholderColor,
496
- secureTextEntry,
497
- sizeConfig,
498
- startElement,
499
- styles?.input,
500
- styles?.inputContainer,
501
- testID,
502
- textInputProps,
503
- theme.role.underlay,
504
- theme.text.placeholder,
505
- value,
506
- ]);
507
-
508
- // Memoize render error function
509
- const renderError = useCallback((): ReactElement | null => {
510
- return error ? (
511
- typeof error === 'string' ? (
512
- <Text
513
- style={[
514
- css`
515
- flex: 1;
516
- color: ${theme.text.validation};
517
- font-size: ${sizeConfig.errorFontSize}px;
518
- `,
519
- styles?.error,
520
- ]}
521
- >
522
- {error}
523
- </Text>
524
- ) : (
525
- error?.(status)
526
- )
527
- ) : null;
528
- }, [error, sizeConfig, status, styles?.error, theme.text.validation]);
529
-
530
- // Memoize render counter function
531
- const renderCounter = useCallback((): ReactElement | null => {
532
- if (hideCounter) {
533
- return null;
534
- }
535
-
536
- return maxLength ? (
537
- <Text
538
- style={[
539
- css`
540
- color: ${theme.text.placeholder};
541
- font-size: ${sizeConfig.counterFontSize}px;
542
- `,
543
- styles?.counter,
544
- ]}
545
- >{`${value.length}/${maxLength}`}</Text>
546
- ) : null;
547
- }, [
548
- hideCounter,
549
- maxLength,
550
- sizeConfig,
551
- styles?.counter,
552
- theme.text.placeholder,
553
- value.length,
554
- ]);
555
-
556
- return (
557
- <View
558
- ref={safePlatformSelect({web: webRef, default: undefined})}
559
- style={[
560
- css`
561
- width: 100%;
562
- `,
563
- style,
564
- ]}
565
- testID="edit-text"
566
- >
567
- {renderLabel()}
568
- {renderContainer(renderInput())}
569
- {renderError() || renderCounter() ? (
570
- <View
571
- style={css`
572
- margin-top: 6px;
573
-
574
- flex-direction: ${!renderError() && renderCounter()
575
- ? 'row-reverse'
576
- : 'row'};
577
- gap: 4px;
578
- `}
579
- >
580
- {renderError()}
581
- {renderCounter()}
582
- </View>
583
- ) : null}
584
- </View>
585
- );
586
- },
587
- );
588
-
589
- // Export memoized EditText component for better performance
590
- export default React.memo(EditText) as typeof EditText;
@@ -1,133 +0,0 @@
1
- import React, {type ReactElement, useState} from 'react';
2
- import {View, Text} from 'react-native';
3
- import {styled, css} from 'kstyled';
4
- import type {Meta, StoryObj} from '@storybook/react';
5
- import {action} from '@storybook/addon-actions';
6
- import {withThemeProvider} from '../../../../.storybook/decorators';
7
- import {Fab} from './Fab';
8
- import type {IconName} from '../Icon/Icon';
9
- import type {ButtonSizeType} from '../Button/Button';
10
-
11
- const Container = styled(View)`
12
- position: relative;
13
- height: 500px;
14
- width: 100%;
15
- `;
16
-
17
- const InfoText = styled(Text)`
18
- position: absolute;
19
- top: 20px;
20
- left: 20px;
21
- color: ${({theme}) => theme.text.basic};
22
- font-size: 16px;
23
- `;
24
-
25
- interface FabStoryProps {
26
- buttonSize?: ButtonSizeType;
27
- theme?: 'light' | 'dark';
28
- }
29
-
30
- function FabInteractiveStory(props: FabStoryProps): ReactElement {
31
- const [isActive, setIsActive] = useState(false);
32
- const [lastPressed, setLastPressed] = useState<string>('');
33
-
34
- const handleFabPress = () => {
35
- const newState = !isActive;
36
- setIsActive(newState);
37
- action('FAB Toggled')({isActive: newState});
38
- };
39
-
40
- const handleItemPress = (icon?: IconName) => {
41
- setLastPressed(icon || '');
42
- setIsActive(false);
43
- action('FAB Item Pressed')({icon});
44
- };
45
-
46
- return (
47
- <Container>
48
- <InfoText>
49
- {isActive ? 'FAB is open' : 'FAB is closed'}
50
- {lastPressed && `\nLast pressed: ${lastPressed}`}
51
- </InfoText>
52
- <Fab
53
- icons={['MagnifyingGlass', 'Heart', 'Gear']}
54
- isActive={isActive}
55
- animationDuration={300}
56
- fabIcon="Plus"
57
- onPressFab={handleFabPress}
58
- onPressItem={handleItemPress}
59
- buttonSize={props.buttonSize}
60
- />
61
- </Container>
62
- );
63
- }
64
-
65
- const FAB_DOCS = `
66
- A Floating Action Button (FAB) component with expandable menu items.
67
-
68
- ## Features
69
- - **Expandable Menu**: Shows multiple action buttons when activated
70
- - **Smooth Animations**: Rotate and slide animations for FAB and items
71
- - **Flexible Sizing**: Preset sizes and custom numeric values via buttonSize
72
- - **Icon Support**: Uses IconButton for consistent icon rendering
73
- - **Customizable Gap**: Control spacing between FAB items
74
- - **Animation Duration**: Configurable animation speed
75
-
76
- ## Size Options (via buttonSize)
77
- The FAB uses IconButton internally, so it supports ButtonSizeType:
78
- - \`small\`: Small button size (8/16px padding)
79
- - \`medium\`: Medium button size (12/24px padding) - default
80
- - \`large\`: Large button size (16/24px padding)
81
- - Custom number: Calculated padding (size * 0.6 vertical, size * 1.2 horizontal)
82
-
83
- ## Usage
84
- \`\`\`tsx
85
- <Fab
86
- icons={['MagnifyingGlass', 'Heart', 'Gear']}
87
- isActive={isActive}
88
- fabIcon="Plus"
89
- onPressFab={() => setIsActive(!isActive)}
90
- onPressItem={(icon) => console.log(icon)}
91
- buttonSize="medium"
92
- gap={12}
93
- animationDuration={300}
94
- />
95
- \`\`\`
96
- `;
97
-
98
- const meta = {
99
- title: 'Fab',
100
- component: FabInteractiveStory,
101
- parameters: {
102
- notes: FAB_DOCS,
103
- docs: {
104
- description: {
105
- component: FAB_DOCS,
106
- },
107
- },
108
- },
109
- argTypes: {
110
- buttonSize: {
111
- control: 'radio',
112
- options: ['small', 'medium', 'large', 16, 20, 24, 32],
113
- description: 'Button size: "small" (8/16px), "medium" (12/24px), "large" (16/24px), or custom number for calculated padding',
114
- },
115
- },
116
- decorators: [withThemeProvider],
117
- } satisfies Meta<typeof FabInteractiveStory>;
118
-
119
- export default meta;
120
-
121
- type Story = StoryObj<typeof meta>;
122
-
123
- export const Basic: Story = {
124
- args: {
125
- buttonSize: 'medium',
126
- },
127
- argTypes: {
128
- theme: {
129
- control: 'select',
130
- options: ['light', 'dark'],
131
- },
132
- },
133
- };