@shadow-garden/bapbong-ui 0.1.2 → 0.3.0
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/index.cjs +336 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +335 -9
- package/dist/lib/context-menu.d.ts.map +1 -1
- package/dist/lib/link-panel.d.ts +47 -0
- package/dist/lib/link-panel.d.ts.map +1 -0
- package/dist/lib/toolbar.d.ts +22 -2
- package/dist/lib/toolbar.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
openCellProperties: () => openCellProperties,
|
|
30
30
|
promptDialog: () => promptDialog,
|
|
31
31
|
showContextMenu: () => showContextMenu,
|
|
32
|
+
showLinkPanel: () => showLinkPanel,
|
|
32
33
|
tableGridPicker: () => tableGridPicker
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -287,6 +288,20 @@ var STYLE2 = `
|
|
|
287
288
|
.bb-color-none{grid-column:1/-1;font:inherit;font-size:12px;padding:3px 0;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:5px;background:var(--bb-ui-bg,#fff);cursor:pointer}
|
|
288
289
|
.bb-color-none:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
289
290
|
.bb-i-bold{font-weight:700}.bb-i-italic{font-style:italic}.bb-i-underline{text-decoration:underline}.bb-i-strike{text-decoration:line-through}
|
|
291
|
+
.bb-toolbar-split{display:flex;align-items:center;gap:0}
|
|
292
|
+
.bb-toolbar-split .bb-toolbar-btn{min-width:26px;padding:0 5px}
|
|
293
|
+
.bb-split-arrow{min-width:14px !important;width:15px;padding:0 !important}
|
|
294
|
+
.bb-split-arrow svg{display:block}
|
|
295
|
+
.bb-split-pop{position:absolute;z-index:1200;display:grid;grid-template-columns:repeat(3,auto);gap:8px;padding:10px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16)}
|
|
296
|
+
.bb-split-pop[hidden]{display:none}
|
|
297
|
+
.bb-split-card{display:flex;flex-direction:column;gap:6px;width:76px;padding:9px 8px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:6px;background:transparent;cursor:pointer;font-family:inherit}
|
|
298
|
+
.bb-split-card:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
299
|
+
.bb-split-card.is-selected{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#b5d4f4)}
|
|
300
|
+
.bb-split-row{display:flex;align-items:center;gap:4px}
|
|
301
|
+
.bb-split-row[data-lvl="1"]{padding-left:9px}
|
|
302
|
+
.bb-split-row[data-lvl="2"]{padding-left:18px}
|
|
303
|
+
.bb-split-marker{flex:none;min-width:14px;font-size:9px;line-height:1.1;text-align:right;color:var(--bb-ui-fg,#2c2c2a);white-space:nowrap}
|
|
304
|
+
.bb-split-bar{flex:1;height:3px;border-radius:2px;background:var(--bb-ui-border,#d9d9d4)}
|
|
290
305
|
`;
|
|
291
306
|
function defaultToolbarGroups(commands) {
|
|
292
307
|
const names = [...commands].map((c) => c.name);
|
|
@@ -306,6 +321,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
306
321
|
const buttons = [];
|
|
307
322
|
const selects = [];
|
|
308
323
|
const colors = [];
|
|
324
|
+
const splits = [];
|
|
309
325
|
let latest = null;
|
|
310
326
|
for (const group of groups) {
|
|
311
327
|
const entries = group.filter(
|
|
@@ -381,7 +397,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
381
397
|
btn2.addEventListener("mousedown", (e) => e.preventDefault());
|
|
382
398
|
btn2.addEventListener("click", () => {
|
|
383
399
|
const open = pop2.hidden;
|
|
384
|
-
wrap.querySelectorAll(".bb-color-pop").forEach((p) => p.hidden = true);
|
|
400
|
+
wrap.querySelectorAll(".bb-color-pop,.bb-split-pop").forEach((p) => p.hidden = true);
|
|
385
401
|
pop2.hidden = !open;
|
|
386
402
|
if (!pop2.hidden) {
|
|
387
403
|
const wrapRect = wrap.getBoundingClientRect();
|
|
@@ -410,6 +426,94 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
410
426
|
colors.push({ spec: entry, bar });
|
|
411
427
|
continue;
|
|
412
428
|
}
|
|
429
|
+
if (typeof entry !== "string" && entry.kind === "split") {
|
|
430
|
+
const splitWrap = document.createElement("div");
|
|
431
|
+
splitWrap.className = "bb-toolbar-split";
|
|
432
|
+
const item2 = items[entry.name] ?? { title: entry.name };
|
|
433
|
+
const main = document.createElement("button");
|
|
434
|
+
main.type = "button";
|
|
435
|
+
main.className = "bb-toolbar-btn";
|
|
436
|
+
main.title = item2.title;
|
|
437
|
+
main.setAttribute("aria-label", item2.title);
|
|
438
|
+
if (item2.svg) main.innerHTML = item2.svg;
|
|
439
|
+
else main.textContent = item2.label ?? entry.name;
|
|
440
|
+
main.addEventListener("mousedown", (e) => e.preventDefault());
|
|
441
|
+
main.addEventListener("click", () => {
|
|
442
|
+
if (!latest) return;
|
|
443
|
+
editor.commands.get(entry.name)?.run(latest, (tr) => editor.dispatch(tr));
|
|
444
|
+
editor.focus();
|
|
445
|
+
});
|
|
446
|
+
buttons.push({ name: entry.name, el: main });
|
|
447
|
+
const arrow = document.createElement("button");
|
|
448
|
+
arrow.type = "button";
|
|
449
|
+
arrow.className = "bb-toolbar-btn bb-split-arrow";
|
|
450
|
+
arrow.title = `${item2.title} styles`;
|
|
451
|
+
arrow.setAttribute("aria-label", `${item2.title} styles`);
|
|
452
|
+
arrow.setAttribute("aria-haspopup", "true");
|
|
453
|
+
arrow.innerHTML = '<svg viewBox="0 0 8 8" width="8" height="8" fill="currentColor" aria-hidden="true"><path d="M1 2.5h6L4 6z"/></svg>';
|
|
454
|
+
const pop2 = document.createElement("div");
|
|
455
|
+
pop2.className = "bb-split-pop";
|
|
456
|
+
pop2.hidden = true;
|
|
457
|
+
const cards = [];
|
|
458
|
+
for (const opt of entry.options) {
|
|
459
|
+
const card = document.createElement("button");
|
|
460
|
+
card.type = "button";
|
|
461
|
+
card.className = "bb-split-card";
|
|
462
|
+
card.dataset["value"] = opt.value;
|
|
463
|
+
if (opt.title) card.title = opt.title;
|
|
464
|
+
opt.rows.forEach((marker, i) => {
|
|
465
|
+
const row = document.createElement("span");
|
|
466
|
+
row.className = "bb-split-row";
|
|
467
|
+
row.dataset["lvl"] = String(i);
|
|
468
|
+
const m = document.createElement("span");
|
|
469
|
+
m.className = "bb-split-marker";
|
|
470
|
+
m.textContent = marker;
|
|
471
|
+
const bar = document.createElement("span");
|
|
472
|
+
bar.className = "bb-split-bar";
|
|
473
|
+
row.append(m, bar);
|
|
474
|
+
card.appendChild(row);
|
|
475
|
+
});
|
|
476
|
+
card.addEventListener("mousedown", (e) => e.preventDefault());
|
|
477
|
+
card.addEventListener("click", () => {
|
|
478
|
+
entry.onSelect(opt.value);
|
|
479
|
+
pop2.hidden = true;
|
|
480
|
+
editor.focus();
|
|
481
|
+
});
|
|
482
|
+
pop2.appendChild(card);
|
|
483
|
+
cards.push(card);
|
|
484
|
+
}
|
|
485
|
+
arrow.addEventListener("mousedown", (e) => e.preventDefault());
|
|
486
|
+
arrow.addEventListener("click", () => {
|
|
487
|
+
const open = pop2.hidden;
|
|
488
|
+
wrap.querySelectorAll(".bb-split-pop,.bb-color-pop").forEach((p) => p.hidden = true);
|
|
489
|
+
pop2.hidden = !open;
|
|
490
|
+
if (!pop2.hidden) {
|
|
491
|
+
const wrapRect = wrap.getBoundingClientRect();
|
|
492
|
+
const btnRect = arrow.getBoundingClientRect();
|
|
493
|
+
pop2.style.top = `${btnRect.bottom - wrapRect.top + 4}px`;
|
|
494
|
+
const left = Math.max(
|
|
495
|
+
0,
|
|
496
|
+
Math.min(
|
|
497
|
+
btnRect.left - wrapRect.left,
|
|
498
|
+
wrap.clientWidth - pop2.offsetWidth - 4
|
|
499
|
+
)
|
|
500
|
+
);
|
|
501
|
+
pop2.style.left = `${left}px`;
|
|
502
|
+
const onDoc = (ev) => {
|
|
503
|
+
if (!splitWrap.contains(ev.target) && !pop2.contains(ev.target)) {
|
|
504
|
+
pop2.hidden = true;
|
|
505
|
+
document.removeEventListener("pointerdown", onDoc, true);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
document.addEventListener("pointerdown", onDoc, true);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
splitWrap.append(main, arrow);
|
|
512
|
+
wrap.appendChild(pop2);
|
|
513
|
+
groupEl.appendChild(splitWrap);
|
|
514
|
+
splits.push({ spec: entry, cards });
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
413
517
|
const item = items[entry] ?? { title: entry };
|
|
414
518
|
const btn = document.createElement("button");
|
|
415
519
|
btn.type = "button";
|
|
@@ -461,7 +565,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
461
565
|
while (pop.firstChild) root.insertBefore(pop.firstChild, moreBtn);
|
|
462
566
|
moreBtn.style.display = "none";
|
|
463
567
|
closePop();
|
|
464
|
-
wrap.querySelectorAll(".bb-color-pop").forEach((p) => p.hidden = true);
|
|
568
|
+
wrap.querySelectorAll(".bb-color-pop,.bb-split-pop").forEach((p) => p.hidden = true);
|
|
465
569
|
if (fits()) return;
|
|
466
570
|
moreBtn.style.display = "";
|
|
467
571
|
const groupEls = Array.from(root.children).filter(
|
|
@@ -491,6 +595,14 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
491
595
|
for (const { spec, el } of selects) el.value = spec.value(state);
|
|
492
596
|
for (const { spec, bar } of colors)
|
|
493
597
|
bar.style.background = spec.value(state) ?? "";
|
|
598
|
+
for (const { spec, cards } of splits) {
|
|
599
|
+
const selected = spec.value(state);
|
|
600
|
+
for (const card of cards)
|
|
601
|
+
card.classList.toggle(
|
|
602
|
+
"is-selected",
|
|
603
|
+
selected != null && card.dataset["value"] === selected
|
|
604
|
+
);
|
|
605
|
+
}
|
|
494
606
|
};
|
|
495
607
|
const off = editor.onChange((c) => refresh(c.state));
|
|
496
608
|
try {
|
|
@@ -904,9 +1016,16 @@ function showContextMenu(entries, at) {
|
|
|
904
1016
|
}
|
|
905
1017
|
document.body.appendChild(el);
|
|
906
1018
|
const r = el.getBoundingClientRect();
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
const
|
|
1019
|
+
const pad = 4;
|
|
1020
|
+
const fitsX = (v) => v >= pad && v + r.width <= window.innerWidth - pad;
|
|
1021
|
+
const fitsY = (v) => v >= pad && v + r.height <= window.innerHeight - pad;
|
|
1022
|
+
const x = fitsX(at.x) ? at.x : fitsX(at.x - r.width) ? at.x - r.width : Math.max(pad, Math.min(at.x, window.innerWidth - r.width - pad));
|
|
1023
|
+
const y = fitsY(at.y) ? at.y : fitsY(at.y - r.height) ? at.y - r.height : Math.max(pad, Math.min(at.y, window.innerHeight - r.height - pad));
|
|
1024
|
+
el.style.left = `${x}px`;
|
|
1025
|
+
el.style.top = `${y}px`;
|
|
1026
|
+
const items = () => Array.from(
|
|
1027
|
+
el.querySelectorAll(".bb-ctx-item:not(:disabled)")
|
|
1028
|
+
);
|
|
910
1029
|
const onDown = (e) => {
|
|
911
1030
|
if (!el.contains(e.target)) closeCurrent();
|
|
912
1031
|
};
|
|
@@ -936,6 +1055,213 @@ function showContextMenu(entries, at) {
|
|
|
936
1055
|
return { close: closeCurrent };
|
|
937
1056
|
}
|
|
938
1057
|
|
|
1058
|
+
// packages/ui/src/lib/link-panel.ts
|
|
1059
|
+
var STYLE6 = `
|
|
1060
|
+
.bb-linkpanel{position:fixed;z-index:1200;width:300px;padding:8px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);color:var(--bb-ui-fg,#2c2c2a);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);box-sizing:border-box}
|
|
1061
|
+
.bb-linkpanel *{box-sizing:border-box}
|
|
1062
|
+
.bb-linkpanel-row{display:flex;align-items:center;gap:8px}
|
|
1063
|
+
.bb-linkpanel-form{display:flex;flex-direction:column;gap:8px}
|
|
1064
|
+
.bb-linkpanel-field{display:flex;align-items:center;gap:7px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:7px;padding:0 9px;background:var(--bb-ui-bg,#fff)}
|
|
1065
|
+
.bb-linkpanel-field:focus-within{border-color:var(--bb-ui-accent,#d85a30)}
|
|
1066
|
+
.bb-linkpanel-field svg{flex:none;opacity:.55}
|
|
1067
|
+
.bb-linkpanel-input{flex:1;min-width:0;height:30px;border:0;background:transparent;color:inherit;font:inherit;font-size:13px;outline:none}
|
|
1068
|
+
.bb-linkpanel-href{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--bb-ui-accent,#d85a30)}
|
|
1069
|
+
.bb-linkpanel-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:0;border-radius:6px;background:transparent;color:inherit;cursor:pointer;flex:none}
|
|
1070
|
+
.bb-linkpanel-btn:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
1071
|
+
.bb-linkpanel-apply{font:inherit;font-size:13px;font-weight:600;color:var(--bb-ui-accent,#d85a30);background:transparent;border:0;border-radius:6px;padding:6px 8px;cursor:pointer;flex:none}
|
|
1072
|
+
.bb-linkpanel-apply:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
1073
|
+
.bb-linkpanel-apply:disabled{opacity:.4;cursor:default}
|
|
1074
|
+
`;
|
|
1075
|
+
var ICONS = {
|
|
1076
|
+
link: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 9.5 9.5 6.5"/><path d="M7.5 4.5 9 3a2.47 2.47 0 0 1 3.5 0L13 3.5a2.47 2.47 0 0 1 0 3.5l-1.5 1.5"/><path d="M8.5 11.5 7 13a2.47 2.47 0 0 1-3.5 0L3 12.5a2.47 2.47 0 0 1 0-3.5l1.5-1.5"/></svg>',
|
|
1077
|
+
text: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4V2.5h10V4M8 2.5v11M6 13.5h4"/></svg>',
|
|
1078
|
+
copy: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5v-2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2"/></svg>',
|
|
1079
|
+
edit: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9.5 3.5 3 3L6 13l-3.5.5L3 10z"/><path d="m11 2 .9-.9a1.55 1.55 0 0 1 2.2 0l.8.8a1.55 1.55 0 0 1 0 2.2L14 5"/></svg>',
|
|
1080
|
+
unlink: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.5 4.5 9 3a2.47 2.47 0 0 1 3.5 0L13 3.5a2.47 2.47 0 0 1 0 3.5l-1.5 1.5"/><path d="M8.5 11.5 7 13a2.47 2.47 0 0 1-3.5 0L3 12.5a2.47 2.47 0 0 1 0-3.5l1.5-1.5"/><path d="m3 3 10 10"/></svg>'
|
|
1081
|
+
};
|
|
1082
|
+
var current2 = null;
|
|
1083
|
+
var currentKey = null;
|
|
1084
|
+
var currentHandle = null;
|
|
1085
|
+
var pendingClose = null;
|
|
1086
|
+
function cancelPendingClose() {
|
|
1087
|
+
if (pendingClose !== null) {
|
|
1088
|
+
clearTimeout(pendingClose);
|
|
1089
|
+
pendingClose = null;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
function closeCurrent2() {
|
|
1093
|
+
cancelPendingClose();
|
|
1094
|
+
if (current2) {
|
|
1095
|
+
const dispose = current2;
|
|
1096
|
+
current2 = null;
|
|
1097
|
+
currentKey = null;
|
|
1098
|
+
currentHandle = null;
|
|
1099
|
+
dispose();
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
function place(el, anchor) {
|
|
1103
|
+
const r = el.getBoundingClientRect();
|
|
1104
|
+
const pad = 6;
|
|
1105
|
+
const gap = 6;
|
|
1106
|
+
const x = Math.max(
|
|
1107
|
+
pad,
|
|
1108
|
+
Math.min(anchor.x, window.innerWidth - r.width - pad)
|
|
1109
|
+
);
|
|
1110
|
+
const below = anchor.y + anchor.height + gap;
|
|
1111
|
+
const above = anchor.y - gap - r.height;
|
|
1112
|
+
const y = below + r.height <= window.innerHeight - pad || above < pad ? Math.min(below, window.innerHeight - r.height - pad) : above;
|
|
1113
|
+
el.style.left = `${x}px`;
|
|
1114
|
+
el.style.top = `${y}px`;
|
|
1115
|
+
}
|
|
1116
|
+
function showLinkPanel(opts) {
|
|
1117
|
+
injectStyle("bb-ui-linkpanel-styles", STYLE6);
|
|
1118
|
+
if (opts.key && current2 && currentKey === opts.key && currentHandle) {
|
|
1119
|
+
cancelPendingClose();
|
|
1120
|
+
return currentHandle;
|
|
1121
|
+
}
|
|
1122
|
+
closeCurrent2();
|
|
1123
|
+
const el = document.createElement("div");
|
|
1124
|
+
el.className = "bb-linkpanel";
|
|
1125
|
+
el.setAttribute("role", "dialog");
|
|
1126
|
+
el.setAttribute("aria-label", "Link");
|
|
1127
|
+
const iconBtn = (svg, label, onClick) => {
|
|
1128
|
+
const b = document.createElement("button");
|
|
1129
|
+
b.type = "button";
|
|
1130
|
+
b.className = "bb-linkpanel-btn";
|
|
1131
|
+
b.title = label;
|
|
1132
|
+
b.setAttribute("aria-label", label);
|
|
1133
|
+
b.innerHTML = svg;
|
|
1134
|
+
b.addEventListener("mousedown", (e) => e.preventDefault());
|
|
1135
|
+
b.addEventListener("click", onClick);
|
|
1136
|
+
return b;
|
|
1137
|
+
};
|
|
1138
|
+
const field = (icon, placeholder, value) => {
|
|
1139
|
+
const wrap = document.createElement("div");
|
|
1140
|
+
wrap.className = "bb-linkpanel-field";
|
|
1141
|
+
wrap.innerHTML = icon;
|
|
1142
|
+
const input = document.createElement("input");
|
|
1143
|
+
input.className = "bb-linkpanel-input";
|
|
1144
|
+
input.type = "text";
|
|
1145
|
+
input.placeholder = placeholder;
|
|
1146
|
+
input.value = value;
|
|
1147
|
+
input.spellcheck = false;
|
|
1148
|
+
input.autocomplete = "off";
|
|
1149
|
+
wrap.appendChild(input);
|
|
1150
|
+
return { wrap, input };
|
|
1151
|
+
};
|
|
1152
|
+
const renderView = (existing) => {
|
|
1153
|
+
el.textContent = "";
|
|
1154
|
+
const row = document.createElement("div");
|
|
1155
|
+
row.className = "bb-linkpanel-row";
|
|
1156
|
+
const href = document.createElement("span");
|
|
1157
|
+
href.className = "bb-linkpanel-href";
|
|
1158
|
+
href.textContent = existing.href;
|
|
1159
|
+
href.title = existing.href;
|
|
1160
|
+
row.appendChild(href);
|
|
1161
|
+
row.appendChild(
|
|
1162
|
+
iconBtn(ICONS.copy, "Copy link", () => {
|
|
1163
|
+
void navigator.clipboard?.writeText(existing.href).catch(() => void 0);
|
|
1164
|
+
closeCurrent2();
|
|
1165
|
+
})
|
|
1166
|
+
);
|
|
1167
|
+
row.appendChild(
|
|
1168
|
+
iconBtn(ICONS.edit, "Edit link", () => renderForm(existing))
|
|
1169
|
+
);
|
|
1170
|
+
row.appendChild(
|
|
1171
|
+
iconBtn(ICONS.unlink, "Remove link", () => {
|
|
1172
|
+
closeCurrent2();
|
|
1173
|
+
opts.onUnlink?.();
|
|
1174
|
+
})
|
|
1175
|
+
);
|
|
1176
|
+
el.appendChild(row);
|
|
1177
|
+
place(el, opts.anchor);
|
|
1178
|
+
};
|
|
1179
|
+
const renderForm = (prefill) => {
|
|
1180
|
+
el.textContent = "";
|
|
1181
|
+
const form = document.createElement("div");
|
|
1182
|
+
form.className = "bb-linkpanel-form";
|
|
1183
|
+
const withText = !opts.hasSelection;
|
|
1184
|
+
let textInput = null;
|
|
1185
|
+
if (withText) {
|
|
1186
|
+
const t = field(ICONS.text, "Text (optional)", prefill?.text ?? "");
|
|
1187
|
+
textInput = t.input;
|
|
1188
|
+
form.appendChild(t.wrap);
|
|
1189
|
+
}
|
|
1190
|
+
const u = field(ICONS.link, "Paste or type a link", prefill?.href ?? "");
|
|
1191
|
+
const row = document.createElement("div");
|
|
1192
|
+
row.className = "bb-linkpanel-row";
|
|
1193
|
+
row.appendChild(u.wrap);
|
|
1194
|
+
u.wrap.style.flex = "1";
|
|
1195
|
+
const apply = document.createElement("button");
|
|
1196
|
+
apply.type = "button";
|
|
1197
|
+
apply.className = "bb-linkpanel-apply";
|
|
1198
|
+
apply.textContent = "Apply";
|
|
1199
|
+
apply.disabled = !u.input.value.trim();
|
|
1200
|
+
u.input.addEventListener("input", () => {
|
|
1201
|
+
apply.disabled = !u.input.value.trim();
|
|
1202
|
+
});
|
|
1203
|
+
const submit = () => {
|
|
1204
|
+
const href = u.input.value.trim();
|
|
1205
|
+
if (!href) return;
|
|
1206
|
+
const text = textInput?.value ?? void 0;
|
|
1207
|
+
closeCurrent2();
|
|
1208
|
+
opts.onApply(href, text);
|
|
1209
|
+
};
|
|
1210
|
+
apply.addEventListener("click", submit);
|
|
1211
|
+
for (const input of [textInput, u.input]) {
|
|
1212
|
+
input?.addEventListener("keydown", (e) => {
|
|
1213
|
+
if (e.key === "Enter") {
|
|
1214
|
+
e.preventDefault();
|
|
1215
|
+
submit();
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
row.appendChild(apply);
|
|
1220
|
+
form.appendChild(row);
|
|
1221
|
+
el.appendChild(form);
|
|
1222
|
+
place(el, opts.anchor);
|
|
1223
|
+
u.input.focus();
|
|
1224
|
+
u.input.select();
|
|
1225
|
+
};
|
|
1226
|
+
document.body.appendChild(el);
|
|
1227
|
+
if (opts.existing) renderView(opts.existing);
|
|
1228
|
+
else renderForm(null);
|
|
1229
|
+
const onDown = (e) => {
|
|
1230
|
+
if (el.contains(e.target)) return;
|
|
1231
|
+
if (opts.key) {
|
|
1232
|
+
cancelPendingClose();
|
|
1233
|
+
pendingClose = setTimeout(closeCurrent2, 80);
|
|
1234
|
+
} else {
|
|
1235
|
+
closeCurrent2();
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
const onKey = (e) => {
|
|
1239
|
+
if (e.key === "Escape") closeCurrent2();
|
|
1240
|
+
};
|
|
1241
|
+
const onScroll = () => closeCurrent2();
|
|
1242
|
+
setTimeout(() => {
|
|
1243
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
1244
|
+
document.addEventListener("keydown", onKey, true);
|
|
1245
|
+
window.addEventListener("scroll", onScroll, true);
|
|
1246
|
+
}, 0);
|
|
1247
|
+
const dispose = () => {
|
|
1248
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
1249
|
+
document.removeEventListener("keydown", onKey, true);
|
|
1250
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
1251
|
+
el.remove();
|
|
1252
|
+
};
|
|
1253
|
+
current2 = dispose;
|
|
1254
|
+
currentKey = opts.key ?? null;
|
|
1255
|
+
const handle = {
|
|
1256
|
+
close: () => {
|
|
1257
|
+
if (current2 === dispose) closeCurrent2();
|
|
1258
|
+
},
|
|
1259
|
+
isOpen: () => current2 === dispose
|
|
1260
|
+
};
|
|
1261
|
+
currentHandle = handle;
|
|
1262
|
+
return handle;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
939
1265
|
// packages/ui/src/lib/cell-properties.ts
|
|
940
1266
|
var FILL_PRESETS = ["#FCEBEB", "#FAEEDA", "#EAF3DE", "#E6F1FB", "#EEEDFE", "#FBEAF0", "#E1F5EE", "#F1EFE8", "#D3D1C7"];
|
|
941
1267
|
var PEN_COLORS = ["#000000", "#5F5E5A", "#B0B0B0", "#E24B4A", "#185FA5", "#1D9E75", "#BA7517"];
|
|
@@ -963,7 +1289,7 @@ var PRESETS = [
|
|
|
963
1289
|
{ key: "insideH", label: "Inside horizontal", sides: [0, 0, 0, 0, 1, 0], inside: true },
|
|
964
1290
|
{ key: "insideV", label: "Inside vertical", sides: [0, 0, 0, 0, 0, 1], inside: true }
|
|
965
1291
|
];
|
|
966
|
-
var
|
|
1292
|
+
var STYLE7 = `
|
|
967
1293
|
.bb-cp{display:flex;flex-direction:column;gap:16px;min-width:320px;color:var(--bb-ui-fg,#2c2c2a)}
|
|
968
1294
|
.bb-cp *{box-sizing:border-box}
|
|
969
1295
|
.bb-cp-label{font-size:12px;opacity:.7;margin-bottom:8px}
|
|
@@ -1004,7 +1330,7 @@ var section = (label) => {
|
|
|
1004
1330
|
return sec;
|
|
1005
1331
|
};
|
|
1006
1332
|
function openCellProperties({ initial, singleCell, onApply }) {
|
|
1007
|
-
injectStyle("bb-ui-cellprops-styles",
|
|
1333
|
+
injectStyle("bb-ui-cellprops-styles", STYLE7);
|
|
1008
1334
|
let background = initial.background;
|
|
1009
1335
|
let vAlign = initial.vAlign;
|
|
1010
1336
|
let penWidthPt = 1;
|
|
@@ -1177,7 +1503,7 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1177
1503
|
}
|
|
1178
1504
|
|
|
1179
1505
|
// packages/ui/src/lib/table-grid.ts
|
|
1180
|
-
var
|
|
1506
|
+
var STYLE8 = `
|
|
1181
1507
|
.bb-grid{display:flex;flex-direction:column;gap:6px;user-select:none}
|
|
1182
1508
|
.bb-grid-cells{display:grid;gap:2px}
|
|
1183
1509
|
.bb-grid-cell{width:15px;height:15px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:2px;background:var(--bb-ui-bg,#fff);cursor:pointer;padding:0}
|
|
@@ -1185,7 +1511,7 @@ var STYLE7 = `
|
|
|
1185
1511
|
.bb-grid-label{font-size:12px;text-align:center;opacity:.7}
|
|
1186
1512
|
`;
|
|
1187
1513
|
function tableGridPicker(options) {
|
|
1188
|
-
injectStyle("bb-ui-grid-styles",
|
|
1514
|
+
injectStyle("bb-ui-grid-styles", STYLE8);
|
|
1189
1515
|
const maxRows = options.maxRows ?? 8;
|
|
1190
1516
|
const maxCols = options.maxCols ?? 10;
|
|
1191
1517
|
const root = document.createElement("div");
|
|
@@ -1231,5 +1557,6 @@ function tableGridPicker(options) {
|
|
|
1231
1557
|
openCellProperties,
|
|
1232
1558
|
promptDialog,
|
|
1233
1559
|
showContextMenu,
|
|
1560
|
+
showLinkPanel,
|
|
1234
1561
|
tableGridPicker
|
|
1235
1562
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './lib/toolbar.js';
|
|
|
4
4
|
export * from './lib/menubar.js';
|
|
5
5
|
export * from './lib/find.js';
|
|
6
6
|
export * from './lib/context-menu.js';
|
|
7
|
+
export * from './lib/link-panel.js';
|
|
7
8
|
export * from './lib/cell-properties.js';
|
|
8
9
|
export * from './lib/table-grid.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACrE,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACrE,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -252,6 +252,20 @@ var STYLE2 = `
|
|
|
252
252
|
.bb-color-none{grid-column:1/-1;font:inherit;font-size:12px;padding:3px 0;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:5px;background:var(--bb-ui-bg,#fff);cursor:pointer}
|
|
253
253
|
.bb-color-none:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
254
254
|
.bb-i-bold{font-weight:700}.bb-i-italic{font-style:italic}.bb-i-underline{text-decoration:underline}.bb-i-strike{text-decoration:line-through}
|
|
255
|
+
.bb-toolbar-split{display:flex;align-items:center;gap:0}
|
|
256
|
+
.bb-toolbar-split .bb-toolbar-btn{min-width:26px;padding:0 5px}
|
|
257
|
+
.bb-split-arrow{min-width:14px !important;width:15px;padding:0 !important}
|
|
258
|
+
.bb-split-arrow svg{display:block}
|
|
259
|
+
.bb-split-pop{position:absolute;z-index:1200;display:grid;grid-template-columns:repeat(3,auto);gap:8px;padding:10px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16)}
|
|
260
|
+
.bb-split-pop[hidden]{display:none}
|
|
261
|
+
.bb-split-card{display:flex;flex-direction:column;gap:6px;width:76px;padding:9px 8px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:6px;background:transparent;cursor:pointer;font-family:inherit}
|
|
262
|
+
.bb-split-card:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
263
|
+
.bb-split-card.is-selected{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#b5d4f4)}
|
|
264
|
+
.bb-split-row{display:flex;align-items:center;gap:4px}
|
|
265
|
+
.bb-split-row[data-lvl="1"]{padding-left:9px}
|
|
266
|
+
.bb-split-row[data-lvl="2"]{padding-left:18px}
|
|
267
|
+
.bb-split-marker{flex:none;min-width:14px;font-size:9px;line-height:1.1;text-align:right;color:var(--bb-ui-fg,#2c2c2a);white-space:nowrap}
|
|
268
|
+
.bb-split-bar{flex:1;height:3px;border-radius:2px;background:var(--bb-ui-border,#d9d9d4)}
|
|
255
269
|
`;
|
|
256
270
|
function defaultToolbarGroups(commands) {
|
|
257
271
|
const names = [...commands].map((c) => c.name);
|
|
@@ -271,6 +285,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
271
285
|
const buttons = [];
|
|
272
286
|
const selects = [];
|
|
273
287
|
const colors = [];
|
|
288
|
+
const splits = [];
|
|
274
289
|
let latest = null;
|
|
275
290
|
for (const group of groups) {
|
|
276
291
|
const entries = group.filter(
|
|
@@ -346,7 +361,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
346
361
|
btn2.addEventListener("mousedown", (e) => e.preventDefault());
|
|
347
362
|
btn2.addEventListener("click", () => {
|
|
348
363
|
const open = pop2.hidden;
|
|
349
|
-
wrap.querySelectorAll(".bb-color-pop").forEach((p) => p.hidden = true);
|
|
364
|
+
wrap.querySelectorAll(".bb-color-pop,.bb-split-pop").forEach((p) => p.hidden = true);
|
|
350
365
|
pop2.hidden = !open;
|
|
351
366
|
if (!pop2.hidden) {
|
|
352
367
|
const wrapRect = wrap.getBoundingClientRect();
|
|
@@ -375,6 +390,94 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
375
390
|
colors.push({ spec: entry, bar });
|
|
376
391
|
continue;
|
|
377
392
|
}
|
|
393
|
+
if (typeof entry !== "string" && entry.kind === "split") {
|
|
394
|
+
const splitWrap = document.createElement("div");
|
|
395
|
+
splitWrap.className = "bb-toolbar-split";
|
|
396
|
+
const item2 = items[entry.name] ?? { title: entry.name };
|
|
397
|
+
const main = document.createElement("button");
|
|
398
|
+
main.type = "button";
|
|
399
|
+
main.className = "bb-toolbar-btn";
|
|
400
|
+
main.title = item2.title;
|
|
401
|
+
main.setAttribute("aria-label", item2.title);
|
|
402
|
+
if (item2.svg) main.innerHTML = item2.svg;
|
|
403
|
+
else main.textContent = item2.label ?? entry.name;
|
|
404
|
+
main.addEventListener("mousedown", (e) => e.preventDefault());
|
|
405
|
+
main.addEventListener("click", () => {
|
|
406
|
+
if (!latest) return;
|
|
407
|
+
editor.commands.get(entry.name)?.run(latest, (tr) => editor.dispatch(tr));
|
|
408
|
+
editor.focus();
|
|
409
|
+
});
|
|
410
|
+
buttons.push({ name: entry.name, el: main });
|
|
411
|
+
const arrow = document.createElement("button");
|
|
412
|
+
arrow.type = "button";
|
|
413
|
+
arrow.className = "bb-toolbar-btn bb-split-arrow";
|
|
414
|
+
arrow.title = `${item2.title} styles`;
|
|
415
|
+
arrow.setAttribute("aria-label", `${item2.title} styles`);
|
|
416
|
+
arrow.setAttribute("aria-haspopup", "true");
|
|
417
|
+
arrow.innerHTML = '<svg viewBox="0 0 8 8" width="8" height="8" fill="currentColor" aria-hidden="true"><path d="M1 2.5h6L4 6z"/></svg>';
|
|
418
|
+
const pop2 = document.createElement("div");
|
|
419
|
+
pop2.className = "bb-split-pop";
|
|
420
|
+
pop2.hidden = true;
|
|
421
|
+
const cards = [];
|
|
422
|
+
for (const opt of entry.options) {
|
|
423
|
+
const card = document.createElement("button");
|
|
424
|
+
card.type = "button";
|
|
425
|
+
card.className = "bb-split-card";
|
|
426
|
+
card.dataset["value"] = opt.value;
|
|
427
|
+
if (opt.title) card.title = opt.title;
|
|
428
|
+
opt.rows.forEach((marker, i) => {
|
|
429
|
+
const row = document.createElement("span");
|
|
430
|
+
row.className = "bb-split-row";
|
|
431
|
+
row.dataset["lvl"] = String(i);
|
|
432
|
+
const m = document.createElement("span");
|
|
433
|
+
m.className = "bb-split-marker";
|
|
434
|
+
m.textContent = marker;
|
|
435
|
+
const bar = document.createElement("span");
|
|
436
|
+
bar.className = "bb-split-bar";
|
|
437
|
+
row.append(m, bar);
|
|
438
|
+
card.appendChild(row);
|
|
439
|
+
});
|
|
440
|
+
card.addEventListener("mousedown", (e) => e.preventDefault());
|
|
441
|
+
card.addEventListener("click", () => {
|
|
442
|
+
entry.onSelect(opt.value);
|
|
443
|
+
pop2.hidden = true;
|
|
444
|
+
editor.focus();
|
|
445
|
+
});
|
|
446
|
+
pop2.appendChild(card);
|
|
447
|
+
cards.push(card);
|
|
448
|
+
}
|
|
449
|
+
arrow.addEventListener("mousedown", (e) => e.preventDefault());
|
|
450
|
+
arrow.addEventListener("click", () => {
|
|
451
|
+
const open = pop2.hidden;
|
|
452
|
+
wrap.querySelectorAll(".bb-split-pop,.bb-color-pop").forEach((p) => p.hidden = true);
|
|
453
|
+
pop2.hidden = !open;
|
|
454
|
+
if (!pop2.hidden) {
|
|
455
|
+
const wrapRect = wrap.getBoundingClientRect();
|
|
456
|
+
const btnRect = arrow.getBoundingClientRect();
|
|
457
|
+
pop2.style.top = `${btnRect.bottom - wrapRect.top + 4}px`;
|
|
458
|
+
const left = Math.max(
|
|
459
|
+
0,
|
|
460
|
+
Math.min(
|
|
461
|
+
btnRect.left - wrapRect.left,
|
|
462
|
+
wrap.clientWidth - pop2.offsetWidth - 4
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
pop2.style.left = `${left}px`;
|
|
466
|
+
const onDoc = (ev) => {
|
|
467
|
+
if (!splitWrap.contains(ev.target) && !pop2.contains(ev.target)) {
|
|
468
|
+
pop2.hidden = true;
|
|
469
|
+
document.removeEventListener("pointerdown", onDoc, true);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
document.addEventListener("pointerdown", onDoc, true);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
splitWrap.append(main, arrow);
|
|
476
|
+
wrap.appendChild(pop2);
|
|
477
|
+
groupEl.appendChild(splitWrap);
|
|
478
|
+
splits.push({ spec: entry, cards });
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
378
481
|
const item = items[entry] ?? { title: entry };
|
|
379
482
|
const btn = document.createElement("button");
|
|
380
483
|
btn.type = "button";
|
|
@@ -426,7 +529,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
426
529
|
while (pop.firstChild) root.insertBefore(pop.firstChild, moreBtn);
|
|
427
530
|
moreBtn.style.display = "none";
|
|
428
531
|
closePop();
|
|
429
|
-
wrap.querySelectorAll(".bb-color-pop").forEach((p) => p.hidden = true);
|
|
532
|
+
wrap.querySelectorAll(".bb-color-pop,.bb-split-pop").forEach((p) => p.hidden = true);
|
|
430
533
|
if (fits()) return;
|
|
431
534
|
moreBtn.style.display = "";
|
|
432
535
|
const groupEls = Array.from(root.children).filter(
|
|
@@ -456,6 +559,14 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
456
559
|
for (const { spec, el } of selects) el.value = spec.value(state);
|
|
457
560
|
for (const { spec, bar } of colors)
|
|
458
561
|
bar.style.background = spec.value(state) ?? "";
|
|
562
|
+
for (const { spec, cards } of splits) {
|
|
563
|
+
const selected = spec.value(state);
|
|
564
|
+
for (const card of cards)
|
|
565
|
+
card.classList.toggle(
|
|
566
|
+
"is-selected",
|
|
567
|
+
selected != null && card.dataset["value"] === selected
|
|
568
|
+
);
|
|
569
|
+
}
|
|
459
570
|
};
|
|
460
571
|
const off = editor.onChange((c) => refresh(c.state));
|
|
461
572
|
try {
|
|
@@ -869,9 +980,16 @@ function showContextMenu(entries, at) {
|
|
|
869
980
|
}
|
|
870
981
|
document.body.appendChild(el);
|
|
871
982
|
const r = el.getBoundingClientRect();
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
const
|
|
983
|
+
const pad = 4;
|
|
984
|
+
const fitsX = (v) => v >= pad && v + r.width <= window.innerWidth - pad;
|
|
985
|
+
const fitsY = (v) => v >= pad && v + r.height <= window.innerHeight - pad;
|
|
986
|
+
const x = fitsX(at.x) ? at.x : fitsX(at.x - r.width) ? at.x - r.width : Math.max(pad, Math.min(at.x, window.innerWidth - r.width - pad));
|
|
987
|
+
const y = fitsY(at.y) ? at.y : fitsY(at.y - r.height) ? at.y - r.height : Math.max(pad, Math.min(at.y, window.innerHeight - r.height - pad));
|
|
988
|
+
el.style.left = `${x}px`;
|
|
989
|
+
el.style.top = `${y}px`;
|
|
990
|
+
const items = () => Array.from(
|
|
991
|
+
el.querySelectorAll(".bb-ctx-item:not(:disabled)")
|
|
992
|
+
);
|
|
875
993
|
const onDown = (e) => {
|
|
876
994
|
if (!el.contains(e.target)) closeCurrent();
|
|
877
995
|
};
|
|
@@ -901,6 +1019,213 @@ function showContextMenu(entries, at) {
|
|
|
901
1019
|
return { close: closeCurrent };
|
|
902
1020
|
}
|
|
903
1021
|
|
|
1022
|
+
// packages/ui/src/lib/link-panel.ts
|
|
1023
|
+
var STYLE6 = `
|
|
1024
|
+
.bb-linkpanel{position:fixed;z-index:1200;width:300px;padding:8px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);color:var(--bb-ui-fg,#2c2c2a);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);box-sizing:border-box}
|
|
1025
|
+
.bb-linkpanel *{box-sizing:border-box}
|
|
1026
|
+
.bb-linkpanel-row{display:flex;align-items:center;gap:8px}
|
|
1027
|
+
.bb-linkpanel-form{display:flex;flex-direction:column;gap:8px}
|
|
1028
|
+
.bb-linkpanel-field{display:flex;align-items:center;gap:7px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:7px;padding:0 9px;background:var(--bb-ui-bg,#fff)}
|
|
1029
|
+
.bb-linkpanel-field:focus-within{border-color:var(--bb-ui-accent,#d85a30)}
|
|
1030
|
+
.bb-linkpanel-field svg{flex:none;opacity:.55}
|
|
1031
|
+
.bb-linkpanel-input{flex:1;min-width:0;height:30px;border:0;background:transparent;color:inherit;font:inherit;font-size:13px;outline:none}
|
|
1032
|
+
.bb-linkpanel-href{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--bb-ui-accent,#d85a30)}
|
|
1033
|
+
.bb-linkpanel-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:0;border-radius:6px;background:transparent;color:inherit;cursor:pointer;flex:none}
|
|
1034
|
+
.bb-linkpanel-btn:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
1035
|
+
.bb-linkpanel-apply{font:inherit;font-size:13px;font-weight:600;color:var(--bb-ui-accent,#d85a30);background:transparent;border:0;border-radius:6px;padding:6px 8px;cursor:pointer;flex:none}
|
|
1036
|
+
.bb-linkpanel-apply:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
1037
|
+
.bb-linkpanel-apply:disabled{opacity:.4;cursor:default}
|
|
1038
|
+
`;
|
|
1039
|
+
var ICONS = {
|
|
1040
|
+
link: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 9.5 9.5 6.5"/><path d="M7.5 4.5 9 3a2.47 2.47 0 0 1 3.5 0L13 3.5a2.47 2.47 0 0 1 0 3.5l-1.5 1.5"/><path d="M8.5 11.5 7 13a2.47 2.47 0 0 1-3.5 0L3 12.5a2.47 2.47 0 0 1 0-3.5l1.5-1.5"/></svg>',
|
|
1041
|
+
text: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4V2.5h10V4M8 2.5v11M6 13.5h4"/></svg>',
|
|
1042
|
+
copy: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5v-2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2"/></svg>',
|
|
1043
|
+
edit: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9.5 3.5 3 3L6 13l-3.5.5L3 10z"/><path d="m11 2 .9-.9a1.55 1.55 0 0 1 2.2 0l.8.8a1.55 1.55 0 0 1 0 2.2L14 5"/></svg>',
|
|
1044
|
+
unlink: '<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.5 4.5 9 3a2.47 2.47 0 0 1 3.5 0L13 3.5a2.47 2.47 0 0 1 0 3.5l-1.5 1.5"/><path d="M8.5 11.5 7 13a2.47 2.47 0 0 1-3.5 0L3 12.5a2.47 2.47 0 0 1 0-3.5l1.5-1.5"/><path d="m3 3 10 10"/></svg>'
|
|
1045
|
+
};
|
|
1046
|
+
var current2 = null;
|
|
1047
|
+
var currentKey = null;
|
|
1048
|
+
var currentHandle = null;
|
|
1049
|
+
var pendingClose = null;
|
|
1050
|
+
function cancelPendingClose() {
|
|
1051
|
+
if (pendingClose !== null) {
|
|
1052
|
+
clearTimeout(pendingClose);
|
|
1053
|
+
pendingClose = null;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function closeCurrent2() {
|
|
1057
|
+
cancelPendingClose();
|
|
1058
|
+
if (current2) {
|
|
1059
|
+
const dispose = current2;
|
|
1060
|
+
current2 = null;
|
|
1061
|
+
currentKey = null;
|
|
1062
|
+
currentHandle = null;
|
|
1063
|
+
dispose();
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
function place(el, anchor) {
|
|
1067
|
+
const r = el.getBoundingClientRect();
|
|
1068
|
+
const pad = 6;
|
|
1069
|
+
const gap = 6;
|
|
1070
|
+
const x = Math.max(
|
|
1071
|
+
pad,
|
|
1072
|
+
Math.min(anchor.x, window.innerWidth - r.width - pad)
|
|
1073
|
+
);
|
|
1074
|
+
const below = anchor.y + anchor.height + gap;
|
|
1075
|
+
const above = anchor.y - gap - r.height;
|
|
1076
|
+
const y = below + r.height <= window.innerHeight - pad || above < pad ? Math.min(below, window.innerHeight - r.height - pad) : above;
|
|
1077
|
+
el.style.left = `${x}px`;
|
|
1078
|
+
el.style.top = `${y}px`;
|
|
1079
|
+
}
|
|
1080
|
+
function showLinkPanel(opts) {
|
|
1081
|
+
injectStyle("bb-ui-linkpanel-styles", STYLE6);
|
|
1082
|
+
if (opts.key && current2 && currentKey === opts.key && currentHandle) {
|
|
1083
|
+
cancelPendingClose();
|
|
1084
|
+
return currentHandle;
|
|
1085
|
+
}
|
|
1086
|
+
closeCurrent2();
|
|
1087
|
+
const el = document.createElement("div");
|
|
1088
|
+
el.className = "bb-linkpanel";
|
|
1089
|
+
el.setAttribute("role", "dialog");
|
|
1090
|
+
el.setAttribute("aria-label", "Link");
|
|
1091
|
+
const iconBtn = (svg, label, onClick) => {
|
|
1092
|
+
const b = document.createElement("button");
|
|
1093
|
+
b.type = "button";
|
|
1094
|
+
b.className = "bb-linkpanel-btn";
|
|
1095
|
+
b.title = label;
|
|
1096
|
+
b.setAttribute("aria-label", label);
|
|
1097
|
+
b.innerHTML = svg;
|
|
1098
|
+
b.addEventListener("mousedown", (e) => e.preventDefault());
|
|
1099
|
+
b.addEventListener("click", onClick);
|
|
1100
|
+
return b;
|
|
1101
|
+
};
|
|
1102
|
+
const field = (icon, placeholder, value) => {
|
|
1103
|
+
const wrap = document.createElement("div");
|
|
1104
|
+
wrap.className = "bb-linkpanel-field";
|
|
1105
|
+
wrap.innerHTML = icon;
|
|
1106
|
+
const input = document.createElement("input");
|
|
1107
|
+
input.className = "bb-linkpanel-input";
|
|
1108
|
+
input.type = "text";
|
|
1109
|
+
input.placeholder = placeholder;
|
|
1110
|
+
input.value = value;
|
|
1111
|
+
input.spellcheck = false;
|
|
1112
|
+
input.autocomplete = "off";
|
|
1113
|
+
wrap.appendChild(input);
|
|
1114
|
+
return { wrap, input };
|
|
1115
|
+
};
|
|
1116
|
+
const renderView = (existing) => {
|
|
1117
|
+
el.textContent = "";
|
|
1118
|
+
const row = document.createElement("div");
|
|
1119
|
+
row.className = "bb-linkpanel-row";
|
|
1120
|
+
const href = document.createElement("span");
|
|
1121
|
+
href.className = "bb-linkpanel-href";
|
|
1122
|
+
href.textContent = existing.href;
|
|
1123
|
+
href.title = existing.href;
|
|
1124
|
+
row.appendChild(href);
|
|
1125
|
+
row.appendChild(
|
|
1126
|
+
iconBtn(ICONS.copy, "Copy link", () => {
|
|
1127
|
+
void navigator.clipboard?.writeText(existing.href).catch(() => void 0);
|
|
1128
|
+
closeCurrent2();
|
|
1129
|
+
})
|
|
1130
|
+
);
|
|
1131
|
+
row.appendChild(
|
|
1132
|
+
iconBtn(ICONS.edit, "Edit link", () => renderForm(existing))
|
|
1133
|
+
);
|
|
1134
|
+
row.appendChild(
|
|
1135
|
+
iconBtn(ICONS.unlink, "Remove link", () => {
|
|
1136
|
+
closeCurrent2();
|
|
1137
|
+
opts.onUnlink?.();
|
|
1138
|
+
})
|
|
1139
|
+
);
|
|
1140
|
+
el.appendChild(row);
|
|
1141
|
+
place(el, opts.anchor);
|
|
1142
|
+
};
|
|
1143
|
+
const renderForm = (prefill) => {
|
|
1144
|
+
el.textContent = "";
|
|
1145
|
+
const form = document.createElement("div");
|
|
1146
|
+
form.className = "bb-linkpanel-form";
|
|
1147
|
+
const withText = !opts.hasSelection;
|
|
1148
|
+
let textInput = null;
|
|
1149
|
+
if (withText) {
|
|
1150
|
+
const t = field(ICONS.text, "Text (optional)", prefill?.text ?? "");
|
|
1151
|
+
textInput = t.input;
|
|
1152
|
+
form.appendChild(t.wrap);
|
|
1153
|
+
}
|
|
1154
|
+
const u = field(ICONS.link, "Paste or type a link", prefill?.href ?? "");
|
|
1155
|
+
const row = document.createElement("div");
|
|
1156
|
+
row.className = "bb-linkpanel-row";
|
|
1157
|
+
row.appendChild(u.wrap);
|
|
1158
|
+
u.wrap.style.flex = "1";
|
|
1159
|
+
const apply = document.createElement("button");
|
|
1160
|
+
apply.type = "button";
|
|
1161
|
+
apply.className = "bb-linkpanel-apply";
|
|
1162
|
+
apply.textContent = "Apply";
|
|
1163
|
+
apply.disabled = !u.input.value.trim();
|
|
1164
|
+
u.input.addEventListener("input", () => {
|
|
1165
|
+
apply.disabled = !u.input.value.trim();
|
|
1166
|
+
});
|
|
1167
|
+
const submit = () => {
|
|
1168
|
+
const href = u.input.value.trim();
|
|
1169
|
+
if (!href) return;
|
|
1170
|
+
const text = textInput?.value ?? void 0;
|
|
1171
|
+
closeCurrent2();
|
|
1172
|
+
opts.onApply(href, text);
|
|
1173
|
+
};
|
|
1174
|
+
apply.addEventListener("click", submit);
|
|
1175
|
+
for (const input of [textInput, u.input]) {
|
|
1176
|
+
input?.addEventListener("keydown", (e) => {
|
|
1177
|
+
if (e.key === "Enter") {
|
|
1178
|
+
e.preventDefault();
|
|
1179
|
+
submit();
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
row.appendChild(apply);
|
|
1184
|
+
form.appendChild(row);
|
|
1185
|
+
el.appendChild(form);
|
|
1186
|
+
place(el, opts.anchor);
|
|
1187
|
+
u.input.focus();
|
|
1188
|
+
u.input.select();
|
|
1189
|
+
};
|
|
1190
|
+
document.body.appendChild(el);
|
|
1191
|
+
if (opts.existing) renderView(opts.existing);
|
|
1192
|
+
else renderForm(null);
|
|
1193
|
+
const onDown = (e) => {
|
|
1194
|
+
if (el.contains(e.target)) return;
|
|
1195
|
+
if (opts.key) {
|
|
1196
|
+
cancelPendingClose();
|
|
1197
|
+
pendingClose = setTimeout(closeCurrent2, 80);
|
|
1198
|
+
} else {
|
|
1199
|
+
closeCurrent2();
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
const onKey = (e) => {
|
|
1203
|
+
if (e.key === "Escape") closeCurrent2();
|
|
1204
|
+
};
|
|
1205
|
+
const onScroll = () => closeCurrent2();
|
|
1206
|
+
setTimeout(() => {
|
|
1207
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
1208
|
+
document.addEventListener("keydown", onKey, true);
|
|
1209
|
+
window.addEventListener("scroll", onScroll, true);
|
|
1210
|
+
}, 0);
|
|
1211
|
+
const dispose = () => {
|
|
1212
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
1213
|
+
document.removeEventListener("keydown", onKey, true);
|
|
1214
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
1215
|
+
el.remove();
|
|
1216
|
+
};
|
|
1217
|
+
current2 = dispose;
|
|
1218
|
+
currentKey = opts.key ?? null;
|
|
1219
|
+
const handle = {
|
|
1220
|
+
close: () => {
|
|
1221
|
+
if (current2 === dispose) closeCurrent2();
|
|
1222
|
+
},
|
|
1223
|
+
isOpen: () => current2 === dispose
|
|
1224
|
+
};
|
|
1225
|
+
currentHandle = handle;
|
|
1226
|
+
return handle;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
904
1229
|
// packages/ui/src/lib/cell-properties.ts
|
|
905
1230
|
var FILL_PRESETS = ["#FCEBEB", "#FAEEDA", "#EAF3DE", "#E6F1FB", "#EEEDFE", "#FBEAF0", "#E1F5EE", "#F1EFE8", "#D3D1C7"];
|
|
906
1231
|
var PEN_COLORS = ["#000000", "#5F5E5A", "#B0B0B0", "#E24B4A", "#185FA5", "#1D9E75", "#BA7517"];
|
|
@@ -928,7 +1253,7 @@ var PRESETS = [
|
|
|
928
1253
|
{ key: "insideH", label: "Inside horizontal", sides: [0, 0, 0, 0, 1, 0], inside: true },
|
|
929
1254
|
{ key: "insideV", label: "Inside vertical", sides: [0, 0, 0, 0, 0, 1], inside: true }
|
|
930
1255
|
];
|
|
931
|
-
var
|
|
1256
|
+
var STYLE7 = `
|
|
932
1257
|
.bb-cp{display:flex;flex-direction:column;gap:16px;min-width:320px;color:var(--bb-ui-fg,#2c2c2a)}
|
|
933
1258
|
.bb-cp *{box-sizing:border-box}
|
|
934
1259
|
.bb-cp-label{font-size:12px;opacity:.7;margin-bottom:8px}
|
|
@@ -969,7 +1294,7 @@ var section = (label) => {
|
|
|
969
1294
|
return sec;
|
|
970
1295
|
};
|
|
971
1296
|
function openCellProperties({ initial, singleCell, onApply }) {
|
|
972
|
-
injectStyle("bb-ui-cellprops-styles",
|
|
1297
|
+
injectStyle("bb-ui-cellprops-styles", STYLE7);
|
|
973
1298
|
let background = initial.background;
|
|
974
1299
|
let vAlign = initial.vAlign;
|
|
975
1300
|
let penWidthPt = 1;
|
|
@@ -1142,7 +1467,7 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1142
1467
|
}
|
|
1143
1468
|
|
|
1144
1469
|
// packages/ui/src/lib/table-grid.ts
|
|
1145
|
-
var
|
|
1470
|
+
var STYLE8 = `
|
|
1146
1471
|
.bb-grid{display:flex;flex-direction:column;gap:6px;user-select:none}
|
|
1147
1472
|
.bb-grid-cells{display:grid;gap:2px}
|
|
1148
1473
|
.bb-grid-cell{width:15px;height:15px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:2px;background:var(--bb-ui-bg,#fff);cursor:pointer;padding:0}
|
|
@@ -1150,7 +1475,7 @@ var STYLE7 = `
|
|
|
1150
1475
|
.bb-grid-label{font-size:12px;text-align:center;opacity:.7}
|
|
1151
1476
|
`;
|
|
1152
1477
|
function tableGridPicker(options) {
|
|
1153
|
-
injectStyle("bb-ui-grid-styles",
|
|
1478
|
+
injectStyle("bb-ui-grid-styles", STYLE8);
|
|
1154
1479
|
const maxRows = options.maxRows ?? 8;
|
|
1155
1480
|
const maxCols = options.maxCols ?? 10;
|
|
1156
1481
|
const root = document.createElement("div");
|
|
@@ -1195,5 +1520,6 @@ export {
|
|
|
1195
1520
|
openCellProperties,
|
|
1196
1521
|
promptDialog,
|
|
1197
1522
|
showContextMenu,
|
|
1523
|
+
showLinkPanel,
|
|
1198
1524
|
tableGridPicker
|
|
1199
1525
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../../src/lib/context-menu.ts"],"names":[],"mappings":"AAEA,mCAAmC;AACnC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,IAAI,CAAC;CACf;AAuBD;;;;;GAKG;AACH,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../../src/lib/context-menu.ts"],"names":[],"mappings":"AAEA,mCAAmC;AACnC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,IAAI,CAAC;CACf;AAuBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,EAAE,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,iBAAiB,CAmGnB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Floating link panel, anchored at the caret / selection (Google Docs-style).
|
|
3
|
+
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
* - **view** — the caret sits inside an existing link: the href plus three
|
|
6
|
+
* icon actions (copy / edit / remove). Edit switches to the form.
|
|
7
|
+
* - **form with selection** — one URL field; Apply wraps the selection.
|
|
8
|
+
* - **form at a bare caret** — an optional "Text" field above the URL; left
|
|
9
|
+
* empty, the URL itself becomes the inserted text.
|
|
10
|
+
*
|
|
11
|
+
* The panel is one-shot DOM (no framework), closes on Escape / outside click /
|
|
12
|
+
* scroll, and positions with connected pairs: below the anchor first, flipped
|
|
13
|
+
* above when the viewport runs out.
|
|
14
|
+
*/
|
|
15
|
+
export interface LinkPanelAnchor {
|
|
16
|
+
/** Caret top-left in viewport CSS px + the caret's rendered height. */
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
height: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LinkPanelOptions {
|
|
22
|
+
anchor: LinkPanelAnchor;
|
|
23
|
+
/** Existing link under the caret → open in view mode (prefills the form). */
|
|
24
|
+
existing?: {
|
|
25
|
+
href: string;
|
|
26
|
+
text: string;
|
|
27
|
+
} | null;
|
|
28
|
+
/** A text range is selected → the Text field is hidden (the selection is
|
|
29
|
+
* the text). */
|
|
30
|
+
hasSelection?: boolean;
|
|
31
|
+
/** Identity for keep-alive: with a key, an outside click defers the close
|
|
32
|
+
* briefly, and re-showing the SAME key within that window keeps the
|
|
33
|
+
* existing panel untouched — so a caret moving within one link never
|
|
34
|
+
* re-creates it. Hosts pass e.g. the link's doc range. */
|
|
35
|
+
key?: string;
|
|
36
|
+
/** Apply: `text` is present only when the Text field was shown. */
|
|
37
|
+
onApply(href: string, text?: string): void;
|
|
38
|
+
onUnlink?(): void;
|
|
39
|
+
}
|
|
40
|
+
export interface LinkPanelHandle {
|
|
41
|
+
close(): void;
|
|
42
|
+
/** Whether this panel is still on screen (hosts skip redundant reopens —
|
|
43
|
+
* e.g. the caret moving WITHIN the same link should not repaint). */
|
|
44
|
+
isOpen(): boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function showLinkPanel(opts: LinkPanelOptions): LinkPanelHandle;
|
|
47
|
+
//# sourceMappingURL=link-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-panel.d.ts","sourceRoot":"","sources":["../../src/lib/link-panel.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD;qBACiB;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;+DAG2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,IAAI,IAAI,CAAC;IACd;0EACsE;IACtE,MAAM,IAAI,OAAO,CAAC;CACnB;AAwED,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAgLrE"}
|
package/dist/lib/toolbar.d.ts
CHANGED
|
@@ -46,8 +46,28 @@ export interface ToolbarColor {
|
|
|
46
46
|
/** Called with the picked colour (null = cleared). */
|
|
47
47
|
onSelect: (color: string | null) => void;
|
|
48
48
|
}
|
|
49
|
-
/**
|
|
50
|
-
|
|
49
|
+
/** One style card in a split button's dropdown: three preview markers, one per
|
|
50
|
+
* nesting level (e.g. ['1.', 'a.', 'i.']), rendered beside placeholder bars. */
|
|
51
|
+
export interface ToolbarSplitOption {
|
|
52
|
+
value: string;
|
|
53
|
+
rows: [string, string, string];
|
|
54
|
+
title?: string;
|
|
55
|
+
}
|
|
56
|
+
/** A split button: the main button runs the command `name` (toggle, with its
|
|
57
|
+
* active/enabled tracking and icon), and a narrow ▾ opens a dropdown of style
|
|
58
|
+
* cards. The host owns the parameterized command each card runs. */
|
|
59
|
+
export interface ToolbarSplit {
|
|
60
|
+
kind: 'split';
|
|
61
|
+
/** Command for the main button — also the icon/tooltip lookup key. */
|
|
62
|
+
name: string;
|
|
63
|
+
options: ToolbarSplitOption[];
|
|
64
|
+
/** Currently applied option value (highlights its card), or null. */
|
|
65
|
+
value: (state: EditorStateOf) => string | null;
|
|
66
|
+
/** Called when the user picks a card. */
|
|
67
|
+
onSelect: (value: string) => void;
|
|
68
|
+
}
|
|
69
|
+
/** A toolbar group entry: a command name (button) or a control (select/colour/split). */
|
|
70
|
+
export type ToolbarEntry = string | ToolbarSelect | ToolbarColor | ToolbarSplit;
|
|
51
71
|
export interface ToolbarOptions {
|
|
52
72
|
/** Groups rendered as separated clusters — command names (buttons) and/or
|
|
53
73
|
* controls (selects). Defaults to marks then alignments, from the registry. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../../src/lib/toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAEvB;wCACwC;AACxC,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;wEAEwE;AACxE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,qEAAqE;IACrE,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;8EAC8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,sDAAsD;IACtD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC1C;AAED,
|
|
1
|
+
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../../src/lib/toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAEvB;wCACwC;AACxC,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;wEAEwE;AACxE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,qEAAqE;IACrE,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;8EAC8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,sDAAsD;IACtD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC1C;AAED;iFACiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;qEAEqE;AACrE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,qEAAqE;IACrE,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,yCAAyC;IACzC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,CAAC;AAEhF,MAAM,WAAW,cAAc;IAC7B;oFACgF;IAChF,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC;IAC1B,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACrC;AAED,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAyHD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,GAC5B,MAAM,EAAE,EAAE,CAKZ;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAwWf"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadow-garden/bapbong-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "bapbong — framework-agnostic editor UI (toolbar/menubar) that mounts into a host element and binds to editor.commands",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@shadow-garden/bapbong-contracts": "^0.
|
|
59
|
+
"@shadow-garden/bapbong-contracts": "^0.3.0"
|
|
60
60
|
}
|
|
61
61
|
}
|