@dxos/react-ui-menu 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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 +388 -212
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +388 -212
- 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 +26 -1
- package/dist/types/src/builder.d.ts.map +1 -1
- package/dist/types/src/builder.test.d.ts +2 -0
- package/dist/types/src/builder.test.d.ts.map +1 -0
- 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 +50 -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 +5 -5
- package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts +3 -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/index.d.ts +1 -1
- package/dist/types/src/index.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.test.ts +224 -0
- package/src/builder.ts +68 -18
- package/src/components/ActionLabel.tsx +2 -2
- package/src/components/DropdownMenu.tsx +3 -4
- package/src/components/Menu.stories.tsx +265 -0
- package/src/components/Menu.test.tsx +447 -0
- package/src/components/Menu.tsx +348 -0
- package/src/components/ToolbarMenu.stories.tsx +28 -27
- package/src/components/ToolbarMenu.tsx +113 -88
- package/src/components/index.ts +1 -3
- package/src/hooks/useMenuActions.ts +18 -54
- package/src/index.ts +1 -1
- package/src/testing/generator.ts +24 -19
- 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, { createContext, 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,40 @@ 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
|
+
import { composable, composableProps } from "@dxos/ui-theme";
|
|
113
|
+
|
|
114
|
+
// src/types.ts
|
|
115
|
+
import { Node as Node2 } from "@dxos/app-graph";
|
|
116
|
+
var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
|
|
117
|
+
var isSeparator = (node) => node.type === MenuSeparatorType;
|
|
118
|
+
var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
|
|
133
119
|
|
|
134
120
|
// src/components/DropdownMenu.tsx
|
|
121
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
122
|
+
import React2, { useCallback } from "react";
|
|
123
|
+
import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
|
|
135
124
|
var DropdownMenuItem = ({ item, onClick, __menuScope }) => {
|
|
136
125
|
const action = item;
|
|
137
126
|
const handleClick = useCallback((event) => onClick(action, event), [
|
|
138
127
|
action,
|
|
139
128
|
onClick
|
|
140
129
|
]);
|
|
141
|
-
const { iconSize } =
|
|
142
|
-
return /* @__PURE__ */
|
|
130
|
+
const { iconSize } = useMenuScoped("DropdownMenuItem", __menuScope);
|
|
131
|
+
return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Item, {
|
|
143
132
|
onClick: handleClick,
|
|
144
133
|
classNames: "gap-2",
|
|
145
134
|
disabled: action.properties?.disabled,
|
|
146
135
|
...action.properties?.testId && {
|
|
147
136
|
"data-testid": action.properties.testId
|
|
148
137
|
}
|
|
149
|
-
}, action.properties?.icon && /* @__PURE__ */
|
|
138
|
+
}, action.properties?.icon && /* @__PURE__ */ React2.createElement(Icon, {
|
|
150
139
|
icon: action.properties.icon,
|
|
151
140
|
size: iconSize
|
|
152
|
-
}), /* @__PURE__ */
|
|
141
|
+
}), /* @__PURE__ */ React2.createElement(ActionLabel, {
|
|
153
142
|
action
|
|
154
143
|
}));
|
|
155
144
|
};
|
|
@@ -159,7 +148,7 @@ var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenCha
|
|
|
159
148
|
defaultProp: defaultOpen,
|
|
160
149
|
onChange: onOpenChange
|
|
161
150
|
});
|
|
162
|
-
const { onAction } =
|
|
151
|
+
const { onAction } = useMenuScoped("DropdownMenuRoot", __menuScope);
|
|
163
152
|
const handleActionClick = useCallback((action, event) => {
|
|
164
153
|
if (action.properties?.disabled) {
|
|
165
154
|
return;
|
|
@@ -183,15 +172,15 @@ var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenCha
|
|
|
183
172
|
onAction
|
|
184
173
|
]);
|
|
185
174
|
const items = useMenuItems(group, propsItems);
|
|
186
|
-
return /* @__PURE__ */
|
|
175
|
+
return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Root, {
|
|
187
176
|
open: optionsMenuOpen,
|
|
188
177
|
onOpenChange: setOptionsMenuOpen,
|
|
189
178
|
...naturalProps
|
|
190
|
-
}, children, /* @__PURE__ */
|
|
179
|
+
}, 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
180
|
key: item.id,
|
|
192
181
|
item,
|
|
193
182
|
onClick: handleActionClick
|
|
194
|
-
}))), /* @__PURE__ */
|
|
183
|
+
}))), /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Arrow, null))));
|
|
195
184
|
};
|
|
196
185
|
var DropdownMenu = {
|
|
197
186
|
Root: DropdownMenuRoot,
|
|
@@ -200,33 +189,11 @@ var DropdownMenu = {
|
|
|
200
189
|
};
|
|
201
190
|
|
|
202
191
|
// 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);
|
|
192
|
+
var ActionToolbarItem = ({ __menuScope, action }) => {
|
|
193
|
+
const { iconSize, onAction } = useMenuScoped("ActionToolbarItem", __menuScope);
|
|
217
194
|
const { t } = useTranslation2(translationKey);
|
|
218
195
|
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
|
-
};
|
|
196
|
+
const buttonVariant = action.properties.variant === "primary" ? "primary" : "ghost";
|
|
230
197
|
const handleClick = useCallback2(() => {
|
|
231
198
|
if (onAction) {
|
|
232
199
|
onAction(action, {});
|
|
@@ -237,22 +204,36 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
|
237
204
|
action,
|
|
238
205
|
onAction
|
|
239
206
|
]);
|
|
240
|
-
|
|
241
|
-
|
|
207
|
+
const commonProps = {
|
|
208
|
+
variant: buttonVariant,
|
|
242
209
|
disabled,
|
|
210
|
+
classNames,
|
|
243
211
|
onClick: handleClick,
|
|
244
|
-
variant: "ghost",
|
|
245
212
|
...testId && {
|
|
246
213
|
"data-testid": testId
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
if (hidden) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
return icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.IconButton, {
|
|
220
|
+
key: action.id,
|
|
221
|
+
...commonProps,
|
|
222
|
+
icon,
|
|
223
|
+
size: iconSize,
|
|
224
|
+
iconOnly,
|
|
225
|
+
label: actionLabel(action, t)
|
|
226
|
+
}) : /* @__PURE__ */ React3.createElement(NaturalToolbar.Button, {
|
|
227
|
+
key: action.id,
|
|
228
|
+
...commonProps
|
|
229
|
+
}, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
230
|
+
action
|
|
231
|
+
}));
|
|
251
232
|
};
|
|
252
|
-
var DropdownMenuToolbarItem = ({ group, items: propsItems
|
|
233
|
+
var DropdownMenuToolbarItem = ({ __menuScope, group, items: propsItems }) => {
|
|
253
234
|
const { iconOnly, disabled, testId } = group.properties;
|
|
254
235
|
const { t } = useTranslation2(translationKey);
|
|
255
|
-
const { iconSize } =
|
|
236
|
+
const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
|
|
256
237
|
const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
|
|
257
238
|
const activeItem = items?.find((item) => !!item.properties.checked);
|
|
258
239
|
const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
|
|
@@ -266,20 +247,20 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
266
247
|
label: actionLabel(labelAction, t),
|
|
267
248
|
caretDown: true
|
|
268
249
|
} : {
|
|
269
|
-
children: /* @__PURE__ */
|
|
250
|
+
children: /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
270
251
|
action: labelAction
|
|
271
|
-
}), /* @__PURE__ */
|
|
252
|
+
}), /* @__PURE__ */ React3.createElement(Icon2, {
|
|
272
253
|
size: 3,
|
|
273
254
|
icon: "ph--caret-down--bold",
|
|
274
|
-
classNames: "
|
|
255
|
+
classNames: "ms-1"
|
|
275
256
|
}))
|
|
276
257
|
};
|
|
277
|
-
return /* @__PURE__ */
|
|
258
|
+
return /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
|
|
278
259
|
group,
|
|
279
260
|
items
|
|
280
|
-
}, /* @__PURE__ */
|
|
261
|
+
}, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
|
|
281
262
|
asChild: true
|
|
282
|
-
}, /* @__PURE__ */
|
|
263
|
+
}, /* @__PURE__ */ React3.createElement(Root, {
|
|
283
264
|
variant: "ghost",
|
|
284
265
|
disabled,
|
|
285
266
|
...rootProps,
|
|
@@ -288,8 +269,8 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
288
269
|
}
|
|
289
270
|
})));
|
|
290
271
|
};
|
|
291
|
-
var ToggleGroupItem = ({ group, action
|
|
292
|
-
const { iconSize, onAction } =
|
|
272
|
+
var ToggleGroupItem = ({ __menuScope, group, action }) => {
|
|
273
|
+
const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
|
|
293
274
|
const { t } = useTranslation2(translationKey);
|
|
294
275
|
const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
|
|
295
276
|
const handleClick = useCallback2(() => {
|
|
@@ -307,171 +288,331 @@ var ToggleGroupItem = ({ group, action, __menuScope }) => {
|
|
|
307
288
|
group,
|
|
308
289
|
onAction
|
|
309
290
|
]);
|
|
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,
|
|
291
|
+
const commonProps = {
|
|
323
292
|
value: action.id,
|
|
324
293
|
disabled,
|
|
325
|
-
onClick: handleClick,
|
|
326
294
|
variant: "ghost",
|
|
295
|
+
classNames,
|
|
296
|
+
onClick: handleClick,
|
|
327
297
|
...testId && {
|
|
328
298
|
"data-testid": testId
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
return hidden ? null : icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupIconItem, {
|
|
302
|
+
...commonProps,
|
|
303
|
+
icon,
|
|
304
|
+
size: iconSize,
|
|
305
|
+
iconOnly,
|
|
306
|
+
label: actionLabel(action, t)
|
|
307
|
+
}) : /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupItem, commonProps, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
308
|
+
action
|
|
309
|
+
}));
|
|
333
310
|
};
|
|
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
|
-
);
|
|
311
|
+
var ToggleGroupToolbarItem = ({ __menuScope, group, items: itemsProp }) => {
|
|
312
|
+
const items = useMenuItems(group, itemsProp, "ToggleGroupToolbarItem", __menuScope);
|
|
313
|
+
const { selectCardinality } = group.properties;
|
|
314
|
+
const children = items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
|
|
315
|
+
key: action.id,
|
|
316
|
+
group,
|
|
317
|
+
action
|
|
318
|
+
}));
|
|
319
|
+
if (selectCardinality === "multiple") {
|
|
320
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
|
|
321
|
+
type: "multiple",
|
|
322
|
+
value: group.properties.value
|
|
323
|
+
}, children);
|
|
324
|
+
} else {
|
|
325
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
|
|
326
|
+
type: "single",
|
|
327
|
+
value: group.properties.value
|
|
328
|
+
}, children);
|
|
329
|
+
}
|
|
354
330
|
};
|
|
355
|
-
var ToolbarMenu = ({ __menuScope,
|
|
331
|
+
var ToolbarMenu = composable(({ __menuScope, children, ...props }, forwardedRef) => {
|
|
356
332
|
const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
|
|
357
|
-
const { attendableId } =
|
|
333
|
+
const { attendableId, alwaysActive } = useMenuScoped("ToolbarMenu", __menuScope);
|
|
358
334
|
const { hasAttention } = useAttention(attendableId);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
classNames
|
|
369
|
-
],
|
|
370
|
-
layoutManaged: textBlockWidthProp
|
|
371
|
-
}, /* @__PURE__ */ React4.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React4.createElement(ToolbarMenuItem, {
|
|
372
|
-
key: item.id ?? i,
|
|
335
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.Root, {
|
|
336
|
+
...composableProps(props, {
|
|
337
|
+
classNames: attendableId
|
|
338
|
+
}),
|
|
339
|
+
disabled: !alwaysActive && !hasAttention,
|
|
340
|
+
ref: forwardedRef
|
|
341
|
+
}, items?.map((item) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
|
|
342
|
+
key: item.id,
|
|
343
|
+
__menuScope,
|
|
373
344
|
item
|
|
374
|
-
})))
|
|
375
|
-
};
|
|
376
|
-
var ToolbarMenuItem = ({ item }) => {
|
|
345
|
+
})), children);
|
|
346
|
+
});
|
|
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__ */ 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: 151,
|
|
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
|
|
415
505
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
|
419
524
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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;
|
|
423
540
|
}
|
|
424
|
-
|
|
425
|
-
|
|
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
|
+
});
|
|
553
|
+
}
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const items = useAtomValue(graph.connections(sourceNode?.id || Node3.RootId));
|
|
446
|
-
return items;
|
|
585
|
+
newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
|
|
586
|
+
return newGraph;
|
|
587
|
+
}, [
|
|
588
|
+
registry,
|
|
589
|
+
menuGraphProps
|
|
590
|
+
]);
|
|
591
|
+
const items = useCallback4((group) => {
|
|
592
|
+
return graph.connections(group?.id || Node3.RootId, "child");
|
|
447
593
|
}, [
|
|
448
594
|
graph
|
|
449
595
|
]);
|
|
450
596
|
return {
|
|
451
|
-
|
|
597
|
+
items
|
|
452
598
|
};
|
|
453
599
|
};
|
|
454
600
|
|
|
455
601
|
// src/builder.ts
|
|
456
602
|
import { invariant } from "@dxos/invariant";
|
|
457
|
-
var
|
|
458
|
-
var MenuBuilder = Object.freeze({
|
|
459
|
-
make: () => new MenuBuilderImpl({
|
|
460
|
-
nodes: [],
|
|
461
|
-
edges: []
|
|
462
|
-
}, "root")
|
|
463
|
-
});
|
|
603
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
|
|
464
604
|
var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
465
605
|
_data;
|
|
466
606
|
_rootId;
|
|
607
|
+
_separatorCount = 0;
|
|
467
608
|
constructor(_data, _rootId) {
|
|
468
609
|
this._data = _data;
|
|
469
610
|
this._rootId = _rootId;
|
|
470
611
|
}
|
|
471
612
|
root(props) {
|
|
472
613
|
invariant(this._rootId === "root", "Root group can only be at the top level", {
|
|
473
|
-
F:
|
|
474
|
-
L:
|
|
614
|
+
F: __dxlog_file2,
|
|
615
|
+
L: 61,
|
|
475
616
|
S: this,
|
|
476
617
|
A: [
|
|
477
618
|
"this._rootId === 'root'",
|
|
@@ -479,8 +620,8 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
479
620
|
]
|
|
480
621
|
});
|
|
481
622
|
invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", {
|
|
482
|
-
F:
|
|
483
|
-
L:
|
|
623
|
+
F: __dxlog_file2,
|
|
624
|
+
L: 62,
|
|
484
625
|
S: this,
|
|
485
626
|
A: [
|
|
486
627
|
"this._data.nodes.find((node) => node.id === 'root') === undefined",
|
|
@@ -490,11 +631,15 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
490
631
|
this._data.nodes.push(createMenuItemGroup("root", props));
|
|
491
632
|
return this;
|
|
492
633
|
}
|
|
634
|
+
build() {
|
|
635
|
+
return this._data;
|
|
636
|
+
}
|
|
493
637
|
action(id, props, invoke) {
|
|
494
638
|
this._data.nodes.push(createMenuAction(id, invoke, props));
|
|
495
639
|
this._data.edges.push({
|
|
496
640
|
source: this._rootId,
|
|
497
|
-
target: id
|
|
641
|
+
target: id,
|
|
642
|
+
relation: "child"
|
|
498
643
|
});
|
|
499
644
|
return this;
|
|
500
645
|
}
|
|
@@ -502,31 +647,62 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
502
647
|
this._data.nodes.push(createMenuItemGroup(id, props));
|
|
503
648
|
this._data.edges.push({
|
|
504
649
|
source: this._rootId,
|
|
505
|
-
target: id
|
|
650
|
+
target: id,
|
|
651
|
+
relation: "child"
|
|
506
652
|
});
|
|
507
653
|
cb(new _MenuBuilderImpl(this._data, id));
|
|
508
654
|
return this;
|
|
509
655
|
}
|
|
510
|
-
|
|
511
|
-
|
|
656
|
+
subgraph(subgraphOrCb) {
|
|
657
|
+
if (!subgraphOrCb) {
|
|
658
|
+
return this;
|
|
659
|
+
}
|
|
660
|
+
if (typeof subgraphOrCb === "function") {
|
|
661
|
+
subgraphOrCb(new _MenuBuilderImpl(this._data, this._rootId));
|
|
662
|
+
} else {
|
|
663
|
+
this._data.nodes.push(...subgraphOrCb.nodes);
|
|
664
|
+
this._data.edges.push(...subgraphOrCb.edges);
|
|
665
|
+
}
|
|
666
|
+
return this;
|
|
667
|
+
}
|
|
668
|
+
separator(variant = "gap") {
|
|
669
|
+
const id = `separator-${++this._separatorCount}`;
|
|
670
|
+
this._data.nodes.push({
|
|
671
|
+
id,
|
|
672
|
+
type: MenuSeparatorType,
|
|
673
|
+
properties: {
|
|
674
|
+
variant
|
|
675
|
+
},
|
|
676
|
+
data: void 0
|
|
677
|
+
});
|
|
678
|
+
this._data.edges.push({
|
|
679
|
+
source: this._rootId,
|
|
680
|
+
target: id,
|
|
681
|
+
relation: "child"
|
|
682
|
+
});
|
|
683
|
+
return this;
|
|
512
684
|
}
|
|
513
685
|
};
|
|
686
|
+
var MenuBuilder = Object.freeze({
|
|
687
|
+
make: () => new MenuBuilderImpl({
|
|
688
|
+
nodes: [],
|
|
689
|
+
edges: []
|
|
690
|
+
}, "root")
|
|
691
|
+
});
|
|
514
692
|
export {
|
|
515
|
-
|
|
693
|
+
Menu,
|
|
516
694
|
MenuBuilder,
|
|
517
|
-
MenuProvider,
|
|
518
695
|
MenuSeparatorType,
|
|
519
|
-
ToolbarMenu,
|
|
520
696
|
createGapSeparator,
|
|
521
697
|
createLineSeparator,
|
|
522
698
|
createMenuAction,
|
|
523
699
|
createMenuItemGroup,
|
|
524
|
-
createMenuScope,
|
|
525
700
|
isMenuGroup,
|
|
526
701
|
isSeparator,
|
|
527
702
|
menuContextDefaults,
|
|
528
703
|
useMenu,
|
|
529
704
|
useMenuActions,
|
|
530
|
-
useMenuItems
|
|
705
|
+
useMenuItems,
|
|
706
|
+
useMenuScoped
|
|
531
707
|
};
|
|
532
708
|
//# sourceMappingURL=index.mjs.map
|