@dxos/react-ui-menu 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6
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 +385 -214
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +385 -214
- 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 +54 -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 +9 -5
- package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts +7 -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 -3
- package/src/components/Menu.stories.tsx +265 -0
- package/src/components/Menu.test.tsx +448 -0
- package/src/components/Menu.tsx +349 -0
- package/src/components/ToolbarMenu.stories.tsx +26 -25
- package/src/components/ToolbarMenu.tsx +113 -87
- package/src/components/index.ts +1 -3
- package/src/hooks/useMenuActions.ts +18 -55
- package/src/index.ts +1 -1
- package/src/testing/generator.ts +17 -12
- package/src/types.ts +28 -2
- package/src/util.ts +2 -1
- package/dist/types/src/components/MenuContext.d.ts +0 -15
- package/dist/types/src/components/MenuContext.d.ts.map +0 -1
- package/src/components/MenuContext.tsx +0 -62
|
@@ -1,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,176 +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: 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
|
|
407
503
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
413
522
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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;
|
|
417
538
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
+
});
|
|
421
551
|
}
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
const prevPropsRef = useRef(menuGraphProps);
|
|
438
|
-
useEffect(() => {
|
|
439
|
-
if (prevPropsRef.current !== menuGraphProps) {
|
|
440
|
-
prevPropsRef.current = menuGraphProps;
|
|
441
|
-
graph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
|
|
442
|
-
}
|
|
583
|
+
newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
|
|
584
|
+
return newGraph;
|
|
443
585
|
}, [
|
|
444
|
-
|
|
586
|
+
registry,
|
|
445
587
|
menuGraphProps
|
|
446
588
|
]);
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
return items;
|
|
589
|
+
const items = useCallback4((group) => {
|
|
590
|
+
return graph.connections(group?.id || Node3.RootId, "child");
|
|
450
591
|
}, [
|
|
451
592
|
graph
|
|
452
593
|
]);
|
|
453
594
|
return {
|
|
454
|
-
|
|
595
|
+
items
|
|
455
596
|
};
|
|
456
597
|
};
|
|
457
598
|
|
|
458
599
|
// src/builder.ts
|
|
459
600
|
import { invariant } from "@dxos/invariant";
|
|
460
|
-
var
|
|
461
|
-
var MenuBuilder = Object.freeze({
|
|
462
|
-
make: () => new MenuBuilderImpl({
|
|
463
|
-
nodes: [],
|
|
464
|
-
edges: []
|
|
465
|
-
}, "root")
|
|
466
|
-
});
|
|
601
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
|
|
467
602
|
var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
468
603
|
_data;
|
|
469
604
|
_rootId;
|
|
605
|
+
_separatorCount = 0;
|
|
470
606
|
constructor(_data, _rootId) {
|
|
471
607
|
this._data = _data;
|
|
472
608
|
this._rootId = _rootId;
|
|
473
609
|
}
|
|
474
610
|
root(props) {
|
|
475
611
|
invariant(this._rootId === "root", "Root group can only be at the top level", {
|
|
476
|
-
F:
|
|
477
|
-
L:
|
|
612
|
+
F: __dxlog_file2,
|
|
613
|
+
L: 61,
|
|
478
614
|
S: this,
|
|
479
615
|
A: [
|
|
480
616
|
"this._rootId === 'root'",
|
|
@@ -482,8 +618,8 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
482
618
|
]
|
|
483
619
|
});
|
|
484
620
|
invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", {
|
|
485
|
-
F:
|
|
486
|
-
L:
|
|
621
|
+
F: __dxlog_file2,
|
|
622
|
+
L: 62,
|
|
487
623
|
S: this,
|
|
488
624
|
A: [
|
|
489
625
|
"this._data.nodes.find((node) => node.id === 'root') === undefined",
|
|
@@ -493,11 +629,15 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
493
629
|
this._data.nodes.push(createMenuItemGroup("root", props));
|
|
494
630
|
return this;
|
|
495
631
|
}
|
|
632
|
+
build() {
|
|
633
|
+
return this._data;
|
|
634
|
+
}
|
|
496
635
|
action(id, props, invoke) {
|
|
497
636
|
this._data.nodes.push(createMenuAction(id, invoke, props));
|
|
498
637
|
this._data.edges.push({
|
|
499
638
|
source: this._rootId,
|
|
500
|
-
target: id
|
|
639
|
+
target: id,
|
|
640
|
+
relation: "child"
|
|
501
641
|
});
|
|
502
642
|
return this;
|
|
503
643
|
}
|
|
@@ -505,31 +645,62 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
|
505
645
|
this._data.nodes.push(createMenuItemGroup(id, props));
|
|
506
646
|
this._data.edges.push({
|
|
507
647
|
source: this._rootId,
|
|
508
|
-
target: id
|
|
648
|
+
target: id,
|
|
649
|
+
relation: "child"
|
|
509
650
|
});
|
|
510
651
|
cb(new _MenuBuilderImpl(this._data, id));
|
|
511
652
|
return this;
|
|
512
653
|
}
|
|
513
|
-
|
|
514
|
-
|
|
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;
|
|
515
682
|
}
|
|
516
683
|
};
|
|
684
|
+
var MenuBuilder = Object.freeze({
|
|
685
|
+
make: () => new MenuBuilderImpl({
|
|
686
|
+
nodes: [],
|
|
687
|
+
edges: []
|
|
688
|
+
}, "root")
|
|
689
|
+
});
|
|
517
690
|
export {
|
|
518
|
-
|
|
691
|
+
Menu,
|
|
519
692
|
MenuBuilder,
|
|
520
|
-
MenuProvider,
|
|
521
693
|
MenuSeparatorType,
|
|
522
|
-
ToolbarMenu,
|
|
523
694
|
createGapSeparator,
|
|
524
695
|
createLineSeparator,
|
|
525
696
|
createMenuAction,
|
|
526
697
|
createMenuItemGroup,
|
|
527
|
-
createMenuScope,
|
|
528
698
|
isMenuGroup,
|
|
529
699
|
isSeparator,
|
|
530
700
|
menuContextDefaults,
|
|
531
701
|
useMenu,
|
|
532
702
|
useMenuActions,
|
|
533
|
-
useMenuItems
|
|
703
|
+
useMenuItems,
|
|
704
|
+
useMenuScoped
|
|
534
705
|
};
|
|
535
706
|
//# sourceMappingURL=index.mjs.map
|