@dxos/react-ui-menu 0.8.3-staging.0fa589b → 0.8.4-main.1068cf700f
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 +364 -339
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +364 -339
- 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 +14 -0
- package/dist/types/src/builder.d.ts.map +1 -0
- package/dist/types/src/components/ActionLabel.d.ts +3 -3
- package/dist/types/src/components/ActionLabel.d.ts.map +1 -1
- package/dist/types/src/components/DropdownMenu.d.ts +1 -1
- package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
- package/dist/types/src/components/MenuContext.d.ts +3 -1
- package/dist/types/src/components/MenuContext.d.ts.map +1 -1
- package/dist/types/src/components/ToolbarMenu.d.ts +3 -2
- package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
- package/dist/types/src/components/ToolbarMenu.stories.d.ts +20 -0
- package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -0
- package/dist/types/src/hooks/useMenuActions.d.ts +6 -5
- 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 +13 -11
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +13 -24
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +12 -5
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +33 -28
- package/src/builder.ts +62 -0
- package/src/components/ActionLabel.tsx +6 -7
- package/src/components/DropdownMenu.tsx +16 -6
- package/src/components/MenuContext.tsx +18 -4
- package/src/components/ToolbarMenu.stories.tsx +81 -0
- package/src/components/ToolbarMenu.tsx +70 -35
- package/src/hooks/useMenuActions.ts +23 -45
- package/src/index.ts +1 -2
- package/src/testing/generator.ts +63 -58
- package/src/translations.ts +5 -3
- package/src/types.ts +14 -26
- package/src/util.ts +32 -14
- package/dist/lib/node/index.cjs +0 -506
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/types/src/components/Menus.stories.d.ts +0 -25
- package/dist/types/src/components/Menus.stories.d.ts.map +0 -1
- package/src/components/Menus.stories.tsx +0 -67
|
@@ -1,46 +1,34 @@
|
|
|
1
|
-
//
|
|
2
|
-
import { rxFromSignal } from "@dxos/app-graph";
|
|
3
|
-
|
|
4
|
-
// packages/ui/react-ui-menu/src/components/DropdownMenu.tsx
|
|
5
|
-
import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
|
|
1
|
+
// src/components/DropdownMenu.tsx
|
|
6
2
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
7
3
|
import React3, { useCallback } from "react";
|
|
8
|
-
import { DropdownMenu as NaturalDropdownMenu
|
|
9
|
-
|
|
10
|
-
// packages/ui/react-ui-menu/src/components/ActionLabel.tsx
|
|
11
|
-
import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
|
|
12
|
-
import React from "react";
|
|
13
|
-
import { keySymbols } from "@dxos/keyboard";
|
|
14
|
-
import { toLocalizedString, useTranslation } from "@dxos/react-ui";
|
|
15
|
-
import { mx, descriptionText } from "@dxos/react-ui-theme";
|
|
16
|
-
|
|
17
|
-
// packages/ui/react-ui-menu/src/translations.ts
|
|
18
|
-
var translationKey = "menus";
|
|
19
|
-
var translations_default = [
|
|
20
|
-
{
|
|
21
|
-
"en-US": {
|
|
22
|
-
[translationKey]: {}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
];
|
|
4
|
+
import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
|
|
26
5
|
|
|
27
|
-
//
|
|
28
|
-
import
|
|
6
|
+
// src/util.ts
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import { Node } from "@dxos/app-graph";
|
|
9
|
+
import { runAndForwardErrors } from "@dxos/effect";
|
|
29
10
|
import { getHostPlatform } from "@dxos/util";
|
|
11
|
+
var executeMenuAction = async (action, params = {}) => {
|
|
12
|
+
let effect = action.data(params);
|
|
13
|
+
if (action._actionContext) {
|
|
14
|
+
effect = effect.pipe(Effect.provide(action._actionContext));
|
|
15
|
+
}
|
|
16
|
+
await runAndForwardErrors(effect);
|
|
17
|
+
};
|
|
30
18
|
var getShortcut = (action) => {
|
|
31
19
|
return typeof action.properties?.keyBinding === "string" ? action.properties.keyBinding : action.properties?.keyBinding?.[getHostPlatform()];
|
|
32
20
|
};
|
|
33
21
|
var createMenuAction = (id, invoke, properties) => ({
|
|
34
22
|
id,
|
|
35
|
-
type:
|
|
23
|
+
type: Node.ActionType,
|
|
36
24
|
properties,
|
|
37
|
-
data: invoke
|
|
25
|
+
data: () => Effect.sync(invoke)
|
|
38
26
|
});
|
|
39
27
|
var createMenuItemGroup = (id, properties) => ({
|
|
40
28
|
id,
|
|
41
|
-
type:
|
|
29
|
+
type: Node.ActionGroupType,
|
|
42
30
|
properties,
|
|
43
|
-
data: actionGroupSymbol
|
|
31
|
+
data: Node.actionGroupSymbol
|
|
44
32
|
});
|
|
45
33
|
var createGapSeparator = (id = "gap", source = "root") => ({
|
|
46
34
|
nodes: [
|
|
@@ -79,51 +67,59 @@ var createLineSeparator = (id = "line", source = "root") => ({
|
|
|
79
67
|
]
|
|
80
68
|
});
|
|
81
69
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
70
|
+
// src/components/ActionLabel.tsx
|
|
71
|
+
import React from "react";
|
|
72
|
+
import { keySymbols } from "@dxos/keyboard";
|
|
73
|
+
import { toLocalizedString, useTranslation } from "@dxos/react-ui";
|
|
74
|
+
import { descriptionText, mx } from "@dxos/ui-theme";
|
|
75
|
+
|
|
76
|
+
// src/translations.ts
|
|
77
|
+
var translationKey = "@dxos/react-ui-menu";
|
|
78
|
+
var translations = [
|
|
79
|
+
{
|
|
80
|
+
"en-US": {
|
|
81
|
+
[translationKey]: {}
|
|
82
|
+
}
|
|
95
83
|
}
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
// src/components/ActionLabel.tsx
|
|
87
|
+
var ActionLabel = ({ action }) => {
|
|
88
|
+
const { t } = useTranslation(translationKey);
|
|
89
|
+
const shortcut = getShortcut(action);
|
|
90
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
|
|
91
|
+
className: "grow truncate"
|
|
92
|
+
}, toLocalizedString(action.properties.label, t)), shortcut && /* @__PURE__ */ React.createElement("span", {
|
|
93
|
+
className: mx("shrink-0", descriptionText)
|
|
94
|
+
}, keySymbols(shortcut).join("")));
|
|
96
95
|
};
|
|
97
96
|
var actionLabel = (action, t) => {
|
|
98
97
|
const shortcut = getShortcut(action);
|
|
99
98
|
return `${toLocalizedString(action.properties.label, t)}${shortcut ? ` (${keySymbols(shortcut).join("")})` : ""}`;
|
|
100
99
|
};
|
|
101
100
|
|
|
102
|
-
//
|
|
103
|
-
import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
|
|
101
|
+
// src/components/MenuContext.tsx
|
|
104
102
|
import { createContextScope } from "@radix-ui/react-context";
|
|
105
103
|
import React2, { useMemo } from "react";
|
|
106
|
-
var MENU_NAME = "
|
|
104
|
+
var MENU_NAME = "Menu";
|
|
107
105
|
var [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);
|
|
108
106
|
var [MenuContextProvider, useMenu] = createMenuContext(MENU_NAME);
|
|
109
107
|
var menuContextDefaults = {
|
|
110
108
|
iconSize: 5,
|
|
111
|
-
useGroupItems: () => null
|
|
109
|
+
useGroupItems: () => null,
|
|
110
|
+
onAction: void 0
|
|
112
111
|
};
|
|
113
112
|
var useMenuScope = createMenuScope();
|
|
114
|
-
var MenuProvider = ({ children, useGroupItems = menuContextDefaults.useGroupItems, iconSize = menuContextDefaults.iconSize, attendableId }) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
} finally {
|
|
125
|
-
_effect.f();
|
|
126
|
-
}
|
|
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);
|
|
127
123
|
};
|
|
128
124
|
var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
|
|
129
125
|
const { useGroupItems } = useMenu(consumerName, __menuScope);
|
|
@@ -134,68 +130,67 @@ var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __m
|
|
|
134
130
|
]);
|
|
135
131
|
};
|
|
136
132
|
|
|
137
|
-
//
|
|
133
|
+
// src/components/DropdownMenu.tsx
|
|
138
134
|
var DropdownMenuItem = ({ item, onClick, __menuScope }) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
action
|
|
159
|
-
}));
|
|
160
|
-
} finally {
|
|
161
|
-
_effect.f();
|
|
162
|
-
}
|
|
135
|
+
const action = item;
|
|
136
|
+
const handleClick = useCallback((event) => onClick(action, event), [
|
|
137
|
+
action,
|
|
138
|
+
onClick
|
|
139
|
+
]);
|
|
140
|
+
const { iconSize } = useMenu("DropdownMenuItem", __menuScope);
|
|
141
|
+
return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Item, {
|
|
142
|
+
onClick: handleClick,
|
|
143
|
+
classNames: "gap-2",
|
|
144
|
+
disabled: action.properties?.disabled,
|
|
145
|
+
...action.properties?.testId && {
|
|
146
|
+
"data-testid": action.properties.testId
|
|
147
|
+
}
|
|
148
|
+
}, action.properties?.icon && /* @__PURE__ */ React3.createElement(Icon, {
|
|
149
|
+
icon: action.properties.icon,
|
|
150
|
+
size: iconSize
|
|
151
|
+
}), /* @__PURE__ */ React3.createElement(ActionLabel, {
|
|
152
|
+
action
|
|
153
|
+
}));
|
|
163
154
|
};
|
|
164
155
|
var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenChange, caller, children, __menuScope, ...naturalProps }) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
156
|
+
const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({
|
|
157
|
+
prop: open,
|
|
158
|
+
defaultProp: defaultOpen,
|
|
159
|
+
onChange: onOpenChange
|
|
160
|
+
});
|
|
161
|
+
const { onAction } = useMenu("DropdownMenuRoot", __menuScope);
|
|
162
|
+
const handleActionClick = useCallback((action, event) => {
|
|
163
|
+
if (action.properties?.disabled) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
event.stopPropagation();
|
|
167
|
+
setOptionsMenuOpen(false);
|
|
168
|
+
if (onAction) {
|
|
169
|
+
onAction(action, {
|
|
179
170
|
parent: group,
|
|
180
171
|
caller
|
|
181
172
|
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
173
|
+
} else {
|
|
174
|
+
void executeMenuAction(action, {
|
|
175
|
+
parent: group,
|
|
176
|
+
caller
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}, [
|
|
180
|
+
group,
|
|
181
|
+
caller,
|
|
182
|
+
onAction
|
|
183
|
+
]);
|
|
184
|
+
const items = useMenuItems(group, propsItems);
|
|
185
|
+
return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Root, {
|
|
186
|
+
open: optionsMenuOpen,
|
|
187
|
+
onOpenChange: setOptionsMenuOpen,
|
|
188
|
+
...naturalProps
|
|
189
|
+
}, children, /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Portal, null, /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Content, null, /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Viewport, null, items?.map((item) => /* @__PURE__ */ React3.createElement(DropdownMenuItem, {
|
|
190
|
+
key: item.id,
|
|
191
|
+
item,
|
|
192
|
+
onClick: handleActionClick
|
|
193
|
+
}))), /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Arrow, null))));
|
|
199
194
|
};
|
|
200
195
|
var DropdownMenu = {
|
|
201
196
|
Root: DropdownMenuRoot,
|
|
@@ -203,254 +198,224 @@ var DropdownMenu = {
|
|
|
203
198
|
VirtualTrigger: NaturalDropdownMenu.VirtualTrigger
|
|
204
199
|
};
|
|
205
200
|
|
|
206
|
-
//
|
|
207
|
-
import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
|
|
201
|
+
// src/components/ToolbarMenu.tsx
|
|
208
202
|
import React4, { Fragment, useCallback as useCallback2 } from "react";
|
|
209
203
|
import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
210
204
|
import { useAttention } from "@dxos/react-ui-attention";
|
|
211
|
-
import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/
|
|
205
|
+
import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/ui-theme";
|
|
212
206
|
|
|
213
|
-
//
|
|
214
|
-
import {
|
|
207
|
+
// src/types.ts
|
|
208
|
+
import { Node as Node2 } from "@dxos/app-graph";
|
|
215
209
|
var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
|
|
216
210
|
var isSeparator = (node) => node.type === MenuSeparatorType;
|
|
217
|
-
var isMenuGroup = (node) => node.type ===
|
|
211
|
+
var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
|
|
218
212
|
|
|
219
|
-
//
|
|
213
|
+
// src/components/ToolbarMenu.tsx
|
|
220
214
|
var ActionToolbarItem = ({ action, __menuScope }) => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
215
|
+
const { iconSize, onAction } = useMenu("ActionToolbarItem", __menuScope);
|
|
216
|
+
const { t } = useTranslation2(translationKey);
|
|
217
|
+
const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
|
|
218
|
+
const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
|
|
219
|
+
const rootProps = icon ? {
|
|
220
|
+
icon,
|
|
221
|
+
size: iconSize,
|
|
222
|
+
iconOnly,
|
|
223
|
+
label: actionLabel(action, t)
|
|
224
|
+
} : {
|
|
225
|
+
children: /* @__PURE__ */ React4.createElement(ActionLabel, {
|
|
226
226
|
action
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
});
|
|
251
|
-
} finally {
|
|
252
|
-
_effect.f();
|
|
253
|
-
}
|
|
227
|
+
})
|
|
228
|
+
};
|
|
229
|
+
const handleClick = useCallback2(() => {
|
|
230
|
+
if (onAction) {
|
|
231
|
+
onAction(action, {});
|
|
232
|
+
} else {
|
|
233
|
+
void executeMenuAction(action);
|
|
234
|
+
}
|
|
235
|
+
}, [
|
|
236
|
+
action,
|
|
237
|
+
onAction
|
|
238
|
+
]);
|
|
239
|
+
return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
|
|
240
|
+
key: action.id,
|
|
241
|
+
disabled,
|
|
242
|
+
onClick: handleClick,
|
|
243
|
+
variant: "ghost",
|
|
244
|
+
...testId && {
|
|
245
|
+
"data-testid": testId
|
|
246
|
+
},
|
|
247
|
+
...rootProps,
|
|
248
|
+
classNames
|
|
249
|
+
});
|
|
254
250
|
};
|
|
255
251
|
var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
})));
|
|
295
|
-
} finally {
|
|
296
|
-
_effect.f();
|
|
297
|
-
}
|
|
252
|
+
const { iconOnly, disabled, testId } = group.properties;
|
|
253
|
+
const { t } = useTranslation2(translationKey);
|
|
254
|
+
const { iconSize } = useMenu("DropdownMenuToolbarItem", __menuScope);
|
|
255
|
+
const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
|
|
256
|
+
const activeItem = items?.find((item) => !!item.properties.checked);
|
|
257
|
+
const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
|
|
258
|
+
activeItem?.properties.icon || group.properties.icon;
|
|
259
|
+
const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
|
|
260
|
+
const labelAction = group.properties.applyActive && activeItem ? activeItem : group;
|
|
261
|
+
const rootProps = icon ? {
|
|
262
|
+
icon,
|
|
263
|
+
size: iconSize,
|
|
264
|
+
iconOnly,
|
|
265
|
+
label: actionLabel(labelAction, t),
|
|
266
|
+
caretDown: true
|
|
267
|
+
} : {
|
|
268
|
+
children: /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(ActionLabel, {
|
|
269
|
+
action: labelAction
|
|
270
|
+
}), /* @__PURE__ */ React4.createElement(Icon2, {
|
|
271
|
+
size: 3,
|
|
272
|
+
icon: "ph--caret-down--bold",
|
|
273
|
+
classNames: "mis-1"
|
|
274
|
+
}))
|
|
275
|
+
};
|
|
276
|
+
return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
|
|
277
|
+
group,
|
|
278
|
+
items
|
|
279
|
+
}, /* @__PURE__ */ React4.createElement(DropdownMenu.Trigger, {
|
|
280
|
+
asChild: true
|
|
281
|
+
}, /* @__PURE__ */ React4.createElement(Root, {
|
|
282
|
+
variant: "ghost",
|
|
283
|
+
disabled,
|
|
284
|
+
...rootProps,
|
|
285
|
+
...testId && {
|
|
286
|
+
"data-testid": testId
|
|
287
|
+
}
|
|
288
|
+
})));
|
|
298
289
|
};
|
|
299
290
|
var ToggleGroupItem = ({ group, action, __menuScope }) => {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
291
|
+
const { iconSize, onAction } = useMenu("ToggleGroupItem", __menuScope);
|
|
292
|
+
const { t } = useTranslation2(translationKey);
|
|
293
|
+
const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
|
|
294
|
+
const handleClick = useCallback2(() => {
|
|
295
|
+
if (onAction) {
|
|
296
|
+
onAction(action, {
|
|
297
|
+
parent: group
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
void executeMenuAction(action, {
|
|
301
|
+
parent: group
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}, [
|
|
305
|
+
action,
|
|
306
|
+
group,
|
|
307
|
+
onAction
|
|
308
|
+
]);
|
|
309
|
+
const Root = icon ? NaturalToolbar.ToggleGroupIconItem : NaturalToolbar.ToggleGroupItem;
|
|
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,
|
|
322
|
+
value: action.id,
|
|
323
|
+
disabled,
|
|
324
|
+
onClick: handleClick,
|
|
325
|
+
variant: "ghost",
|
|
326
|
+
...testId && {
|
|
327
|
+
"data-testid": testId
|
|
328
|
+
},
|
|
329
|
+
...rootProps,
|
|
330
|
+
classNames
|
|
331
|
+
});
|
|
337
332
|
};
|
|
338
333
|
var ToggleGroupToolbarItem = ({ group, items: propsItems, __menuScope }) => {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
)
|
|
359
|
-
);
|
|
360
|
-
} finally {
|
|
361
|
-
_effect.f();
|
|
362
|
-
}
|
|
334
|
+
const items = useMenuItems(group, propsItems, "ToggleGroupToolbarItem", __menuScope);
|
|
335
|
+
const { selectCardinality, value } = group.properties;
|
|
336
|
+
return (
|
|
337
|
+
// TODO(thure): Fix.
|
|
338
|
+
// @ts-ignore
|
|
339
|
+
/* @__PURE__ */ React4.createElement(
|
|
340
|
+
NaturalToolbar.ToggleGroup,
|
|
341
|
+
{
|
|
342
|
+
type: selectCardinality,
|
|
343
|
+
value
|
|
344
|
+
},
|
|
345
|
+
// TODO(thure): Handle other menu item types.
|
|
346
|
+
items.map((action) => /* @__PURE__ */ React4.createElement(ToggleGroupItem, {
|
|
347
|
+
key: action.id,
|
|
348
|
+
group,
|
|
349
|
+
action
|
|
350
|
+
}))
|
|
351
|
+
)
|
|
352
|
+
);
|
|
363
353
|
};
|
|
364
|
-
var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
354
|
+
var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthProp, ...props }) => {
|
|
355
|
+
const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
|
|
356
|
+
const { attendableId, alwaysActive } = useMenu("ToolbarMenu", __menuScope);
|
|
357
|
+
const { hasAttention } = useAttention(attendableId);
|
|
358
|
+
const InnerRoot = textBlockWidthProp ? "div" : Fragment;
|
|
359
|
+
const innerRootProps = textBlockWidthProp ? {
|
|
360
|
+
role: "none",
|
|
361
|
+
className: mx2(textBlockWidth, toolbarLayout, "bs-full")
|
|
362
|
+
} : {};
|
|
363
|
+
return /* @__PURE__ */ React4.createElement(NaturalToolbar.Root, {
|
|
364
|
+
...props,
|
|
365
|
+
classNames: [
|
|
366
|
+
attendableId,
|
|
367
|
+
classNames
|
|
368
|
+
],
|
|
369
|
+
disabled: !alwaysActive && !hasAttention,
|
|
370
|
+
layoutManaged: textBlockWidthProp
|
|
371
|
+
}, /* @__PURE__ */ React4.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React4.createElement(ToolbarMenuItem, {
|
|
372
|
+
key: item.id ?? i,
|
|
373
|
+
item
|
|
374
|
+
}))));
|
|
375
|
+
};
|
|
376
|
+
var ToolbarMenuItem = ({ item }) => {
|
|
377
|
+
if (isSeparator(item)) {
|
|
378
|
+
return /* @__PURE__ */ React4.createElement(NaturalToolbar.Separator, {
|
|
384
379
|
variant: item.properties.variant
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
if (isMenuGroup(item)) {
|
|
383
|
+
if (item.properties.variant === "dropdownMenu") {
|
|
384
|
+
return /* @__PURE__ */ React4.createElement(DropdownMenuToolbarItem, {
|
|
389
385
|
group: item
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
/* @__PURE__ */ React4.createElement(ToggleGroupToolbarItem, {
|
|
394
|
-
key: item.id,
|
|
386
|
+
});
|
|
387
|
+
} else {
|
|
388
|
+
return /* @__PURE__ */ React4.createElement(ToggleGroupToolbarItem, {
|
|
395
389
|
group: item
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
// TODO(thure): Figure out type narrowing that doesn’t require so much use of `as`.
|
|
399
|
-
/* @__PURE__ */ React4.createElement(ActionToolbarItem, {
|
|
400
|
-
key: item.id,
|
|
401
|
-
action: item
|
|
402
|
-
})
|
|
403
|
-
))));
|
|
404
|
-
} finally {
|
|
405
|
-
_effect.f();
|
|
390
|
+
});
|
|
391
|
+
}
|
|
406
392
|
}
|
|
393
|
+
return /* @__PURE__ */ React4.createElement(ActionToolbarItem, {
|
|
394
|
+
action: item
|
|
395
|
+
});
|
|
407
396
|
};
|
|
408
397
|
|
|
409
|
-
//
|
|
410
|
-
import { RegistryContext,
|
|
411
|
-
import { useCallback as useCallback3, useContext,
|
|
412
|
-
import { Graph,
|
|
413
|
-
var edgesArrayToRecord = (edges) => {
|
|
414
|
-
return Object.fromEntries(Object.entries(edges.reduce((acc, { source, target }) => {
|
|
415
|
-
if (!acc[source]) {
|
|
416
|
-
acc[source] = {
|
|
417
|
-
inbound: [],
|
|
418
|
-
outbound: []
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
if (!acc[target]) {
|
|
422
|
-
acc[target] = {
|
|
423
|
-
inbound: [],
|
|
424
|
-
outbound: []
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
const sourceEdges = acc[source];
|
|
428
|
-
if (!sourceEdges.outbound.includes(target)) {
|
|
429
|
-
sourceEdges.outbound.push(target);
|
|
430
|
-
}
|
|
431
|
-
const targetEdges = acc[target];
|
|
432
|
-
if (!targetEdges.inbound.includes(source)) {
|
|
433
|
-
targetEdges.inbound.push(source);
|
|
434
|
-
}
|
|
435
|
-
return acc;
|
|
436
|
-
}, {})));
|
|
437
|
-
};
|
|
398
|
+
// src/hooks/useMenuActions.ts
|
|
399
|
+
import { RegistryContext, useAtomValue } from "@effect-atom/atom-react";
|
|
400
|
+
import { useCallback as useCallback3, useContext, useRef } from "react";
|
|
401
|
+
import { Graph, Node as Node3 } from "@dxos/app-graph";
|
|
438
402
|
var useMenuActions = (props) => {
|
|
439
403
|
const registry = useContext(RegistryContext);
|
|
440
|
-
const menuGraphProps =
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
404
|
+
const menuGraphProps = useAtomValue(props);
|
|
405
|
+
const graphRef = useRef(null);
|
|
406
|
+
if (!graphRef.current || graphRef.current.props !== menuGraphProps) {
|
|
407
|
+
const newGraph = Graph.make({
|
|
408
|
+
registry
|
|
409
|
+
});
|
|
410
|
+
newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
|
|
411
|
+
graphRef.current = {
|
|
412
|
+
graph: newGraph,
|
|
413
|
+
props: menuGraphProps
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const graph = graphRef.current.graph;
|
|
452
417
|
const useGroupItems = useCallback3((sourceNode) => {
|
|
453
|
-
const items =
|
|
418
|
+
const items = useAtomValue(graph.connections(sourceNode?.id || Node3.RootId));
|
|
454
419
|
return items;
|
|
455
420
|
}, [
|
|
456
421
|
graph
|
|
@@ -459,8 +424,69 @@ var useMenuActions = (props) => {
|
|
|
459
424
|
useGroupItems
|
|
460
425
|
};
|
|
461
426
|
};
|
|
427
|
+
|
|
428
|
+
// src/builder.ts
|
|
429
|
+
import { invariant } from "@dxos/invariant";
|
|
430
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
|
|
431
|
+
var MenuBuilder = Object.freeze({
|
|
432
|
+
make: () => new MenuBuilderImpl({
|
|
433
|
+
nodes: [],
|
|
434
|
+
edges: []
|
|
435
|
+
}, "root")
|
|
436
|
+
});
|
|
437
|
+
var MenuBuilderImpl = class _MenuBuilderImpl {
|
|
438
|
+
_data;
|
|
439
|
+
_rootId;
|
|
440
|
+
constructor(_data, _rootId) {
|
|
441
|
+
this._data = _data;
|
|
442
|
+
this._rootId = _rootId;
|
|
443
|
+
}
|
|
444
|
+
root(props) {
|
|
445
|
+
invariant(this._rootId === "root", "Root group can only be at the top level", {
|
|
446
|
+
F: __dxlog_file,
|
|
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
|
+
});
|
|
463
|
+
this._data.nodes.push(createMenuItemGroup("root", props));
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
action(id, props, invoke) {
|
|
467
|
+
this._data.nodes.push(createMenuAction(id, invoke, props));
|
|
468
|
+
this._data.edges.push({
|
|
469
|
+
source: this._rootId,
|
|
470
|
+
target: id
|
|
471
|
+
});
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
group(id, props, cb) {
|
|
475
|
+
this._data.nodes.push(createMenuItemGroup(id, props));
|
|
476
|
+
this._data.edges.push({
|
|
477
|
+
source: this._rootId,
|
|
478
|
+
target: id
|
|
479
|
+
});
|
|
480
|
+
cb(new _MenuBuilderImpl(this._data, id));
|
|
481
|
+
return this;
|
|
482
|
+
}
|
|
483
|
+
build() {
|
|
484
|
+
return this._data;
|
|
485
|
+
}
|
|
486
|
+
};
|
|
462
487
|
export {
|
|
463
488
|
DropdownMenu,
|
|
489
|
+
MenuBuilder,
|
|
464
490
|
MenuProvider,
|
|
465
491
|
MenuSeparatorType,
|
|
466
492
|
ToolbarMenu,
|
|
@@ -472,7 +498,6 @@ export {
|
|
|
472
498
|
isMenuGroup,
|
|
473
499
|
isSeparator,
|
|
474
500
|
menuContextDefaults,
|
|
475
|
-
rxFromSignal,
|
|
476
501
|
useMenu,
|
|
477
502
|
useMenuActions,
|
|
478
503
|
useMenuItems
|