@dxos/react-ui-menu 0.8.4-main.bc674ce → 0.8.4-main.c351d160a8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +373 -209
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +373 -209
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/builder.d.ts +6 -0
- package/dist/types/src/builder.d.ts.map +1 -1
- package/dist/types/src/components/DropdownMenu.d.ts +4 -2
- package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/Menu.d.ts +48 -0
- package/dist/types/src/components/Menu.d.ts.map +1 -0
- package/dist/types/src/components/Menu.stories.d.ts +21 -0
- package/dist/types/src/components/Menu.stories.d.ts.map +1 -0
- package/dist/types/src/components/Menu.test.d.ts +2 -0
- package/dist/types/src/components/Menu.test.d.ts.map +1 -0
- package/dist/types/src/components/ToolbarMenu.d.ts +3 -5
- package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useMenuActions.d.ts +2 -2
- package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +9 -5
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +26 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +2 -0
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -21
- package/src/builder.ts +35 -8
- package/src/components/ActionLabel.tsx +2 -2
- package/src/components/DropdownMenu.tsx +3 -3
- package/src/components/Menu.stories.tsx +265 -0
- package/src/components/Menu.test.tsx +448 -0
- package/src/components/Menu.tsx +347 -0
- package/src/components/ToolbarMenu.stories.tsx +21 -21
- package/src/components/ToolbarMenu.tsx +99 -81
- package/src/components/index.ts +1 -3
- package/src/hooks/useMenuActions.ts +18 -55
- package/src/testing/generator.ts +17 -12
- package/src/types.ts +28 -2
- package/src/util.ts +2 -1
- package/dist/types/src/components/MenuContext.d.ts +0 -15
- package/dist/types/src/components/MenuContext.d.ts.map +0 -1
- package/src/components/MenuContext.tsx +0 -62
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
// src/components/
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
// src/components/Menu.tsx
|
|
2
|
+
import { Atom, RegistryContext, useAtomValue } from "@effect-atom/atom-react";
|
|
3
|
+
import { createContextScope } from "@radix-ui/react-context";
|
|
4
|
+
import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
|
|
5
|
+
import React4, { useCallback as useCallback3, useContext, useMemo } from "react";
|
|
6
|
+
import { log } from "@dxos/log";
|
|
7
|
+
import { Icon as Icon3, DropdownMenu as NaturalDropdownMenu2 } from "@dxos/react-ui";
|
|
5
8
|
|
|
6
9
|
// src/util.ts
|
|
7
10
|
import * as Effect from "effect/Effect";
|
|
@@ -44,7 +47,8 @@ var createGapSeparator = (id = "gap", source = "root") => ({
|
|
|
44
47
|
edges: [
|
|
45
48
|
{
|
|
46
49
|
source,
|
|
47
|
-
target: id
|
|
50
|
+
target: id,
|
|
51
|
+
relation: "child"
|
|
48
52
|
}
|
|
49
53
|
]
|
|
50
54
|
});
|
|
@@ -62,7 +66,8 @@ var createLineSeparator = (id = "line", source = "root") => ({
|
|
|
62
66
|
edges: [
|
|
63
67
|
{
|
|
64
68
|
source,
|
|
65
|
-
target: id
|
|
69
|
+
target: id,
|
|
70
|
+
relation: "child"
|
|
66
71
|
}
|
|
67
72
|
]
|
|
68
73
|
});
|
|
@@ -71,7 +76,7 @@ var createLineSeparator = (id = "line", source = "root") => ({
|
|
|
71
76
|
import React from "react";
|
|
72
77
|
import { keySymbols } from "@dxos/keyboard";
|
|
73
78
|
import { toLocalizedString, useTranslation } from "@dxos/react-ui";
|
|
74
|
-
import {
|
|
79
|
+
import { mx } from "@dxos/ui-theme";
|
|
75
80
|
|
|
76
81
|
// src/translations.ts
|
|
77
82
|
var translationKey = "@dxos/react-ui-menu";
|
|
@@ -90,7 +95,7 @@ var ActionLabel = ({ action }) => {
|
|
|
90
95
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
|
|
91
96
|
className: "grow truncate"
|
|
92
97
|
}, toLocalizedString(action.properties.label, t)), shortcut && /* @__PURE__ */ React.createElement("span", {
|
|
93
|
-
className: mx("shrink-0",
|
|
98
|
+
className: mx("shrink-0", "text-description")
|
|
94
99
|
}, keySymbols(shortcut).join("")));
|
|
95
100
|
};
|
|
96
101
|
var actionLabel = (action, t) => {
|
|
@@ -98,56 +103,39 @@ var actionLabel = (action, t) => {
|
|
|
98
103
|
return `${toLocalizedString(action.properties.label, t)}${shortcut ? ` (${keySymbols(shortcut).join("")})` : ""}`;
|
|
99
104
|
};
|
|
100
105
|
|
|
101
|
-
// src/components/
|
|
102
|
-
import {
|
|
103
|
-
import
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
};
|
|
112
|
-
var useMenuScope = createMenuScope();
|
|
113
|
-
var MenuProvider = ({ children, useGroupItems = menuContextDefaults.useGroupItems, iconSize = menuContextDefaults.iconSize, attendableId, 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
|
+
|
|
111
|
+
// src/types.ts
|
|
112
|
+
import { Node as Node2 } from "@dxos/app-graph";
|
|
113
|
+
var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
|
|
114
|
+
var isSeparator = (node) => node.type === MenuSeparatorType;
|
|
115
|
+
var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
|
|
131
116
|
|
|
132
117
|
// src/components/DropdownMenu.tsx
|
|
118
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
119
|
+
import React2, { useCallback } from "react";
|
|
120
|
+
import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
|
|
133
121
|
var DropdownMenuItem = ({ item, onClick, __menuScope }) => {
|
|
134
122
|
const action = item;
|
|
135
123
|
const handleClick = useCallback((event) => onClick(action, event), [
|
|
136
124
|
action,
|
|
137
125
|
onClick
|
|
138
126
|
]);
|
|
139
|
-
const { iconSize } =
|
|
140
|
-
return /* @__PURE__ */
|
|
127
|
+
const { iconSize } = useMenuScoped("DropdownMenuItem", __menuScope);
|
|
128
|
+
return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Item, {
|
|
141
129
|
onClick: handleClick,
|
|
142
130
|
classNames: "gap-2",
|
|
143
131
|
disabled: action.properties?.disabled,
|
|
144
132
|
...action.properties?.testId && {
|
|
145
133
|
"data-testid": action.properties.testId
|
|
146
134
|
}
|
|
147
|
-
}, action.properties?.icon && /* @__PURE__ */
|
|
135
|
+
}, action.properties?.icon && /* @__PURE__ */ React2.createElement(Icon, {
|
|
148
136
|
icon: action.properties.icon,
|
|
149
137
|
size: iconSize
|
|
150
|
-
}), /* @__PURE__ */
|
|
138
|
+
}), /* @__PURE__ */ React2.createElement(ActionLabel, {
|
|
151
139
|
action
|
|
152
140
|
}));
|
|
153
141
|
};
|
|
@@ -157,7 +145,7 @@ var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenCha
|
|
|
157
145
|
defaultProp: defaultOpen,
|
|
158
146
|
onChange: onOpenChange
|
|
159
147
|
});
|
|
160
|
-
const { onAction } =
|
|
148
|
+
const { onAction } = useMenuScoped("DropdownMenuRoot", __menuScope);
|
|
161
149
|
const handleActionClick = useCallback((action, event) => {
|
|
162
150
|
if (action.properties?.disabled) {
|
|
163
151
|
return;
|
|
@@ -181,15 +169,15 @@ var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenCha
|
|
|
181
169
|
onAction
|
|
182
170
|
]);
|
|
183
171
|
const items = useMenuItems(group, propsItems);
|
|
184
|
-
return /* @__PURE__ */
|
|
172
|
+
return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Root, {
|
|
185
173
|
open: optionsMenuOpen,
|
|
186
174
|
onOpenChange: setOptionsMenuOpen,
|
|
187
175
|
...naturalProps
|
|
188
|
-
}, children, /* @__PURE__ */
|
|
176
|
+
}, children, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Portal, null, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Content, null, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Viewport, null, items?.map((item) => /* @__PURE__ */ React2.createElement(DropdownMenuItem, {
|
|
189
177
|
key: item.id,
|
|
190
178
|
item,
|
|
191
179
|
onClick: handleActionClick
|
|
192
|
-
}))), /* @__PURE__ */
|
|
180
|
+
}))), /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Arrow, null))));
|
|
193
181
|
};
|
|
194
182
|
var DropdownMenu = {
|
|
195
183
|
Root: DropdownMenuRoot,
|
|
@@ -198,33 +186,11 @@ var DropdownMenu = {
|
|
|
198
186
|
};
|
|
199
187
|
|
|
200
188
|
// 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);
|
|
189
|
+
var ActionToolbarItem = ({ __menuScope, action }) => {
|
|
190
|
+
const { iconSize, onAction } = useMenuScoped("ActionToolbarItem", __menuScope);
|
|
215
191
|
const { t } = useTranslation2(translationKey);
|
|
216
192
|
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
|
-
};
|
|
193
|
+
const buttonVariant = action.properties.variant === "primary" ? "primary" : "ghost";
|
|
228
194
|
const handleClick = useCallback2(() => {
|
|
229
195
|
if (onAction) {
|
|
230
196
|
onAction(action, {});
|
|
@@ -235,22 +201,36 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
|
235
201
|
action,
|
|
236
202
|
onAction
|
|
237
203
|
]);
|
|
238
|
-
|
|
239
|
-
|
|
204
|
+
const commonProps = {
|
|
205
|
+
variant: buttonVariant,
|
|
240
206
|
disabled,
|
|
207
|
+
classNames,
|
|
241
208
|
onClick: handleClick,
|
|
242
|
-
variant: "ghost",
|
|
243
209
|
...testId && {
|
|
244
210
|
"data-testid": testId
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
if (hidden) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.IconButton, {
|
|
217
|
+
key: action.id,
|
|
218
|
+
...commonProps,
|
|
219
|
+
icon,
|
|
220
|
+
size: iconSize,
|
|
221
|
+
iconOnly,
|
|
222
|
+
label: actionLabel(action, t)
|
|
223
|
+
}) : /* @__PURE__ */ React3.createElement(NaturalToolbar.Button, {
|
|
224
|
+
key: action.id,
|
|
225
|
+
...commonProps
|
|
226
|
+
}, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
227
|
+
action
|
|
228
|
+
}));
|
|
249
229
|
};
|
|
250
|
-
var DropdownMenuToolbarItem = ({ group, items: propsItems
|
|
230
|
+
var DropdownMenuToolbarItem = ({ __menuScope, group, items: propsItems }) => {
|
|
251
231
|
const { iconOnly, disabled, testId } = group.properties;
|
|
252
232
|
const { t } = useTranslation2(translationKey);
|
|
253
|
-
const { iconSize } =
|
|
233
|
+
const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
|
|
254
234
|
const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
|
|
255
235
|
const activeItem = items?.find((item) => !!item.properties.checked);
|
|
256
236
|
const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
|
|
@@ -264,20 +244,20 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
264
244
|
label: actionLabel(labelAction, t),
|
|
265
245
|
caretDown: true
|
|
266
246
|
} : {
|
|
267
|
-
children: /* @__PURE__ */
|
|
247
|
+
children: /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
268
248
|
action: labelAction
|
|
269
|
-
}), /* @__PURE__ */
|
|
249
|
+
}), /* @__PURE__ */ React3.createElement(Icon2, {
|
|
270
250
|
size: 3,
|
|
271
251
|
icon: "ph--caret-down--bold",
|
|
272
|
-
classNames: "
|
|
252
|
+
classNames: "ms-1"
|
|
273
253
|
}))
|
|
274
254
|
};
|
|
275
|
-
return /* @__PURE__ */
|
|
255
|
+
return /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
|
|
276
256
|
group,
|
|
277
257
|
items
|
|
278
|
-
}, /* @__PURE__ */
|
|
258
|
+
}, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
|
|
279
259
|
asChild: true
|
|
280
|
-
}, /* @__PURE__ */
|
|
260
|
+
}, /* @__PURE__ */ React3.createElement(Root, {
|
|
281
261
|
variant: "ghost",
|
|
282
262
|
disabled,
|
|
283
263
|
...rootProps,
|
|
@@ -286,8 +266,8 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
|
286
266
|
}
|
|
287
267
|
})));
|
|
288
268
|
};
|
|
289
|
-
var ToggleGroupItem = ({ group, action
|
|
290
|
-
const { iconSize, onAction } =
|
|
269
|
+
var ToggleGroupItem = ({ __menuScope, group, action }) => {
|
|
270
|
+
const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
|
|
291
271
|
const { t } = useTranslation2(translationKey);
|
|
292
272
|
const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
|
|
293
273
|
const handleClick = useCallback2(() => {
|
|
@@ -305,176 +285,332 @@ var ToggleGroupItem = ({ group, action, __menuScope }) => {
|
|
|
305
285
|
group,
|
|
306
286
|
onAction
|
|
307
287
|
]);
|
|
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,
|
|
288
|
+
const commonProps = {
|
|
321
289
|
value: action.id,
|
|
322
290
|
disabled,
|
|
323
|
-
onClick: handleClick,
|
|
324
291
|
variant: "ghost",
|
|
292
|
+
classNames,
|
|
293
|
+
onClick: handleClick,
|
|
325
294
|
...testId && {
|
|
326
295
|
"data-testid": testId
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
return hidden ? null : icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupIconItem, {
|
|
299
|
+
...commonProps,
|
|
300
|
+
icon,
|
|
301
|
+
size: iconSize,
|
|
302
|
+
iconOnly,
|
|
303
|
+
label: actionLabel(action, t)
|
|
304
|
+
}) : /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupItem, commonProps, /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
305
|
+
action
|
|
306
|
+
}));
|
|
331
307
|
};
|
|
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
|
-
);
|
|
308
|
+
var ToggleGroupToolbarItem = ({ __menuScope, group, items: itemsProp }) => {
|
|
309
|
+
const items = useMenuItems(group, itemsProp, "ToggleGroupToolbarItem", __menuScope);
|
|
310
|
+
const { selectCardinality } = group.properties;
|
|
311
|
+
const children = items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
|
|
312
|
+
key: action.id,
|
|
313
|
+
group,
|
|
314
|
+
action
|
|
315
|
+
}));
|
|
316
|
+
if (selectCardinality === "multiple") {
|
|
317
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
|
|
318
|
+
type: "multiple",
|
|
319
|
+
value: group.properties.value
|
|
320
|
+
}, children);
|
|
321
|
+
} else {
|
|
322
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
|
|
323
|
+
type: "single",
|
|
324
|
+
value: group.properties.value
|
|
325
|
+
}, children);
|
|
326
|
+
}
|
|
352
327
|
};
|
|
353
|
-
var ToolbarMenu = ({ __menuScope, classNames,
|
|
328
|
+
var ToolbarMenu = ({ __menuScope, classNames, ...props }) => {
|
|
354
329
|
const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
|
|
355
|
-
const { attendableId } =
|
|
330
|
+
const { attendableId, alwaysActive } = useMenuScoped("ToolbarMenu", __menuScope);
|
|
356
331
|
const { hasAttention } = useAttention(attendableId);
|
|
357
|
-
|
|
358
|
-
const innerRootProps = textBlockWidthProp ? {
|
|
359
|
-
role: "none",
|
|
360
|
-
className: mx2(textBlockWidth, toolbarLayout, "bs-full")
|
|
361
|
-
} : {};
|
|
362
|
-
return /* @__PURE__ */ React4.createElement(NaturalToolbar.Root, {
|
|
332
|
+
return /* @__PURE__ */ React3.createElement(NaturalToolbar.Root, {
|
|
363
333
|
...props,
|
|
364
334
|
classNames: [
|
|
365
|
-
attendableId
|
|
335
|
+
attendableId,
|
|
366
336
|
classNames
|
|
367
337
|
],
|
|
368
|
-
|
|
369
|
-
},
|
|
370
|
-
key: item.id
|
|
338
|
+
disabled: !alwaysActive && !hasAttention
|
|
339
|
+
}, items?.map((item) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
|
|
340
|
+
key: item.id,
|
|
341
|
+
__menuScope,
|
|
371
342
|
item
|
|
372
|
-
})))
|
|
343
|
+
})));
|
|
373
344
|
};
|
|
374
|
-
var ToolbarMenuItem = ({ item }) => {
|
|
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__ */ React4.createContext({
|
|
389
|
+
closeMenu: () => {
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
var DEFAULT_PRIORITY = 100;
|
|
393
|
+
var sortMenuItems = (items) => [
|
|
394
|
+
...items
|
|
395
|
+
].sort((a, b) => {
|
|
396
|
+
if (a.priority !== b.priority) {
|
|
397
|
+
return a.priority - b.priority;
|
|
398
|
+
}
|
|
399
|
+
return a.id.localeCompare(b.id);
|
|
400
|
+
});
|
|
401
|
+
var MenuProvider = ({ children, items = menuContextDefaults.items, iconSize = menuContextDefaults.iconSize, attendableId, alwaysActive, onAction }) => {
|
|
402
|
+
const { scope } = useMenuScope(void 0);
|
|
403
|
+
const registry = useContext(RegistryContext);
|
|
404
|
+
const menuItemsAtom = useMemo(() => Atom.make(/* @__PURE__ */ new Map()).pipe(Atom.keepAlive), []);
|
|
405
|
+
const addMenuItems = useCallback3((props) => {
|
|
406
|
+
const priority = props.priority ?? DEFAULT_PRIORITY;
|
|
407
|
+
const prev = registry.get(menuItemsAtom);
|
|
408
|
+
const next = new Map(prev);
|
|
409
|
+
next.set(props.id, {
|
|
410
|
+
...props,
|
|
411
|
+
priority
|
|
412
|
+
});
|
|
413
|
+
registry.set(menuItemsAtom, next);
|
|
414
|
+
}, [
|
|
415
|
+
registry,
|
|
416
|
+
menuItemsAtom
|
|
417
|
+
]);
|
|
418
|
+
const removeMenuItems = useCallback3((id) => {
|
|
419
|
+
const prev = registry.get(menuItemsAtom);
|
|
420
|
+
const next = new Map(prev);
|
|
421
|
+
next.delete(id);
|
|
422
|
+
registry.set(menuItemsAtom, next);
|
|
423
|
+
}, [
|
|
424
|
+
registry,
|
|
425
|
+
menuItemsAtom
|
|
426
|
+
]);
|
|
427
|
+
return /* @__PURE__ */ React4.createElement(MenuContextProvider, {
|
|
428
|
+
items,
|
|
429
|
+
iconSize,
|
|
430
|
+
attendableId,
|
|
431
|
+
alwaysActive,
|
|
432
|
+
menuItemsAtom,
|
|
433
|
+
addMenuItems,
|
|
434
|
+
removeMenuItems,
|
|
435
|
+
onAction,
|
|
436
|
+
scope
|
|
437
|
+
}, children);
|
|
438
|
+
};
|
|
439
|
+
var resolveItems = (baseItems, group, entries) => {
|
|
440
|
+
const applicable = [
|
|
441
|
+
...entries.values()
|
|
442
|
+
].filter((entry) => !entry.groupFilter || entry.groupFilter(group));
|
|
443
|
+
if (applicable.length === 0) {
|
|
444
|
+
return baseItems;
|
|
445
|
+
}
|
|
446
|
+
const sorted = sortMenuItems(applicable);
|
|
447
|
+
const replacements = sorted.filter((entry) => entry.mode === "replacement");
|
|
448
|
+
if (replacements.length > 0) {
|
|
449
|
+
if (replacements.length > 1) {
|
|
450
|
+
log.warn("multiple replacement entries found", {
|
|
451
|
+
ids: replacements.map((r) => r.id).join(", "),
|
|
452
|
+
using: replacements[0].id
|
|
453
|
+
}, {
|
|
454
|
+
F: __dxlog_file,
|
|
455
|
+
L: 152,
|
|
456
|
+
S: void 0,
|
|
457
|
+
C: (f, a) => f(...a)
|
|
458
|
+
});
|
|
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
|
|
503
|
+
}
|
|
504
|
+
}, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Root, {
|
|
505
|
+
open: menuOpen,
|
|
506
|
+
onOpenChange: setMenuOpen
|
|
507
|
+
}, children)));
|
|
508
|
+
};
|
|
509
|
+
var MenuContentItem = ({ item, onClick, __menuScope }) => {
|
|
510
|
+
const action = item;
|
|
511
|
+
const handleClick = useCallback3((event) => onClick(action, event), [
|
|
512
|
+
action,
|
|
513
|
+
onClick
|
|
514
|
+
]);
|
|
515
|
+
const { iconSize } = useMenuScoped("MenuContentItem", __menuScope);
|
|
516
|
+
return /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Item, {
|
|
517
|
+
onClick: handleClick,
|
|
518
|
+
classNames: "gap-2",
|
|
519
|
+
disabled: action.properties?.disabled,
|
|
520
|
+
...action.properties?.testId && {
|
|
521
|
+
"data-testid": action.properties.testId
|
|
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: 41,
|
|
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: 42,
|
|
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,55 @@ 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(subgraph) {
|
|
655
|
+
this._data.nodes.push(...subgraph.nodes);
|
|
656
|
+
this._data.edges.push(...subgraph.edges);
|
|
657
|
+
return this;
|
|
658
|
+
}
|
|
659
|
+
separator(id, variant = "gap") {
|
|
660
|
+
id ??= `separator-${++this._separatorCount}`;
|
|
661
|
+
this._data.nodes.push({
|
|
662
|
+
id,
|
|
663
|
+
type: MenuSeparatorType,
|
|
664
|
+
properties: {
|
|
665
|
+
variant
|
|
666
|
+
},
|
|
667
|
+
data: void 0
|
|
668
|
+
});
|
|
669
|
+
this._data.edges.push({
|
|
670
|
+
source: this._rootId,
|
|
671
|
+
target: id,
|
|
672
|
+
relation: "child"
|
|
673
|
+
});
|
|
674
|
+
return this;
|
|
515
675
|
}
|
|
516
676
|
};
|
|
677
|
+
var MenuBuilder = Object.freeze({
|
|
678
|
+
make: () => new MenuBuilderImpl({
|
|
679
|
+
nodes: [],
|
|
680
|
+
edges: []
|
|
681
|
+
}, "root")
|
|
682
|
+
});
|
|
517
683
|
export {
|
|
518
|
-
|
|
684
|
+
Menu,
|
|
519
685
|
MenuBuilder,
|
|
520
|
-
MenuProvider,
|
|
521
686
|
MenuSeparatorType,
|
|
522
|
-
ToolbarMenu,
|
|
523
687
|
createGapSeparator,
|
|
524
688
|
createLineSeparator,
|
|
525
689
|
createMenuAction,
|
|
526
690
|
createMenuItemGroup,
|
|
527
|
-
createMenuScope,
|
|
528
691
|
isMenuGroup,
|
|
529
692
|
isSeparator,
|
|
530
693
|
menuContextDefaults,
|
|
531
694
|
useMenu,
|
|
532
695
|
useMenuActions,
|
|
533
|
-
useMenuItems
|
|
696
|
+
useMenuItems,
|
|
697
|
+
useMenuScoped
|
|
534
698
|
};
|
|
535
699
|
//# sourceMappingURL=index.mjs.map
|