@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.
- package/coverage.svg +1 -1
- package/lib/commonjs/components/Menu/index.js +499 -0
- package/lib/commonjs/components/Menu/index.js.map +1 -0
- package/lib/commonjs/components/Menu/styles.js +100 -0
- package/lib/commonjs/components/Menu/styles.js.map +1 -0
- package/lib/commonjs/components/Select/index.js +57 -22
- package/lib/commonjs/components/Select/index.js.map +1 -1
- package/lib/commonjs/components/Select/styles.js +1 -40
- package/lib/commonjs/components/Select/styles.js.map +1 -1
- package/lib/commonjs/components/Slider/getSliderPopupPosition.js +16 -6
- package/lib/commonjs/components/Slider/getSliderPopupPosition.js.map +1 -1
- package/lib/commonjs/components/Slider/index.js +17 -5
- package/lib/commonjs/components/Slider/index.js.map +1 -1
- package/lib/commonjs/components/Toast/index.js +1 -1
- package/lib/commonjs/components/Toast/index.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/components/picker/surfaces.js +16 -9
- package/lib/commonjs/components/picker/surfaces.js.map +1 -1
- package/lib/commonjs/figma/Menu.Item.figma.js +52 -0
- package/lib/commonjs/figma/Menu.Item.figma.js.map +1 -0
- package/lib/commonjs/figma/Menu.figma.js +29 -0
- package/lib/commonjs/figma/Menu.figma.js.map +1 -0
- package/lib/commonjs/providers/forge-design/provider.js +25 -11
- package/lib/commonjs/providers/forge-design/provider.js.map +1 -1
- package/lib/commonjs/providers/toast/insets-container.js +4 -1
- package/lib/commonjs/providers/toast/insets-container.js.map +1 -1
- package/lib/commonjs/providers/toast/provider.js +126 -45
- package/lib/commonjs/providers/toast/provider.js.map +1 -1
- package/lib/commonjs/providers/toast/toast-item-renderer.js +2 -1
- package/lib/commonjs/providers/toast/toast-item-renderer.js.map +1 -1
- package/lib/module/components/Menu/index.js +495 -0
- package/lib/module/components/Menu/index.js.map +1 -0
- package/lib/module/components/Menu/styles.js +96 -0
- package/lib/module/components/Menu/styles.js.map +1 -0
- package/lib/module/components/Select/index.js +55 -20
- package/lib/module/components/Select/index.js.map +1 -1
- package/lib/module/components/Select/styles.js +0 -39
- package/lib/module/components/Select/styles.js.map +1 -1
- package/lib/module/components/Slider/getSliderPopupPosition.js +16 -6
- package/lib/module/components/Slider/getSliderPopupPosition.js.map +1 -1
- package/lib/module/components/Slider/index.js +20 -8
- package/lib/module/components/Slider/index.js.map +1 -1
- package/lib/module/components/Toast/index.js +1 -1
- package/lib/module/components/Toast/index.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/picker/surfaces.js +18 -11
- package/lib/module/components/picker/surfaces.js.map +1 -1
- package/lib/module/figma/Menu.Item.figma.js +46 -0
- package/lib/module/figma/Menu.Item.figma.js.map +1 -0
- package/lib/module/figma/Menu.figma.js +23 -0
- package/lib/module/figma/Menu.figma.js.map +1 -0
- package/lib/module/providers/forge-design/provider.js +26 -12
- package/lib/module/providers/forge-design/provider.js.map +1 -1
- package/lib/module/providers/toast/insets-container.js +5 -2
- package/lib/module/providers/toast/insets-container.js.map +1 -1
- package/lib/module/providers/toast/provider.js +126 -45
- package/lib/module/providers/toast/provider.js.map +1 -1
- package/lib/module/providers/toast/toast-item-renderer.js +2 -1
- package/lib/module/providers/toast/toast-item-renderer.js.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts +119 -0
- package/lib/typescript/components/Menu/index.d.ts.map +1 -0
- package/lib/typescript/components/Menu/styles.d.ts +24 -0
- package/lib/typescript/components/Menu/styles.d.ts.map +1 -0
- package/lib/typescript/components/Select/index.d.ts.map +1 -1
- package/lib/typescript/components/Select/styles.d.ts +0 -7
- package/lib/typescript/components/Select/styles.d.ts.map +1 -1
- package/lib/typescript/components/Slider/getSliderPopupPosition.d.ts +1 -1
- package/lib/typescript/components/Slider/getSliderPopupPosition.d.ts.map +1 -1
- package/lib/typescript/components/Slider/index.d.ts.map +1 -1
- package/lib/typescript/components/Toast/types.d.ts +2 -2
- package/lib/typescript/components/Toast/types.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/components/picker/surfaces.d.ts.map +1 -1
- package/lib/typescript/figma/Menu.Item.figma.d.ts +8 -0
- package/lib/typescript/figma/Menu.Item.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Menu.figma.d.ts +8 -0
- package/lib/typescript/figma/Menu.figma.d.ts.map +1 -0
- package/lib/typescript/providers/forge-design/provider.d.ts.map +1 -1
- package/lib/typescript/providers/toast/insets-container.d.ts.map +1 -1
- package/lib/typescript/providers/toast/lifecycle.context.d.ts +2 -2
- package/lib/typescript/providers/toast/lifecycle.context.d.ts.map +1 -1
- package/lib/typescript/providers/toast/provider.d.ts.map +1 -1
- package/lib/typescript/providers/toast/toast-item-renderer.d.ts.map +1 -1
- package/lib/typescript/providers/toast/types.d.ts +22 -8
- package/lib/typescript/providers/toast/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/Menu/index.tsx +654 -0
- package/src/components/Menu/styles.ts +153 -0
- package/src/components/Select/index.tsx +58 -23
- package/src/components/Select/styles.ts +0 -59
- package/src/components/Slider/getSliderPopupPosition.ts +19 -7
- package/src/components/Slider/index.tsx +25 -9
- package/src/components/Toast/index.tsx +1 -1
- package/src/components/Toast/types.ts +2 -2
- package/src/components/index.ts +1 -0
- package/src/components/picker/surfaces.tsx +13 -9
- package/src/providers/forge-design/provider.tsx +35 -16
- package/src/providers/toast/insets-container.tsx +3 -2
- package/src/providers/toast/lifecycle.context.tsx +2 -2
- package/src/providers/toast/provider.tsx +150 -51
- package/src/providers/toast/toast-item-renderer.tsx +5 -1
- 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;
|