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