@12-apps/ui 1.4.0 → 1.5.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 (66) hide show
  1. package/eslint.config.js +12 -0
  2. package/package.json +5 -4
  3. package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +16 -6
  4. package/src/components/data-display/Banner/Banner.test.stories.tsx +26 -18
  5. package/src/components/data-display/Carousel/Carousel.chrome.tsx +83 -0
  6. package/src/components/data-display/Carousel/Carousel.hooks.ts +88 -0
  7. package/src/components/data-display/Carousel/Carousel.parts.tsx +346 -0
  8. package/src/components/data-display/Carousel/Carousel.slide.tsx +190 -0
  9. package/src/components/data-display/Carousel/Carousel.styles.ts +86 -0
  10. package/src/components/data-display/Carousel/Carousel.test.stories.tsx +15 -5
  11. package/src/components/data-display/Carousel/Carousel.tsx +84 -732
  12. package/src/components/data-display/HoverCard/HoverCard.animations.ts +73 -0
  13. package/src/components/data-display/HoverCard/HoverCard.content.tsx +143 -0
  14. package/src/components/data-display/HoverCard/HoverCard.hooks.ts +180 -0
  15. package/src/components/data-display/HoverCard/HoverCard.styles.ts +178 -0
  16. package/src/components/data-display/HoverCard/HoverCard.tsx +117 -631
  17. package/src/components/data-display/Sheet/Sheet.animations.ts +44 -0
  18. package/src/components/data-display/Sheet/Sheet.body.tsx +124 -0
  19. package/src/components/data-display/Sheet/Sheet.drag.ts +203 -0
  20. package/src/components/data-display/Sheet/Sheet.hooks.ts +318 -0
  21. package/src/components/data-display/Sheet/Sheet.parts.tsx +224 -0
  22. package/src/components/data-display/Sheet/Sheet.styles.ts +257 -0
  23. package/src/components/data-display/Sheet/Sheet.test.stories.tsx +25 -21
  24. package/src/components/data-display/Sheet/Sheet.tsx +125 -948
  25. package/src/components/feedback/Dialog/Dialog.test.stories.tsx +37 -28
  26. package/src/components/feedback/Modal/Modal.styles.ts +126 -0
  27. package/src/components/feedback/Modal/Modal.test.stories.tsx +5 -5
  28. package/src/components/feedback/Modal/Modal.transitions.tsx +40 -0
  29. package/src/components/feedback/Modal/Modal.tsx +37 -242
  30. package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +9 -11
  31. package/src/components/form/InputOTP/InputOTP.hooks.ts +104 -0
  32. package/src/components/form/InputOTP/InputOTP.styles.ts +97 -0
  33. package/src/components/form/InputOTP/InputOTP.tsx +74 -221
  34. package/src/components/form/Label/Label.parts.tsx +75 -0
  35. package/src/components/form/Label/Label.styles.ts +206 -0
  36. package/src/components/form/Label/Label.test.stories.tsx +20 -14
  37. package/src/components/form/Label/Label.tsx +73 -325
  38. package/src/components/form/Menubar/Menubar.hooks.ts +41 -0
  39. package/src/components/form/Menubar/Menubar.item.tsx +121 -0
  40. package/src/components/form/Menubar/Menubar.separator.tsx +25 -0
  41. package/src/components/form/Menubar/Menubar.styles.ts +110 -0
  42. package/src/components/form/Menubar/Menubar.toolbar.tsx +103 -0
  43. package/src/components/form/Menubar/Menubar.tsx +64 -347
  44. package/src/components/form/RadioGroup/RadioGroup.animations.ts +48 -0
  45. package/src/components/form/RadioGroup/RadioGroup.styles.ts +305 -0
  46. package/src/components/form/RadioGroup/RadioGroup.tsx +81 -651
  47. package/src/components/form/RadioGroup/RadioGroup.variants.tsx +291 -0
  48. package/src/components/form/Slider/Slider.animations.ts +40 -0
  49. package/src/components/form/Slider/Slider.styles.ts +292 -0
  50. package/src/components/form/Slider/Slider.tsx +106 -357
  51. package/src/components/form/Switch/Switch.animations.ts +62 -0
  52. package/src/components/form/Switch/Switch.parts.tsx +213 -0
  53. package/src/components/form/Switch/Switch.styles.ts +355 -0
  54. package/src/components/form/Switch/Switch.tsx +87 -566
  55. package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +26 -29
  56. package/src/components/layout/Resizable/Resizable.test.stories.tsx +11 -7
  57. package/src/components/layout/Spacer/Spacer.test.stories.tsx +11 -9
  58. package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +7 -7
  59. package/src/components/typography/Heading/Heading.styles.ts +101 -0
  60. package/src/components/typography/Heading/Heading.tsx +48 -164
  61. package/src/components/typography/Text/Text.test.stories.tsx +27 -30
  62. package/src/components/utility/AnimatedIcon/AnimatedIcon.animations.ts +274 -0
  63. package/src/components/utility/AnimatedIcon/AnimatedIcon.styles.ts +231 -0
  64. package/src/components/utility/AnimatedIcon/AnimatedIcon.tsx +39 -559
  65. package/src/utils/index.ts +1 -0
  66. package/src/utils/withDefaults.ts +20 -0
@@ -1,578 +1,99 @@
1
- import type {
2
- Theme} from '@mui/material';
3
- import {
4
- alpha,
5
- Box,
6
- FormHelperText,
7
- keyframes,
8
- Switch as MuiSwitch,
9
- Typography,
10
- } from '@mui/material';
11
- import { styled } from '@mui/material/styles';
1
+ import { Box } from '@mui/material';
12
2
  import React, { forwardRef } from 'react';
13
3
 
14
- import type { SwitchProps } from './Switch.types';
15
-
16
- const glowAnimation = keyframes`
17
- 0% {
18
- box-shadow: 0 0 5px currentColor;
19
- }
20
- 50% {
21
- box-shadow: 0 0 15px currentColor, 0 0 25px currentColor;
22
- }
23
- 100% {
24
- box-shadow: 0 0 5px currentColor;
25
- }
26
- `;
27
-
28
- const bounceAnimation = keyframes`
29
- 0%, 100% {
30
- transform: scale(1);
31
- }
32
- 50% {
33
- transform: scale(1.1);
34
- }
35
- `;
36
-
37
- const pulseAnimation = keyframes`
38
- 0%, 100% {
39
- opacity: 1;
40
- transform: scale(1);
41
- }
42
- 50% {
43
- opacity: 0.7;
44
- transform: scale(0.95);
45
- }
46
- `;
4
+ import { withDefaults } from '../../../utils/withDefaults';
47
5
 
48
- const rippleAnimation = keyframes`
49
- 0% {
50
- transform: scale(0);
51
- opacity: 1;
52
- }
53
- 100% {
54
- transform: scale(2);
55
- opacity: 0;
56
- }
57
- `;
58
-
59
- const spinAnimation = keyframes`
60
- 0% {
61
- transform: rotate(0deg);
62
- }
63
- 100% {
64
- transform: rotate(360deg);
65
- }
66
- `;
67
-
68
- const shimmerAnimation = keyframes`
69
- 0% {
70
- background-position: -200% 50%;
71
- }
72
- 100% {
73
- background-position: 200% 50%;
74
- }
75
- `;
76
-
77
- interface ThemePalette {
78
- main: string;
79
- dark?: string;
80
- light?: string;
81
- contrastText?: string;
82
- }
83
-
84
- const getColorFromTheme = (theme: Theme, color: string) => {
85
- if (color === 'neutral') {
86
- return {
87
- main: theme.palette.grey?.[700] || '#616161',
88
- dark: theme.palette.grey?.[800] || '#424242',
89
- light: theme.palette.grey?.[500] || '#9e9e9e',
90
- contrastText: '#fff',
91
- };
92
- }
93
-
94
- const colorMap: Record<string, ThemePalette> = {
95
- primary: theme.palette.primary,
96
- secondary: theme.palette.secondary,
97
- success: theme.palette.success,
98
- warning: theme.palette.warning,
99
- danger: theme.palette.error,
100
- };
101
-
102
- const palette = colorMap[color] || theme.palette.primary;
103
-
104
- // Ensure palette has required properties
105
- return {
106
- main: palette?.main || theme.palette.primary.main,
107
- dark: palette?.dark || palette?.main || theme.palette.primary.dark,
108
- light: palette?.light || palette?.main || theme.palette.primary.light,
109
- contrastText: palette?.contrastText || '#fff',
110
- };
111
- };
112
-
113
- const StyledSwitch = styled(MuiSwitch, {
114
- shouldForwardProp: (prop) =>
115
- ![
116
- 'customVariant',
117
- 'customColor',
118
- 'customSize',
119
- 'glow',
120
- 'glass',
121
- 'gradient',
122
- 'trackWidth',
123
- 'trackHeight',
124
- 'onText',
125
- 'offText',
126
- 'loading',
127
- 'ripple',
128
- 'pulse',
129
- ].includes(prop as string),
130
- })<{
131
- customVariant?: string;
132
- customColor?: string;
133
- customSize?: string;
134
- glow?: boolean;
135
- glass?: boolean;
136
- gradient?: boolean;
137
- trackWidth?: number;
138
- trackHeight?: number;
139
- onText?: string;
140
- offText?: string;
141
- loading?: boolean;
142
- ripple?: boolean;
143
- pulse?: boolean;
144
- }>(({
145
- theme,
146
- customVariant,
147
- customColor = 'primary',
148
- customSize = 'md',
149
- glow,
150
- glass,
151
- gradient,
152
- trackWidth,
153
- trackHeight,
154
- onText,
155
- offText,
156
- loading,
157
- ripple,
158
- pulse,
159
- }) => {
160
- const colorPalette = getColorFromTheme(theme, customColor);
161
-
162
- const sizeMap = {
163
- xs: { width: 34, height: 18, padding: 1, thumbSize: 14 },
164
- sm: { width: 42, height: 22, padding: 1, thumbSize: 18 },
165
- md: { width: 50, height: 26, padding: 1, thumbSize: 22 },
166
- lg: { width: 58, height: 30, padding: 2, thumbSize: 24 },
167
- xl: { width: 66, height: 34, padding: 2, thumbSize: 28 },
168
- };
169
-
170
- const currentSize = sizeMap[customSize as keyof typeof sizeMap];
171
- const width = trackWidth || currentSize.width;
172
- const height = trackHeight || currentSize.height;
173
- const thumbSize = currentSize.thumbSize;
174
-
175
- const iosVariant = customVariant === 'ios';
176
- const androidVariant = customVariant === 'android';
177
- const labelVariant = customVariant === 'label';
178
- const materialVariant = customVariant === 'material';
179
-
180
- return {
181
- width,
182
- height,
183
- padding: 0,
184
- overflow: 'visible',
185
- '& .MuiSwitch-switchBase': {
186
- padding: currentSize.padding,
187
- margin: 0,
188
- transitionDuration: '300ms',
189
- transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
190
- ...(iosVariant && {
191
- transform: 'translateX(2px)',
192
- }),
193
- '&:hover': {
194
- '& .MuiSwitch-thumb': {
195
- transform: loading ? 'none' : 'scale(1.05)',
196
- boxShadow: `${theme.shadows[4]}, 0 0 12px ${alpha(colorPalette.main, 0.2)}`,
197
- },
198
- ...(ripple && {
199
- '&::after': {
200
- content: '""',
201
- position: 'absolute',
202
- top: '50%',
203
- left: '50%',
204
- width: '100%',
205
- height: '100%',
206
- borderRadius: '50%',
207
- background: alpha(colorPalette.main, 0.2),
208
- animation: `${rippleAnimation} 0.6s ease-out`,
209
- transform: 'translate(-50%, -50%)',
210
- pointerEvents: 'none',
211
- },
212
- }),
213
- },
214
- '&.Mui-checked': {
215
- transform: `translateX(${width - thumbSize - currentSize.padding * 2}px)`,
216
- color: '#fff',
217
- '& .MuiSwitch-thumb': {
218
- animation: loading ? 'none' : `${bounceAnimation} 0.3s ease-out`,
219
- },
220
- '& + .MuiSwitch-track': {
221
- backgroundColor: gradient ? colorPalette.main : colorPalette.main,
222
- opacity: 1,
223
- border: 0,
224
- position: 'relative',
225
- overflow: 'hidden',
226
- ...(glow && {
227
- animation: `${glowAnimation} 2s ease-in-out infinite`,
228
- boxShadow: `0 0 10px ${alpha(colorPalette.main, 0.6)}, inset 0 0 10px ${alpha(colorPalette.main, 0.2)}`,
229
- }),
230
- ...(gradient && {
231
- background: `linear-gradient(90deg, ${colorPalette.light || colorPalette.main} 0%, ${colorPalette.main} 50%, ${colorPalette.dark || colorPalette.main} 100%)`,
232
- backgroundSize: '200% 100%',
233
- animation: `${shimmerAnimation} 3s ease infinite`,
234
- }),
235
- },
236
- '&.Mui-disabled + .MuiSwitch-track': {
237
- opacity: 0.5,
238
- },
239
- ...(iosVariant && {
240
- transform: `translateX(${width - thumbSize - 4}px)`,
241
- }),
242
- },
243
- '&.Mui-focusVisible .MuiSwitch-thumb': {
244
- color: colorPalette.main,
245
- border: `6px solid ${alpha(colorPalette.main, 0.2)}`,
246
- },
247
- '&.Mui-disabled .MuiSwitch-thumb': {
248
- color: theme.palette.grey[100],
249
- },
250
- '&.Mui-disabled + .MuiSwitch-track': {
251
- opacity: 0.3,
252
- },
253
- },
254
- '& .MuiSwitch-thumb': {
255
- width: thumbSize,
256
- height: thumbSize,
257
- borderRadius: iosVariant
258
- ? '50%'
259
- : androidVariant
260
- ? 4
261
- : materialVariant
262
- ? thumbSize / 3
263
- : '50%',
264
- backgroundColor: '#fff',
265
- boxShadow: iosVariant
266
- ? `0 3px 1px 0 ${alpha('#000', 0.04)}, 0 3px 8px 0 ${alpha('#000', 0.12)}, 0 1px 0 0 ${alpha('#000', 0.08)}`
267
- : androidVariant
268
- ? theme.shadows[3]
269
- : theme.shadows[2],
270
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
271
- position: 'relative',
272
- display: 'flex',
273
- alignItems: 'center',
274
- justifyContent: 'center',
275
- ...(glass && {
276
- backgroundColor: alpha(theme.palette.background.paper, 0.9),
277
- backdropFilter: 'blur(10px)',
278
- border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
279
- }),
280
- ...(pulse && {
281
- animation: `${pulseAnimation} 2s ease-in-out infinite`,
282
- }),
283
- ...(loading && {
284
- '&::after': {
285
- content: '""',
286
- position: 'absolute',
287
- width: thumbSize * 0.6,
288
- height: thumbSize * 0.6,
289
- border: `2px solid ${colorPalette.main}`,
290
- borderTop: `2px solid transparent`,
291
- borderRadius: '50%',
292
- animation: `${spinAnimation} 1s linear infinite`,
293
- },
294
- }),
295
- ...(materialVariant && {
296
- '&::before': {
297
- content: '""',
298
- position: 'absolute',
299
- width: '100%',
300
- height: '100%',
301
- borderRadius: 'inherit',
302
- backgroundColor: colorPalette.main,
303
- opacity: 0,
304
- transform: 'scale(0)',
305
- transition: 'all 0.3s ease',
306
- },
307
- }),
308
- },
309
- '& .MuiSwitch-track': {
310
- borderRadius: iosVariant
311
- ? height / 2
312
- : androidVariant
313
- ? height / 3
314
- : materialVariant
315
- ? height / 2.5
316
- : height / 2,
317
- backgroundColor: iosVariant
318
- ? alpha(theme.palette.common.black, 0.1)
319
- : androidVariant
320
- ? alpha(theme.palette.action.disabled, 0.2)
321
- : alpha(theme.palette.action.disabled, 0.3),
322
- opacity: 1,
323
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
324
- position: 'relative',
325
- boxShadow: iosVariant
326
- ? `inset 0 0 0 0.5px ${alpha('#000', 0.1)}, inset 0 2px 3px ${alpha('#000', 0.12)}`
327
- : 'none',
328
- ...(glass && {
329
- backgroundColor: alpha(theme.palette.background.paper, 0.1),
330
- backdropFilter: 'blur(20px)',
331
- border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
332
- }),
333
- ...(gradient &&
334
- !glass && {
335
- background: `linear-gradient(135deg, ${alpha(colorPalette.light || colorPalette.main, 0.3)}, ${alpha(colorPalette.main, 0.2)})`,
336
- }),
337
- ...(labelVariant &&
338
- (onText || offText) && {
339
- display: 'flex',
340
- alignItems: 'center',
341
- justifyContent: 'space-between',
342
- paddingLeft: theme.spacing(1),
343
- paddingRight: theme.spacing(1),
344
- fontSize: '0.75rem',
345
- fontWeight: 500,
346
- color: theme.palette.text.secondary,
347
- '&::before, &::after': {
348
- content: '""',
349
- position: 'absolute',
350
- fontSize: '0.75rem',
351
- fontWeight: 500,
352
- top: '50%',
353
- transform: 'translateY(-50%)',
354
- zIndex: 1,
355
- },
356
- ...(onText && {
357
- '&::before': {
358
- content: `"${onText}"`,
359
- left: theme.spacing(1),
360
- color: '#fff',
361
- },
362
- }),
363
- ...(offText && {
364
- '&::after': {
365
- content: `"${offText}"`,
366
- right: theme.spacing(1),
367
- color: theme.palette.text.secondary,
368
- },
369
- }),
370
- }),
371
- },
372
- };
373
- });
374
-
375
- const LabelContainer = styled(Box, {
376
- shouldForwardProp: (prop) => !['labelPosition', 'error'].includes(prop as string),
377
- })<{ labelPosition?: string; error?: boolean }>(({ theme, labelPosition, error }) => ({
378
- display: 'flex',
379
- alignItems: labelPosition === 'top' || labelPosition === 'bottom' ? 'flex-start' : 'center',
380
- flexDirection:
381
- labelPosition === 'top' ? 'column' : labelPosition === 'bottom' ? 'column-reverse' : 'row',
382
- gap: theme.spacing(labelPosition === 'top' || labelPosition === 'bottom' ? 1 : 2),
383
- width: '100%',
384
- ...(error && {
385
- '& .MuiTypography-root': {
386
- color: theme.palette.error.main,
387
- },
388
- }),
389
- }));
390
-
391
- export const Switch = forwardRef<HTMLButtonElement, SwitchProps>(
392
- (
393
- {
394
- variant = 'default',
395
- color = 'primary',
396
- size = 'md',
397
- label,
398
- description,
399
- glow = false,
400
- glass = false,
401
- gradient = false,
402
- labelPosition = 'end',
403
- onIcon,
404
- offIcon,
405
- onText,
406
- offText,
407
- error = false,
408
- helperText,
409
- trackWidth,
410
- trackHeight,
411
- checked,
412
- onChange,
413
- animated = true,
414
- loading = false,
415
- ripple = false,
416
- pulse = false,
417
- dataTestId,
418
- ...props
419
- },
420
- ref,
421
- ) => {
422
- const switchElement = (
423
- <Box sx={{ position: 'relative', display: 'inline-flex' }}>
424
- <StyledSwitch
425
- ref={ref}
426
- customVariant={variant}
427
- customColor={color}
428
- customSize={size}
429
- glow={glow}
430
- glass={glass}
431
- gradient={gradient}
432
- trackWidth={trackWidth}
433
- trackHeight={trackHeight}
434
- onText={onText}
435
- offText={offText}
436
- checked={checked}
437
- onChange={onChange}
438
- loading={loading}
439
- ripple={ripple}
440
- pulse={pulse}
441
- disabled={loading || props.disabled}
442
- inputProps={{
443
- 'aria-label': props['aria-label'],
444
- 'aria-describedby': props['aria-describedby'],
445
- ...props.inputProps,
446
- ...(dataTestId ? { 'data-testid': dataTestId } : { 'data-testid': 'switch' }),
447
- } as React.InputHTMLAttributes<HTMLInputElement>}
448
- {...props}
449
- />
6
+ import {
7
+ LabelContainer,
8
+ SwitchControl,
9
+ SwitchHelper,
10
+ SwitchLabel,
11
+ } from './Switch.parts';
12
+ import type { SwitchProps } from './Switch.types';
450
13
 
451
- {/* Icon overlays */}
452
- {(onIcon || offIcon) && (
453
- <>
454
- {onIcon && (
455
- <Box
456
- sx={{
457
- position: 'absolute',
458
- top: '50%',
459
- left: checked ? 4 : '50%',
460
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
461
- opacity: checked ? 1 : 0,
462
- transform:
463
- checked && animated
464
- ? 'translate(-50%, -50%) scale(1)'
465
- : 'translate(-50%, -50%) scale(0.8)',
466
- pointerEvents: 'none',
467
- zIndex: 2,
468
- color: '#fff',
469
- fontSize:
470
- size === 'xs'
471
- ? 12
472
- : size === 'sm'
473
- ? 14
474
- : size === 'md'
475
- ? 16
476
- : size === 'lg'
477
- ? 18
478
- : 20,
479
- }}
480
- >
481
- {onIcon}
482
- </Box>
483
- )}
484
- {offIcon && (
485
- <Box
486
- sx={{
487
- position: 'absolute',
488
- top: '50%',
489
- right: !checked ? 4 : '50%',
490
- transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
491
- opacity: !checked ? 1 : 0,
492
- transform:
493
- !checked && animated
494
- ? 'translate(50%, -50%) scale(1)'
495
- : 'translate(50%, -50%) scale(0.8)',
496
- pointerEvents: 'none',
497
- zIndex: 2,
498
- color: 'text.secondary',
499
- fontSize:
500
- size === 'xs'
501
- ? 12
502
- : size === 'sm'
503
- ? 14
504
- : size === 'md'
505
- ? 16
506
- : size === 'lg'
507
- ? 18
508
- : 20,
509
- }}
510
- >
511
- {offIcon}
512
- </Box>
513
- )}
514
- </>
515
- )}
14
+ const DEFAULTS = {
15
+ variant: 'default',
16
+ color: 'primary',
17
+ size: 'md',
18
+ glow: false,
19
+ glass: false,
20
+ gradient: false,
21
+ labelPosition: 'end',
22
+ error: false,
23
+ animated: true,
24
+ loading: false,
25
+ ripple: false,
26
+ pulse: false,
27
+ } satisfies Partial<SwitchProps>;
28
+
29
+ type ResolvedProps = SwitchProps & Required<Pick<SwitchProps, keyof typeof DEFAULTS>>;
30
+
31
+ export const Switch = forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
32
+ const {
33
+ variant, color, size, label, description, glow, glass, gradient, labelPosition,
34
+ onIcon, offIcon, onText, offText, error, helperText, trackWidth, trackHeight,
35
+ checked, onChange, animated, loading, ripple, pulse, dataTestId,
36
+ ...rest
37
+ } = withDefaults(props, DEFAULTS) as ResolvedProps;
38
+
39
+ const containerTestId = dataTestId ? `${dataTestId}-container` : 'switch-container';
40
+
41
+ const control = (
42
+ <SwitchControl
43
+ customVariant={variant}
44
+ customColor={color}
45
+ customSize={size}
46
+ glow={glow}
47
+ glass={glass}
48
+ gradient={gradient}
49
+ trackWidth={trackWidth}
50
+ trackHeight={trackHeight}
51
+ onText={onText}
52
+ offText={offText}
53
+ loading={loading}
54
+ ripple={ripple}
55
+ pulse={pulse}
56
+ checked={checked}
57
+ onChange={onChange}
58
+ onIcon={onIcon}
59
+ offIcon={offIcon}
60
+ animated={animated}
61
+ size={size}
62
+ dataTestId={dataTestId}
63
+ switchRef={ref}
64
+ rest={rest}
65
+ />
66
+ );
67
+
68
+ const helper = <SwitchHelper helperText={helperText} error={error} dataTestId={dataTestId} />;
69
+
70
+ if (!label) {
71
+ return (
72
+ <Box data-testid={containerTestId}>
73
+ {control}
74
+ {helper}
516
75
  </Box>
517
76
  );
77
+ }
518
78
 
519
- if (!label) {
520
- return (
521
- <Box data-testid={dataTestId ? `${dataTestId}-container` : 'switch-container'}>
522
- {switchElement}
523
- {helperText && (
524
- <FormHelperText
525
- error={error}
526
- sx={{ mt: 1 }}
527
- data-testid={dataTestId ? `${dataTestId}-helper` : 'switch-helper'}
528
- >
529
- {helperText}
530
- </FormHelperText>
531
- )}
532
- </Box>
533
- );
534
- }
79
+ return (
80
+ <Box data-testid={containerTestId}>
81
+ <LabelContainer labelPosition={labelPosition} error={error}>
82
+ {labelPosition === 'start' && control}
535
83
 
536
- return (
537
- <Box data-testid={dataTestId ? `${dataTestId}-container` : 'switch-container'}>
538
- <LabelContainer labelPosition={labelPosition} error={error}>
539
- {labelPosition === 'start' && switchElement}
540
-
541
- <Box sx={{ flex: 1 }}>
542
- <Typography
543
- variant="body2"
544
- fontWeight={500}
545
- color={error ? 'error.main' : 'text.primary'}
546
- data-testid={dataTestId ? `${dataTestId}-label` : 'switch-label'}
547
- >
548
- {label}
549
- </Typography>
550
- {description && (
551
- <Typography
552
- variant="caption"
553
- color={error ? 'error.main' : 'text.secondary'}
554
- sx={{ display: 'block', mt: 0.5 }}
555
- >
556
- {description}
557
- </Typography>
558
- )}
559
- </Box>
84
+ <SwitchLabel
85
+ label={label}
86
+ description={description}
87
+ error={error}
88
+ dataTestId={dataTestId}
89
+ />
560
90
 
561
- {labelPosition !== 'start' && switchElement}
562
- </LabelContainer>
91
+ {labelPosition !== 'start' && control}
92
+ </LabelContainer>
563
93
 
564
- {helperText && (
565
- <FormHelperText
566
- error={error}
567
- sx={{ mt: 1 }}
568
- data-testid={dataTestId ? `${dataTestId}-helper` : 'switch-helper'}
569
- >
570
- {helperText}
571
- </FormHelperText>
572
- )}
573
- </Box>
574
- );
575
- },
576
- );
94
+ {helper}
95
+ </Box>
96
+ );
97
+ });
577
98
 
578
99
  Switch.displayName = 'Switch';