@udixio/ui-react 2.10.13 → 2.10.15

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 (173) hide show
  1. package/package.json +4 -2
  2. package/.eslintrc.mjs +0 -22
  3. package/.storybook/main.ts +0 -20
  4. package/.storybook/preview.ts +0 -1
  5. package/CHANGELOG.md +0 -1144
  6. package/postcss.config.mjs +0 -5
  7. package/src/index.css +0 -4
  8. package/src/index.ts +0 -1
  9. package/src/lib/components/AnchorPositioner.tsx +0 -185
  10. package/src/lib/components/Button.tsx +0 -208
  11. package/src/lib/components/Card.tsx +0 -47
  12. package/src/lib/components/Carousel.tsx +0 -437
  13. package/src/lib/components/CarouselItem.tsx +0 -61
  14. package/src/lib/components/Checkbox.tsx +0 -120
  15. package/src/lib/components/Chip.tsx +0 -341
  16. package/src/lib/components/Chips.tsx +0 -331
  17. package/src/lib/components/ContextMenu.tsx +0 -109
  18. package/src/lib/components/DatePicker.tsx +0 -432
  19. package/src/lib/components/Divider.tsx +0 -20
  20. package/src/lib/components/Fab.tsx +0 -127
  21. package/src/lib/components/FabMenu.tsx +0 -239
  22. package/src/lib/components/IconButton.tsx +0 -146
  23. package/src/lib/components/Menu.tsx +0 -88
  24. package/src/lib/components/MenuGroup.tsx +0 -34
  25. package/src/lib/components/MenuHeadline.tsx +0 -9
  26. package/src/lib/components/MenuItem.tsx +0 -215
  27. package/src/lib/components/NavigationRail.tsx +0 -186
  28. package/src/lib/components/NavigationRailItem.tsx +0 -227
  29. package/src/lib/components/ProgressIndicator.tsx +0 -214
  30. package/src/lib/components/SideSheet.tsx +0 -135
  31. package/src/lib/components/Slider.tsx +0 -374
  32. package/src/lib/components/Snackbar.tsx +0 -77
  33. package/src/lib/components/Switch.tsx +0 -107
  34. package/src/lib/components/Tab.tsx +0 -123
  35. package/src/lib/components/TabGroup.tsx +0 -66
  36. package/src/lib/components/TabGroupContext.tsx +0 -16
  37. package/src/lib/components/TabPanel.tsx +0 -27
  38. package/src/lib/components/TabPanels.tsx +0 -76
  39. package/src/lib/components/Tabs.tsx +0 -105
  40. package/src/lib/components/TextField.tsx +0 -586
  41. package/src/lib/components/Tooltip.tsx +0 -217
  42. package/src/lib/components/index.ts +0 -34
  43. package/src/lib/config/config.interface.ts +0 -9
  44. package/src/lib/config/define-config.ts +0 -16
  45. package/src/lib/config/index.ts +0 -2
  46. package/src/lib/effects/AnimateOnScroll.ts +0 -391
  47. package/src/lib/effects/State.tsx +0 -90
  48. package/src/lib/effects/SyncedFixedWrapper.tsx +0 -62
  49. package/src/lib/effects/ThemeProvider.tsx +0 -174
  50. package/src/lib/effects/block-scroll.effect.tsx +0 -313
  51. package/src/lib/effects/custom-scroll/custom-scroll.effect.tsx +0 -407
  52. package/src/lib/effects/custom-scroll/custom-scroll.interface.ts +0 -29
  53. package/src/lib/effects/custom-scroll/custom-scroll.style.ts +0 -32
  54. package/src/lib/effects/custom-scroll/index.ts +0 -3
  55. package/src/lib/effects/index.ts +0 -7
  56. package/src/lib/effects/ripple/RippleEffect.tsx +0 -116
  57. package/src/lib/effects/ripple/index.tsx +0 -1
  58. package/src/lib/effects/scrollDriven.ts +0 -239
  59. package/src/lib/effects/smooth-scroll.effect.tsx +0 -112
  60. package/src/lib/effects/theme.worker.ts +0 -97
  61. package/src/lib/hooks/index.ts +0 -10
  62. package/src/lib/hooks/useTooltipTrigger.ts +0 -270
  63. package/src/lib/icon/icon.tsx +0 -125
  64. package/src/lib/icon/index.ts +0 -1
  65. package/src/lib/index.ts +0 -8
  66. package/src/lib/interfaces/button.interface.ts +0 -65
  67. package/src/lib/interfaces/card.interface.ts +0 -11
  68. package/src/lib/interfaces/carousel-item.interface.ts +0 -12
  69. package/src/lib/interfaces/carousel.interface.ts +0 -41
  70. package/src/lib/interfaces/checkbox.interface.ts +0 -39
  71. package/src/lib/interfaces/chip.interface.ts +0 -97
  72. package/src/lib/interfaces/chips.interface.ts +0 -37
  73. package/src/lib/interfaces/date-picker.interface.ts +0 -79
  74. package/src/lib/interfaces/divider.interface.ts +0 -7
  75. package/src/lib/interfaces/fab-menu.interface.ts +0 -12
  76. package/src/lib/interfaces/fab.interface.ts +0 -27
  77. package/src/lib/interfaces/icon-button.interface.ts +0 -38
  78. package/src/lib/interfaces/index.ts +0 -26
  79. package/src/lib/interfaces/menu-group.interface.ts +0 -13
  80. package/src/lib/interfaces/menu-item.interface.ts +0 -29
  81. package/src/lib/interfaces/menu.interface.ts +0 -19
  82. package/src/lib/interfaces/navigation-rail-item.interface.ts +0 -39
  83. package/src/lib/interfaces/navigation-rail.interface.ts +0 -39
  84. package/src/lib/interfaces/progress-indicator.interface.ts +0 -41
  85. package/src/lib/interfaces/side-sheet.interface.tsx +0 -28
  86. package/src/lib/interfaces/slider.interface.ts +0 -27
  87. package/src/lib/interfaces/snackbar.interface.ts +0 -13
  88. package/src/lib/interfaces/switch.interface.ts +0 -14
  89. package/src/lib/interfaces/tab-group.interface.ts +0 -13
  90. package/src/lib/interfaces/tab-panels.interface.ts +0 -21
  91. package/src/lib/interfaces/tab.interface.ts +0 -31
  92. package/src/lib/interfaces/tabs.interface.ts +0 -22
  93. package/src/lib/interfaces/text-field.interface.ts +0 -61
  94. package/src/lib/interfaces/tooltip.interface.ts +0 -61
  95. package/src/lib/styles/button.style.ts +0 -136
  96. package/src/lib/styles/card.style.ts +0 -29
  97. package/src/lib/styles/carousel-item.style.ts +0 -24
  98. package/src/lib/styles/carousel.style.ts +0 -22
  99. package/src/lib/styles/checkbox.style.ts +0 -64
  100. package/src/lib/styles/chip.style.ts +0 -62
  101. package/src/lib/styles/chips.style.ts +0 -20
  102. package/src/lib/styles/date-picker.style.ts +0 -43
  103. package/src/lib/styles/divider.style.ts +0 -31
  104. package/src/lib/styles/fab-menu.style.ts +0 -29
  105. package/src/lib/styles/fab.style.ts +0 -49
  106. package/src/lib/styles/icon-button.style.ts +0 -168
  107. package/src/lib/styles/index.ts +0 -25
  108. package/src/lib/styles/menu-group.style.ts +0 -34
  109. package/src/lib/styles/menu-headline.style.ts +0 -20
  110. package/src/lib/styles/menu-item.style.ts +0 -45
  111. package/src/lib/styles/menu.style.ts +0 -32
  112. package/src/lib/styles/navigation-rail-item.style.ts +0 -56
  113. package/src/lib/styles/navigation-rail.style.ts +0 -36
  114. package/src/lib/styles/progress-indicator.style.ts +0 -72
  115. package/src/lib/styles/side-sheet.style.ts +0 -45
  116. package/src/lib/styles/slider.style.ts +0 -41
  117. package/src/lib/styles/snackbar.style.ts +0 -26
  118. package/src/lib/styles/switch.style.ts +0 -67
  119. package/src/lib/styles/tab-panels.style.ts +0 -35
  120. package/src/lib/styles/tab.style.ts +0 -78
  121. package/src/lib/styles/tabs.style.ts +0 -22
  122. package/src/lib/styles/text-field.style.ts +0 -115
  123. package/src/lib/styles/tooltip.style.ts +0 -48
  124. package/src/lib/utils/component-helper.ts +0 -134
  125. package/src/lib/utils/component.ts +0 -34
  126. package/src/lib/utils/index.ts +0 -7
  127. package/src/lib/utils/string.ts +0 -9
  128. package/src/lib/utils/styles/classnames.ts +0 -49
  129. package/src/lib/utils/styles/get-classname.ts +0 -96
  130. package/src/lib/utils/styles/index.ts +0 -4
  131. package/src/lib/utils/styles/use-classnames.ts +0 -25
  132. package/src/stories/action/button.stories.tsx +0 -86
  133. package/src/stories/action/fab.stories.tsx +0 -54
  134. package/src/stories/action/icon-button.stories.tsx +0 -134
  135. package/src/stories/assets/accessibility.png +0 -0
  136. package/src/stories/assets/accessibility.svg +0 -5
  137. package/src/stories/assets/addon-library.png +0 -0
  138. package/src/stories/assets/assets.png +0 -0
  139. package/src/stories/assets/context.png +0 -0
  140. package/src/stories/assets/discord.svg +0 -15
  141. package/src/stories/assets/docs.png +0 -0
  142. package/src/stories/assets/figma-plugin.png +0 -0
  143. package/src/stories/assets/github.svg +0 -3
  144. package/src/stories/assets/share.png +0 -0
  145. package/src/stories/assets/styling.png +0 -0
  146. package/src/stories/assets/testing.png +0 -0
  147. package/src/stories/assets/theming.png +0 -0
  148. package/src/stories/assets/tutorials.svg +0 -12
  149. package/src/stories/assets/youtube.svg +0 -4
  150. package/src/stories/communication/ProgressIndicator.stories.tsx +0 -57
  151. package/src/stories/communication/SnackBar.stories.tsx +0 -32
  152. package/src/stories/communication/tool-tip.stories.tsx +0 -133
  153. package/src/stories/containment/card.stories.tsx +0 -42
  154. package/src/stories/containment/carousel.stories.tsx +0 -65
  155. package/src/stories/containment/divider.stories.tsx +0 -35
  156. package/src/stories/containment/slide-sheet.stories.tsx +0 -45
  157. package/src/stories/effect/smooth-scroll.stories.tsx +0 -54
  158. package/src/stories/navigation/navigation-rail/navigation-rail-item.stories.tsx +0 -65
  159. package/src/stories/navigation/navigation-rail/navigation-rail.stories.tsx +0 -122
  160. package/src/stories/navigation/tabs/tab.stories.tsx +0 -57
  161. package/src/stories/navigation/tabs/tabs.stories.tsx +0 -102
  162. package/src/stories/selection/slider.stories.tsx +0 -85
  163. package/src/stories/selection/switch.stories.tsx +0 -46
  164. package/src/stories/text-inputs/text-field.stories.tsx +0 -135
  165. package/src/tests/Button.spec.tsx +0 -67
  166. package/src/tests/useClassNames.spec.tsx +0 -82
  167. package/src/udixio.css +0 -120
  168. package/theme.config.ts +0 -7
  169. package/tsconfig.json +0 -16
  170. package/tsconfig.lib.json +0 -51
  171. package/tsconfig.spec.json +0 -37
  172. package/tsconfig.storybook.json +0 -38
  173. package/vite.config.ts +0 -96
@@ -1,239 +0,0 @@
1
- import React, { useId, useRef, useState } from 'react';
2
- import { FabMenuInterface } from '../interfaces/fab-menu.interface';
3
- import { useFabMenuStyle } from '../styles/fab-menu.style';
4
- import { ReactProps } from '../utils/component';
5
- import { Fab } from './Fab';
6
- import { Button } from './Button';
7
- import { ButtonInterface } from '../interfaces';
8
- import { classNames } from '../utils';
9
- import { IconButton } from './IconButton';
10
- import { faClose } from '@fortawesome/free-solid-svg-icons';
11
- import { AnimatePresence, motion } from 'motion/react';
12
-
13
- /**
14
- * Floating action buttons (FABs) help people take primary actions
15
- * @status beta
16
- * @category Action
17
- * @devx
18
- * - Only `Button` children are rendered as actions.
19
- * - Controlled via `open`/`onOpenChange` or `defaultOpen`.
20
- * @a11y
21
- * - No focus trap or Escape handling when open.
22
- * @limitations
23
- * - No outside-click handling; close uses the explicit close button.
24
- */
25
- export const FabMenu = ({
26
- className,
27
- label,
28
- variant = 'primary',
29
- size = 'medium',
30
- href,
31
- icon,
32
- extended = false,
33
- ref,
34
- transition,
35
- children,
36
- open: openProp,
37
- defaultOpen = false,
38
- onOpenChange,
39
- ...restProps
40
- }: ReactProps<FabMenuInterface>) => {
41
- transition = { duration: 0.3, ease: 'easeInOut', ...transition };
42
-
43
- const defaultRef = useRef(null);
44
- const resolvedRef = ref || defaultRef;
45
-
46
- // Controlled/uncontrolled open state
47
- const isControlled = typeof openProp === 'boolean';
48
- const [internalOpen, setInternalOpen] = useState<boolean>(defaultOpen);
49
- const open = isControlled ? (openProp as boolean) : internalOpen;
50
- const setOpen = (next: boolean) => {
51
- if (!isControlled) setInternalOpen(next);
52
- onOpenChange?.(next);
53
- };
54
-
55
- const buttonChildren = React.Children.toArray(children).filter(
56
- (child) => React.isValidElement(child) && child.type === Button,
57
- );
58
-
59
- const styles = useFabMenuStyle({
60
- href,
61
- icon,
62
- extended,
63
- label,
64
- size,
65
- variant,
66
- className,
67
- transition,
68
- children: label,
69
- open,
70
- });
71
-
72
- const MotionFab = motion.create(Fab);
73
- const MotionIconButton = motion.create(IconButton);
74
- const renderFab = (props) => (
75
- <MotionFab
76
- icon={icon}
77
- extended={extended}
78
- label={label}
79
- variant={(variant + 'Container') as any}
80
- size={size}
81
- className={styles.fab + ' ' + (className ?? '')}
82
- aria-expanded={open}
83
- onClick={() => setOpen(true)}
84
- style={{ transition: 'border-radius 0.3s ease-in-out' }}
85
- transition={{
86
- duration: transition.duration,
87
- ease: 'easeInOut',
88
- borderRadius: { duration: transition.duration, ease: 'easeInOut' },
89
- background: { duration: transition.duration, ease: 'easeInOut' },
90
- ...transition,
91
- }}
92
- {...props}
93
- />
94
- );
95
-
96
- const id = useId();
97
-
98
- return (
99
- <div className={styles.fabMenu} ref={resolvedRef} {...restProps}>
100
- <AnimatePresence>
101
- {open && (
102
- <div className={styles.actions} role="menu" aria-hidden={!open}>
103
- {(() => {
104
- const total = buttonChildren.length;
105
- return buttonChildren.map((child, index) => {
106
- const childProps = (
107
- child as React.ReactElement<ReactProps<ButtonInterface>>
108
- ).props;
109
- const reverseIndex = total - 1 - index; // inverser l'ordre d'animation
110
- const delay = (transition?.delay ?? 0) + reverseIndex * 0.06; // délai échelonné inversé, un peu plus marqué
111
-
112
- const variants = {
113
- open: {
114
- overflow: 'visible',
115
- opacity: 1,
116
- width: 'auto',
117
- transition: {
118
- ...transition,
119
- delay,
120
- opacity: {
121
- delay: transition?.duration / 2 + delay,
122
- },
123
- },
124
- },
125
- close: {
126
- overflow: 'hidden',
127
- opacity: 0,
128
- width: 0,
129
- transition: {
130
- ...transition,
131
- delay,
132
- opacity: {
133
- duration: transition?.duration / 1.5,
134
- },
135
- },
136
- },
137
- };
138
-
139
- return (
140
- <motion.div
141
- initial={'close'}
142
- animate={'open'}
143
- variants={variants}
144
- transition={transition}
145
- exit={'close'}
146
- >
147
- {React.cloneElement(
148
- child as React.ReactElement<ReactProps<ButtonInterface>>,
149
- {
150
- key: index,
151
- shape: 'rounded',
152
- variant: 'filled',
153
- className: () => ({
154
- button: classNames(
155
- 'max-w-full overflow-hidden text-nowrap',
156
- {
157
- 'px-0': !open,
158
- 'bg-primary-container text-on-primary-container ':
159
- variant === 'primary',
160
- 'bg-secondary-container text-on-secondary-container':
161
- variant === 'secondary',
162
- 'bg-tertiary-container text-on-tertiary-container':
163
- variant === 'tertiary',
164
- },
165
- ),
166
- stateLayer: classNames({
167
- 'state-on-primary-container': variant === 'primary',
168
- 'state-on-secondary-container':
169
- variant === 'secondary',
170
- 'state-on-tertiary-container':
171
- variant === 'tertiary',
172
- }),
173
- }),
174
- },
175
- )}
176
- </motion.div>
177
- );
178
- });
179
- })()}
180
- </div>
181
- )}
182
- </AnimatePresence>
183
-
184
- {renderFab({
185
- className: 'invisible pointer-events-none',
186
- })}
187
- <div className={'absolute right-0 top-0'}>
188
- {!open &&
189
- renderFab({
190
- className: '',
191
- layout: true,
192
- layoutId: 'fab-menu' + id,
193
- })}
194
- {open && (
195
- <>
196
- <MotionIconButton
197
- layout
198
- layoutId={'fab-menu' + id}
199
- variant={'filled'}
200
- className={() => ({
201
- iconButton: classNames({
202
- 'bg-primary text-on-primary': variant === 'primary',
203
- 'bg-secondary text-on-secondary': variant === 'secondary',
204
- 'bg-tertiary text-on-tertiary': variant === 'tertiary',
205
- }),
206
- stateLayer: classNames({
207
- '[--default-color:var(--color-on-primary)]':
208
- variant === 'primary',
209
- '[--default-color:var(--color-on-secondary)]':
210
- variant === 'secondary',
211
- '[--default-color:var(--color-on-tertiary)]':
212
- variant === 'tertiary',
213
- }),
214
- })}
215
- style={{ transition: 'border-radius 0.3s ease-in-out' }}
216
- transition={{
217
- duration: transition.duration,
218
- ease: 'easeInOut',
219
- borderRadius: {
220
- duration: transition.duration,
221
- ease: 'easeInOut',
222
- },
223
- background: {
224
- duration: transition.duration,
225
- ease: 'easeInOut',
226
- },
227
- ...transition,
228
- }}
229
- icon={faClose}
230
- onClick={() => setOpen(false)}
231
- >
232
- Close
233
- </MotionIconButton>
234
- </>
235
- )}
236
- </div>
237
- </div>
238
- );
239
- };
@@ -1,146 +0,0 @@
1
- import React, { useEffect, useRef } from 'react';
2
-
3
- import { Icon } from '../icon';
4
- import { IconButtonInterface } from '../interfaces';
5
- import { useIconButtonStyle } from '../styles';
6
- import { classNames, ReactProps } from '../utils';
7
- import { State } from '../effects';
8
- import { Tooltip } from './Tooltip';
9
-
10
- export type IconButtonVariant = 'standard' | 'filled' | 'tonal' | 'outlined';
11
-
12
- /**
13
- * Icon buttons help people take minor actions with one tap
14
- * @status beta
15
- * @category Action
16
- * @devx
17
- * - Requires `label` or children to provide an aria-label.
18
- * - Uses `title` as tooltip text; native title attribute is suppressed.
19
- * @limitations
20
- * - Tooltip is always rendered (no explicit opt-out).
21
- */
22
- export const IconButton = ({
23
- variant = 'standard',
24
- href,
25
- disabled = false,
26
- title,
27
- label,
28
- onToggle,
29
- activated = false,
30
- onClick,
31
- icon,
32
- size = 'medium',
33
- iconSelected,
34
- className,
35
- ref,
36
- width = 'default',
37
- shape = 'rounded',
38
- allowShapeTransformation = true,
39
- transition,
40
- children,
41
- ...restProps
42
- }: ReactProps<IconButtonInterface>) => {
43
- if (children) label = children;
44
- if (!label) {
45
- throw new Error(
46
- 'IconButton component requires either a label prop or children content to provide an accessible aria-label',
47
- );
48
- }
49
- if (!title && title !== null) {
50
- title = label;
51
- }
52
-
53
- const [isActive, setIsActive] = React.useState(activated);
54
-
55
- const handleClick = (e: React.MouseEvent<any, MouseEvent>) => {
56
- if (disabled) {
57
- e.preventDefault();
58
- }
59
- if (onToggle) {
60
- setIsActive(!isActive);
61
- onToggle(!isActive);
62
- } else if (onClick) {
63
- onClick(e);
64
- }
65
- };
66
-
67
- useEffect(() => {
68
- setIsActive(activated);
69
- }, [activated]);
70
-
71
- // Détermine le type de l'élément à rendre : un bouton ou un lien
72
- const ElementType = href ? 'a' : 'button';
73
-
74
- const styles = useIconButtonStyle({
75
- transition,
76
- shape,
77
- allowShapeTransformation,
78
- width,
79
- href,
80
- activated: isActive,
81
- label,
82
- iconSelected,
83
- isActive,
84
- onToggle,
85
- disabled,
86
- icon,
87
- variant,
88
- className,
89
- size,
90
- children: label,
91
- ...restProps,
92
- });
93
-
94
- const defaultRef = useRef<HTMLDivElement>(null);
95
- const resolvedRef = ref || defaultRef;
96
-
97
- transition = { duration: 0.3, ...transition };
98
-
99
- return (
100
- <ElementType
101
- disabled={disabled}
102
- href={href}
103
- style={{ transition: transition.duration + 's' }}
104
- className={styles.iconButton}
105
- aria-label={label}
106
- {...(restProps as any)}
107
- title={undefined}
108
- onClick={handleClick}
109
- ref={resolvedRef}
110
- >
111
- {title !== null && (
112
- <Tooltip
113
- targetRef={resolvedRef}
114
- trigger={disabled ? null : undefined}
115
- text={title}
116
- ></Tooltip>
117
- )}
118
-
119
- <div className={styles.touchTarget} />
120
- <State
121
- style={{ transition: transition.duration + 's' }}
122
- className={styles.stateLayer}
123
- colorName={classNames(
124
- variant === 'standard' && {
125
- 'on-surface-variant': !isActive,
126
- 'on-primary': isActive,
127
- },
128
- variant === 'filled' && {
129
- 'on-surface-variant': !isActive && Boolean(onToggle),
130
- 'on-primary': isActive || !onToggle,
131
- },
132
- variant === 'tonal' && {
133
- 'on-secondary': isActive && Boolean(onToggle),
134
- 'on-secondary-container': !isActive || !onToggle,
135
- },
136
- variant === 'outlined' && {
137
- 'inverse-on-surface': isActive && Boolean(onToggle),
138
- 'on-surface-variant': !isActive || !onToggle,
139
- },
140
- )}
141
- stateClassName={'state-ripple-group-[icon-button]'}
142
- />
143
- {icon ? <Icon icon={icon} className={styles.icon} /> : children}
144
- </ElementType>
145
- );
146
- };
@@ -1,88 +0,0 @@
1
- import React, { useRef } from 'react';
2
- import { MenuInterface } from '../interfaces/menu.interface';
3
- import { useMenuStyle } from '../styles/menu.style';
4
- import { classNames } from '../utils';
5
- import { ReactProps } from '../utils/component';
6
- import { MenuItem } from './MenuItem';
7
- import { Divider } from './Divider';
8
- import { MenuHeadline } from './MenuHeadline';
9
- import { MenuGroup } from './MenuGroup';
10
-
11
- /**
12
- * Menu displays a list of choices on a temporary surface.
13
- * @status beta
14
- * @category Selection
15
- * @limitations
16
- * - Don’t use MenuGroup in scrollable menus
17
- * @devx
18
- * - Used internally by `TextField` for `type="select"`.
19
- * - Supports keyboard navigation and auto-scrolling to selected item.
20
- * @a11y
21
- * - `role="listbox"` with `aria-selected` management.
22
- */
23
- export const Menu = ({
24
- children,
25
- className,
26
- variant = 'standard',
27
- ...restProps
28
- }: ReactProps<MenuInterface>) => {
29
- /* pass restProps to include key such as variant */
30
- const hasGroups = React.Children.toArray(children).some(
31
- (child) => React.isValidElement(child) && child.type === MenuGroup,
32
- );
33
- const styles = useMenuStyle({
34
- children,
35
- className,
36
- variant,
37
- hasGroups,
38
- ...restProps,
39
- });
40
-
41
- const listRef = useRef<HTMLDivElement>(null);
42
-
43
- const renderChildren = (nodes: React.ReactNode): React.ReactNode => {
44
- return React.Children.map(nodes, (child) => {
45
- if (!React.isValidElement(child)) return child;
46
-
47
- // Handle MenuGroup: add surface styles if grouped
48
- if (child.type === MenuGroup) {
49
- const groupChildren = renderChildren((child.props as any).children);
50
- return React.cloneElement(child, {
51
- children: groupChildren,
52
- variant: variant,
53
- } as any);
54
- }
55
-
56
- if (child.type === MenuItem) {
57
- return React.cloneElement(child, {
58
- variant: (child.props as any).variant ?? variant,
59
- } as any);
60
- }
61
-
62
- if (child.type === MenuHeadline) {
63
- return React.cloneElement(child, {
64
- variant: variant,
65
- } as any);
66
- }
67
-
68
- if (child.type === Divider) {
69
- return React.cloneElement(child, {
70
- className: classNames('my-1', (child.props as any).className),
71
- } as any);
72
- }
73
-
74
- return child;
75
- });
76
- };
77
-
78
- return (
79
- <div ref={listRef} className={styles.menu} role="listbox" {...restProps}>
80
- {renderChildren(children)}
81
- {React.Children.count(children) === 0 && (
82
- <div className="px-4 py-3 text-on-surface-variant opacity-60 italic text-body-medium">
83
- No options
84
- </div>
85
- )}
86
- </div>
87
- );
88
- };
@@ -1,34 +0,0 @@
1
- import React from 'react';
2
- import { ReactProps } from '../utils/component';
3
- import { useMenuGroupStyle } from '../styles/menu-group.style';
4
- import { MenuGroupInterface } from '../interfaces/menu-group.interface';
5
-
6
- /**
7
- * MenuGroup renders a group of menu items with persistent styling.
8
- * It is primarily used to apply grouping logic or context to its children.
9
- *
10
- * @status beta
11
- * @category Selection
12
- */
13
- export const MenuGroup = ({
14
- children,
15
- className,
16
- variant,
17
- label,
18
- ...restProps
19
- }: ReactProps<MenuGroupInterface> & React.HTMLAttributes<HTMLDivElement>) => {
20
- const styles = useMenuGroupStyle({
21
- children,
22
- className,
23
- variant,
24
- label,
25
- ...restProps,
26
- });
27
-
28
- return (
29
- <div className={styles.menuGroup} role="group" {...restProps}>
30
- {label && <div className={styles.groupLabel}>{label}</div>}
31
- {children}
32
- </div>
33
- );
34
- };
@@ -1,9 +0,0 @@
1
-
2
- import React from 'react';
3
- import { useMenuHeadlineStyle, MenuHeadlineInterface } from '../styles/menu-headline.style';
4
- import { ReactProps } from '../utils';
5
-
6
- export const MenuHeadline = ({ label, children, variant, className, ...restProps }: ReactProps<MenuHeadlineInterface> & { children?: React.ReactNode }) => {
7
- const styles = useMenuHeadlineStyle({ variant, className });
8
- return <div className={styles.headline} role="group" aria-label={label} {...restProps}>{children ?? label}</div>;
9
- };