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