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