@luxfi/ui 7.4.10 → 7.4.11
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/dist/accordion.cjs +107 -166
- package/dist/accordion.d.cts +18 -51
- package/dist/accordion.d.ts +18 -51
- package/dist/accordion.js +109 -166
- package/dist/chrome.cjs +193 -379
- package/dist/chrome.js +195 -379
- package/dist/collapsible.cjs +35 -26
- package/dist/collapsible.d.cts +2 -1
- package/dist/collapsible.d.ts +2 -1
- package/dist/collapsible.js +34 -24
- package/dist/dialog.d.cts +1 -1
- package/dist/dialog.d.ts +1 -1
- package/dist/drawer.cjs +92 -115
- package/dist/drawer.d.cts +43 -10
- package/dist/drawer.d.ts +43 -10
- package/dist/drawer.js +94 -115
- package/dist/field.cjs +60 -109
- package/dist/field.d.cts +0 -1
- package/dist/field.d.ts +0 -1
- package/dist/field.js +61 -109
- package/dist/heading.cjs +10 -17
- package/dist/heading.d.cts +0 -2
- package/dist/heading.d.ts +0 -2
- package/dist/heading.js +10 -17
- package/dist/icon-button.cjs +82 -82
- package/dist/icon-button.d.cts +16 -12
- package/dist/icon-button.d.ts +16 -12
- package/dist/icon-button.js +82 -82
- package/dist/index.cjs +1073 -1419
- package/dist/index.d.cts +1 -5
- package/dist/index.d.ts +1 -5
- package/dist/index.js +1075 -1414
- package/dist/input-group.cjs +11 -12
- package/dist/input-group.js +11 -12
- package/dist/input.cjs +27 -27
- package/dist/input.d.cts +21 -1
- package/dist/input.d.ts +21 -1
- package/dist/input.js +25 -28
- package/dist/menu.cjs +160 -271
- package/dist/menu.d.cts +41 -57
- package/dist/menu.d.ts +41 -57
- package/dist/menu.js +161 -251
- package/dist/popover.cjs +1 -1
- package/dist/popover.js +1 -1
- package/dist/select.cjs +40 -39
- package/dist/select.d.cts +35 -4
- package/dist/select.d.ts +35 -4
- package/dist/select.js +40 -38
- package/dist/separator.cjs +8 -33
- package/dist/separator.js +8 -33
- package/dist/tabs.cjs +108 -197
- package/dist/tabs.d.cts +16 -60
- package/dist/tabs.d.ts +16 -60
- package/dist/tabs.js +109 -196
- package/dist/textarea.cjs +56 -27
- package/dist/textarea.js +57 -28
- package/dist/toaster.cjs +97 -79
- package/dist/toaster.d.cts +7 -8
- package/dist/toaster.d.ts +7 -8
- package/dist/toaster.js +78 -80
- package/package.json +2 -16
- package/src/accordion.tsx +173 -227
- package/src/collapsible.tsx +55 -52
- package/src/drawer.tsx +123 -115
- package/src/field.tsx +68 -124
- package/src/heading.tsx +22 -26
- package/src/icon-button.tsx +134 -141
- package/src/ink.tsx +37 -0
- package/src/input-group.tsx +21 -12
- package/src/input.tsx +41 -32
- package/src/menu.tsx +224 -397
- package/src/popover.tsx +1 -1
- package/src/select.tsx +80 -59
- package/src/separator.tsx +13 -34
- package/src/tabs.tsx +166 -296
- package/src/textarea.tsx +19 -29
- package/src/toaster.tsx +118 -86
- package/tokens.css +61 -0
package/src/menu.tsx
CHANGED
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import * as React from 'react';
|
|
3
|
+
import { Menu, Text, View, XStack } from '@hanzo/gui';
|
|
5
4
|
import { Check, ChevronRight } from 'lucide-react';
|
|
5
|
+
import * as React from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
7
|
+
import { ink } from './ink';
|
|
8
|
+
|
|
9
|
+
// The Lux menu, on gui's Menu — the same fifteen parts Radix's dropdown has,
|
|
10
|
+
// under the names this package already published.
|
|
11
|
+
//
|
|
12
|
+
// One thing genuinely got simpler rather than merely moving. Radix splits
|
|
13
|
+
// position into `side` + `align`, so the old file carried a `parsePlacement`
|
|
14
|
+
// that took `'bottom-start'` apart into `{ side: 'bottom', align: 'start' }`,
|
|
15
|
+
// plus a context to carry the two halves down to the content. gui's Menu sits
|
|
16
|
+
// on Popper, which takes the floating-ui `placement` string whole and derives
|
|
17
|
+
// side and align itself. The parser, the context, and the four props they fed
|
|
18
|
+
// are all gone: the caller's string reaches the engine untouched.
|
|
19
|
+
|
|
20
|
+
const ITEM_HOVER = 'var(--color-popover-item-hover)';
|
|
21
|
+
const TEXT = 'var(--color-text-primary)';
|
|
22
|
+
|
|
23
|
+
/** Resting/highlighted look shared by every selectable row. */
|
|
24
|
+
const ROW = {
|
|
25
|
+
position: 'relative',
|
|
26
|
+
flexDirection: 'row',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
cursor: 'pointer',
|
|
29
|
+
userSelect: 'none',
|
|
30
|
+
borderRadius: 6,
|
|
31
|
+
fontSize: 14,
|
|
32
|
+
backgroundColor: 'transparent',
|
|
33
|
+
hoverStyle: { backgroundColor: ITEM_HOVER },
|
|
34
|
+
focusStyle: { backgroundColor: ITEM_HOVER },
|
|
35
|
+
} as const;
|
|
37
36
|
|
|
38
|
-
// ---
|
|
37
|
+
// --- MenuRoot ---
|
|
39
38
|
|
|
40
39
|
interface Positioning {
|
|
41
40
|
readonly placement?: string;
|
|
@@ -45,427 +44,255 @@ interface Positioning {
|
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
interface MenuPositioning {
|
|
49
|
-
readonly side: Side;
|
|
50
|
-
readonly align: Align;
|
|
51
|
-
readonly sideOffset: number;
|
|
52
|
-
readonly alignOffset: number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const PositioningContext = React.createContext<MenuPositioning>({
|
|
56
|
-
side: 'bottom',
|
|
57
|
-
align: 'start',
|
|
58
|
-
sideOffset: 4,
|
|
59
|
-
alignOffset: 0,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// --- MenuRoot ---
|
|
63
|
-
|
|
64
47
|
export interface MenuRootProps {
|
|
65
48
|
readonly children?: React.ReactNode;
|
|
66
49
|
readonly open?: boolean;
|
|
67
50
|
readonly defaultOpen?: boolean;
|
|
68
51
|
readonly onOpenChange?: (details: { open: boolean }) => void;
|
|
69
52
|
readonly positioning?: Positioning;
|
|
70
|
-
readonly lazyMount?: boolean;
|
|
71
|
-
readonly unmountOnExit?: boolean;
|
|
72
53
|
readonly modal?: boolean;
|
|
73
54
|
}
|
|
74
55
|
|
|
75
|
-
export const MenuRoot = (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
modal
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} = props;
|
|
90
|
-
|
|
91
|
-
const mergedPositioning: Positioning = {
|
|
92
|
-
placement: 'bottom-start',
|
|
93
|
-
...positioning,
|
|
94
|
-
offset: {
|
|
95
|
-
mainAxis: 4,
|
|
96
|
-
...positioning?.offset,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const { side, align } = parsePlacement(mergedPositioning.placement);
|
|
101
|
-
|
|
102
|
-
const positioningValue = React.useMemo<MenuPositioning>(() => ({
|
|
103
|
-
side,
|
|
104
|
-
align,
|
|
105
|
-
sideOffset: mergedPositioning.offset?.mainAxis ?? 4,
|
|
106
|
-
alignOffset: mergedPositioning.offset?.crossAxis ?? 0,
|
|
107
|
-
}), [ side, align, mergedPositioning.offset?.mainAxis, mergedPositioning.offset?.crossAxis ]);
|
|
108
|
-
|
|
109
|
-
const handleOpenChange = React.useCallback((isOpen: boolean) => {
|
|
110
|
-
onOpenChange?.({ open: isOpen });
|
|
111
|
-
}, [ onOpenChange ]);
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<PositioningContext.Provider value={ positioningValue }>
|
|
115
|
-
<DropdownMenu.Root
|
|
116
|
-
open={ open }
|
|
117
|
-
defaultOpen={ defaultOpen }
|
|
118
|
-
onOpenChange={ handleOpenChange }
|
|
119
|
-
modal={ modal }
|
|
120
|
-
>
|
|
121
|
-
{ children }
|
|
122
|
-
</DropdownMenu.Root>
|
|
123
|
-
</PositioningContext.Provider>
|
|
124
|
-
);
|
|
125
|
-
};
|
|
56
|
+
export const MenuRoot = ({
|
|
57
|
+
children, open, defaultOpen, onOpenChange, positioning, modal = true,
|
|
58
|
+
}: MenuRootProps): React.ReactElement => (
|
|
59
|
+
<Menu
|
|
60
|
+
open={ open }
|
|
61
|
+
defaultOpen={ defaultOpen }
|
|
62
|
+
onOpenChange={ onOpenChange ? (isOpen: boolean) => onOpenChange({ open: isOpen }) : undefined }
|
|
63
|
+
modal={ modal }
|
|
64
|
+
placement={ (positioning?.placement ?? 'bottom-start') as never }
|
|
65
|
+
offset={ positioning?.offset?.mainAxis ?? 4 }
|
|
66
|
+
>
|
|
67
|
+
{ children }
|
|
68
|
+
</Menu>
|
|
69
|
+
);
|
|
126
70
|
|
|
127
71
|
// --- MenuTrigger ---
|
|
128
72
|
|
|
129
|
-
export
|
|
130
|
-
readonly asChild?: boolean;
|
|
131
|
-
}
|
|
73
|
+
export type MenuTriggerProps = React.ComponentProps<typeof Menu.Trigger>;
|
|
132
74
|
|
|
133
|
-
export const MenuTrigger = React.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
>(function MenuTrigger(props, ref) {
|
|
137
|
-
const { asChild = false, ...rest } = props;
|
|
138
|
-
return <DropdownMenu.Trigger asChild={ asChild } ref={ ref } { ...rest }/>;
|
|
139
|
-
});
|
|
75
|
+
export const MenuTrigger = ({ asChild = false, ...rest }: MenuTriggerProps): React.ReactElement => (
|
|
76
|
+
<Menu.Trigger asChild={ asChild } { ...rest }/>
|
|
77
|
+
);
|
|
140
78
|
|
|
141
79
|
// --- MenuContent ---
|
|
142
80
|
|
|
143
|
-
export
|
|
81
|
+
export type MenuContentProps = React.ComponentProps<typeof Menu.Content> & {
|
|
144
82
|
readonly portalled?: boolean;
|
|
145
|
-
readonly portalRef?: React.RefObject<HTMLElement>;
|
|
146
|
-
|
|
147
|
-
/** Legacy Chakra zIndex prop - mapped to inline style */
|
|
148
|
-
readonly zIndex?: string | number;
|
|
149
|
-
|
|
150
|
-
/** Legacy Chakra minW prop - mapped to inline style */
|
|
151
83
|
readonly minW?: string | number;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export const MenuContent = React.forwardRef<
|
|
155
|
-
HTMLDivElement,
|
|
156
|
-
MenuContentProps
|
|
157
|
-
>(function MenuContent(props, ref) {
|
|
158
|
-
const { portalled = true, portalRef, className, zIndex, minW, style, ...rest } = props;
|
|
159
|
-
const positioning = React.useContext(PositioningContext);
|
|
160
|
-
|
|
161
|
-
const mergedStyle: React.CSSProperties = {
|
|
162
|
-
...style,
|
|
163
|
-
...(zIndex !== undefined ? { zIndex: typeof zIndex === 'string' ? `var(--z-index-${ zIndex }, ${ zIndex })` : zIndex } : {}),
|
|
164
|
-
...(minW !== undefined ? { minWidth: minW } : {}),
|
|
165
|
-
};
|
|
84
|
+
};
|
|
166
85
|
|
|
86
|
+
export const MenuContent = ({
|
|
87
|
+
portalled = true, minW, children, ...rest
|
|
88
|
+
}: MenuContentProps): React.ReactElement => {
|
|
167
89
|
const content = (
|
|
168
|
-
<
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
182
|
-
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
|
183
|
-
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
184
|
-
className,
|
|
185
|
-
) }
|
|
186
|
-
style={ Object.keys(mergedStyle).length > 0 ? mergedStyle : undefined }
|
|
90
|
+
<Menu.Content
|
|
91
|
+
unstyled
|
|
92
|
+
zIndex={ 50 }
|
|
93
|
+
minWidth={ (minW ?? 128) as never }
|
|
94
|
+
overflow="hidden"
|
|
95
|
+
borderRadius={ 8 }
|
|
96
|
+
padding={ 4 }
|
|
97
|
+
borderWidth={ 1 }
|
|
98
|
+
borderColor={ 'var(--color-popover-border)' as never }
|
|
99
|
+
backgroundColor={ 'var(--color-popover-bg)' as never }
|
|
100
|
+
transition="quick"
|
|
101
|
+
enterStyle={{ opacity: 0, scale: 0.95 }}
|
|
102
|
+
exitStyle={{ opacity: 0, scale: 0.95 }}
|
|
187
103
|
{ ...rest }
|
|
188
|
-
|
|
104
|
+
>
|
|
105
|
+
{ children }
|
|
106
|
+
</Menu.Content>
|
|
189
107
|
);
|
|
190
108
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<DropdownMenu.Portal container={ portalRef?.current ?? undefined }>
|
|
197
|
-
{ content }
|
|
198
|
-
</DropdownMenu.Portal>
|
|
199
|
-
);
|
|
200
|
-
});
|
|
109
|
+
return portalled ? <Menu.Portal>{ content }</Menu.Portal> : content;
|
|
110
|
+
};
|
|
201
111
|
|
|
202
112
|
// --- MenuItem ---
|
|
203
113
|
|
|
204
|
-
export
|
|
114
|
+
export type MenuItemProps = React.ComponentProps<typeof Menu.Item> & {
|
|
205
115
|
|
|
206
|
-
/** Informational
|
|
116
|
+
/** Informational identifier — the row's own name, not read by the engine. */
|
|
207
117
|
readonly value?: string;
|
|
208
|
-
|
|
209
|
-
readonly asChild?: boolean;
|
|
210
|
-
|
|
211
|
-
/** Chakra compat - accepted but not used by Radix */
|
|
212
|
-
readonly closeOnSelect?: boolean;
|
|
213
|
-
}
|
|
118
|
+
};
|
|
214
119
|
|
|
215
|
-
export const MenuItem = React.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
className,
|
|
233
|
-
) }
|
|
234
|
-
{ ...(rest as React.ComponentPropsWithoutRef<typeof DropdownMenu.Item>) }
|
|
120
|
+
export const MenuItem = ({ children, value: _value, asChild, ...rest }: MenuItemProps): React.ReactElement => (
|
|
121
|
+
// `asChild` means the caller brought its own element and owns the look. Its
|
|
122
|
+
// props are then forwarded to that element verbatim, so the skin must NOT be
|
|
123
|
+
// applied: `unstyled` and friends are not DOM attributes and React rejects
|
|
124
|
+
// them out loud ("Received `true` for a non-boolean attribute `unstyled`").
|
|
125
|
+
// Two shapes, said once, rather than one shape that is wrong in one of them.
|
|
126
|
+
asChild ? (
|
|
127
|
+
<Menu.Item asChild { ...rest }>{ children }</Menu.Item>
|
|
128
|
+
) : (
|
|
129
|
+
<Menu.Item
|
|
130
|
+
unstyled
|
|
131
|
+
{ ...ROW }
|
|
132
|
+
gap={ 8 }
|
|
133
|
+
paddingHorizontal={ 8 }
|
|
134
|
+
paddingVertical={ 6 }
|
|
135
|
+
disabledStyle={{ opacity: 0.5, pointerEvents: 'none' }}
|
|
136
|
+
{ ...rest }
|
|
235
137
|
>
|
|
236
|
-
{ children }
|
|
237
|
-
</
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// --- MenuItemText ---
|
|
138
|
+
{ ink(children, undefined, { fontSize: 14, color: TEXT }) }
|
|
139
|
+
</Menu.Item>
|
|
140
|
+
)
|
|
141
|
+
);
|
|
242
142
|
|
|
243
|
-
|
|
143
|
+
// --- MenuItemText / MenuItemCommand ---
|
|
244
144
|
|
|
245
|
-
export
|
|
246
|
-
HTMLSpanElement,
|
|
247
|
-
MenuItemTextProps
|
|
248
|
-
>(function MenuItemText(props, ref) {
|
|
249
|
-
const { className, ...rest } = props;
|
|
250
|
-
return <span ref={ ref } className={ cn('flex-1', className) } { ...rest }/>;
|
|
251
|
-
});
|
|
145
|
+
export type MenuItemTextProps = React.ComponentProps<typeof Text>;
|
|
252
146
|
|
|
253
|
-
|
|
147
|
+
export const MenuItemText = (props: MenuItemTextProps): React.ReactElement => (
|
|
148
|
+
<Text flex={ 1 } fontSize={ 14 } color={ TEXT as never } { ...props }/>
|
|
149
|
+
);
|
|
254
150
|
|
|
255
|
-
export
|
|
151
|
+
export type MenuItemCommandProps = React.ComponentProps<typeof Text>;
|
|
256
152
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const { className, ...rest } = props;
|
|
262
|
-
return (
|
|
263
|
-
<span
|
|
264
|
-
ref={ ref }
|
|
265
|
-
className={ cn('ml-auto text-xs tracking-widest opacity-60', className) }
|
|
266
|
-
{ ...rest }
|
|
267
|
-
/>
|
|
268
|
-
);
|
|
269
|
-
});
|
|
153
|
+
/** The keyboard shortcut on the trailing edge of a row. */
|
|
154
|
+
export const MenuItemCommand = (props: MenuItemCommandProps): React.ReactElement => (
|
|
155
|
+
<Text marginLeft="auto" fontSize={ 12 } letterSpacing={ 1 } opacity={ 0.6 } color={ TEXT as never } { ...props }/>
|
|
156
|
+
);
|
|
270
157
|
|
|
271
158
|
// --- MenuSeparator ---
|
|
272
159
|
|
|
273
|
-
export
|
|
274
|
-
|
|
275
|
-
export const MenuSeparator = React.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
/>
|
|
286
|
-
);
|
|
287
|
-
});
|
|
160
|
+
export type MenuSeparatorProps = React.ComponentProps<typeof Menu.Separator>;
|
|
161
|
+
|
|
162
|
+
export const MenuSeparator = (props: MenuSeparatorProps): React.ReactElement => (
|
|
163
|
+
<Menu.Separator
|
|
164
|
+
unstyled
|
|
165
|
+
height={ 1 }
|
|
166
|
+
marginVertical={ 4 }
|
|
167
|
+
marginHorizontal={ -4 }
|
|
168
|
+
backgroundColor={ 'var(--color-border-divider)' as never }
|
|
169
|
+
{ ...props }
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
288
172
|
|
|
289
173
|
// --- MenuItemGroup ---
|
|
290
174
|
|
|
291
|
-
export
|
|
175
|
+
export type MenuItemGroupProps = React.ComponentProps<typeof Menu.Group> & {
|
|
292
176
|
readonly title?: string;
|
|
293
|
-
}
|
|
177
|
+
};
|
|
294
178
|
|
|
295
|
-
export const MenuItemGroup = React.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
179
|
+
export const MenuItemGroup = ({ title, children, ...rest }: MenuItemGroupProps): React.ReactElement => (
|
|
180
|
+
<Menu.Group unstyled { ...rest }>
|
|
181
|
+
{ title && (
|
|
182
|
+
<Menu.Label
|
|
183
|
+
unstyled
|
|
184
|
+
paddingHorizontal={ 8 }
|
|
185
|
+
paddingVertical={ 6 }
|
|
186
|
+
fontSize={ 12 }
|
|
187
|
+
fontWeight="600"
|
|
188
|
+
userSelect="none"
|
|
189
|
+
opacity={ 0.6 }
|
|
190
|
+
color={ TEXT as never }
|
|
191
|
+
>
|
|
192
|
+
{ title }
|
|
193
|
+
</Menu.Label>
|
|
194
|
+
) }
|
|
195
|
+
{ children }
|
|
196
|
+
</Menu.Group>
|
|
197
|
+
);
|
|
311
198
|
|
|
312
199
|
// --- MenuArrow ---
|
|
313
200
|
|
|
314
|
-
export
|
|
315
|
-
|
|
316
|
-
export const MenuArrow = React.forwardRef<
|
|
317
|
-
SVGSVGElement,
|
|
318
|
-
MenuArrowProps
|
|
319
|
-
>(function MenuArrow(props, ref) {
|
|
320
|
-
const { className, ...rest } = props;
|
|
321
|
-
return (
|
|
322
|
-
<DropdownMenu.Arrow
|
|
323
|
-
ref={ ref }
|
|
324
|
-
className={ cn('fill-[var(--color-popover-bg,var(--color-dialog-bg))]', className) }
|
|
325
|
-
{ ...rest }
|
|
326
|
-
/>
|
|
327
|
-
);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// --- MenuCheckboxItem ---
|
|
331
|
-
|
|
332
|
-
export interface MenuCheckboxItemProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
333
|
-
readonly checked?: boolean;
|
|
334
|
-
readonly onCheckedChange?: (checked: boolean) => void;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export const MenuCheckboxItem = React.forwardRef<
|
|
338
|
-
HTMLDivElement,
|
|
339
|
-
MenuCheckboxItemProps
|
|
340
|
-
>(function MenuCheckboxItem(props, ref) {
|
|
341
|
-
const { className, children, checked, onCheckedChange, onClick, ...rest } = props;
|
|
342
|
-
return (
|
|
343
|
-
<DropdownMenu.CheckboxItem
|
|
344
|
-
ref={ ref }
|
|
345
|
-
checked={ checked }
|
|
346
|
-
onCheckedChange={ onCheckedChange }
|
|
347
|
-
onClick={ onClick }
|
|
348
|
-
className={ cn(
|
|
349
|
-
'relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm',
|
|
350
|
-
'outline-none transition-colors',
|
|
351
|
-
'data-[highlighted]:bg-[var(--color-popover-item-hover,rgba(0,0,0,0.04))]',
|
|
352
|
-
'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
353
|
-
className,
|
|
354
|
-
) }
|
|
355
|
-
{ ...(rest as React.ComponentPropsWithoutRef<typeof DropdownMenu.CheckboxItem>) }
|
|
356
|
-
>
|
|
357
|
-
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
358
|
-
<DropdownMenu.ItemIndicator>
|
|
359
|
-
<Check className="h-4 w-4"/>
|
|
360
|
-
</DropdownMenu.ItemIndicator>
|
|
361
|
-
</span>
|
|
362
|
-
{ children }
|
|
363
|
-
</DropdownMenu.CheckboxItem>
|
|
364
|
-
);
|
|
365
|
-
});
|
|
201
|
+
export type MenuArrowProps = React.ComponentProps<typeof Menu.Arrow>;
|
|
366
202
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
readonly value?: string;
|
|
371
|
-
readonly onValueChange?: (value: string) => void;
|
|
372
|
-
}
|
|
203
|
+
export const MenuArrow = (props: MenuArrowProps): React.ReactElement => (
|
|
204
|
+
<Menu.Arrow backgroundColor={ 'var(--color-popover-bg)' as never } { ...props }/>
|
|
205
|
+
);
|
|
373
206
|
|
|
374
|
-
|
|
375
|
-
HTMLDivElement,
|
|
376
|
-
MenuRadioItemGroupProps
|
|
377
|
-
>(function MenuRadioItemGroup(props, ref) {
|
|
378
|
-
const { value, onValueChange, ...rest } = props;
|
|
379
|
-
return (
|
|
380
|
-
<DropdownMenu.RadioGroup
|
|
381
|
-
ref={ ref }
|
|
382
|
-
value={ value }
|
|
383
|
-
onValueChange={ onValueChange }
|
|
384
|
-
{ ...rest }
|
|
385
|
-
/>
|
|
386
|
-
);
|
|
387
|
-
});
|
|
207
|
+
// --- The indicator well, shared by checkbox and radio rows ---
|
|
388
208
|
|
|
389
|
-
|
|
209
|
+
const Indicator = () => (
|
|
210
|
+
<View position="absolute" left={ 8 } width={ 14 } height={ 14 } alignItems="center" justifyContent="center">
|
|
211
|
+
<Menu.ItemIndicator>
|
|
212
|
+
<Check size={ 16 }/>
|
|
213
|
+
</Menu.ItemIndicator>
|
|
214
|
+
</View>
|
|
215
|
+
);
|
|
390
216
|
|
|
391
|
-
|
|
392
|
-
readonly value: string;
|
|
393
|
-
}
|
|
217
|
+
// --- MenuCheckboxItem ---
|
|
394
218
|
|
|
395
|
-
export
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
219
|
+
export type MenuCheckboxItemProps = React.ComponentProps<typeof Menu.CheckboxItem>;
|
|
220
|
+
|
|
221
|
+
export const MenuCheckboxItem = ({ children, ...rest }: MenuCheckboxItemProps): React.ReactElement => (
|
|
222
|
+
<Menu.CheckboxItem
|
|
223
|
+
unstyled
|
|
224
|
+
{ ...ROW }
|
|
225
|
+
paddingLeft={ 32 }
|
|
226
|
+
paddingRight={ 8 }
|
|
227
|
+
paddingVertical={ 6 }
|
|
228
|
+
disabledStyle={{ opacity: 0.5, pointerEvents: 'none' }}
|
|
229
|
+
{ ...rest }
|
|
230
|
+
>
|
|
231
|
+
<Indicator/>
|
|
232
|
+
{ ink(children, undefined, { fontSize: 14, color: TEXT }) }
|
|
233
|
+
</Menu.CheckboxItem>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// --- MenuRadioItemGroup / MenuRadioItem ---
|
|
237
|
+
|
|
238
|
+
export type MenuRadioItemGroupProps = React.ComponentProps<typeof Menu.RadioGroup>;
|
|
239
|
+
|
|
240
|
+
export const MenuRadioItemGroup = (props: MenuRadioItemGroupProps): React.ReactElement => (
|
|
241
|
+
<Menu.RadioGroup { ...props }/>
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
export type MenuRadioItemProps = React.ComponentProps<typeof Menu.RadioItem>;
|
|
245
|
+
|
|
246
|
+
export const MenuRadioItem = ({ children, ...rest }: MenuRadioItemProps): React.ReactElement => (
|
|
247
|
+
<Menu.RadioItem
|
|
248
|
+
unstyled
|
|
249
|
+
{ ...ROW }
|
|
250
|
+
paddingLeft={ 32 }
|
|
251
|
+
paddingRight={ 8 }
|
|
252
|
+
paddingVertical={ 6 }
|
|
253
|
+
disabledStyle={{ opacity: 0.5, pointerEvents: 'none' }}
|
|
254
|
+
{ ...rest }
|
|
255
|
+
>
|
|
256
|
+
<Indicator/>
|
|
257
|
+
{ ink(children, undefined, { fontSize: 14, color: TEXT }) }
|
|
258
|
+
</Menu.RadioItem>
|
|
259
|
+
);
|
|
424
260
|
|
|
425
261
|
// --- MenuContextTrigger ---
|
|
426
|
-
// Radix DropdownMenu does not have a native context-menu trigger.
|
|
427
|
-
// For API compatibility, export a no-op wrapper. Real context-menu
|
|
428
|
-
// support would require @radix-ui/react-context-menu.
|
|
429
262
|
|
|
430
|
-
export
|
|
263
|
+
export type MenuContextTriggerProps = React.ComponentProps<typeof View> & {
|
|
431
264
|
readonly asChild?: boolean;
|
|
432
|
-
}
|
|
265
|
+
};
|
|
433
266
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
})
|
|
267
|
+
/**
|
|
268
|
+
* A right-click target. This menu is a DROPDOWN — it opens from its trigger, not
|
|
269
|
+
* from a pointer position — so the wrapper passes its children through. gui does
|
|
270
|
+
* ship a real `ContextMenu`; a surface that wants one should reach for that
|
|
271
|
+
* rather than have this quietly behave like it.
|
|
272
|
+
*/
|
|
273
|
+
export const MenuContextTrigger = ({ asChild: _asChild, ...rest }: MenuContextTriggerProps): React.ReactElement => (
|
|
274
|
+
<View { ...rest }/>
|
|
275
|
+
);
|
|
441
276
|
|
|
442
|
-
// --- MenuTriggerItem ---
|
|
277
|
+
// --- MenuTriggerItem (a submenu's row) ---
|
|
443
278
|
|
|
444
|
-
export
|
|
279
|
+
export type MenuTriggerItemProps = React.ComponentProps<typeof Menu.SubTrigger> & {
|
|
445
280
|
readonly startIcon?: React.ReactNode;
|
|
446
|
-
}
|
|
281
|
+
};
|
|
447
282
|
|
|
448
|
-
export const MenuTriggerItem = React.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
>
|
|
465
|
-
{ startIcon }
|
|
466
|
-
<span className="flex-1">{ children }</span>
|
|
467
|
-
<ChevronRight className="h-4 w-4"/>
|
|
468
|
-
</DropdownMenu.SubTrigger>
|
|
469
|
-
</DropdownMenu.Sub>
|
|
470
|
-
);
|
|
471
|
-
});
|
|
283
|
+
export const MenuTriggerItem = ({ startIcon, children, ...rest }: MenuTriggerItemProps): React.ReactElement => (
|
|
284
|
+
<Menu.Sub>
|
|
285
|
+
<Menu.SubTrigger
|
|
286
|
+
unstyled
|
|
287
|
+
{ ...ROW }
|
|
288
|
+
gap={ 8 }
|
|
289
|
+
paddingHorizontal={ 8 }
|
|
290
|
+
paddingVertical={ 6 }
|
|
291
|
+
{ ...rest }
|
|
292
|
+
>
|
|
293
|
+
{ startIcon }
|
|
294
|
+
<XStack flex={ 1 }>{ ink(children, undefined, { fontSize: 14, color: TEXT }) }</XStack>
|
|
295
|
+
<ChevronRight size={ 16 }/>
|
|
296
|
+
</Menu.SubTrigger>
|
|
297
|
+
</Menu.Sub>
|
|
298
|
+
);
|