@cdx-ui/components 0.0.1-beta.159 → 0.0.1-beta.160

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 (105) hide show
  1. package/coverage.svg +1 -1
  2. package/lib/commonjs/components/Menu/index.js +499 -0
  3. package/lib/commonjs/components/Menu/index.js.map +1 -0
  4. package/lib/commonjs/components/Menu/styles.js +100 -0
  5. package/lib/commonjs/components/Menu/styles.js.map +1 -0
  6. package/lib/commonjs/components/Select/index.js +57 -22
  7. package/lib/commonjs/components/Select/index.js.map +1 -1
  8. package/lib/commonjs/components/Select/styles.js +1 -40
  9. package/lib/commonjs/components/Select/styles.js.map +1 -1
  10. package/lib/commonjs/components/Slider/getSliderPopupPosition.js +16 -6
  11. package/lib/commonjs/components/Slider/getSliderPopupPosition.js.map +1 -1
  12. package/lib/commonjs/components/Slider/index.js +17 -5
  13. package/lib/commonjs/components/Slider/index.js.map +1 -1
  14. package/lib/commonjs/components/Toast/index.js +1 -1
  15. package/lib/commonjs/components/Toast/index.js.map +1 -1
  16. package/lib/commonjs/components/index.js +12 -0
  17. package/lib/commonjs/components/index.js.map +1 -1
  18. package/lib/commonjs/components/picker/surfaces.js +16 -9
  19. package/lib/commonjs/components/picker/surfaces.js.map +1 -1
  20. package/lib/commonjs/figma/Menu.Item.figma.js +52 -0
  21. package/lib/commonjs/figma/Menu.Item.figma.js.map +1 -0
  22. package/lib/commonjs/figma/Menu.figma.js +29 -0
  23. package/lib/commonjs/figma/Menu.figma.js.map +1 -0
  24. package/lib/commonjs/providers/forge-design/provider.js +25 -11
  25. package/lib/commonjs/providers/forge-design/provider.js.map +1 -1
  26. package/lib/commonjs/providers/toast/insets-container.js +4 -1
  27. package/lib/commonjs/providers/toast/insets-container.js.map +1 -1
  28. package/lib/commonjs/providers/toast/provider.js +126 -45
  29. package/lib/commonjs/providers/toast/provider.js.map +1 -1
  30. package/lib/commonjs/providers/toast/toast-item-renderer.js +2 -1
  31. package/lib/commonjs/providers/toast/toast-item-renderer.js.map +1 -1
  32. package/lib/module/components/Menu/index.js +495 -0
  33. package/lib/module/components/Menu/index.js.map +1 -0
  34. package/lib/module/components/Menu/styles.js +96 -0
  35. package/lib/module/components/Menu/styles.js.map +1 -0
  36. package/lib/module/components/Select/index.js +55 -20
  37. package/lib/module/components/Select/index.js.map +1 -1
  38. package/lib/module/components/Select/styles.js +0 -39
  39. package/lib/module/components/Select/styles.js.map +1 -1
  40. package/lib/module/components/Slider/getSliderPopupPosition.js +16 -6
  41. package/lib/module/components/Slider/getSliderPopupPosition.js.map +1 -1
  42. package/lib/module/components/Slider/index.js +20 -8
  43. package/lib/module/components/Slider/index.js.map +1 -1
  44. package/lib/module/components/Toast/index.js +1 -1
  45. package/lib/module/components/Toast/index.js.map +1 -1
  46. package/lib/module/components/index.js +1 -0
  47. package/lib/module/components/index.js.map +1 -1
  48. package/lib/module/components/picker/surfaces.js +18 -11
  49. package/lib/module/components/picker/surfaces.js.map +1 -1
  50. package/lib/module/figma/Menu.Item.figma.js +46 -0
  51. package/lib/module/figma/Menu.Item.figma.js.map +1 -0
  52. package/lib/module/figma/Menu.figma.js +23 -0
  53. package/lib/module/figma/Menu.figma.js.map +1 -0
  54. package/lib/module/providers/forge-design/provider.js +26 -12
  55. package/lib/module/providers/forge-design/provider.js.map +1 -1
  56. package/lib/module/providers/toast/insets-container.js +5 -2
  57. package/lib/module/providers/toast/insets-container.js.map +1 -1
  58. package/lib/module/providers/toast/provider.js +126 -45
  59. package/lib/module/providers/toast/provider.js.map +1 -1
  60. package/lib/module/providers/toast/toast-item-renderer.js +2 -1
  61. package/lib/module/providers/toast/toast-item-renderer.js.map +1 -1
  62. package/lib/typescript/components/Menu/index.d.ts +119 -0
  63. package/lib/typescript/components/Menu/index.d.ts.map +1 -0
  64. package/lib/typescript/components/Menu/styles.d.ts +24 -0
  65. package/lib/typescript/components/Menu/styles.d.ts.map +1 -0
  66. package/lib/typescript/components/Select/index.d.ts.map +1 -1
  67. package/lib/typescript/components/Select/styles.d.ts +0 -7
  68. package/lib/typescript/components/Select/styles.d.ts.map +1 -1
  69. package/lib/typescript/components/Slider/getSliderPopupPosition.d.ts +1 -1
  70. package/lib/typescript/components/Slider/getSliderPopupPosition.d.ts.map +1 -1
  71. package/lib/typescript/components/Slider/index.d.ts.map +1 -1
  72. package/lib/typescript/components/Toast/types.d.ts +2 -2
  73. package/lib/typescript/components/Toast/types.d.ts.map +1 -1
  74. package/lib/typescript/components/index.d.ts +1 -0
  75. package/lib/typescript/components/index.d.ts.map +1 -1
  76. package/lib/typescript/components/picker/surfaces.d.ts.map +1 -1
  77. package/lib/typescript/figma/Menu.Item.figma.d.ts +8 -0
  78. package/lib/typescript/figma/Menu.Item.figma.d.ts.map +1 -0
  79. package/lib/typescript/figma/Menu.figma.d.ts +8 -0
  80. package/lib/typescript/figma/Menu.figma.d.ts.map +1 -0
  81. package/lib/typescript/providers/forge-design/provider.d.ts.map +1 -1
  82. package/lib/typescript/providers/toast/insets-container.d.ts.map +1 -1
  83. package/lib/typescript/providers/toast/lifecycle.context.d.ts +2 -2
  84. package/lib/typescript/providers/toast/lifecycle.context.d.ts.map +1 -1
  85. package/lib/typescript/providers/toast/provider.d.ts.map +1 -1
  86. package/lib/typescript/providers/toast/toast-item-renderer.d.ts.map +1 -1
  87. package/lib/typescript/providers/toast/types.d.ts +22 -8
  88. package/lib/typescript/providers/toast/types.d.ts.map +1 -1
  89. package/package.json +5 -5
  90. package/src/components/Menu/index.tsx +654 -0
  91. package/src/components/Menu/styles.ts +153 -0
  92. package/src/components/Select/index.tsx +58 -23
  93. package/src/components/Select/styles.ts +0 -59
  94. package/src/components/Slider/getSliderPopupPosition.ts +19 -7
  95. package/src/components/Slider/index.tsx +25 -9
  96. package/src/components/Toast/index.tsx +1 -1
  97. package/src/components/Toast/types.ts +2 -2
  98. package/src/components/index.ts +1 -0
  99. package/src/components/picker/surfaces.tsx +13 -9
  100. package/src/providers/forge-design/provider.tsx +35 -16
  101. package/src/providers/toast/insets-container.tsx +3 -2
  102. package/src/providers/toast/lifecycle.context.tsx +2 -2
  103. package/src/providers/toast/provider.tsx +150 -51
  104. package/src/providers/toast/toast-item-renderer.tsx +5 -1
  105. package/src/providers/toast/types.ts +22 -8
@@ -0,0 +1,654 @@
1
+ import { forwardRef, useMemo, type ReactNode } from 'react';
2
+ import { Platform, ScrollView, Text, View, type TextProps, type ViewProps } from 'react-native';
3
+ import { Check } from '@cdx-ui/icons';
4
+ import {
5
+ MenuCheckboxItem as MenuCheckboxItemPrimitive,
6
+ MenuItem as MenuItemPrimitive,
7
+ MenuOverlayRoot as MenuOverlayRootPrimitive,
8
+ MenuPopup as MenuPopupPrimitive,
9
+ MenuRadioGroup as MenuRadioGroupPrimitive,
10
+ MenuRadioItem as MenuRadioItemPrimitive,
11
+ MenuRoot as MenuRootPrimitive,
12
+ MenuTrigger as MenuTriggerPrimitive,
13
+ Slot,
14
+ dataAttributes,
15
+ mergeDataAttributes,
16
+ useOptionalMenuItemContext,
17
+ useOptionalMenuPopupContext,
18
+ type IMenuCheckboxItemProps,
19
+ type IMenuItemProps,
20
+ type IMenuOverlayRootProps,
21
+ type IMenuPopupProps,
22
+ type IMenuProps,
23
+ type IMenuRadioGroupProps,
24
+ type IMenuRadioItemProps,
25
+ type IMenuTriggerProps,
26
+ } from '@cdx-ui/primitives';
27
+ import { cn, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
28
+ import { Icon, type IconProps } from '../Icon';
29
+ import { positionToDataIcon, type IconSlotPosition } from '../../utils/positionToDataIcon';
30
+ import {
31
+ menuItemIconVariants,
32
+ menuItemLabelVariants,
33
+ menuItemIndicatorVariants,
34
+ menuItemShortcutVariants,
35
+ menuItemVariants,
36
+ menuRootVariants,
37
+ menuSectionLabelVariants,
38
+ menuSeparatorLineVariants,
39
+ menuSeparatorVariants,
40
+ type MenuItemVariantProps,
41
+ type MenuRootVariantProps,
42
+ } from './styles';
43
+
44
+ const SCOPE = 'MENU';
45
+ const MENU_MAX_HEIGHT = 240;
46
+
47
+ const useMenuStyleContext = (): Pick<MenuItemVariantProps, 'size' | 'color'> => {
48
+ const ctx = useStyleContext(SCOPE) as MenuItemVariantProps | undefined;
49
+ return { size: ctx?.size ?? 'default', color: ctx?.color ?? 'neutral' };
50
+ };
51
+
52
+ const useMenuItemStyleValue = (
53
+ sizeProp: MenuItemVariantProps['size'],
54
+ color: NonNullable<MenuItemVariantProps['color']>,
55
+ ) => {
56
+ const { size: contextSize } = useMenuStyleContext();
57
+ const parent = useParentContext();
58
+ const size = sizeProp ?? contextSize ?? 'default';
59
+ const contextValue = useMemo(
60
+ () => ({ ...parent, [SCOPE]: { size, color } }),
61
+ [parent, size, color],
62
+ );
63
+
64
+ return { contextValue, size };
65
+ };
66
+
67
+ // =============================================================================
68
+ // OVERLAY ROOT
69
+ // =============================================================================
70
+
71
+ export interface MenuProps extends ViewProps, IMenuOverlayRootProps {
72
+ className?: string;
73
+ }
74
+
75
+ const MenuOverlayRoot = forwardRef<View, MenuProps>(
76
+ ({ className, style, children, ...props }, ref) => (
77
+ <MenuOverlayRootPrimitive
78
+ ref={ref}
79
+ className={className}
80
+ style={style}
81
+ {...props}
82
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-root' }))}
83
+ >
84
+ {children}
85
+ </MenuOverlayRootPrimitive>
86
+ ),
87
+ );
88
+
89
+ MenuOverlayRoot.displayName = 'Menu';
90
+
91
+ // =============================================================================
92
+ // TRIGGER
93
+ // =============================================================================
94
+
95
+ export interface MenuTriggerProps extends IMenuTriggerProps {
96
+ className?: string;
97
+ children?: ReactNode;
98
+ }
99
+
100
+ const MenuTrigger = forwardRef<View, MenuTriggerProps>(
101
+ ({ className, style, children, ...props }, ref) => (
102
+ <MenuTriggerPrimitive
103
+ ref={ref}
104
+ className={className}
105
+ style={style}
106
+ {...props}
107
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-trigger' }))}
108
+ >
109
+ {children}
110
+ </MenuTriggerPrimitive>
111
+ ),
112
+ );
113
+
114
+ MenuTrigger.displayName = 'Menu.Trigger';
115
+
116
+ // =============================================================================
117
+ // POPUP
118
+ // =============================================================================
119
+
120
+ export interface MenuPopupProps extends IMenuPopupProps {
121
+ className?: string;
122
+ children?: ReactNode;
123
+ }
124
+
125
+ const MenuPopup = forwardRef<View, MenuPopupProps>(
126
+ ({ className, style, children, ...props }, ref) => (
127
+ <MenuPopupPrimitive
128
+ ref={ref}
129
+ className={cn(
130
+ 'min-w-48',
131
+ Platform.select({
132
+ web: 'w-max max-w-full',
133
+ default: 'self-start',
134
+ }),
135
+ className,
136
+ )}
137
+ style={style}
138
+ {...props}
139
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-popup' }))}
140
+ >
141
+ {children}
142
+ </MenuPopupPrimitive>
143
+ ),
144
+ );
145
+
146
+ MenuPopup.displayName = 'Menu.Popup';
147
+
148
+ // =============================================================================
149
+ // CONTENT (menu list surface)
150
+ // =============================================================================
151
+
152
+ export interface MenuContentProps
153
+ extends ViewProps, Omit<IMenuProps, 'scrollable'>, MenuRootVariantProps {
154
+ className?: string;
155
+ /** Visual-only surface without menu semantics (e.g. inside Select listbox). */
156
+ asSurface?: boolean;
157
+ }
158
+
159
+ const MenuContent = forwardRef<View, MenuContentProps>(
160
+ ({ scrollable = false, asSurface = false, className, style, children, ...props }, ref) => {
161
+ const parent = useParentContext();
162
+ const popup = useOptionalMenuPopupContext();
163
+ const ctx = useMemo(() => ({ ...parent, [SCOPE]: { size: 'default' as const } }), [parent]);
164
+ const shouldScroll = scrollable || popup !== null;
165
+ const constrainedStyle =
166
+ popup === null
167
+ ? undefined
168
+ : {
169
+ flexShrink: 1,
170
+ maxHeight:
171
+ popup.availableHeight === undefined
172
+ ? undefined
173
+ : Math.min(MENU_MAX_HEIGHT, popup.availableHeight),
174
+ maxWidth: popup.availableWidth,
175
+ };
176
+
177
+ const computedClassName = cn(menuRootVariants({ scrollable: shouldScroll }), className);
178
+
179
+ const content = shouldScroll ? (
180
+ <ScrollView
181
+ className="shrink"
182
+ style={constrainedStyle}
183
+ scrollEventThrottle={16}
184
+ nestedScrollEnabled
185
+ >
186
+ {children}
187
+ </ScrollView>
188
+ ) : (
189
+ children
190
+ );
191
+
192
+ return (
193
+ <ParentContext.Provider value={ctx}>
194
+ <MenuRootPrimitive
195
+ ref={ref}
196
+ scrollable={shouldScroll}
197
+ asSurface={asSurface}
198
+ className={computedClassName}
199
+ style={[style, constrainedStyle]}
200
+ {...props}
201
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-content' }))}
202
+ >
203
+ {content}
204
+ </MenuRootPrimitive>
205
+ </ParentContext.Provider>
206
+ );
207
+ },
208
+ );
209
+
210
+ MenuContent.displayName = 'Menu.Content';
211
+
212
+ // =============================================================================
213
+ // ITEM
214
+ // =============================================================================
215
+
216
+ export interface MenuItemProps extends Omit<MenuItemVariantProps, 'color'>, IMenuItemProps {
217
+ asChild?: boolean;
218
+ /**
219
+ * Color theme applied to the item content and interaction states.
220
+ * @default 'neutral'
221
+ */
222
+ color?: 'neutral' | 'danger';
223
+ className?: string;
224
+ children?: ReactNode;
225
+ }
226
+
227
+ const MenuItem = forwardRef<View, MenuItemProps>(
228
+ (
229
+ {
230
+ asChild,
231
+ color = 'neutral',
232
+ inset = false,
233
+ size: sizeProp,
234
+ className,
235
+ style,
236
+ children,
237
+ ...props
238
+ },
239
+ ref,
240
+ ) => {
241
+ const { contextValue, size } = useMenuItemStyleValue(sizeProp, color);
242
+
243
+ const computedClassName = cn(menuItemVariants({ color, inset, size }), className);
244
+
245
+ return (
246
+ <ParentContext.Provider value={contextValue}>
247
+ <MenuItemPrimitive
248
+ ref={ref}
249
+ asChild={asChild}
250
+ className={computedClassName}
251
+ style={style}
252
+ {...props}
253
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-item' }))}
254
+ >
255
+ {children}
256
+ </MenuItemPrimitive>
257
+ </ParentContext.Provider>
258
+ );
259
+ },
260
+ );
261
+
262
+ MenuItem.displayName = 'Menu.Item';
263
+
264
+ export interface MenuCheckboxItemProps
265
+ extends Omit<MenuItemVariantProps, 'color'>, IMenuCheckboxItemProps {
266
+ asChild?: boolean;
267
+ color?: 'neutral' | 'danger';
268
+ className?: string;
269
+ children?: ReactNode;
270
+ }
271
+
272
+ const MenuCheckboxItem = forwardRef<View, MenuCheckboxItemProps>(
273
+ (
274
+ {
275
+ asChild,
276
+ color = 'neutral',
277
+ inset = false,
278
+ size: sizeProp,
279
+ className,
280
+ style,
281
+ children,
282
+ ...props
283
+ },
284
+ ref,
285
+ ) => {
286
+ const { contextValue, size } = useMenuItemStyleValue(sizeProp, color);
287
+
288
+ return (
289
+ <ParentContext.Provider value={contextValue}>
290
+ <MenuCheckboxItemPrimitive
291
+ ref={ref}
292
+ asChild={asChild}
293
+ className={cn(menuItemVariants({ color, inset, size }), className)}
294
+ style={style}
295
+ {...props}
296
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-checkbox-item' }))}
297
+ >
298
+ {children}
299
+ </MenuCheckboxItemPrimitive>
300
+ </ParentContext.Provider>
301
+ );
302
+ },
303
+ );
304
+
305
+ MenuCheckboxItem.displayName = 'Menu.CheckboxItem';
306
+
307
+ export interface MenuRadioGroupProps extends IMenuRadioGroupProps {
308
+ asChild?: boolean;
309
+ className?: string;
310
+ children?: ReactNode;
311
+ }
312
+
313
+ const MenuRadioGroup = forwardRef<View, MenuRadioGroupProps>(
314
+ ({ asChild, className, style, children, ...props }, ref) => (
315
+ <MenuRadioGroupPrimitive
316
+ ref={ref}
317
+ asChild={asChild}
318
+ className={className}
319
+ style={style}
320
+ {...props}
321
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-radio-group' }))}
322
+ >
323
+ {children}
324
+ </MenuRadioGroupPrimitive>
325
+ ),
326
+ );
327
+
328
+ MenuRadioGroup.displayName = 'Menu.RadioGroup';
329
+
330
+ export interface MenuRadioItemProps
331
+ extends Omit<MenuItemVariantProps, 'color'>, IMenuRadioItemProps {
332
+ asChild?: boolean;
333
+ color?: 'neutral' | 'danger';
334
+ className?: string;
335
+ children?: ReactNode;
336
+ }
337
+
338
+ const MenuRadioItem = forwardRef<View, MenuRadioItemProps>(
339
+ (
340
+ {
341
+ asChild,
342
+ color = 'neutral',
343
+ inset = false,
344
+ size: sizeProp,
345
+ className,
346
+ style,
347
+ children,
348
+ ...props
349
+ },
350
+ ref,
351
+ ) => {
352
+ const { contextValue, size } = useMenuItemStyleValue(sizeProp, color);
353
+
354
+ return (
355
+ <ParentContext.Provider value={contextValue}>
356
+ <MenuRadioItemPrimitive
357
+ ref={ref}
358
+ asChild={asChild}
359
+ className={cn(menuItemVariants({ color, inset, size }), className)}
360
+ style={style}
361
+ {...props}
362
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-radio-item' }))}
363
+ >
364
+ {children}
365
+ </MenuRadioItemPrimitive>
366
+ </ParentContext.Provider>
367
+ );
368
+ },
369
+ );
370
+
371
+ MenuRadioItem.displayName = 'Menu.RadioItem';
372
+
373
+ // =============================================================================
374
+ // ITEM SLOTS
375
+ // =============================================================================
376
+
377
+ export interface MenuItemLabelProps extends TextProps, Pick<MenuItemVariantProps, 'size'> {
378
+ asChild?: boolean;
379
+ className?: string;
380
+ /** Checked state when used outside a selectable Menu item (e.g. `Select.ItemLabel`). */
381
+ isChecked?: boolean;
382
+ }
383
+
384
+ const MenuItemLabel = forwardRef<Text, MenuItemLabelProps>(
385
+ ({ asChild, className, style, children, isChecked: checkedProp, size, ...props }, ref) => {
386
+ const { color } = useMenuStyleContext();
387
+ const menuItemCtx = useOptionalMenuItemContext();
388
+ const checked = checkedProp ?? menuItemCtx?.checked ?? false;
389
+ const Comp = asChild ? Slot : Text;
390
+
391
+ return (
392
+ <Comp
393
+ ref={ref}
394
+ className={cn(
395
+ menuItemLabelVariants({ size, color }),
396
+ checked ? 'font-medium' : undefined,
397
+ className,
398
+ )}
399
+ style={style}
400
+ {...props}
401
+ {...mergeDataAttributes(
402
+ dataAttributes({
403
+ slot: 'menu-item-label',
404
+ state: checked ? 'checked' : 'unchecked',
405
+ }),
406
+ props,
407
+ )}
408
+ >
409
+ {children}
410
+ </Comp>
411
+ );
412
+ },
413
+ );
414
+
415
+ MenuItemLabel.displayName = 'Menu.ItemLabel';
416
+
417
+ export interface MenuItemIconProps extends Omit<IconProps, 'as' | 'children'> {
418
+ asChild?: boolean;
419
+ as?: IconProps['as'];
420
+ position?: IconSlotPosition;
421
+ className?: string;
422
+ children?: ReactNode;
423
+ }
424
+
425
+ const MenuItemIcon = ({
426
+ asChild,
427
+ as,
428
+ position = 'leading',
429
+ className,
430
+ style,
431
+ children,
432
+ ...props
433
+ }: MenuItemIconProps) => {
434
+ const { color } = useMenuStyleContext();
435
+ const computedClassName = cn(menuItemIconVariants({ color }), className);
436
+ const slotData = mergeDataAttributes(
437
+ props,
438
+ dataAttributes({ slot: 'menu-item-icon' }),
439
+ positionToDataIcon(position),
440
+ );
441
+
442
+ if (asChild) {
443
+ return (
444
+ <Slot
445
+ className={computedClassName}
446
+ style={style}
447
+ accessibilityElementsHidden
448
+ aria-hidden
449
+ {...props}
450
+ {...slotData}
451
+ >
452
+ {children}
453
+ </Slot>
454
+ );
455
+ }
456
+
457
+ if (!as) {
458
+ return null;
459
+ }
460
+
461
+ return (
462
+ <Icon
463
+ as={as}
464
+ className={computedClassName}
465
+ style={style}
466
+ accessibilityElementsHidden
467
+ aria-hidden
468
+ {...props}
469
+ {...slotData}
470
+ />
471
+ );
472
+ };
473
+
474
+ MenuItemIcon.displayName = 'Menu.ItemIcon';
475
+
476
+ export interface MenuItemShortcutProps extends TextProps {
477
+ asChild?: boolean;
478
+ className?: string;
479
+ }
480
+
481
+ const MenuItemShortcut = forwardRef<Text, MenuItemShortcutProps>(
482
+ ({ asChild, className, style, children, ...props }, ref) => {
483
+ const Comp = asChild ? Slot : Text;
484
+
485
+ return (
486
+ <Comp
487
+ ref={ref}
488
+ className={cn(menuItemShortcutVariants(), className)}
489
+ style={style}
490
+ accessibilityElementsHidden
491
+ aria-hidden
492
+ {...props}
493
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-item-shortcut' }))}
494
+ >
495
+ {children}
496
+ </Comp>
497
+ );
498
+ },
499
+ );
500
+
501
+ MenuItemShortcut.displayName = 'Menu.ItemShortcut';
502
+
503
+ export interface MenuItemIndicatorProps extends Omit<IconProps, 'as' | 'children'> {
504
+ asChild?: boolean;
505
+ as?: IconProps['as'];
506
+ isChecked?: boolean;
507
+ position?: IconSlotPosition;
508
+ className?: string;
509
+ children?: ReactNode;
510
+ }
511
+
512
+ const MenuItemIndicator = ({
513
+ asChild,
514
+ as = Check,
515
+ isChecked: checkedProp,
516
+ position = 'trailing',
517
+ className,
518
+ style,
519
+ children,
520
+ ...props
521
+ }: MenuItemIndicatorProps) => {
522
+ const menuItemContext = useOptionalMenuItemContext();
523
+ const checked = checkedProp ?? menuItemContext?.checked ?? false;
524
+ if (!checked) {
525
+ return null;
526
+ }
527
+
528
+ const computedClassName = cn(menuItemIndicatorVariants(), className);
529
+ const slotData = mergeDataAttributes(
530
+ props,
531
+ dataAttributes({ slot: 'menu-item-indicator' }),
532
+ positionToDataIcon(position),
533
+ );
534
+
535
+ if (asChild) {
536
+ return (
537
+ <Slot
538
+ className={computedClassName}
539
+ style={style}
540
+ accessibilityElementsHidden
541
+ aria-hidden
542
+ {...props}
543
+ {...slotData}
544
+ >
545
+ {children}
546
+ </Slot>
547
+ );
548
+ }
549
+
550
+ return (
551
+ <Icon
552
+ as={as}
553
+ className={computedClassName}
554
+ style={style}
555
+ accessibilityElementsHidden
556
+ aria-hidden
557
+ {...props}
558
+ {...slotData}
559
+ />
560
+ );
561
+ };
562
+
563
+ MenuItemIndicator.displayName = 'Menu.ItemIndicator';
564
+
565
+ // =============================================================================
566
+ // NON-INTERACTIVE SLOTS
567
+ // =============================================================================
568
+
569
+ export interface MenuSectionLabelProps extends TextProps {
570
+ asChild?: boolean;
571
+ className?: string;
572
+ }
573
+
574
+ const MenuSectionLabel = forwardRef<Text, MenuSectionLabelProps>(
575
+ ({ asChild, className, style, children, ...props }, ref) => {
576
+ const Comp = asChild ? Slot : Text;
577
+
578
+ return (
579
+ <Comp
580
+ ref={ref}
581
+ className={cn(menuSectionLabelVariants(), className)}
582
+ style={style}
583
+ accessibilityRole="header"
584
+ {...props}
585
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-section-label' }))}
586
+ >
587
+ {children}
588
+ </Comp>
589
+ );
590
+ },
591
+ );
592
+
593
+ MenuSectionLabel.displayName = 'Menu.SectionLabel';
594
+
595
+ export interface MenuSeparatorProps extends ViewProps {
596
+ className?: string;
597
+ }
598
+
599
+ const MenuSeparator = forwardRef<View, MenuSeparatorProps>(
600
+ ({ className, style, ...props }, ref) => {
601
+ return (
602
+ <View
603
+ ref={ref}
604
+ className={cn(menuSeparatorVariants(), className)}
605
+ style={style}
606
+ accessibilityElementsHidden
607
+ importantForAccessibility="no-hide-descendants"
608
+ aria-hidden
609
+ {...props}
610
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'menu-separator' }))}
611
+ >
612
+ <View className={menuSeparatorLineVariants()} />
613
+ </View>
614
+ );
615
+ },
616
+ );
617
+
618
+ MenuSeparator.displayName = 'Menu.Separator';
619
+
620
+ // =============================================================================
621
+ // COMPOUND EXPORT
622
+ // =============================================================================
623
+
624
+ type MenuCompoundComponent = typeof MenuOverlayRoot & {
625
+ Trigger: typeof MenuTrigger;
626
+ Popup: typeof MenuPopup;
627
+ Content: typeof MenuContent;
628
+ Item: typeof MenuItem;
629
+ CheckboxItem: typeof MenuCheckboxItem;
630
+ RadioGroup: typeof MenuRadioGroup;
631
+ RadioItem: typeof MenuRadioItem;
632
+ ItemLabel: typeof MenuItemLabel;
633
+ ItemIcon: typeof MenuItemIcon;
634
+ ItemShortcut: typeof MenuItemShortcut;
635
+ ItemIndicator: typeof MenuItemIndicator;
636
+ SectionLabel: typeof MenuSectionLabel;
637
+ Separator: typeof MenuSeparator;
638
+ };
639
+
640
+ export const Menu = Object.assign(MenuOverlayRoot, {
641
+ Trigger: MenuTrigger,
642
+ Popup: MenuPopup,
643
+ Content: MenuContent,
644
+ Item: MenuItem,
645
+ CheckboxItem: MenuCheckboxItem,
646
+ RadioGroup: MenuRadioGroup,
647
+ RadioItem: MenuRadioItem,
648
+ ItemLabel: MenuItemLabel,
649
+ ItemIcon: MenuItemIcon,
650
+ ItemShortcut: MenuItemShortcut,
651
+ ItemIndicator: MenuItemIndicator,
652
+ SectionLabel: MenuSectionLabel,
653
+ Separator: MenuSeparator,
654
+ }) as MenuCompoundComponent;