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