@udixio/ui-react 2.10.12 → 2.10.14

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 (179) hide show
  1. package/dist/index.cjs +3 -3
  2. package/dist/index.js +2696 -2710
  3. package/dist/lib/effects/ThemeProvider.d.ts.map +1 -1
  4. package/dist/theme.worker.js +6633 -0
  5. package/package.json +4 -1
  6. package/.eslintrc.mjs +0 -22
  7. package/.storybook/main.ts +0 -20
  8. package/.storybook/preview.ts +0 -1
  9. package/CHANGELOG.md +0 -1130
  10. package/dist/scrollDriven-AP2yWhzi.js +0 -121
  11. package/postcss.config.mjs +0 -5
  12. package/src/index.css +0 -4
  13. package/src/index.ts +0 -1
  14. package/src/lib/components/AnchorPositioner.tsx +0 -185
  15. package/src/lib/components/Button.tsx +0 -208
  16. package/src/lib/components/Card.tsx +0 -47
  17. package/src/lib/components/Carousel.tsx +0 -437
  18. package/src/lib/components/CarouselItem.tsx +0 -61
  19. package/src/lib/components/Checkbox.tsx +0 -120
  20. package/src/lib/components/Chip.tsx +0 -341
  21. package/src/lib/components/Chips.tsx +0 -331
  22. package/src/lib/components/ContextMenu.tsx +0 -109
  23. package/src/lib/components/DatePicker.tsx +0 -432
  24. package/src/lib/components/Divider.tsx +0 -20
  25. package/src/lib/components/Fab.tsx +0 -127
  26. package/src/lib/components/FabMenu.tsx +0 -239
  27. package/src/lib/components/IconButton.tsx +0 -146
  28. package/src/lib/components/Menu.tsx +0 -88
  29. package/src/lib/components/MenuGroup.tsx +0 -34
  30. package/src/lib/components/MenuHeadline.tsx +0 -9
  31. package/src/lib/components/MenuItem.tsx +0 -215
  32. package/src/lib/components/NavigationRail.tsx +0 -186
  33. package/src/lib/components/NavigationRailItem.tsx +0 -227
  34. package/src/lib/components/ProgressIndicator.tsx +0 -214
  35. package/src/lib/components/SideSheet.tsx +0 -135
  36. package/src/lib/components/Slider.tsx +0 -374
  37. package/src/lib/components/Snackbar.tsx +0 -77
  38. package/src/lib/components/Switch.tsx +0 -107
  39. package/src/lib/components/Tab.tsx +0 -123
  40. package/src/lib/components/TabGroup.tsx +0 -66
  41. package/src/lib/components/TabGroupContext.tsx +0 -16
  42. package/src/lib/components/TabPanel.tsx +0 -27
  43. package/src/lib/components/TabPanels.tsx +0 -76
  44. package/src/lib/components/Tabs.tsx +0 -105
  45. package/src/lib/components/TextField.tsx +0 -586
  46. package/src/lib/components/Tooltip.tsx +0 -217
  47. package/src/lib/components/index.ts +0 -34
  48. package/src/lib/config/config.interface.ts +0 -9
  49. package/src/lib/config/define-config.ts +0 -16
  50. package/src/lib/config/index.ts +0 -2
  51. package/src/lib/effects/AnimateOnScroll.ts +0 -391
  52. package/src/lib/effects/State.tsx +0 -90
  53. package/src/lib/effects/SyncedFixedWrapper.tsx +0 -62
  54. package/src/lib/effects/ThemeProvider.tsx +0 -172
  55. package/src/lib/effects/block-scroll.effect.tsx +0 -313
  56. package/src/lib/effects/custom-scroll/custom-scroll.effect.tsx +0 -407
  57. package/src/lib/effects/custom-scroll/custom-scroll.interface.ts +0 -29
  58. package/src/lib/effects/custom-scroll/custom-scroll.style.ts +0 -32
  59. package/src/lib/effects/custom-scroll/index.ts +0 -3
  60. package/src/lib/effects/index.ts +0 -7
  61. package/src/lib/effects/ripple/RippleEffect.tsx +0 -116
  62. package/src/lib/effects/ripple/index.tsx +0 -1
  63. package/src/lib/effects/scrollDriven.ts +0 -239
  64. package/src/lib/effects/smooth-scroll.effect.tsx +0 -112
  65. package/src/lib/effects/theme.worker.ts +0 -97
  66. package/src/lib/hooks/index.ts +0 -10
  67. package/src/lib/hooks/useTooltipTrigger.ts +0 -270
  68. package/src/lib/icon/icon.tsx +0 -125
  69. package/src/lib/icon/index.ts +0 -1
  70. package/src/lib/index.ts +0 -8
  71. package/src/lib/interfaces/button.interface.ts +0 -65
  72. package/src/lib/interfaces/card.interface.ts +0 -11
  73. package/src/lib/interfaces/carousel-item.interface.ts +0 -12
  74. package/src/lib/interfaces/carousel.interface.ts +0 -41
  75. package/src/lib/interfaces/checkbox.interface.ts +0 -39
  76. package/src/lib/interfaces/chip.interface.ts +0 -97
  77. package/src/lib/interfaces/chips.interface.ts +0 -37
  78. package/src/lib/interfaces/date-picker.interface.ts +0 -79
  79. package/src/lib/interfaces/divider.interface.ts +0 -7
  80. package/src/lib/interfaces/fab-menu.interface.ts +0 -12
  81. package/src/lib/interfaces/fab.interface.ts +0 -27
  82. package/src/lib/interfaces/icon-button.interface.ts +0 -38
  83. package/src/lib/interfaces/index.ts +0 -26
  84. package/src/lib/interfaces/menu-group.interface.ts +0 -13
  85. package/src/lib/interfaces/menu-item.interface.ts +0 -29
  86. package/src/lib/interfaces/menu.interface.ts +0 -19
  87. package/src/lib/interfaces/navigation-rail-item.interface.ts +0 -39
  88. package/src/lib/interfaces/navigation-rail.interface.ts +0 -39
  89. package/src/lib/interfaces/progress-indicator.interface.ts +0 -41
  90. package/src/lib/interfaces/side-sheet.interface.tsx +0 -28
  91. package/src/lib/interfaces/slider.interface.ts +0 -27
  92. package/src/lib/interfaces/snackbar.interface.ts +0 -13
  93. package/src/lib/interfaces/switch.interface.ts +0 -14
  94. package/src/lib/interfaces/tab-group.interface.ts +0 -13
  95. package/src/lib/interfaces/tab-panels.interface.ts +0 -21
  96. package/src/lib/interfaces/tab.interface.ts +0 -31
  97. package/src/lib/interfaces/tabs.interface.ts +0 -22
  98. package/src/lib/interfaces/text-field.interface.ts +0 -61
  99. package/src/lib/interfaces/tooltip.interface.ts +0 -61
  100. package/src/lib/styles/button.style.ts +0 -136
  101. package/src/lib/styles/card.style.ts +0 -29
  102. package/src/lib/styles/carousel-item.style.ts +0 -24
  103. package/src/lib/styles/carousel.style.ts +0 -22
  104. package/src/lib/styles/checkbox.style.ts +0 -64
  105. package/src/lib/styles/chip.style.ts +0 -62
  106. package/src/lib/styles/chips.style.ts +0 -20
  107. package/src/lib/styles/date-picker.style.ts +0 -43
  108. package/src/lib/styles/divider.style.ts +0 -31
  109. package/src/lib/styles/fab-menu.style.ts +0 -29
  110. package/src/lib/styles/fab.style.ts +0 -49
  111. package/src/lib/styles/icon-button.style.ts +0 -168
  112. package/src/lib/styles/index.ts +0 -25
  113. package/src/lib/styles/menu-group.style.ts +0 -34
  114. package/src/lib/styles/menu-headline.style.ts +0 -20
  115. package/src/lib/styles/menu-item.style.ts +0 -45
  116. package/src/lib/styles/menu.style.ts +0 -32
  117. package/src/lib/styles/navigation-rail-item.style.ts +0 -56
  118. package/src/lib/styles/navigation-rail.style.ts +0 -36
  119. package/src/lib/styles/progress-indicator.style.ts +0 -72
  120. package/src/lib/styles/side-sheet.style.ts +0 -45
  121. package/src/lib/styles/slider.style.ts +0 -41
  122. package/src/lib/styles/snackbar.style.ts +0 -26
  123. package/src/lib/styles/switch.style.ts +0 -67
  124. package/src/lib/styles/tab-panels.style.ts +0 -35
  125. package/src/lib/styles/tab.style.ts +0 -78
  126. package/src/lib/styles/tabs.style.ts +0 -22
  127. package/src/lib/styles/text-field.style.ts +0 -115
  128. package/src/lib/styles/tooltip.style.ts +0 -48
  129. package/src/lib/utils/component-helper.ts +0 -134
  130. package/src/lib/utils/component.ts +0 -34
  131. package/src/lib/utils/index.ts +0 -7
  132. package/src/lib/utils/string.ts +0 -9
  133. package/src/lib/utils/styles/classnames.ts +0 -49
  134. package/src/lib/utils/styles/get-classname.ts +0 -96
  135. package/src/lib/utils/styles/index.ts +0 -4
  136. package/src/lib/utils/styles/use-classnames.ts +0 -25
  137. package/src/stories/action/button.stories.tsx +0 -86
  138. package/src/stories/action/fab.stories.tsx +0 -54
  139. package/src/stories/action/icon-button.stories.tsx +0 -134
  140. package/src/stories/assets/accessibility.png +0 -0
  141. package/src/stories/assets/accessibility.svg +0 -5
  142. package/src/stories/assets/addon-library.png +0 -0
  143. package/src/stories/assets/assets.png +0 -0
  144. package/src/stories/assets/context.png +0 -0
  145. package/src/stories/assets/discord.svg +0 -15
  146. package/src/stories/assets/docs.png +0 -0
  147. package/src/stories/assets/figma-plugin.png +0 -0
  148. package/src/stories/assets/github.svg +0 -3
  149. package/src/stories/assets/share.png +0 -0
  150. package/src/stories/assets/styling.png +0 -0
  151. package/src/stories/assets/testing.png +0 -0
  152. package/src/stories/assets/theming.png +0 -0
  153. package/src/stories/assets/tutorials.svg +0 -12
  154. package/src/stories/assets/youtube.svg +0 -4
  155. package/src/stories/communication/ProgressIndicator.stories.tsx +0 -57
  156. package/src/stories/communication/SnackBar.stories.tsx +0 -32
  157. package/src/stories/communication/tool-tip.stories.tsx +0 -133
  158. package/src/stories/containment/card.stories.tsx +0 -42
  159. package/src/stories/containment/carousel.stories.tsx +0 -65
  160. package/src/stories/containment/divider.stories.tsx +0 -35
  161. package/src/stories/containment/slide-sheet.stories.tsx +0 -45
  162. package/src/stories/effect/smooth-scroll.stories.tsx +0 -54
  163. package/src/stories/navigation/navigation-rail/navigation-rail-item.stories.tsx +0 -65
  164. package/src/stories/navigation/navigation-rail/navigation-rail.stories.tsx +0 -122
  165. package/src/stories/navigation/tabs/tab.stories.tsx +0 -57
  166. package/src/stories/navigation/tabs/tabs.stories.tsx +0 -102
  167. package/src/stories/selection/slider.stories.tsx +0 -85
  168. package/src/stories/selection/switch.stories.tsx +0 -46
  169. package/src/stories/text-inputs/text-field.stories.tsx +0 -135
  170. package/src/tests/Button.spec.tsx +0 -67
  171. package/src/tests/useClassNames.spec.tsx +0 -82
  172. package/src/udixio.css +0 -120
  173. package/theme.config.ts +0 -7
  174. package/tsconfig.json +0 -16
  175. package/tsconfig.lib.json +0 -51
  176. package/tsconfig.spec.json +0 -37
  177. package/tsconfig.storybook.json +0 -38
  178. package/vite.config.ts +0 -82
  179. /package/dist/{scrollDriven-DWAu7CR0.cjs → scrollDriven.js} +0 -0
@@ -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
- };