@ai-matrx/context-menu 0.1.0 → 0.1.2
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/CHANGELOG.md +13 -0
- package/dist/builder.cjs +158 -13
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +1 -1
- package/dist/builder.d.ts +1 -1
- package/dist/builder.js +159 -14
- package/dist/builder.js.map +1 -1
- package/dist/design-store.cjs.map +1 -1
- package/dist/design-store.d.cts +1 -1
- package/dist/design-store.d.ts +1 -1
- package/dist/design-store.js.map +1 -1
- package/dist/index.cjs +102 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +102 -5
- package/dist/index.js.map +1 -1
- package/dist/{model-BdF0TqZ3.d.cts → model-CkLCR6WY.d.cts} +19 -3
- package/dist/{model-BdF0TqZ3.d.ts → model-CkLCR6WY.d.ts} +19 -3
- package/package.json +7 -7
package/dist/builder.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { M as MenuDesign } from './model-
|
|
3
|
+
import { M as MenuDesign } from './model-CkLCR6WY.cjs';
|
|
4
4
|
import { DesignStore } from './design-store.cjs';
|
|
5
5
|
import '@ai-matrx/records/typed-table';
|
|
6
6
|
import '@ai-matrx/records/core';
|
package/dist/builder.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { M as MenuDesign } from './model-
|
|
3
|
+
import { M as MenuDesign } from './model-CkLCR6WY.js';
|
|
4
4
|
import { DesignStore } from './design-store.js';
|
|
5
5
|
import '@ai-matrx/records/typed-table';
|
|
6
6
|
import '@ai-matrx/records/core';
|
package/dist/builder.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/builder/MenuBuilder.tsx
|
|
4
|
-
import { useCallback, useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
|
|
4
|
+
import { createContext, useCallback, useContext, useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/design/catalog.ts
|
|
7
7
|
var o = (id, label, icon, group, extra = {}) => ({ id, label, icon, group, status: "live", ...extra });
|
|
@@ -143,6 +143,25 @@ var TEMPLATES = [
|
|
|
143
143
|
{ id: "sub-organize", label: "Organize \u25B8", icon: "Tags", build: () => sub("Organize", "Tags", ["cm:attach", "pin-message", "cm:view-history", "cm:compare"]) },
|
|
144
144
|
{ id: "sub-tools", label: "Tools \u25B8", icon: "Wrench", build: () => sub("Tools", "Wrench", ["cm:speak", "cm:quick-actions", "feedback", "cm:x:surface", "cm:admin"]) }
|
|
145
145
|
];
|
|
146
|
+
var SLOT_SOURCES = [
|
|
147
|
+
{ id: "any", label: "Everything not placed", phrase: "Everything else", icon: "Ellipsis" },
|
|
148
|
+
{ id: "agents", label: "Agents & shortcuts (library)", phrase: "All other agents", icon: "Bot", runtime: true },
|
|
149
|
+
{ id: "page-rows", label: "This page's own rows", phrase: "All of this page's actions", icon: "PanelTop", runtime: true },
|
|
150
|
+
...CATALOG_GROUPS.map((g) => ({
|
|
151
|
+
id: `group:${g}`,
|
|
152
|
+
label: g,
|
|
153
|
+
phrase: g === "AI" ? "All other AI items" : `All other ${g.toLowerCase()} options`,
|
|
154
|
+
icon: CATALOG.find((c) => c.group === g)?.icon ?? "Circle"
|
|
155
|
+
}))
|
|
156
|
+
];
|
|
157
|
+
function slotSource(id) {
|
|
158
|
+
return SLOT_SOURCES.find((s) => s.id === id);
|
|
159
|
+
}
|
|
160
|
+
function slotOptions(fills, placed) {
|
|
161
|
+
if (fills === "agents" || fills === "page-rows") return [];
|
|
162
|
+
const pool = fills === "any" ? CATALOG : CATALOG.filter((c) => `group:${c.group}` === fills);
|
|
163
|
+
return pool.filter((c) => !placed.has(c.id));
|
|
164
|
+
}
|
|
146
165
|
|
|
147
166
|
// src/design/MenuPreview.tsx
|
|
148
167
|
import { useMemo, useState } from "react";
|
|
@@ -176,6 +195,20 @@ function emptyDesign(name = "Untitled menu") {
|
|
|
176
195
|
function isContainer(node) {
|
|
177
196
|
return node.kind === "section" || node.kind === "submenu";
|
|
178
197
|
}
|
|
198
|
+
function placedCatalogIds(nodes, out = /* @__PURE__ */ new Set()) {
|
|
199
|
+
for (const n of nodes) {
|
|
200
|
+
if (n.catalogId) out.add(n.catalogId);
|
|
201
|
+
if (isContainer(n)) placedCatalogIds(n.children, out);
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function catalogUseCounts(nodes, out = /* @__PURE__ */ new Map()) {
|
|
206
|
+
for (const n of nodes) {
|
|
207
|
+
if (n.catalogId) out.set(n.catalogId, (out.get(n.catalogId) ?? 0) + 1);
|
|
208
|
+
if (isContainer(n)) catalogUseCounts(n.children, out);
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
179
212
|
function cloneNode(node) {
|
|
180
213
|
const copy = { ...node, id: newId(node.kind[0]) };
|
|
181
214
|
if (isContainer(copy)) copy.children = copy.children.map(cloneNode);
|
|
@@ -199,7 +232,7 @@ function contains(node, id) {
|
|
|
199
232
|
function countItems(nodes) {
|
|
200
233
|
let n = 0;
|
|
201
234
|
for (const node of nodes) {
|
|
202
|
-
if (node.kind === "item") n += 1;
|
|
235
|
+
if (node.kind === "item" || node.kind === "slot") n += 1;
|
|
203
236
|
else if (isContainer(node)) n += countItems(node.children) + (node.kind === "submenu" ? 1 : 0);
|
|
204
237
|
}
|
|
205
238
|
return n;
|
|
@@ -289,7 +322,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
289
322
|
function matches(node, q) {
|
|
290
323
|
if (!q) return true;
|
|
291
324
|
if (node.kind === "divider") return false;
|
|
292
|
-
if (node.kind === "item") return node.label.toLowerCase().includes(q);
|
|
325
|
+
if (node.kind === "item" || node.kind === "slot") return node.label.toLowerCase().includes(q);
|
|
293
326
|
return node.label.toLowerCase().includes(q) || node.children.some((c) => matches(c, q));
|
|
294
327
|
}
|
|
295
328
|
function flatten(nodes, q, out = []) {
|
|
@@ -304,6 +337,8 @@ function MenuPreview({ design: design2, renderIcon, highlightId, onSelectNode, o
|
|
|
304
337
|
const q = query.trim().toLowerCase();
|
|
305
338
|
const { look } = design2;
|
|
306
339
|
const results = useMemo(() => q ? flatten(design2.nodes, q) : null, [design2.nodes, q]);
|
|
340
|
+
const placed = useMemo(() => placedCatalogIds(design2.nodes), [design2.nodes]);
|
|
341
|
+
const ctx = { design: design2, renderIcon, highlightId, onSelectNode, onPick, placed };
|
|
307
342
|
return /* @__PURE__ */ jsxs(
|
|
308
343
|
"div",
|
|
309
344
|
{
|
|
@@ -316,7 +351,7 @@ function MenuPreview({ design: design2, renderIcon, highlightId, onSelectNode, o
|
|
|
316
351
|
renderIcon("Search", "cm-icon"),
|
|
317
352
|
/* @__PURE__ */ jsx("input", { value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Type to filter\u2026", "aria-label": "Filter menu" })
|
|
318
353
|
] }),
|
|
319
|
-
results ? results.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "No matches" }) : results.map((n) => /* @__PURE__ */ jsx(Item, { node: n, ctx
|
|
354
|
+
results ? results.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "No matches" }) : results.map((n) => /* @__PURE__ */ jsx(Item, { node: n, ctx }, n.id)) : design2.nodes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Empty menu \u2014 drag options in" }) : /* @__PURE__ */ jsx(Nodes, { nodes: design2.nodes, ctx })
|
|
320
355
|
]
|
|
321
356
|
}
|
|
322
357
|
);
|
|
@@ -326,6 +361,7 @@ function Nodes({ nodes, ctx }) {
|
|
|
326
361
|
if (n.kind === "divider") return /* @__PURE__ */ jsx("div", { role: "separator", className: "cm-divider" }, n.id);
|
|
327
362
|
if (n.kind === "item") return /* @__PURE__ */ jsx(Item, { node: n, ctx }, n.id);
|
|
328
363
|
if (n.kind === "submenu") return /* @__PURE__ */ jsx(Submenu, { node: n, ctx }, n.id);
|
|
364
|
+
if (n.kind === "slot") return /* @__PURE__ */ jsx(Slot, { node: n, ctx }, n.id);
|
|
329
365
|
return /* @__PURE__ */ jsx(Section, { node: n, ctx }, n.id);
|
|
330
366
|
}) });
|
|
331
367
|
}
|
|
@@ -409,8 +445,7 @@ function Submenu({ node, ctx }) {
|
|
|
409
445
|
const [open, setOpen] = useState(false);
|
|
410
446
|
const [side, setSide] = useState("right");
|
|
411
447
|
const show = (el) => {
|
|
412
|
-
|
|
413
|
-
setSide(r.right + look.width + 8 > window.innerWidth ? "left" : "right");
|
|
448
|
+
setSide(flyoutSide(el, look.width));
|
|
414
449
|
setOpen(true);
|
|
415
450
|
};
|
|
416
451
|
return /* @__PURE__ */ jsxs("div", { className: "cm-sub", onMouseEnter: (e) => show(e.currentTarget), onMouseLeave: () => setOpen(false), children: [
|
|
@@ -439,6 +474,63 @@ function Submenu({ node, ctx }) {
|
|
|
439
474
|
open && /* @__PURE__ */ jsx("div", { role: "menu", className: "cm-menu cm-flyout", "data-side": side, "data-density": look.density, style: { width: look.width }, children: node.children.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Empty submenu" }) : /* @__PURE__ */ jsx(Nodes, { nodes: node.children, ctx }) })
|
|
440
475
|
] });
|
|
441
476
|
}
|
|
477
|
+
function flyoutSide(row, width) {
|
|
478
|
+
const r = row.getBoundingClientRect();
|
|
479
|
+
const bounds = row.closest("[data-menu-bounds]")?.getBoundingClientRect() ?? { left: 0, right: window.innerWidth };
|
|
480
|
+
if (r.right + width + 8 <= bounds.right) return "right";
|
|
481
|
+
if (r.left - width - 8 >= bounds.left) return "left";
|
|
482
|
+
return "below";
|
|
483
|
+
}
|
|
484
|
+
function Slot({ node, ctx }) {
|
|
485
|
+
const { look } = ctx.design;
|
|
486
|
+
const source = slotSource(node.fills);
|
|
487
|
+
const options = slotOptions(node.fills, ctx.placed);
|
|
488
|
+
const [open, setOpen] = useState(false);
|
|
489
|
+
const [side, setSide] = useState("right");
|
|
490
|
+
const count = source?.runtime ? "live" : String(options.length);
|
|
491
|
+
const row = (
|
|
492
|
+
// ui-exception: a menu row of the menu component itself
|
|
493
|
+
/* @__PURE__ */ jsxs(
|
|
494
|
+
"button",
|
|
495
|
+
{
|
|
496
|
+
type: "button",
|
|
497
|
+
role: "menuitem",
|
|
498
|
+
"aria-haspopup": node.shape === "submenu" ? "menu" : void 0,
|
|
499
|
+
title: source ? `Placeholder: ${source.label}` : "Placeholder",
|
|
500
|
+
className: `cm-item cm-slot ${ctx.highlightId === node.id ? "cm-hl" : ""}`,
|
|
501
|
+
onClick: () => {
|
|
502
|
+
ctx.onSelectNode?.(node.id);
|
|
503
|
+
if (node.shape === "submenu") setOpen(true);
|
|
504
|
+
},
|
|
505
|
+
children: [
|
|
506
|
+
look.showIcons && /* @__PURE__ */ jsx("span", { className: "cm-icon-slot", children: ctx.renderIcon(node.icon ?? source?.icon ?? "Ellipsis", "cm-icon") }),
|
|
507
|
+
/* @__PURE__ */ jsx("span", { className: "cm-text", children: /* @__PURE__ */ jsx("span", { className: "cm-label", children: node.label }) }),
|
|
508
|
+
/* @__PURE__ */ jsx("span", { className: "cm-count", children: count }),
|
|
509
|
+
node.shape === "submenu" && ctx.renderIcon("ChevronRight", "cm-icon cm-chev")
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
if (node.shape === "items") return row;
|
|
515
|
+
return /* @__PURE__ */ jsxs(
|
|
516
|
+
"div",
|
|
517
|
+
{
|
|
518
|
+
className: "cm-sub",
|
|
519
|
+
onMouseEnter: (e) => {
|
|
520
|
+
setSide(flyoutSide(e.currentTarget, look.width));
|
|
521
|
+
setOpen(true);
|
|
522
|
+
},
|
|
523
|
+
onMouseLeave: () => setOpen(false),
|
|
524
|
+
children: [
|
|
525
|
+
row,
|
|
526
|
+
open && /* @__PURE__ */ jsx("div", { role: "menu", className: "cm-menu cm-flyout", "data-side": side, "data-density": look.density, style: { width: look.width }, children: source?.runtime ? /* @__PURE__ */ jsxs("div", { className: "cm-runtime", children: [
|
|
527
|
+
"Filled at runtime: ",
|
|
528
|
+
source.label
|
|
529
|
+
] }) : options.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Nothing left \u2014 every option of this kind is placed" }) : options.map((o2) => /* @__PURE__ */ jsx(Item, { node: { id: `slot-${node.id}-${o2.id}`, kind: "item", label: o2.label, icon: o2.icon, catalogId: o2.id, ...o2.shortcut ? { shortcut: o2.shortcut } : {}, ...o2.ai ? { ai: true } : {}, ...o2.danger ? { danger: true } : {} }, ctx }, o2.id)) })
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
}
|
|
442
534
|
|
|
443
535
|
// src/design/starters.ts
|
|
444
536
|
function maker(slug) {
|
|
@@ -965,6 +1057,7 @@ var SURFACE_LABELS = {
|
|
|
965
1057
|
anywhere: "Opens anywhere"
|
|
966
1058
|
};
|
|
967
1059
|
var SURFACE_OPTIONS = MENU_SURFACES.map((value) => ({ value, label: SURFACE_LABELS[value] }));
|
|
1060
|
+
var DesignContext = createContext(null);
|
|
968
1061
|
var DRAFT_KEY = "matrx.context-menu.builder.draft.v1";
|
|
969
1062
|
function usePointerDrag(onDrop) {
|
|
970
1063
|
const [ghost, setGhost] = useState2(null);
|
|
@@ -1040,6 +1133,8 @@ function nodeFromPayload(p) {
|
|
|
1040
1133
|
if (p.kind === "section") return { id: newId("s"), kind: "section", label: "New section", children: [] };
|
|
1041
1134
|
if (p.kind === "submenu") return { id: newId("m"), kind: "submenu", label: "More", icon: "Ellipsis", children: [] };
|
|
1042
1135
|
if (p.kind === "divider") return { id: newId("d"), kind: "divider" };
|
|
1136
|
+
if (p.kind === "slot-items") return { id: newId("p"), kind: "slot", shape: "items", fills: "any", label: "Everything else" };
|
|
1137
|
+
if (p.kind === "slot-menu") return { id: newId("p"), kind: "slot", shape: "submenu", fills: "any", label: "More", icon: "Ellipsis" };
|
|
1043
1138
|
return { id: newId("i"), kind: "item", label: "New option", icon: "Circle" };
|
|
1044
1139
|
}
|
|
1045
1140
|
return null;
|
|
@@ -1193,7 +1288,7 @@ function MenuBuilder({ store, storeLabel, initialDesign, accountSlot }) {
|
|
|
1193
1288
|
for (const v of versions ?? []) byId.set(v.designId, [...byId.get(v.designId) ?? [], v]);
|
|
1194
1289
|
return [...byId.values()];
|
|
1195
1290
|
}, [versions]);
|
|
1196
|
-
return /* @__PURE__ */ jsxs2("div", { className: "flex h-dvh flex-col bg-background text-foreground", children: [
|
|
1291
|
+
return /* @__PURE__ */ jsx3(DesignContext.Provider, { value: design2, children: /* @__PURE__ */ jsxs2("div", { className: "flex h-dvh flex-col bg-background text-foreground", children: [
|
|
1197
1292
|
/* @__PURE__ */ jsxs2("header", { className: "flex flex-wrap items-center gap-y-1 border-b border-border px-3 py-2", children: [
|
|
1198
1293
|
/* @__PURE__ */ jsxs2(ControlRow, { className: "min-w-0 flex-1", children: [
|
|
1199
1294
|
/* @__PURE__ */ jsx3(Field, { "aria-label": "Design name", width: "lg", value: design2.name, onChange: (e) => set((d) => ({ ...d, name: e.target.value })) }),
|
|
@@ -1316,7 +1411,7 @@ function MenuBuilder({ store, storeLabel, initialDesign, accountSlot }) {
|
|
|
1316
1411
|
children: dnd.ghost.label
|
|
1317
1412
|
}
|
|
1318
1413
|
)
|
|
1319
|
-
] });
|
|
1414
|
+
] }) });
|
|
1320
1415
|
}
|
|
1321
1416
|
function Panel({ title, children, right }) {
|
|
1322
1417
|
return /* @__PURE__ */ jsxs2("section", { className: "border-b border-border p-3", children: [
|
|
@@ -1335,7 +1430,9 @@ function Palette({ onAdd, startDrag, click }) {
|
|
|
1335
1430
|
const n = nodeFromPayload(p);
|
|
1336
1431
|
if (n) onAdd(n);
|
|
1337
1432
|
};
|
|
1338
|
-
const
|
|
1433
|
+
const design2 = useContext(DesignContext);
|
|
1434
|
+
const uses = useMemo2(() => catalogUseCounts(design2?.nodes ?? []), [design2?.nodes]);
|
|
1435
|
+
const Option = ({ p, icon, label, end, dimmed }) => /* @__PURE__ */ jsx3(
|
|
1339
1436
|
Tile2,
|
|
1340
1437
|
{
|
|
1341
1438
|
layout: "row",
|
|
@@ -1343,6 +1440,7 @@ function Palette({ onAdd, startDrag, click }) {
|
|
|
1343
1440
|
icon: renderLucide(icon),
|
|
1344
1441
|
title: label,
|
|
1345
1442
|
end,
|
|
1443
|
+
dimmed: dimmed === true,
|
|
1346
1444
|
onPointerDown: (e) => startDrag(e, p, label),
|
|
1347
1445
|
onClick: click(() => add(p))
|
|
1348
1446
|
}
|
|
@@ -1355,14 +1453,27 @@ function Palette({ onAdd, startDrag, click }) {
|
|
|
1355
1453
|
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "section" }, icon: "Rows3", label: "Section" }),
|
|
1356
1454
|
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "submenu" }, icon: "ChevronsRight", label: "Submenu" }),
|
|
1357
1455
|
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "divider" }, icon: "Minus", label: "Divider" }),
|
|
1358
|
-
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "item" }, icon: "Plus", label: "Custom option" })
|
|
1456
|
+
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "item" }, icon: "Plus", label: "Custom option" }),
|
|
1457
|
+
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "slot-items" }, icon: "ListPlus", label: "Placeholder \u2014 items" }),
|
|
1458
|
+
/* @__PURE__ */ jsx3(Option, { p: { type: "new", kind: "slot-menu" }, icon: "FolderPlus", label: "Placeholder \u2014 submenu" })
|
|
1359
1459
|
] }),
|
|
1360
1460
|
/* @__PURE__ */ jsx3(RowGroup, { title: "Ready-made", children: TEMPLATES.map((t) => /* @__PURE__ */ jsx3(Option, { p: { type: "template", id: t.id }, icon: t.icon, label: t.label }, t.id)) })
|
|
1361
1461
|
] }),
|
|
1362
1462
|
CATALOG_GROUPS.map((g) => {
|
|
1363
|
-
const
|
|
1463
|
+
const matching = CATALOG.filter((c) => c.group === g && shown(c));
|
|
1464
|
+
const items = [...matching.filter((c) => !uses.has(c.id)), ...matching.filter((c) => uses.has(c.id))];
|
|
1364
1465
|
if (items.length === 0) return null;
|
|
1365
|
-
return /* @__PURE__ */ jsx3(RowGroup, { title: g, children: items.map((c) => /* @__PURE__ */ jsx3(
|
|
1466
|
+
return /* @__PURE__ */ jsx3(RowGroup, { title: g, children: items.map((c) => /* @__PURE__ */ jsx3(
|
|
1467
|
+
Option,
|
|
1468
|
+
{
|
|
1469
|
+
p: { type: "catalog", id: c.id },
|
|
1470
|
+
icon: c.icon,
|
|
1471
|
+
label: c.label,
|
|
1472
|
+
dimmed: uses.has(c.id),
|
|
1473
|
+
...uses.has(c.id) ? { end: /* @__PURE__ */ jsx3(Badge, { children: (uses.get(c.id) ?? 0) > 1 ? `used \xD7${uses.get(c.id)}` : "used" }) } : c.status === "proposed" ? { end: /* @__PURE__ */ jsx3(Badge, { children: "new" }) } : {}
|
|
1474
|
+
},
|
|
1475
|
+
c.id
|
|
1476
|
+
)) }, g);
|
|
1366
1477
|
})
|
|
1367
1478
|
] })
|
|
1368
1479
|
] });
|
|
@@ -1394,7 +1505,7 @@ function Rows(props) {
|
|
|
1394
1505
|
const container = isContainer(node);
|
|
1395
1506
|
const isOver = over?.id === node.id;
|
|
1396
1507
|
const label = node.kind === "divider" ? "\u2500\u2500\u2500\u2500 divider \u2500\u2500\u2500\u2500" : node.label || (node.kind === "section" ? "(unlabelled section)" : "(no label)");
|
|
1397
|
-
const icon = node.kind === "item" || node.kind === "submenu" ? node.icon : node.kind === "section" ? "Rows3" : "Minus";
|
|
1508
|
+
const icon = node.kind === "item" || node.kind === "submenu" ? node.icon : node.kind === "slot" ? node.icon ?? slotSource(node.fills)?.icon ?? "Ellipsis" : node.kind === "section" ? "Rows3" : "Minus";
|
|
1398
1509
|
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
1399
1510
|
/* @__PURE__ */ jsxs2(
|
|
1400
1511
|
"div",
|
|
@@ -1420,6 +1531,7 @@ function Rows(props) {
|
|
|
1420
1531
|
icon && /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground", children: renderLucide(icon, "size-4") }),
|
|
1421
1532
|
/* @__PURE__ */ jsx3("span", { className: `min-w-0 flex-1 truncate ${node.kind === "divider" ? "text-xs text-muted-foreground" : ""}`, children: label }),
|
|
1422
1533
|
node.kind === "submenu" && /* @__PURE__ */ jsx3("span", { className: "text-[10px] text-muted-foreground", children: "submenu" }),
|
|
1534
|
+
node.kind === "slot" && /* @__PURE__ */ jsx3(Badge, { children: node.shape === "submenu" ? "placeholder submenu" : "placeholder" }),
|
|
1423
1535
|
node.kind === "section" && /* @__PURE__ */ jsx3("span", { className: "text-[10px] text-muted-foreground", children: node.layout && node.layout !== "list" ? `${node.layout} section` : "section" }),
|
|
1424
1536
|
node.kind === "item" && node.shortcut && /* @__PURE__ */ jsx3("kbd", { className: "text-[10px] text-muted-foreground", children: node.shortcut }),
|
|
1425
1537
|
/* @__PURE__ */ jsxs2("span", { className: "hidden items-center gap-0.5 group-hover:flex", children: [
|
|
@@ -1522,6 +1634,7 @@ function Stage({ design: design2, selected, onSelect }) {
|
|
|
1522
1634
|
onClick: (e) => {
|
|
1523
1635
|
if (e.target === e.currentTarget) setAt(null);
|
|
1524
1636
|
},
|
|
1637
|
+
"data-menu-bounds": "",
|
|
1525
1638
|
className: "relative min-h-[300px] overflow-visible rounded-lg bg-muted/60 p-4",
|
|
1526
1639
|
children: [
|
|
1527
1640
|
at ? /* @__PURE__ */ jsx3(
|
|
@@ -1572,9 +1685,10 @@ var FLAGS = [
|
|
|
1572
1685
|
];
|
|
1573
1686
|
function Inspector({ node, onChange }) {
|
|
1574
1687
|
const catalog = node.catalogId ? catalogOption(node.catalogId) : void 0;
|
|
1575
|
-
return /* @__PURE__ */ jsxs2(Panel, { title: `Selected ${node.kind}`, right: catalog ? /* @__PURE__ */ jsx3(Badge, { children: catalog.id }) : void 0, children: [
|
|
1688
|
+
return /* @__PURE__ */ jsxs2(Panel, { title: `Selected ${node.kind === "slot" ? "placeholder" : node.kind}`, right: catalog ? /* @__PURE__ */ jsx3(Badge, { children: catalog.id }) : void 0, children: [
|
|
1576
1689
|
node.kind === "divider" ? /* @__PURE__ */ jsx3("p", { className: "type-secondary", children: "A divider has no settings." }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1577
1690
|
/* @__PURE__ */ jsx3(Labelled, { label: "Label", children: /* @__PURE__ */ jsx3(Field, { "aria-label": "Label", value: node.label, onChange: (e) => onChange({ label: e.target.value }) }) }),
|
|
1691
|
+
node.kind === "slot" && /* @__PURE__ */ jsx3(SlotSettings, { node, onChange }),
|
|
1578
1692
|
(node.kind === "item" || node.kind === "submenu") && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1579
1693
|
/* @__PURE__ */ jsx3(Labelled, { label: "Icon", children: /* @__PURE__ */ jsx3(IconPicker, { value: node.icon ?? "", onChange: (icon) => onChange({ icon }) }) }),
|
|
1580
1694
|
/* @__PURE__ */ jsx3(Labelled, { label: "Hint", children: /* @__PURE__ */ jsx3(Field, { "aria-label": "Hint", value: node.hint ?? "", onChange: (e) => onChange({ hint: e.target.value }) }) })
|
|
@@ -1591,6 +1705,37 @@ function Inspector({ node, onChange }) {
|
|
|
1591
1705
|
/* @__PURE__ */ jsx3(Labelled, { label: "Note for the build", children: /* @__PURE__ */ jsx3(Textarea, { "aria-label": "Note for the build", rows: 2, autoGrow: true, value: node.note ?? "", onChange: (e) => onChange({ note: e.target.value }) }) })
|
|
1592
1706
|
] });
|
|
1593
1707
|
}
|
|
1708
|
+
var SLOT_SHAPES = [
|
|
1709
|
+
{ value: "items", label: "Rows here" },
|
|
1710
|
+
{ value: "submenu", label: "One submenu" }
|
|
1711
|
+
];
|
|
1712
|
+
var SLOT_OPTIONS = SLOT_SOURCES.map((src) => ({ value: src.id, label: src.label }));
|
|
1713
|
+
function SlotSettings({ node, onChange }) {
|
|
1714
|
+
const source = slotSource(node.fills);
|
|
1715
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1716
|
+
/* @__PURE__ */ jsx3(Labelled, { label: "Holds", children: /* @__PURE__ */ jsx3(
|
|
1717
|
+
Select,
|
|
1718
|
+
{
|
|
1719
|
+
"aria-label": "What this placeholder holds",
|
|
1720
|
+
value: node.fills,
|
|
1721
|
+
options: SLOT_OPTIONS,
|
|
1722
|
+
onValueChange: (fills) => {
|
|
1723
|
+
const relabel = !node.label || node.label === source?.phrase || node.label === "Everything else" || node.label === "More";
|
|
1724
|
+
onChange({ fills, ...relabel ? { label: slotSource(fills)?.phrase ?? node.label } : {} });
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
) }),
|
|
1728
|
+
/* @__PURE__ */ jsx3(SettingRow, { label: "Shows as", children: /* @__PURE__ */ jsx3(SegmentedControl, { "aria-label": "Shows as", value: node.shape, data: SLOT_SHAPES, onValueChange: (shape) => onChange({ shape }) }) }),
|
|
1729
|
+
/* @__PURE__ */ jsx3(SlotContents, { fills: node.fills })
|
|
1730
|
+
] });
|
|
1731
|
+
}
|
|
1732
|
+
function SlotContents({ fills }) {
|
|
1733
|
+
const source = slotSource(fills);
|
|
1734
|
+
const design2 = useContext(DesignContext);
|
|
1735
|
+
const left = useMemo2(() => design2 ? slotOptions(fills, placedCatalogIds(design2.nodes)) : [], [design2, fills]);
|
|
1736
|
+
if (source?.runtime) return /* @__PURE__ */ jsx3("p", { className: "type-secondary mt-2", children: "Filled by the live menu at runtime." });
|
|
1737
|
+
return /* @__PURE__ */ jsx3("p", { className: "type-secondary mt-2", children: left.length === 0 ? "Nothing left \u2014 every option of this kind is already placed." : `Holds ${left.length}: ${left.slice(0, 6).map((o2) => o2.label).join(", ")}${left.length > 6 ? "\u2026" : ""}` });
|
|
1738
|
+
}
|
|
1594
1739
|
function Labelled({ label, children }) {
|
|
1595
1740
|
return /* @__PURE__ */ jsxs2("div", { className: "mt-2 grid gap-1", children: [
|
|
1596
1741
|
/* @__PURE__ */ jsx3("span", { className: "type-meta", children: label }),
|