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