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