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