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