@dxos/react-ui-menu 0.8.4-main.69d29f4 → 0.8.4-main.6fa680abb7
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/lib/browser/index.mjs +320 -145
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +320 -145
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/builder.d.ts +6 -0
- package/dist/types/src/builder.d.ts.map +1 -1
- package/dist/types/src/components/DropdownMenu.d.ts +1 -1
- package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menu.d.ts +46 -0
- package/dist/types/src/components/Menu.d.ts.map +1 -0
- package/dist/types/src/components/Menu.test.d.ts +2 -0
- package/dist/types/src/components/Menu.test.d.ts.map +1 -0
- package/dist/types/src/components/MenuItems.stories.d.ts +21 -0
- package/dist/types/src/components/MenuItems.stories.d.ts.map +1 -0
- package/dist/types/src/components/ToolbarMenu.d.ts +1 -1
- package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useMenuActions.d.ts +2 -2
- package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +3 -3
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +25 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +2 -0
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -21
- package/src/builder.ts +35 -8
- package/src/components/ActionLabel.tsx +2 -2
- package/src/components/DropdownMenu.tsx +3 -3
- package/src/components/Menu.test.tsx +448 -0
- package/src/components/Menu.tsx +349 -0
- package/src/components/MenuItems.stories.tsx +265 -0
- package/src/components/ToolbarMenu.stories.tsx +17 -18
- package/src/components/ToolbarMenu.tsx +12 -11
- package/src/components/index.ts +1 -3
- package/src/hooks/useMenuActions.ts +15 -50
- package/src/testing/generator.ts +9 -9
- package/src/types.ts +27 -2
- package/src/util.ts +2 -1
- package/dist/types/src/components/MenuContext.d.ts +0 -15
- package/dist/types/src/components/MenuContext.d.ts.map +0 -1
- package/src/components/MenuContext.tsx +0 -62
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
// src/components/
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
// src/components/Menu.tsx
|
|
2
|
+
import { Atom, RegistryContext, useAtomValue } from "@effect-atom/atom-react";
|
|
3
|
+
import { createContextScope } from "@radix-ui/react-context";
|
|
4
|
+
import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
|
|
5
|
+
import React4, { useCallback as useCallback3, useContext, useMemo } from "react";
|
|
6
|
+
import { log } from "@dxos/log";
|
|
7
|
+
import { Icon as Icon3, DropdownMenu as NaturalDropdownMenu2 } from "@dxos/react-ui";
|
|
5
8
|
|
|
6
9
|
// src/util.ts
|
|
7
10
|
import * as Effect from "effect/Effect";
|
|
@@ -44,7 +47,8 @@ var createGapSeparator = (id = "gap", source = "root") => ({
|
|
|
44
47
|
edges: [
|
|
45
48
|
{
|
|
46
49
|
source,
|
|
47
|
-
target: id
|
|
50
|
+
target: id,
|
|
51
|
+
relation: "child"
|
|
48
52
|
}
|
|
49
53
|
]
|
|
50
54
|
});
|
|
@@ -62,7 +66,8 @@ var createLineSeparator = (id = "line", source = "root") => ({
|
|
|
62
66
|
edges: [
|
|
63
67
|
{
|
|
64
68
|
source,
|
|
65
|
-
target: id
|
|
69
|
+
target: id,
|
|
70
|
+
relation: "child"
|
|
66
71
|
}
|
|
67
72
|
]
|
|
68
73
|
});
|
|
@@ -71,7 +76,7 @@ var createLineSeparator = (id = "line", source = "root") => ({
|
|
|
71
76
|
import React from "react";
|
|
72
77
|
import { keySymbols } from "@dxos/keyboard";
|
|
73
78
|
import { toLocalizedString, useTranslation } from "@dxos/react-ui";
|
|
74
|
-
import {
|
|
79
|
+
import { mx } from "@dxos/ui-theme";
|
|
75
80
|
|
|
76
81
|
// src/translations.ts
|
|
77
82
|
var translationKey = "@dxos/react-ui-menu";
|
|
@@ -90,7 +95,7 @@ var ActionLabel = ({ action }) => {
|
|
|
90
95
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
|
|
91
96
|
className: "grow truncate"
|
|
92
97
|
}, toLocalizedString(action.properties.label, t)), shortcut && /* @__PURE__ */ React.createElement("span", {
|
|
93
|
-
className: mx("shrink-0",
|
|
98
|
+
className: mx("shrink-0", "text-description")
|
|
94
99
|
}, keySymbols(shortcut).join("")));
|
|
95
100
|
};
|
|
96
101
|
var actionLabel = (action, t) => {
|
|
@@ -98,56 +103,40 @@ var actionLabel = (action, t) => {
|
|
|
98
103
|
return `${toLocalizedString(action.properties.label, t)}${shortcut ? ` (${keySymbols(shortcut).join("")})` : ""}`;
|
|
99
104
|
};
|
|
100
105
|
|
|
101
|
-
// src/components/
|
|
102
|
-
import {
|
|
103
|
-
import
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
var useMenuScope = createMenuScope();
|
|
113
|
-
var MenuProvider = ({ children, useGroupItems = menuContextDefaults.useGroupItems, iconSize = menuContextDefaults.iconSize, attendableId, onAction }) => {
|
|
114
|
-
const { scope } = useMenuScope(void 0);
|
|
115
|
-
return /* @__PURE__ */ React2.createElement(MenuContextProvider, {
|
|
116
|
-
useGroupItems,
|
|
117
|
-
iconSize,
|
|
118
|
-
attendableId,
|
|
119
|
-
onAction,
|
|
120
|
-
scope
|
|
121
|
-
}, children);
|
|
122
|
-
};
|
|
123
|
-
var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
|
|
124
|
-
const { useGroupItems } = useMenu(consumerName, __menuScope);
|
|
125
|
-
const groupItems = useGroupItems(group);
|
|
126
|
-
return useMemo(() => propsItems ?? groupItems ?? void 0, [
|
|
127
|
-
propsItems,
|
|
128
|
-
groupItems
|
|
129
|
-
]);
|
|
130
|
-
};
|
|
106
|
+
// src/components/ToolbarMenu.tsx
|
|
107
|
+
import React3, { Fragment, useCallback as useCallback2 } from "react";
|
|
108
|
+
import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
109
|
+
import { useAttention } from "@dxos/react-ui-attention";
|
|
110
|
+
import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/ui-theme";
|
|
111
|
+
|
|
112
|
+
// src/types.ts
|
|
113
|
+
import { Node as Node2 } from "@dxos/app-graph";
|
|
114
|
+
var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
|
|
115
|
+
var isSeparator = (node) => node.type === MenuSeparatorType;
|
|
116
|
+
var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
|
|
131
117
|
|
|
132
118
|
// src/components/DropdownMenu.tsx
|
|
119
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
120
|
+
import React2, { useCallback } from "react";
|
|
121
|
+
import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
|
|
133
122
|
var DropdownMenuItem = ({ item, onClick, __menuScope }) => {
|
|
134
123
|
const action = item;
|
|
135
124
|
const handleClick = useCallback((event) => onClick(action, event), [
|
|
136
125
|
action,
|
|
137
126
|
onClick
|
|
138
127
|
]);
|
|
139
|
-
const { iconSize } =
|
|
140
|
-
return /* @__PURE__ */
|
|
128
|
+
const { iconSize } = useMenuScoped("DropdownMenuItem", __menuScope);
|
|
129
|
+
return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Item, {
|
|
141
130
|
onClick: handleClick,
|
|
142
131
|
classNames: "gap-2",
|
|
143
132
|
disabled: action.properties?.disabled,
|
|
144
133
|
...action.properties?.testId && {
|
|
145
134
|
"data-testid": action.properties.testId
|
|
146
135
|
}
|
|
147
|
-
}, action.properties?.icon && /* @__PURE__ */
|
|
136
|
+
}, action.properties?.icon && /* @__PURE__ */ React2.createElement(Icon, {
|
|
148
137
|
icon: action.properties.icon,
|
|
149
138
|
size: iconSize
|
|
150
|
-
}), /* @__PURE__ */
|
|
139
|
+
}), /* @__PURE__ */ React2.createElement(ActionLabel, {
|
|
151
140
|
action
|
|
152
141
|
}));
|
|
153
142
|
};
|
|
@@ -157,7 +146,7 @@ var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenCha
|
|
|
157
146
|
defaultProp: defaultOpen,
|
|
158
147
|
onChange: onOpenChange
|
|
159
148
|
});
|
|
160
|
-
const { onAction } =
|
|
149
|
+
const { onAction } = useMenuScoped("DropdownMenuRoot", __menuScope);
|
|
161
150
|
const handleActionClick = useCallback((action, event) => {
|
|
162
151
|
if (action.properties?.disabled) {
|
|
163
152
|
return;
|
|
@@ -181,15 +170,15 @@ var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenCha
|
|
|
181
170
|
onAction
|
|
182
171
|
]);
|
|
183
172
|
const items = useMenuItems(group, propsItems);
|
|
184
|
-
return /* @__PURE__ */
|
|
173
|
+
return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Root, {
|
|
185
174
|
open: optionsMenuOpen,
|
|
186
175
|
onOpenChange: setOptionsMenuOpen,
|
|
187
176
|
...naturalProps
|
|
188
|
-
}, children, /* @__PURE__ */
|
|
177
|
+
}, children, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Portal, null, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Content, null, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Viewport, null, items?.map((item) => /* @__PURE__ */ React2.createElement(DropdownMenuItem, {
|
|
189
178
|
key: item.id,
|
|
190
179
|
item,
|
|
191
180
|
onClick: handleActionClick
|
|
192
|
-
}))), /* @__PURE__ */
|
|
181
|
+
}))), /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Arrow, null))));
|
|
193
182
|
};
|
|
194
183
|
var DropdownMenu = {
|
|
195
184
|
Root: DropdownMenuRoot,
|
|
@@ -197,23 +186,11 @@ var DropdownMenu = {
|
|
|
197
186
|
VirtualTrigger: NaturalDropdownMenu.VirtualTrigger
|
|
198
187
|
};
|
|
199
188
|
|
|
200
|
-
// src/components/ToolbarMenu.tsx
|
|
201
|
-
import React4, { Fragment, useCallback as useCallback2 } from "react";
|
|
202
|
-
import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
203
|
-
import { useAttention } from "@dxos/react-ui-attention";
|
|
204
|
-
import { mx as mx2, textBlockWidth, toolbarInactive, toolbarLayout } from "@dxos/ui-theme";
|
|
205
|
-
|
|
206
|
-
// src/types.ts
|
|
207
|
-
import { Node as Node2 } from "@dxos/app-graph";
|
|
208
|
-
var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
|
|
209
|
-
var isSeparator = (node) => node.type === MenuSeparatorType;
|
|
210
|
-
var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
|
|
211
|
-
|
|
212
189
|
// src/components/ToolbarMenu.tsx
|
|
213
190
|
var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
214
|
-
const { iconSize, onAction } =
|
|
191
|
+
const { iconSize, onAction } = useMenuScoped("ActionToolbarItem", __menuScope);
|
|
215
192
|
const { t } = useTranslation2(translationKey);
|
|
216
|
-
const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
|
|
193
|
+
const { icon, iconOnly = true, disabled, testId, hidden, classNames, variant } = action.properties;
|
|
217
194
|
const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
|
|
218
195
|
const rootProps = icon ? {
|
|
219
196
|
icon,
|
|
@@ -221,7 +198,7 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
|
221
198
|
iconOnly,
|
|
222
199
|
label: actionLabel(action, t)
|
|
223
200
|
} : {
|
|
224
|
-
children: /* @__PURE__ */
|
|
201
|
+
children: /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
225
202
|
action
|
|
226
203
|
})
|
|
227
204
|
};
|
|
@@ -235,11 +212,11 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
|
235
212
|
action,
|
|
236
213
|
onAction
|
|
237
214
|
]);
|
|
238
|
-
return hidden ? null : /* @__PURE__ */
|
|
215
|
+
return hidden ? null : /* @__PURE__ */ React3.createElement(Root, {
|
|
239
216
|
key: action.id,
|
|
240
217
|
disabled,
|
|
241
218
|
onClick: handleClick,
|
|
242
|
-
variant: "ghost",
|
|
219
|
+
variant: variant ?? "ghost",
|
|
243
220
|
...testId && {
|
|
244
221
|
"data-testid": testId
|
|
245
222
|
},
|
|
@@ -250,7 +227,7 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
|
250
227
|
var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
251
228
|
const { iconOnly, disabled, testId } = group.properties;
|
|
252
229
|
const { t } = useTranslation2(translationKey);
|
|
253
|
-
const { iconSize } =
|
|
230
|
+
const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
|
|
254
231
|
const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
|
|
255
232
|
const activeItem = items?.find((item) => !!item.properties.checked);
|
|
256
233
|
const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
|
|
@@ -264,20 +241,20 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
264
241
|
label: actionLabel(labelAction, t),
|
|
265
242
|
caretDown: true
|
|
266
243
|
} : {
|
|
267
|
-
children: /* @__PURE__ */
|
|
244
|
+
children: /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
268
245
|
action: labelAction
|
|
269
|
-
}), /* @__PURE__ */
|
|
246
|
+
}), /* @__PURE__ */ React3.createElement(Icon2, {
|
|
270
247
|
size: 3,
|
|
271
248
|
icon: "ph--caret-down--bold",
|
|
272
|
-
classNames: "
|
|
249
|
+
classNames: "ms-1"
|
|
273
250
|
}))
|
|
274
251
|
};
|
|
275
|
-
return /* @__PURE__ */
|
|
252
|
+
return /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
|
|
276
253
|
group,
|
|
277
254
|
items
|
|
278
|
-
}, /* @__PURE__ */
|
|
255
|
+
}, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
|
|
279
256
|
asChild: true
|
|
280
|
-
}, /* @__PURE__ */
|
|
257
|
+
}, /* @__PURE__ */ React3.createElement(Root, {
|
|
281
258
|
variant: "ghost",
|
|
282
259
|
disabled,
|
|
283
260
|
...rootProps,
|
|
@@ -287,7 +264,7 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
287
264
|
})));
|
|
288
265
|
};
|
|
289
266
|
var ToggleGroupItem = ({ group, action, __menuScope }) => {
|
|
290
|
-
const { iconSize, onAction } =
|
|
267
|
+
const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
|
|
291
268
|
const { t } = useTranslation2(translationKey);
|
|
292
269
|
const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
|
|
293
270
|
const handleClick = useCallback2(() => {
|
|
@@ -312,11 +289,11 @@ var ToggleGroupItem = ({ group, action, __menuScope }) => {
|
|
|
312
289
|
iconOnly,
|
|
313
290
|
label: actionLabel(action, t)
|
|
314
291
|
} : {
|
|
315
|
-
children: /* @__PURE__ */
|
|
292
|
+
children: /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
316
293
|
action
|
|
317
294
|
})
|
|
318
295
|
};
|
|
319
|
-
return hidden ? null : /* @__PURE__ */
|
|
296
|
+
return hidden ? null : /* @__PURE__ */ React3.createElement(Root, {
|
|
320
297
|
key: action.id,
|
|
321
298
|
value: action.id,
|
|
322
299
|
disabled,
|
|
@@ -335,14 +312,14 @@ var ToggleGroupToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
335
312
|
return (
|
|
336
313
|
// TODO(thure): Fix.
|
|
337
314
|
// @ts-ignore
|
|
338
|
-
/* @__PURE__ */
|
|
315
|
+
/* @__PURE__ */ React3.createElement(
|
|
339
316
|
NaturalToolbar.ToggleGroup,
|
|
340
317
|
{
|
|
341
318
|
type: selectCardinality,
|
|
342
319
|
value
|
|
343
320
|
},
|
|
344
321
|
// TODO(thure): Handle other menu item types.
|
|
345
|
-
items.map((action) => /* @__PURE__ */
|
|
322
|
+
items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
|
|
346
323
|
key: action.id,
|
|
347
324
|
group,
|
|
348
325
|
action
|
|
@@ -352,124 +329,294 @@ var ToggleGroupToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
352
329
|
};
|
|
353
330
|
var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthProp, ...props }) => {
|
|
354
331
|
const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
|
|
355
|
-
const { attendableId } =
|
|
332
|
+
const { attendableId, alwaysActive } = useMenuScoped("ToolbarMenu", __menuScope);
|
|
356
333
|
const { hasAttention } = useAttention(attendableId);
|
|
357
334
|
const InnerRoot = textBlockWidthProp ? "div" : Fragment;
|
|
358
335
|
const innerRootProps = textBlockWidthProp ? {
|
|
359
336
|
role: "none",
|
|
360
|
-
className: mx2(textBlockWidth, toolbarLayout, "
|
|
337
|
+
className: mx2(textBlockWidth, toolbarLayout, "h-full")
|
|
361
338
|
} : {};
|
|
362
|
-
return /* @__PURE__ */
|
|
339
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.Root, {
|
|
363
340
|
...props,
|
|
364
341
|
classNames: [
|
|
365
|
-
attendableId
|
|
342
|
+
attendableId,
|
|
366
343
|
classNames
|
|
367
344
|
],
|
|
345
|
+
disabled: !alwaysActive && !hasAttention,
|
|
368
346
|
layoutManaged: textBlockWidthProp
|
|
369
|
-
}, /* @__PURE__ */
|
|
347
|
+
}, /* @__PURE__ */ React3.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
|
|
370
348
|
key: item.id ?? i,
|
|
371
349
|
item
|
|
372
350
|
}))));
|
|
373
351
|
};
|
|
374
352
|
var ToolbarMenuItem = ({ item }) => {
|
|
375
353
|
if (isSeparator(item)) {
|
|
376
|
-
return /* @__PURE__ */
|
|
354
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.Separator, {
|
|
377
355
|
variant: item.properties.variant
|
|
378
356
|
});
|
|
379
357
|
}
|
|
380
358
|
if (isMenuGroup(item)) {
|
|
381
359
|
if (item.properties.variant === "dropdownMenu") {
|
|
382
|
-
return /* @__PURE__ */
|
|
360
|
+
return /* @__PURE__ */ React3.createElement(DropdownMenuToolbarItem, {
|
|
383
361
|
group: item
|
|
384
362
|
});
|
|
385
363
|
} else {
|
|
386
|
-
return /* @__PURE__ */
|
|
364
|
+
return /* @__PURE__ */ React3.createElement(ToggleGroupToolbarItem, {
|
|
387
365
|
group: item
|
|
388
366
|
});
|
|
389
367
|
}
|
|
390
368
|
}
|
|
391
|
-
return /* @__PURE__ */
|
|
369
|
+
return /* @__PURE__ */ React3.createElement(ActionToolbarItem, {
|
|
392
370
|
action: item
|
|
393
371
|
});
|
|
394
372
|
};
|
|
395
373
|
|
|
396
|
-
// src/
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
var
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
374
|
+
// src/components/Menu.tsx
|
|
375
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/components/Menu.tsx";
|
|
376
|
+
var MENU_NAME = "Menu";
|
|
377
|
+
var [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);
|
|
378
|
+
var nullItemsAtom = Atom.make(null);
|
|
379
|
+
var defaultItemsAccessor = () => nullItemsAtom;
|
|
380
|
+
var menuContextDefaults = {
|
|
381
|
+
iconSize: 5,
|
|
382
|
+
items: defaultItemsAccessor,
|
|
383
|
+
onAction: void 0,
|
|
384
|
+
menuItemsAtom: Atom.make(/* @__PURE__ */ new Map()),
|
|
385
|
+
addMenuItems: () => {
|
|
386
|
+
},
|
|
387
|
+
removeMenuItems: () => {
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
var [MenuContextProvider, useMenuScoped] = createMenuContext(MENU_NAME, menuContextDefaults);
|
|
391
|
+
var useMenuScope = createMenuScope();
|
|
392
|
+
var MenuDropdownContext = /* @__PURE__ */ React4.createContext({
|
|
393
|
+
closeMenu: () => {
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
var DEFAULT_PRIORITY = 100;
|
|
397
|
+
var sortMenuItems = (items) => [
|
|
398
|
+
...items
|
|
399
|
+
].sort((a, b) => {
|
|
400
|
+
if (a.priority !== b.priority) {
|
|
401
|
+
return a.priority - b.priority;
|
|
402
|
+
}
|
|
403
|
+
return a.id.localeCompare(b.id);
|
|
404
|
+
});
|
|
405
|
+
var MenuProvider = ({ children, items = menuContextDefaults.items, iconSize = menuContextDefaults.iconSize, attendableId, alwaysActive, onAction }) => {
|
|
406
|
+
const { scope } = useMenuScope(void 0);
|
|
407
|
+
const registry = useContext(RegistryContext);
|
|
408
|
+
const menuItemsAtom = useMemo(() => Atom.make(/* @__PURE__ */ new Map()).pipe(Atom.keepAlive), []);
|
|
409
|
+
const addMenuItems = useCallback3((props) => {
|
|
410
|
+
const priority = props.priority ?? DEFAULT_PRIORITY;
|
|
411
|
+
const prev = registry.get(menuItemsAtom);
|
|
412
|
+
const next = new Map(prev);
|
|
413
|
+
next.set(props.id, {
|
|
414
|
+
...props,
|
|
415
|
+
priority
|
|
416
|
+
});
|
|
417
|
+
registry.set(menuItemsAtom, next);
|
|
418
|
+
}, [
|
|
419
|
+
registry,
|
|
420
|
+
menuItemsAtom
|
|
421
|
+
]);
|
|
422
|
+
const removeMenuItems = useCallback3((id) => {
|
|
423
|
+
const prev = registry.get(menuItemsAtom);
|
|
424
|
+
const next = new Map(prev);
|
|
425
|
+
next.delete(id);
|
|
426
|
+
registry.set(menuItemsAtom, next);
|
|
427
|
+
}, [
|
|
428
|
+
registry,
|
|
429
|
+
menuItemsAtom
|
|
430
|
+
]);
|
|
431
|
+
return /* @__PURE__ */ React4.createElement(MenuContextProvider, {
|
|
432
|
+
items,
|
|
433
|
+
iconSize,
|
|
434
|
+
attendableId,
|
|
435
|
+
alwaysActive,
|
|
436
|
+
menuItemsAtom,
|
|
437
|
+
addMenuItems,
|
|
438
|
+
removeMenuItems,
|
|
439
|
+
onAction,
|
|
440
|
+
scope
|
|
441
|
+
}, children);
|
|
442
|
+
};
|
|
443
|
+
var resolveItems = (baseItems, group, entries) => {
|
|
444
|
+
const applicable = [
|
|
445
|
+
...entries.values()
|
|
446
|
+
].filter((entry) => !entry.groupFilter || entry.groupFilter(group));
|
|
447
|
+
if (applicable.length === 0) {
|
|
448
|
+
return baseItems;
|
|
449
|
+
}
|
|
450
|
+
const sorted = sortMenuItems(applicable);
|
|
451
|
+
const replacements = sorted.filter((entry) => entry.mode === "replacement");
|
|
452
|
+
if (replacements.length > 0) {
|
|
453
|
+
if (replacements.length > 1) {
|
|
454
|
+
log.warn("multiple replacement entries found", {
|
|
455
|
+
ids: replacements.map((r) => r.id).join(", "),
|
|
456
|
+
using: replacements[0].id
|
|
457
|
+
}, {
|
|
458
|
+
F: __dxlog_file,
|
|
459
|
+
L: 152,
|
|
460
|
+
S: void 0,
|
|
461
|
+
C: (f, a) => f(...a)
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
return replacements[0].items;
|
|
465
|
+
}
|
|
466
|
+
const additive = sorted.filter((entry) => entry.mode === "additive");
|
|
467
|
+
const additiveItems = additive.flatMap((entry) => entry.items);
|
|
468
|
+
if (!baseItems || baseItems.length === 0) {
|
|
469
|
+
return additiveItems.length > 0 ? additiveItems : null;
|
|
470
|
+
}
|
|
471
|
+
return [
|
|
472
|
+
...baseItems,
|
|
473
|
+
...additiveItems
|
|
474
|
+
];
|
|
475
|
+
};
|
|
476
|
+
var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
|
|
477
|
+
const { items, menuItemsAtom } = useMenuScoped(consumerName, __menuScope);
|
|
478
|
+
const groupItems = useAtomValue(items(group));
|
|
479
|
+
const entries = useAtomValue(menuItemsAtom) ?? /* @__PURE__ */ new Map();
|
|
480
|
+
const baseItems = useMemo(() => propsItems ?? groupItems ?? null, [
|
|
481
|
+
propsItems,
|
|
482
|
+
groupItems
|
|
483
|
+
]);
|
|
484
|
+
const resolved = useMemo(() => resolveItems(baseItems, group, entries), [
|
|
485
|
+
baseItems,
|
|
486
|
+
group,
|
|
487
|
+
entries
|
|
488
|
+
]);
|
|
489
|
+
return resolved ?? void 0;
|
|
490
|
+
};
|
|
491
|
+
var useMenu = (consumerName) => {
|
|
492
|
+
return useMenuScoped(consumerName, void 0);
|
|
493
|
+
};
|
|
494
|
+
var MenuRoot = ({ children, open, defaultOpen, onOpenChange, caller, ...props }) => {
|
|
495
|
+
const [menuOpen, setMenuOpen] = useControllableState2({
|
|
496
|
+
prop: open,
|
|
497
|
+
defaultProp: defaultOpen,
|
|
498
|
+
onChange: onOpenChange
|
|
499
|
+
});
|
|
500
|
+
const closeMenu = useCallback3(() => setMenuOpen(false), [
|
|
501
|
+
setMenuOpen
|
|
502
|
+
]);
|
|
503
|
+
return /* @__PURE__ */ React4.createElement(MenuProvider, props, /* @__PURE__ */ React4.createElement(MenuDropdownContext.Provider, {
|
|
504
|
+
value: {
|
|
505
|
+
closeMenu,
|
|
506
|
+
caller
|
|
407
507
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
508
|
+
}, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Root, {
|
|
509
|
+
open: menuOpen,
|
|
510
|
+
onOpenChange: setMenuOpen
|
|
511
|
+
}, children)));
|
|
512
|
+
};
|
|
513
|
+
var MenuContentItem = ({ item, onClick, __menuScope }) => {
|
|
514
|
+
const action = item;
|
|
515
|
+
const handleClick = useCallback3((event) => onClick(action, event), [
|
|
516
|
+
action,
|
|
517
|
+
onClick
|
|
518
|
+
]);
|
|
519
|
+
const { iconSize } = useMenuScoped("MenuContentItem", __menuScope);
|
|
520
|
+
return /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Item, {
|
|
521
|
+
onClick: handleClick,
|
|
522
|
+
classNames: "gap-2",
|
|
523
|
+
disabled: action.properties?.disabled,
|
|
524
|
+
...action.properties?.testId && {
|
|
525
|
+
"data-testid": action.properties.testId
|
|
413
526
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
527
|
+
}, action.properties?.icon && /* @__PURE__ */ React4.createElement(Icon3, {
|
|
528
|
+
icon: action.properties.icon,
|
|
529
|
+
size: iconSize
|
|
530
|
+
}), /* @__PURE__ */ React4.createElement(ActionLabel, {
|
|
531
|
+
action
|
|
532
|
+
}));
|
|
533
|
+
};
|
|
534
|
+
var MenuContent = ({ group, items: propsItems, caller: callerOverride, __menuScope }) => {
|
|
535
|
+
const { closeMenu, caller: contextCaller } = useContext(MenuDropdownContext);
|
|
536
|
+
const caller = callerOverride ?? contextCaller;
|
|
537
|
+
const { onAction } = useMenuScoped("MenuContent", __menuScope);
|
|
538
|
+
const resolvedItems = useMenuItems(group, propsItems, "MenuContent", __menuScope);
|
|
539
|
+
const handleActionClick = useCallback3((action, event) => {
|
|
540
|
+
if (action.properties?.disabled) {
|
|
541
|
+
return;
|
|
417
542
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
543
|
+
event.stopPropagation();
|
|
544
|
+
closeMenu();
|
|
545
|
+
if (onAction) {
|
|
546
|
+
onAction(action, {
|
|
547
|
+
parent: group,
|
|
548
|
+
caller
|
|
549
|
+
});
|
|
550
|
+
} else {
|
|
551
|
+
void executeMenuAction(action, {
|
|
552
|
+
parent: group,
|
|
553
|
+
caller
|
|
554
|
+
});
|
|
421
555
|
}
|
|
422
|
-
|
|
423
|
-
|
|
556
|
+
}, [
|
|
557
|
+
group,
|
|
558
|
+
caller,
|
|
559
|
+
onAction,
|
|
560
|
+
closeMenu
|
|
561
|
+
]);
|
|
562
|
+
return /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Portal, null, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Content, null, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Viewport, null, resolvedItems?.map((item) => /* @__PURE__ */ React4.createElement(MenuContentItem, {
|
|
563
|
+
key: item.id,
|
|
564
|
+
item,
|
|
565
|
+
onClick: handleActionClick
|
|
566
|
+
}))), /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Arrow, null)));
|
|
424
567
|
};
|
|
568
|
+
var Menu = {
|
|
569
|
+
Root: MenuRoot,
|
|
570
|
+
Trigger: NaturalDropdownMenu2.Trigger,
|
|
571
|
+
Content: MenuContent,
|
|
572
|
+
VirtualTrigger: NaturalDropdownMenu2.VirtualTrigger,
|
|
573
|
+
Toolbar: ToolbarMenu
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// src/hooks/useMenuActions.ts
|
|
577
|
+
import { RegistryContext as RegistryContext2, useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
|
|
578
|
+
import { useCallback as useCallback4, useContext as useContext2, useRef } from "react";
|
|
579
|
+
import { Graph, Node as Node3 } from "@dxos/app-graph";
|
|
425
580
|
var useMenuActions = (props) => {
|
|
426
|
-
const registry =
|
|
427
|
-
const menuGraphProps =
|
|
581
|
+
const registry = useContext2(RegistryContext2);
|
|
582
|
+
const menuGraphProps = useAtomValue2(props);
|
|
428
583
|
const graphRef = useRef(null);
|
|
429
|
-
if (!graphRef.current) {
|
|
430
|
-
|
|
431
|
-
registry
|
|
432
|
-
nodes: menuGraphProps.nodes,
|
|
433
|
-
edges: edgesArrayToRecord(menuGraphProps.edges)
|
|
584
|
+
if (!graphRef.current || graphRef.current.props !== menuGraphProps) {
|
|
585
|
+
const newGraph = Graph.make({
|
|
586
|
+
registry
|
|
434
587
|
});
|
|
588
|
+
newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
|
|
589
|
+
graphRef.current = {
|
|
590
|
+
graph: newGraph,
|
|
591
|
+
props: menuGraphProps
|
|
592
|
+
};
|
|
435
593
|
}
|
|
436
|
-
const graph = graphRef.current;
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
prevPropsRef.current = menuGraphProps;
|
|
440
|
-
graph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
|
|
441
|
-
}
|
|
442
|
-
const useGroupItems = useCallback3((sourceNode) => {
|
|
443
|
-
const items = useAtomValue(graph.connections(sourceNode?.id || Node3.RootId));
|
|
444
|
-
return items;
|
|
594
|
+
const graph = graphRef.current.graph;
|
|
595
|
+
const items = useCallback4((group) => {
|
|
596
|
+
return graph.connections(group?.id || Node3.RootId, "child");
|
|
445
597
|
}, [
|
|
446
598
|
graph
|
|
447
599
|
]);
|
|
448
600
|
return {
|
|
449
|
-
|
|
601
|
+
items
|
|
450
602
|
};
|
|
451
603
|
};
|
|
452
604
|
|
|
453
605
|
// src/builder.ts
|
|
454
606
|
import { invariant } from "@dxos/invariant";
|
|
455
|
-
var
|
|
456
|
-
var MenuBuilder = Object.freeze({
|
|
457
|
-
make: () => new MenuBuilderImpl({
|
|
458
|
-
nodes: [],
|
|
459
|
-
edges: []
|
|
460
|
-
}, "root")
|
|
461
|
-
});
|
|
607
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
|
|
462
608
|
var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
463
609
|
_data;
|
|
464
610
|
_rootId;
|
|
611
|
+
_separatorCount = 0;
|
|
465
612
|
constructor(_data, _rootId) {
|
|
466
613
|
this._data = _data;
|
|
467
614
|
this._rootId = _rootId;
|
|
468
615
|
}
|
|
469
616
|
root(props) {
|
|
470
617
|
invariant(this._rootId === "root", "Root group can only be at the top level", {
|
|
471
|
-
F:
|
|
472
|
-
L:
|
|
618
|
+
F: __dxlog_file2,
|
|
619
|
+
L: 41,
|
|
473
620
|
S: this,
|
|
474
621
|
A: [
|
|
475
622
|
"this._rootId === 'root'",
|
|
@@ -477,8 +624,8 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
477
624
|
]
|
|
478
625
|
});
|
|
479
626
|
invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", {
|
|
480
|
-
F:
|
|
481
|
-
L:
|
|
627
|
+
F: __dxlog_file2,
|
|
628
|
+
L: 42,
|
|
482
629
|
S: this,
|
|
483
630
|
A: [
|
|
484
631
|
"this._data.nodes.find((node) => node.id === 'root') === undefined",
|
|
@@ -488,11 +635,15 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
488
635
|
this._data.nodes.push(createMenuItemGroup("root", props));
|
|
489
636
|
return this;
|
|
490
637
|
}
|
|
638
|
+
build() {
|
|
639
|
+
return this._data;
|
|
640
|
+
}
|
|
491
641
|
action(id, props, invoke) {
|
|
492
642
|
this._data.nodes.push(createMenuAction(id, invoke, props));
|
|
493
643
|
this._data.edges.push({
|
|
494
644
|
source: this._rootId,
|
|
495
|
-
target: id
|
|
645
|
+
target: id,
|
|
646
|
+
relation: "child"
|
|
496
647
|
});
|
|
497
648
|
return this;
|
|
498
649
|
}
|
|
@@ -500,31 +651,55 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
500
651
|
this._data.nodes.push(createMenuItemGroup(id, props));
|
|
501
652
|
this._data.edges.push({
|
|
502
653
|
source: this._rootId,
|
|
503
|
-
target: id
|
|
654
|
+
target: id,
|
|
655
|
+
relation: "child"
|
|
504
656
|
});
|
|
505
657
|
cb(new _MenuBuilderImpl(this._data, id));
|
|
506
658
|
return this;
|
|
507
659
|
}
|
|
508
|
-
|
|
509
|
-
|
|
660
|
+
subgraph(subgraph) {
|
|
661
|
+
this._data.nodes.push(...subgraph.nodes);
|
|
662
|
+
this._data.edges.push(...subgraph.edges);
|
|
663
|
+
return this;
|
|
664
|
+
}
|
|
665
|
+
separator(id, variant = "gap") {
|
|
666
|
+
id ??= `separator-${++this._separatorCount}`;
|
|
667
|
+
this._data.nodes.push({
|
|
668
|
+
id,
|
|
669
|
+
type: MenuSeparatorType,
|
|
670
|
+
properties: {
|
|
671
|
+
variant
|
|
672
|
+
},
|
|
673
|
+
data: void 0
|
|
674
|
+
});
|
|
675
|
+
this._data.edges.push({
|
|
676
|
+
source: this._rootId,
|
|
677
|
+
target: id,
|
|
678
|
+
relation: "child"
|
|
679
|
+
});
|
|
680
|
+
return this;
|
|
510
681
|
}
|
|
511
682
|
};
|
|
683
|
+
var MenuBuilder = Object.freeze({
|
|
684
|
+
make: () => new MenuBuilderImpl({
|
|
685
|
+
nodes: [],
|
|
686
|
+
edges: []
|
|
687
|
+
}, "root")
|
|
688
|
+
});
|
|
512
689
|
export {
|
|
513
|
-
|
|
690
|
+
Menu,
|
|
514
691
|
MenuBuilder,
|
|
515
|
-
MenuProvider,
|
|
516
692
|
MenuSeparatorType,
|
|
517
|
-
ToolbarMenu,
|
|
518
693
|
createGapSeparator,
|
|
519
694
|
createLineSeparator,
|
|
520
695
|
createMenuAction,
|
|
521
696
|
createMenuItemGroup,
|
|
522
|
-
createMenuScope,
|
|
523
697
|
isMenuGroup,
|
|
524
698
|
isSeparator,
|
|
525
699
|
menuContextDefaults,
|
|
526
700
|
useMenu,
|
|
527
701
|
useMenuActions,
|
|
528
|
-
useMenuItems
|
|
702
|
+
useMenuItems,
|
|
703
|
+
useMenuScoped
|
|
529
704
|
};
|
|
530
705
|
//# sourceMappingURL=index.mjs.map
|