@shadow-garden/bapbong-ui 0.13.2 → 0.14.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 +1432 -153
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1423 -153
- package/dist/lib/color-picker.d.ts.map +1 -1
- package/dist/lib/dialog.d.ts +24 -0
- package/dist/lib/dialog.d.ts.map +1 -1
- package/dist/lib/internal.d.ts +25 -1
- package/dist/lib/internal.d.ts.map +1 -1
- package/dist/lib/menubar.d.ts +11 -1
- package/dist/lib/menubar.d.ts.map +1 -1
- package/dist/lib/scrollbars.d.ts +4 -0
- package/dist/lib/scrollbars.d.ts.map +1 -0
- package/dist/lib/shortcuts-dialog.d.ts +23 -0
- package/dist/lib/shortcuts-dialog.d.ts.map +1 -0
- package/dist/lib/symbol-dialog.d.ts +34 -0
- package/dist/lib/symbol-dialog.d.ts.map +1 -0
- package/dist/lib/symbol-popover.d.ts +30 -0
- package/dist/lib/symbol-popover.d.ts.map +1 -0
- package/dist/lib/symbol-sets.d.ts +53 -0
- package/dist/lib/symbol-sets.d.ts.map +1 -0
- package/dist/lib/toolbar.d.ts +18 -2
- package/dist/lib/toolbar.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
// packages/ui/src/lib/internal.ts
|
|
2
|
+
import { keyLabel } from "@shadow-garden/bapbong-contracts";
|
|
3
|
+
var IS_MAC = typeof navigator !== "undefined" ? /Mac|iP(hone|[oa]d)/.test(navigator.platform) : false;
|
|
4
|
+
function shortcutLabel(command, registries) {
|
|
5
|
+
for (const r of registries) {
|
|
6
|
+
if (!r) continue;
|
|
7
|
+
for (const b of r)
|
|
8
|
+
if (b.command === command) return keyLabel(b.key, IS_MAC);
|
|
9
|
+
}
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
2
12
|
function injectStyle(id, css) {
|
|
3
13
|
if (document.getElementById(id)) return;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
document.head.appendChild(
|
|
14
|
+
const el5 = document.createElement("style");
|
|
15
|
+
el5.id = id;
|
|
16
|
+
el5.textContent = css;
|
|
17
|
+
document.head.appendChild(el5);
|
|
8
18
|
}
|
|
9
|
-
function placeFloating(
|
|
10
|
-
const r =
|
|
19
|
+
function placeFloating(el5, anchor) {
|
|
20
|
+
const r = el5.getBoundingClientRect();
|
|
11
21
|
const pad = 6;
|
|
12
22
|
const gap = 6;
|
|
13
23
|
const x = Math.max(
|
|
@@ -17,16 +27,75 @@ function placeFloating(el2, anchor) {
|
|
|
17
27
|
const below = anchor.y + anchor.height + gap;
|
|
18
28
|
const above = anchor.y - gap - r.height;
|
|
19
29
|
const y = below + r.height <= window.innerHeight - pad || above < pad ? Math.min(below, window.innerHeight - r.height - pad) : above;
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
el5.style.left = `${x}px`;
|
|
31
|
+
el5.style.top = `${y}px`;
|
|
32
|
+
}
|
|
33
|
+
function rovingGrid(cells) {
|
|
34
|
+
let cur = [0, 0];
|
|
35
|
+
const at = (r, c) => cells[r]?.[c];
|
|
36
|
+
const focusCell = (r, c) => {
|
|
37
|
+
const rows = cells.length;
|
|
38
|
+
if (rows === 0) return;
|
|
39
|
+
const nr = Math.min(rows - 1, Math.max(0, r));
|
|
40
|
+
const nc = Math.min(cells[nr].length - 1, Math.max(0, c));
|
|
41
|
+
const prev = at(cur[0], cur[1]);
|
|
42
|
+
if (prev) prev.tabIndex = -1;
|
|
43
|
+
const next = at(nr, nc);
|
|
44
|
+
if (!next) return;
|
|
45
|
+
next.tabIndex = 0;
|
|
46
|
+
next.focus();
|
|
47
|
+
cur = [nr, nc];
|
|
48
|
+
};
|
|
49
|
+
cells.forEach(
|
|
50
|
+
(row, r) => row.forEach((cell, c) => {
|
|
51
|
+
cell.setAttribute("role", "gridcell");
|
|
52
|
+
cell.tabIndex = r === 0 && c === 0 ? 0 : -1;
|
|
53
|
+
cell.addEventListener("keydown", (e) => {
|
|
54
|
+
const delta = {
|
|
55
|
+
ArrowRight: [0, 1],
|
|
56
|
+
ArrowLeft: [0, -1],
|
|
57
|
+
ArrowDown: [1, 0],
|
|
58
|
+
ArrowUp: [-1, 0],
|
|
59
|
+
Home: [0, -Infinity],
|
|
60
|
+
End: [0, Infinity]
|
|
61
|
+
};
|
|
62
|
+
const d = delta[e.key];
|
|
63
|
+
if (!d) return;
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
focusCell(r + d[0], c + d[1]);
|
|
66
|
+
});
|
|
67
|
+
cell.addEventListener("focus", () => {
|
|
68
|
+
cur = [r, c];
|
|
69
|
+
});
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
return { focusCell };
|
|
22
73
|
}
|
|
23
74
|
|
|
24
75
|
// packages/ui/src/lib/dialog.ts
|
|
76
|
+
function panelAnchor(o) {
|
|
77
|
+
return () => {
|
|
78
|
+
const areaEl = o.area();
|
|
79
|
+
if (!areaEl) return null;
|
|
80
|
+
const a = areaEl.getBoundingClientRect();
|
|
81
|
+
let top = a.top;
|
|
82
|
+
for (const el5 of o.avoid?.() ?? []) {
|
|
83
|
+
if (!el5) continue;
|
|
84
|
+
const b = el5.getBoundingClientRect();
|
|
85
|
+
const overlapsX = b.right > a.left && b.left < a.right;
|
|
86
|
+
const overTop = b.top < a.bottom && b.bottom > top;
|
|
87
|
+
if (overlapsX && overTop) top = Math.max(top, b.bottom);
|
|
88
|
+
}
|
|
89
|
+
if (top >= a.bottom) return null;
|
|
90
|
+
return new DOMRect(a.left, top, a.width, a.bottom - top);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
25
93
|
var STYLE = `
|
|
26
94
|
.bb-dialog{position:fixed;inset:auto;z-index:1100;margin:0;padding:0;border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;background:var(--bb-ui-dialog-bg,var(--bb-ui-menu-bg,#fff));-webkit-backdrop-filter:var(--bb-ui-dialog-filter,none);backdrop-filter:var(--bb-ui-dialog-filter,none);color:var(--bb-ui-fg,#2c2c2a);box-shadow:0 12px 40px rgba(0,0,0,.18);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);min-width:280px;max-width:min(92vw,440px)}
|
|
27
95
|
.bb-dialog *{box-sizing:border-box}
|
|
28
96
|
.bb-dialog::backdrop{background:rgba(0,0,0,.28)}
|
|
29
97
|
.bb-dialog-modal{top:50%;left:50%;transform:translate(-50%,-50%)}
|
|
98
|
+
.bb-dialog-wide{max-width:min(92vw,640px)}
|
|
30
99
|
.bb-dialog-float{top:62px;right:24px}
|
|
31
100
|
.bb-dialog-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 10px 9px 12px;border-bottom:1px solid var(--bb-ui-border,#e3e3e0)}
|
|
32
101
|
.bb-dialog-title{font-size:13px;font-weight:600}
|
|
@@ -60,8 +129,8 @@ var Dialog = class {
|
|
|
60
129
|
injectStyle("bb-ui-dialog-styles", STYLE);
|
|
61
130
|
this.modal = options.modal ?? false;
|
|
62
131
|
this.anchor = options.anchor;
|
|
63
|
-
const
|
|
64
|
-
|
|
132
|
+
const el5 = document.createElement("dialog");
|
|
133
|
+
el5.className = `bb-dialog ${this.modal ? "bb-dialog-modal" : "bb-dialog-float"}` + (options.wide ? " bb-dialog-wide" : "") + (options.className ? ` ${options.className}` : "");
|
|
65
134
|
const header = document.createElement("div");
|
|
66
135
|
header.className = "bb-dialog-header";
|
|
67
136
|
this.title = document.createElement("span");
|
|
@@ -76,18 +145,18 @@ var Dialog = class {
|
|
|
76
145
|
header.append(this.title, close);
|
|
77
146
|
this.body = document.createElement("div");
|
|
78
147
|
this.body.className = "bb-dialog-body";
|
|
79
|
-
|
|
80
|
-
document.body.appendChild(
|
|
81
|
-
|
|
148
|
+
el5.append(header, this.body);
|
|
149
|
+
document.body.appendChild(el5);
|
|
150
|
+
el5.addEventListener("close", () => {
|
|
82
151
|
this.detachReposition();
|
|
83
152
|
this.closeListeners.forEach((cb) => cb());
|
|
84
153
|
});
|
|
85
154
|
if (!this.modal) {
|
|
86
|
-
|
|
155
|
+
el5.addEventListener("keydown", (e) => {
|
|
87
156
|
if (e.key === "Escape") this.close();
|
|
88
157
|
});
|
|
89
158
|
}
|
|
90
|
-
this.el =
|
|
159
|
+
this.el = el5;
|
|
91
160
|
}
|
|
92
161
|
attachReposition() {
|
|
93
162
|
window.addEventListener("scroll", this.reposition, true);
|
|
@@ -346,45 +415,24 @@ function openColorPicker({
|
|
|
346
415
|
grid.className = "bb-cpk-grid";
|
|
347
416
|
grid.setAttribute("role", "grid");
|
|
348
417
|
const cells = [];
|
|
349
|
-
COLOR_PALETTE.forEach((row
|
|
418
|
+
COLOR_PALETTE.forEach((row) => {
|
|
350
419
|
const rowCells = [];
|
|
351
|
-
row.forEach((color
|
|
420
|
+
row.forEach((color) => {
|
|
352
421
|
const sw = document.createElement("button");
|
|
353
422
|
sw.type = "button";
|
|
354
423
|
sw.className = "bb-cpk-sw";
|
|
355
424
|
sw.style.background = color;
|
|
356
425
|
sw.title = color;
|
|
357
|
-
sw.setAttribute("role", "gridcell");
|
|
358
426
|
sw.setAttribute("aria-label", color);
|
|
359
|
-
sw.tabIndex = -1;
|
|
360
427
|
if (color.toUpperCase() === value?.toUpperCase()) sw.classList.add("on");
|
|
361
428
|
sw.addEventListener("click", () => pick(color));
|
|
362
|
-
sw.addEventListener("keydown", (e) => onGridKey(e, r2, c));
|
|
363
429
|
grid.append(sw);
|
|
364
430
|
rowCells.push(sw);
|
|
365
431
|
});
|
|
366
432
|
cells.push(rowCells);
|
|
367
433
|
});
|
|
368
434
|
panel.append(grid);
|
|
369
|
-
|
|
370
|
-
const delta = {
|
|
371
|
-
ArrowRight: [0, 1],
|
|
372
|
-
ArrowLeft: [0, -1],
|
|
373
|
-
ArrowDown: [1, 0],
|
|
374
|
-
ArrowUp: [-1, 0]
|
|
375
|
-
};
|
|
376
|
-
const d = delta[e.key];
|
|
377
|
-
if (!d) return;
|
|
378
|
-
e.preventDefault();
|
|
379
|
-
const rows = cells.length;
|
|
380
|
-
const cols = cells[0].length;
|
|
381
|
-
const nr = Math.min(rows - 1, Math.max(0, r2 + d[0]));
|
|
382
|
-
const nc = Math.min(cols - 1, Math.max(0, c + d[1]));
|
|
383
|
-
cells[r2][c].tabIndex = -1;
|
|
384
|
-
const next = cells[nr][nc];
|
|
385
|
-
next.tabIndex = 0;
|
|
386
|
-
next.focus();
|
|
387
|
-
}
|
|
435
|
+
const nav = rovingGrid(cells);
|
|
388
436
|
panel.append(
|
|
389
437
|
Object.assign(document.createElement("div"), {
|
|
390
438
|
className: "bb-cpk-cap",
|
|
@@ -440,36 +488,38 @@ function openColorPicker({
|
|
|
440
488
|
if (outside) close();
|
|
441
489
|
});
|
|
442
490
|
window.addEventListener("scroll", close, true);
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
491
|
+
const onRow = cells.findIndex(
|
|
492
|
+
(row) => row.some((el5) => el5.classList.contains("on"))
|
|
493
|
+
);
|
|
494
|
+
const onCol = onRow >= 0 ? cells[onRow].findIndex((el5) => el5.classList.contains("on")) : 0;
|
|
495
|
+
nav.focusCell(Math.max(0, onRow), Math.max(0, onCol));
|
|
446
496
|
return { close };
|
|
447
497
|
}
|
|
448
498
|
function colorButton(o) {
|
|
449
499
|
injectStyle("bb-ui-colorpicker-styles", STYLE2);
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
500
|
+
const el5 = document.createElement("button");
|
|
501
|
+
el5.type = "button";
|
|
502
|
+
el5.className = `bb-cpk-btn${o.glyph ? " glyphed" : ""}`;
|
|
503
|
+
el5.title = o.title;
|
|
504
|
+
el5.setAttribute("aria-label", o.title);
|
|
505
|
+
el5.setAttribute("aria-haspopup", "dialog");
|
|
456
506
|
let chip;
|
|
457
507
|
if (o.glyph) {
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
508
|
+
const g2 = document.createElement("span");
|
|
509
|
+
g2.className = "bb-cpk-btn-glyph";
|
|
510
|
+
g2.innerHTML = o.glyph;
|
|
461
511
|
chip = document.createElement("span");
|
|
462
512
|
chip.className = "bb-cpk-btn-bar";
|
|
463
|
-
|
|
513
|
+
el5.append(g2, chip);
|
|
464
514
|
} else {
|
|
465
515
|
chip = document.createElement("span");
|
|
466
516
|
chip.className = "bb-cpk-btn-chip";
|
|
467
|
-
|
|
468
|
-
if (o.label)
|
|
517
|
+
el5.append(chip);
|
|
518
|
+
if (o.label) el5.append(document.createTextNode(o.label));
|
|
469
519
|
const caret = document.createElement("span");
|
|
470
520
|
caret.className = "bb-cpk-btn-caret";
|
|
471
521
|
caret.textContent = "\u25BE";
|
|
472
|
-
|
|
522
|
+
el5.append(caret);
|
|
473
523
|
}
|
|
474
524
|
function sync() {
|
|
475
525
|
const v = o.value();
|
|
@@ -477,10 +527,10 @@ function colorButton(o) {
|
|
|
477
527
|
chip.classList.toggle("none", !v && !o.glyph);
|
|
478
528
|
chip.textContent = !v && !o.glyph ? "\u29B8" : "";
|
|
479
529
|
}
|
|
480
|
-
|
|
481
|
-
|
|
530
|
+
el5.addEventListener("mousedown", (e) => e.preventDefault());
|
|
531
|
+
el5.addEventListener("click", () => {
|
|
482
532
|
openColorPicker({
|
|
483
|
-
anchor:
|
|
533
|
+
anchor: el5,
|
|
484
534
|
value: o.value(),
|
|
485
535
|
clearLabel: o.clearLabel,
|
|
486
536
|
onPick: (c) => {
|
|
@@ -490,7 +540,7 @@ function colorButton(o) {
|
|
|
490
540
|
});
|
|
491
541
|
});
|
|
492
542
|
sync();
|
|
493
|
-
return { el:
|
|
543
|
+
return { el: el5, sync };
|
|
494
544
|
}
|
|
495
545
|
|
|
496
546
|
// packages/ui/src/lib/toolbar.ts
|
|
@@ -598,7 +648,7 @@ function defaultToolbarGroups(commands) {
|
|
|
598
648
|
const names = [...commands].map((c) => c.name);
|
|
599
649
|
const aligns = names.filter((n) => n.startsWith("align-"));
|
|
600
650
|
const rest = names.filter((n) => !n.startsWith("align-"));
|
|
601
|
-
return [rest, aligns].filter((
|
|
651
|
+
return [rest, aligns].filter((g2) => g2.length > 0);
|
|
602
652
|
}
|
|
603
653
|
function mountToolbar(host, editor, options = {}) {
|
|
604
654
|
injectStyle("bb-ui-toolbar-styles", STYLE3);
|
|
@@ -643,6 +693,20 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
643
693
|
selects.push({ spec: entry, el: sel });
|
|
644
694
|
continue;
|
|
645
695
|
}
|
|
696
|
+
if (typeof entry !== "string" && entry.kind === "button") {
|
|
697
|
+
const btn2 = document.createElement("button");
|
|
698
|
+
btn2.type = "button";
|
|
699
|
+
btn2.className = "bb-toolbar-btn";
|
|
700
|
+
btn2.title = entry.title;
|
|
701
|
+
btn2.setAttribute("aria-label", entry.title);
|
|
702
|
+
btn2.setAttribute("aria-haspopup", "dialog");
|
|
703
|
+
if (entry.svg) btn2.innerHTML = entry.svg;
|
|
704
|
+
else btn2.textContent = entry.label ?? entry.title;
|
|
705
|
+
btn2.addEventListener("mousedown", (e) => e.preventDefault());
|
|
706
|
+
btn2.addEventListener("click", () => entry.onClick(btn2));
|
|
707
|
+
groupEl.appendChild(btn2);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
646
710
|
if (typeof entry !== "string" && entry.kind === "color") {
|
|
647
711
|
const ctl = colorButton({
|
|
648
712
|
title: entry.title,
|
|
@@ -753,6 +817,10 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
753
817
|
btn.type = "button";
|
|
754
818
|
btn.className = "bb-toolbar-btn" + (item.className ? ` ${item.className}` : "");
|
|
755
819
|
btn.title = item.title;
|
|
820
|
+
btn.addEventListener("mouseenter", () => {
|
|
821
|
+
const key = shortcutLabel(entry, [editor.keybindings]);
|
|
822
|
+
btn.title = key ? `${item.title} (${key})` : item.title;
|
|
823
|
+
});
|
|
756
824
|
btn.setAttribute("aria-label", item.title);
|
|
757
825
|
if (item.svg) btn.innerHTML = item.svg;
|
|
758
826
|
else btn.textContent = item.label ?? entry;
|
|
@@ -803,7 +871,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
803
871
|
if (fits()) return;
|
|
804
872
|
moreBtn.style.display = "";
|
|
805
873
|
const groupEls = Array.from(root.children).filter(
|
|
806
|
-
(
|
|
874
|
+
(el5) => el5.classList.contains("bb-toolbar-group")
|
|
807
875
|
);
|
|
808
876
|
for (let i = groupEls.length - 1; i >= 0 && !fits(); i--) {
|
|
809
877
|
pop.insertBefore(groupEls[i], pop.firstChild);
|
|
@@ -818,15 +886,15 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
818
886
|
layout();
|
|
819
887
|
const refresh = (state) => {
|
|
820
888
|
latest = state;
|
|
821
|
-
for (const { name, el:
|
|
889
|
+
for (const { name, el: el5 } of buttons) {
|
|
822
890
|
const cmd = editor.commands.get(name);
|
|
823
891
|
if (!cmd) continue;
|
|
824
892
|
const active = cmd.isActive?.(state) ?? false;
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
893
|
+
el5.classList.toggle("is-active", active);
|
|
894
|
+
el5.setAttribute("aria-pressed", String(active));
|
|
895
|
+
el5.disabled = cmd.isEnabled ? !cmd.isEnabled(state) : false;
|
|
828
896
|
}
|
|
829
|
-
for (const { spec, el:
|
|
897
|
+
for (const { spec, el: el5 } of selects) el5.value = spec.value(state);
|
|
830
898
|
for (const c of colors) c.sync();
|
|
831
899
|
for (const { spec, cards } of splits) {
|
|
832
900
|
const selected = spec.value(state);
|
|
@@ -915,19 +983,18 @@ function makeRow(label, opts) {
|
|
|
915
983
|
text.className = "bb-menu-label";
|
|
916
984
|
text.textContent = label;
|
|
917
985
|
item.append(check, text);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
}
|
|
986
|
+
const sc = document.createElement("span");
|
|
987
|
+
sc.className = "bb-menu-shortcut";
|
|
988
|
+
sc.textContent = opts.shortcut ?? "";
|
|
989
|
+
if (!opts.shortcut) sc.hidden = true;
|
|
990
|
+
item.appendChild(sc);
|
|
924
991
|
if (opts.hasSub) {
|
|
925
992
|
const arrow = document.createElement("span");
|
|
926
993
|
arrow.className = "bb-menu-arrow";
|
|
927
994
|
arrow.textContent = "\u203A";
|
|
928
995
|
item.appendChild(arrow);
|
|
929
996
|
}
|
|
930
|
-
return { item, check };
|
|
997
|
+
return { item, check, shortcutEl: sc };
|
|
931
998
|
}
|
|
932
999
|
function buildMenuEntries(entries, container, ctx) {
|
|
933
1000
|
for (const entry of entries) {
|
|
@@ -958,10 +1025,11 @@ function buildMenuEntries(entries, container, ctx) {
|
|
|
958
1025
|
const editor = ctx.editor;
|
|
959
1026
|
const cmd = editor?.commands.get(entry.command);
|
|
960
1027
|
if (!editor || !cmd) continue;
|
|
961
|
-
const { item, check } = makeRow(
|
|
1028
|
+
const { item, check, shortcutEl } = makeRow(
|
|
962
1029
|
entry.label ?? ctx.labels[entry.command] ?? entry.command,
|
|
963
|
-
{}
|
|
1030
|
+
{ shortcut: ctx.shortcutOf?.(entry.command) }
|
|
964
1031
|
);
|
|
1032
|
+
ctx.shortcutRef?.(shortcutEl, entry.command);
|
|
965
1033
|
item.setAttribute("role", "menuitemcheckbox");
|
|
966
1034
|
item.addEventListener("click", () => {
|
|
967
1035
|
const s = ctx.state();
|
|
@@ -974,9 +1042,10 @@ function buildMenuEntries(entries, container, ctx) {
|
|
|
974
1042
|
if (cmd.isEnabled) ctx.enable(item, (s) => cmd.isEnabled(s));
|
|
975
1043
|
container.appendChild(item);
|
|
976
1044
|
} else {
|
|
977
|
-
const { item, check } = makeRow(entry.label, {
|
|
978
|
-
shortcut: entry.shortcut
|
|
1045
|
+
const { item, check, shortcutEl } = makeRow(entry.label, {
|
|
1046
|
+
shortcut: (entry.shortcutOf && ctx.shortcutOf?.(entry.shortcutOf)) ?? entry.shortcut
|
|
979
1047
|
});
|
|
1048
|
+
if (entry.shortcutOf) ctx.shortcutRef?.(shortcutEl, entry.shortcutOf);
|
|
980
1049
|
item.setAttribute(
|
|
981
1050
|
"role",
|
|
982
1051
|
entry.isActive ? "menuitemcheckbox" : "menuitem"
|
|
@@ -1002,15 +1071,15 @@ function closeCurrentPopup() {
|
|
|
1002
1071
|
function showPopup(content, at, onClose) {
|
|
1003
1072
|
injectStyle("bb-ui-menubar-styles", STYLE4);
|
|
1004
1073
|
closeCurrentPopup();
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
document.body.appendChild(
|
|
1074
|
+
const el5 = document.createElement("div");
|
|
1075
|
+
el5.className = "bb-menu bb-popup";
|
|
1076
|
+
el5.setAttribute("role", "menu");
|
|
1077
|
+
el5.appendChild(content);
|
|
1078
|
+
document.body.appendChild(el5);
|
|
1010
1079
|
const place = () => {
|
|
1011
|
-
const r =
|
|
1012
|
-
|
|
1013
|
-
|
|
1080
|
+
const r = el5.getBoundingClientRect();
|
|
1081
|
+
el5.style.left = `${Math.max(4, Math.min(at.x, window.innerWidth - r.width - 4))}px`;
|
|
1082
|
+
el5.style.top = `${Math.max(4, Math.min(at.y, window.innerHeight - r.height - 4))}px`;
|
|
1014
1083
|
};
|
|
1015
1084
|
place();
|
|
1016
1085
|
let disposed = false;
|
|
@@ -1020,7 +1089,7 @@ function showPopup(content, at, onClose) {
|
|
|
1020
1089
|
document.removeEventListener("pointerdown", onPointer, true);
|
|
1021
1090
|
document.removeEventListener("keydown", onKey, true);
|
|
1022
1091
|
document.removeEventListener("scroll", onScroll, true);
|
|
1023
|
-
|
|
1092
|
+
el5.remove();
|
|
1024
1093
|
onClose?.();
|
|
1025
1094
|
};
|
|
1026
1095
|
const close = () => {
|
|
@@ -1028,7 +1097,7 @@ function showPopup(content, at, onClose) {
|
|
|
1028
1097
|
dispose();
|
|
1029
1098
|
};
|
|
1030
1099
|
const onPointer = (e) => {
|
|
1031
|
-
if (!
|
|
1100
|
+
if (!el5.contains(e.target)) close();
|
|
1032
1101
|
};
|
|
1033
1102
|
const onKey = (e) => {
|
|
1034
1103
|
if (e.key === "Escape") {
|
|
@@ -1037,13 +1106,13 @@ function showPopup(content, at, onClose) {
|
|
|
1037
1106
|
}
|
|
1038
1107
|
};
|
|
1039
1108
|
const onScroll = (e) => {
|
|
1040
|
-
if (!
|
|
1109
|
+
if (!el5.contains(e.target)) close();
|
|
1041
1110
|
};
|
|
1042
1111
|
document.addEventListener("pointerdown", onPointer, true);
|
|
1043
1112
|
document.addEventListener("keydown", onKey, true);
|
|
1044
1113
|
document.addEventListener("scroll", onScroll, true);
|
|
1045
1114
|
currentPopup = dispose;
|
|
1046
|
-
return { close, el:
|
|
1115
|
+
return { close, el: el5 };
|
|
1047
1116
|
}
|
|
1048
1117
|
function showMenu(entries, at, options = {}) {
|
|
1049
1118
|
const labels = { ...DEFAULT_LABELS, ...options.labels ?? {} };
|
|
@@ -1067,12 +1136,13 @@ function showMenu(entries, at, options = {}) {
|
|
|
1067
1136
|
close: () => handle?.close(),
|
|
1068
1137
|
labels,
|
|
1069
1138
|
editor: options.editor,
|
|
1139
|
+
shortcutOf: (command) => shortcutLabel(command, [options.editor?.keybindings]),
|
|
1070
1140
|
state: stateOf,
|
|
1071
|
-
check: (
|
|
1072
|
-
|
|
1141
|
+
check: (el5, active) => {
|
|
1142
|
+
el5.textContent = evalNow(active, false) ? "\u2713" : "";
|
|
1073
1143
|
},
|
|
1074
|
-
enable: (
|
|
1075
|
-
|
|
1144
|
+
enable: (el5, enabled) => {
|
|
1145
|
+
el5.disabled = !evalNow(enabled, true);
|
|
1076
1146
|
}
|
|
1077
1147
|
});
|
|
1078
1148
|
handle = showPopup(container, at, options.onClose);
|
|
@@ -1116,6 +1186,7 @@ function mountMenubar(host, editor, options = {}) {
|
|
|
1116
1186
|
const open = (idx, focusFirst = false) => {
|
|
1117
1187
|
if (openIdx === idx) return;
|
|
1118
1188
|
close();
|
|
1189
|
+
refreshShortcuts();
|
|
1119
1190
|
const p = panels[idx];
|
|
1120
1191
|
p.dropdown.hidden = false;
|
|
1121
1192
|
p.title.setAttribute("aria-expanded", "true");
|
|
@@ -1124,13 +1195,24 @@ function mountMenubar(host, editor, options = {}) {
|
|
|
1124
1195
|
if (focusFirst)
|
|
1125
1196
|
p.dropdown.querySelector(".bb-menu-item")?.focus();
|
|
1126
1197
|
};
|
|
1198
|
+
const registries = [editor.keybindings, ...options.keybindings ?? []];
|
|
1199
|
+
const shortcutRefs = [];
|
|
1200
|
+
const refreshShortcuts = () => {
|
|
1201
|
+
for (const { el: el5, command } of shortcutRefs) {
|
|
1202
|
+
const label = shortcutLabel(command, registries);
|
|
1203
|
+
el5.textContent = label ?? "";
|
|
1204
|
+
el5.hidden = !label;
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1127
1207
|
const buildCtx = {
|
|
1128
1208
|
close: () => close(),
|
|
1129
1209
|
labels,
|
|
1130
1210
|
editor,
|
|
1211
|
+
shortcutOf: (command) => shortcutLabel(command, registries),
|
|
1212
|
+
shortcutRef: (el5, command) => shortcutRefs.push({ el: el5, command }),
|
|
1131
1213
|
state: () => latest,
|
|
1132
|
-
check: (
|
|
1133
|
-
enable: (
|
|
1214
|
+
check: (el5, active) => checks.push({ el: el5, active }),
|
|
1215
|
+
enable: (el5, enabled) => enables.push({ el: el5, enabled })
|
|
1134
1216
|
};
|
|
1135
1217
|
const buildEntries = (entries, container) => buildMenuEntries(entries, container, buildCtx);
|
|
1136
1218
|
menus.forEach((menu, idx) => {
|
|
@@ -1342,15 +1424,15 @@ function closeCurrent() {
|
|
|
1342
1424
|
function showContextMenu(entries, at) {
|
|
1343
1425
|
injectStyle("bb-ui-contextmenu-styles", STYLE6);
|
|
1344
1426
|
closeCurrent();
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1427
|
+
const el5 = document.createElement("div");
|
|
1428
|
+
el5.className = "bb-ctx";
|
|
1429
|
+
el5.setAttribute("role", "menu");
|
|
1348
1430
|
for (const entry of entries) {
|
|
1349
1431
|
if (entry === "separator") {
|
|
1350
1432
|
const sep = document.createElement("div");
|
|
1351
1433
|
sep.className = "bb-ctx-sep";
|
|
1352
1434
|
sep.setAttribute("role", "separator");
|
|
1353
|
-
|
|
1435
|
+
el5.appendChild(sep);
|
|
1354
1436
|
continue;
|
|
1355
1437
|
}
|
|
1356
1438
|
const item = document.createElement("button");
|
|
@@ -1373,22 +1455,22 @@ function showContextMenu(entries, at) {
|
|
|
1373
1455
|
closeCurrent();
|
|
1374
1456
|
entry.run();
|
|
1375
1457
|
});
|
|
1376
|
-
|
|
1458
|
+
el5.appendChild(item);
|
|
1377
1459
|
}
|
|
1378
|
-
document.body.appendChild(
|
|
1379
|
-
const r =
|
|
1460
|
+
document.body.appendChild(el5);
|
|
1461
|
+
const r = el5.getBoundingClientRect();
|
|
1380
1462
|
const pad = 4;
|
|
1381
1463
|
const fitsX = (v) => v >= pad && v + r.width <= window.innerWidth - pad;
|
|
1382
1464
|
const fitsY = (v) => v >= pad && v + r.height <= window.innerHeight - pad;
|
|
1383
1465
|
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));
|
|
1384
1466
|
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));
|
|
1385
|
-
|
|
1386
|
-
|
|
1467
|
+
el5.style.left = `${x}px`;
|
|
1468
|
+
el5.style.top = `${y}px`;
|
|
1387
1469
|
const items = () => Array.from(
|
|
1388
|
-
|
|
1470
|
+
el5.querySelectorAll(".bb-ctx-item:not(:disabled)")
|
|
1389
1471
|
);
|
|
1390
1472
|
const onDown = (e) => {
|
|
1391
|
-
if (!
|
|
1473
|
+
if (!el5.contains(e.target)) closeCurrent();
|
|
1392
1474
|
};
|
|
1393
1475
|
const onKey = (e) => {
|
|
1394
1476
|
if (e.key === "Escape") return closeCurrent();
|
|
@@ -1410,7 +1492,7 @@ function showContextMenu(entries, at) {
|
|
|
1410
1492
|
document.removeEventListener("pointerdown", onDown, true);
|
|
1411
1493
|
document.removeEventListener("keydown", onKey, true);
|
|
1412
1494
|
window.removeEventListener("scroll", onScroll, true);
|
|
1413
|
-
|
|
1495
|
+
el5.remove();
|
|
1414
1496
|
};
|
|
1415
1497
|
items()[0]?.focus();
|
|
1416
1498
|
return { close: closeCurrent };
|
|
@@ -1471,10 +1553,10 @@ function showLinkPanel(opts) {
|
|
|
1471
1553
|
return currentHandle;
|
|
1472
1554
|
}
|
|
1473
1555
|
closeCurrent2();
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1556
|
+
const el5 = document.createElement("div");
|
|
1557
|
+
el5.className = "bb-linkpanel";
|
|
1558
|
+
el5.setAttribute("role", "dialog");
|
|
1559
|
+
el5.setAttribute("aria-label", "Link");
|
|
1478
1560
|
const iconBtn = (svg, label, onClick) => {
|
|
1479
1561
|
const b = document.createElement("button");
|
|
1480
1562
|
b.type = "button";
|
|
@@ -1501,7 +1583,7 @@ function showLinkPanel(opts) {
|
|
|
1501
1583
|
return { wrap, input };
|
|
1502
1584
|
};
|
|
1503
1585
|
const renderView = (existing) => {
|
|
1504
|
-
|
|
1586
|
+
el5.textContent = "";
|
|
1505
1587
|
const row = document.createElement("div");
|
|
1506
1588
|
row.className = "bb-linkpanel-row";
|
|
1507
1589
|
const internal = opts.internal;
|
|
@@ -1548,11 +1630,11 @@ function showLinkPanel(opts) {
|
|
|
1548
1630
|
})
|
|
1549
1631
|
);
|
|
1550
1632
|
}
|
|
1551
|
-
|
|
1552
|
-
placeFloating(
|
|
1633
|
+
el5.appendChild(row);
|
|
1634
|
+
placeFloating(el5, opts.anchor);
|
|
1553
1635
|
};
|
|
1554
1636
|
const renderForm = (prefill) => {
|
|
1555
|
-
|
|
1637
|
+
el5.textContent = "";
|
|
1556
1638
|
const form = document.createElement("div");
|
|
1557
1639
|
form.className = "bb-linkpanel-form";
|
|
1558
1640
|
const withText = !opts.hasSelection;
|
|
@@ -1593,16 +1675,16 @@ function showLinkPanel(opts) {
|
|
|
1593
1675
|
}
|
|
1594
1676
|
row.appendChild(apply);
|
|
1595
1677
|
form.appendChild(row);
|
|
1596
|
-
|
|
1597
|
-
placeFloating(
|
|
1678
|
+
el5.appendChild(form);
|
|
1679
|
+
placeFloating(el5, opts.anchor);
|
|
1598
1680
|
u.input.focus();
|
|
1599
1681
|
u.input.select();
|
|
1600
1682
|
};
|
|
1601
|
-
document.body.appendChild(
|
|
1683
|
+
document.body.appendChild(el5);
|
|
1602
1684
|
if (opts.existing) renderView(opts.existing);
|
|
1603
1685
|
else renderForm(null);
|
|
1604
1686
|
const onDown = (e) => {
|
|
1605
|
-
if (
|
|
1687
|
+
if (el5.contains(e.target)) return;
|
|
1606
1688
|
if (opts.key) {
|
|
1607
1689
|
cancelPendingClose();
|
|
1608
1690
|
pendingClose = setTimeout(closeCurrent2, 80);
|
|
@@ -1623,7 +1705,7 @@ function showLinkPanel(opts) {
|
|
|
1623
1705
|
document.removeEventListener("pointerdown", onDown, true);
|
|
1624
1706
|
document.removeEventListener("keydown", onKey, true);
|
|
1625
1707
|
window.removeEventListener("scroll", onScroll, true);
|
|
1626
|
-
|
|
1708
|
+
el5.remove();
|
|
1627
1709
|
};
|
|
1628
1710
|
current2 = dispose;
|
|
1629
1711
|
currentKey = opts.key ?? null;
|
|
@@ -1826,8 +1908,8 @@ function openCellProperties({
|
|
|
1826
1908
|
for (const [key, b] of vaBtns) b.classList.toggle("on", vAlign === key);
|
|
1827
1909
|
}
|
|
1828
1910
|
function syncPresets() {
|
|
1829
|
-
for (const { el:
|
|
1830
|
-
|
|
1911
|
+
for (const { el: el5, key } of presetEls)
|
|
1912
|
+
el5.classList.toggle("on", preset === key);
|
|
1831
1913
|
}
|
|
1832
1914
|
syncVAlign();
|
|
1833
1915
|
syncPresets();
|
|
@@ -1872,7 +1954,7 @@ function tableGridPicker(options) {
|
|
|
1872
1954
|
label.className = "bb-grid-label";
|
|
1873
1955
|
label.textContent = "0 \xD7 0";
|
|
1874
1956
|
const cells = [];
|
|
1875
|
-
const
|
|
1957
|
+
const highlight2 = (r, c) => {
|
|
1876
1958
|
for (let i = 0; i < cells.length; i++) {
|
|
1877
1959
|
const row = Math.floor(i / maxCols);
|
|
1878
1960
|
const col = i % maxCols;
|
|
@@ -1886,7 +1968,7 @@ function tableGridPicker(options) {
|
|
|
1886
1968
|
cell.type = "button";
|
|
1887
1969
|
cell.className = "bb-grid-cell";
|
|
1888
1970
|
cell.addEventListener("mousedown", (e) => e.preventDefault());
|
|
1889
|
-
cell.addEventListener("mouseenter", () =>
|
|
1971
|
+
cell.addEventListener("mouseenter", () => highlight2(r, c));
|
|
1890
1972
|
cell.addEventListener("click", () => options.onPick(r + 1, c + 1));
|
|
1891
1973
|
cells.push(cell);
|
|
1892
1974
|
cellsEl.appendChild(cell);
|
|
@@ -1935,9 +2017,9 @@ var STYLE10 = `
|
|
|
1935
2017
|
`;
|
|
1936
2018
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1937
2019
|
function svgEl(name, attrs) {
|
|
1938
|
-
const
|
|
1939
|
-
for (const [k, v] of Object.entries(attrs))
|
|
1940
|
-
return
|
|
2020
|
+
const el5 = document.createElementNS(SVG_NS, name);
|
|
2021
|
+
for (const [k, v] of Object.entries(attrs)) el5.setAttribute(k, String(v));
|
|
2022
|
+
return el5;
|
|
1941
2023
|
}
|
|
1942
2024
|
function pagePreview(ratio) {
|
|
1943
2025
|
const svg = svgEl("svg", { viewBox: "0 0 34 42", class: "bb-ps-icon" });
|
|
@@ -2004,11 +2086,11 @@ function presetRow(icon, name, caption, active, onPick) {
|
|
|
2004
2086
|
return row;
|
|
2005
2087
|
}
|
|
2006
2088
|
function captionLine(text) {
|
|
2007
|
-
const
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
return
|
|
2089
|
+
const el5 = document.createElement("span");
|
|
2090
|
+
el5.className = "bb-ps-dims";
|
|
2091
|
+
el5.style.display = "block";
|
|
2092
|
+
el5.textContent = text;
|
|
2093
|
+
return el5;
|
|
2012
2094
|
}
|
|
2013
2095
|
function customRow(icon, title, description, onPick) {
|
|
2014
2096
|
icon.appendChild(
|
|
@@ -2137,8 +2219,8 @@ function cmField(label, valuePx, min) {
|
|
|
2137
2219
|
return { row, input };
|
|
2138
2220
|
}
|
|
2139
2221
|
function dialogActions() {
|
|
2140
|
-
const
|
|
2141
|
-
|
|
2222
|
+
const el5 = document.createElement("div");
|
|
2223
|
+
el5.className = "bb-pd-actions";
|
|
2142
2224
|
const cancel = document.createElement("button");
|
|
2143
2225
|
cancel.type = "button";
|
|
2144
2226
|
cancel.className = "bb-prompt-btn";
|
|
@@ -2148,8 +2230,8 @@ function dialogActions() {
|
|
|
2148
2230
|
ok.className = "bb-prompt-btn";
|
|
2149
2231
|
ok.dataset["primary"] = "true";
|
|
2150
2232
|
ok.textContent = "OK";
|
|
2151
|
-
|
|
2152
|
-
return { el:
|
|
2233
|
+
el5.append(cancel, ok);
|
|
2234
|
+
return { el: el5, ok, cancel };
|
|
2153
2235
|
}
|
|
2154
2236
|
function readPx(input, fallbackPx) {
|
|
2155
2237
|
const n = Number(input.value);
|
|
@@ -2346,15 +2428,15 @@ function sectionPaperPanel(options) {
|
|
|
2346
2428
|
root.className = "bb-ps";
|
|
2347
2429
|
root.setAttribute("role", "menu");
|
|
2348
2430
|
const group = (text) => {
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
return
|
|
2431
|
+
const el5 = document.createElement("div");
|
|
2432
|
+
el5.className = "bb-ps-group";
|
|
2433
|
+
el5.textContent = text;
|
|
2434
|
+
return el5;
|
|
2353
2435
|
};
|
|
2354
2436
|
const sep = () => {
|
|
2355
|
-
const
|
|
2356
|
-
|
|
2357
|
-
return
|
|
2437
|
+
const el5 = document.createElement("div");
|
|
2438
|
+
el5.className = "bb-ps-sep";
|
|
2439
|
+
return el5;
|
|
2358
2440
|
};
|
|
2359
2441
|
root.appendChild(group(options.labels?.orientation ?? "Orientation"));
|
|
2360
2442
|
const tiles = document.createElement("div");
|
|
@@ -2424,8 +2506,8 @@ var STYLE11 = `
|
|
|
2424
2506
|
`;
|
|
2425
2507
|
function createSectionChip(options) {
|
|
2426
2508
|
injectStyle("bb-ui-section-chip-styles", STYLE11);
|
|
2427
|
-
const
|
|
2428
|
-
|
|
2509
|
+
const el5 = document.createElement("div");
|
|
2510
|
+
el5.className = "bb-secchip";
|
|
2429
2511
|
const title = document.createElement("span");
|
|
2430
2512
|
title.className = "bb-secchip-title";
|
|
2431
2513
|
title.textContent = options.title;
|
|
@@ -2459,16 +2541,16 @@ function createSectionChip(options) {
|
|
|
2459
2541
|
x.innerHTML = '<svg viewBox="0 0 16 16" width="9" height="9" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M3 3l10 10M13 3 3 13"/></svg>';
|
|
2460
2542
|
x.addEventListener("mousedown", (e) => e.preventDefault());
|
|
2461
2543
|
x.addEventListener("click", () => options.onDelete());
|
|
2462
|
-
|
|
2544
|
+
el5.append(title, divider(), nums.btn, divider(), paper.btn, divider(), x);
|
|
2463
2545
|
return {
|
|
2464
|
-
el:
|
|
2546
|
+
el: el5,
|
|
2465
2547
|
update(data) {
|
|
2466
2548
|
nums.label.textContent = data.pageNumbers;
|
|
2467
2549
|
nums.btn.dataset["muted"] = String(!!data.pageNumbersMuted);
|
|
2468
2550
|
paper.label.textContent = data.paper;
|
|
2469
2551
|
},
|
|
2470
2552
|
destroy() {
|
|
2471
|
-
|
|
2553
|
+
el5.remove();
|
|
2472
2554
|
}
|
|
2473
2555
|
};
|
|
2474
2556
|
}
|
|
@@ -2620,7 +2702,7 @@ function openFontDialog({
|
|
|
2620
2702
|
const italicBtn = mkSeg("I", initial.italic, "font-style:italic");
|
|
2621
2703
|
const underBtn = mkSeg("U", initial.underline, "text-decoration:underline");
|
|
2622
2704
|
let color = initial.color ?? null;
|
|
2623
|
-
let
|
|
2705
|
+
let highlight2 = initial.highlight ?? null;
|
|
2624
2706
|
const colorBtn = colorButton({
|
|
2625
2707
|
title: "Text color",
|
|
2626
2708
|
label: "Text color",
|
|
@@ -2635,9 +2717,9 @@ function openFontDialog({
|
|
|
2635
2717
|
title: "Highlight",
|
|
2636
2718
|
label: "Highlight",
|
|
2637
2719
|
clearLabel: "No highlight",
|
|
2638
|
-
value: () =>
|
|
2720
|
+
value: () => highlight2,
|
|
2639
2721
|
onPick: (c) => {
|
|
2640
|
-
|
|
2722
|
+
highlight2 = c;
|
|
2641
2723
|
paint();
|
|
2642
2724
|
}
|
|
2643
2725
|
});
|
|
@@ -2757,7 +2839,7 @@ function openFontDialog({
|
|
|
2757
2839
|
s.textDecorationStyle = dstrikeCb.checked ? "double" : "solid";
|
|
2758
2840
|
s.fontVariant = smallCapsCb.checked ? "small-caps" : "normal";
|
|
2759
2841
|
s.color = color ?? "inherit";
|
|
2760
|
-
s.background =
|
|
2842
|
+
s.background = highlight2 ?? "transparent";
|
|
2761
2843
|
s.letterSpacing = `${spacingPt}pt`;
|
|
2762
2844
|
s.transform = scale === 100 ? "none" : `scaleX(${scale / 100})`;
|
|
2763
2845
|
s.verticalAlign = supCb.checked ? "super" : subCb.checked ? "sub" : `${positionPt}pt`;
|
|
@@ -2786,7 +2868,7 @@ function openFontDialog({
|
|
|
2786
2868
|
smallCaps: checkedOrMixed(smallCapsCb),
|
|
2787
2869
|
vertAlign: supCb.checked ? "super" : subCb.checked ? "sub" : null,
|
|
2788
2870
|
color,
|
|
2789
|
-
highlight,
|
|
2871
|
+
highlight: highlight2,
|
|
2790
2872
|
scalePercent: scale,
|
|
2791
2873
|
letterSpacingTwips: ptToTwips(spacingPt),
|
|
2792
2874
|
positionHalfPoints: ptToHalf(positionPt)
|
|
@@ -2801,28 +2883,1216 @@ function openFontDialog({
|
|
|
2801
2883
|
paint();
|
|
2802
2884
|
dialog.open();
|
|
2803
2885
|
}
|
|
2886
|
+
|
|
2887
|
+
// packages/ui/src/lib/symbol-sets.ts
|
|
2888
|
+
import { AUTOCORRECT_RULES } from "@shadow-garden/bapbong-contracts";
|
|
2889
|
+
var g = (id, label, short, pairs) => ({
|
|
2890
|
+
id,
|
|
2891
|
+
label,
|
|
2892
|
+
short,
|
|
2893
|
+
entries: pairs.map(([char, name]) => ({ char, name }))
|
|
2894
|
+
});
|
|
2895
|
+
var SYMBOL_GROUPS = [
|
|
2896
|
+
g("checks", "Checkboxes & ticks", "Checks", [
|
|
2897
|
+
["\u2610", "Ballot box"],
|
|
2898
|
+
["\u2611", "Ballot box with check"],
|
|
2899
|
+
["\u2612", "Ballot box with X"],
|
|
2900
|
+
["\u2713", "Check mark"],
|
|
2901
|
+
["\u2714", "Heavy check mark"],
|
|
2902
|
+
["\u2717", "Ballot X"],
|
|
2903
|
+
["\u2718", "Heavy ballot X"],
|
|
2904
|
+
["\u2715", "Multiplication X"],
|
|
2905
|
+
["\u2716", "Heavy multiplication X"],
|
|
2906
|
+
["\u274F", "Lower right drop-shadowed white square"],
|
|
2907
|
+
["\u2750", "Upper right drop-shadowed white square"],
|
|
2908
|
+
["\u2751", "Lower right shadowed white square"],
|
|
2909
|
+
["\u2752", "Upper right shadowed white square"],
|
|
2910
|
+
["\u25A2", "White square with rounded corners"],
|
|
2911
|
+
["\u25A3", "White square containing black small square"],
|
|
2912
|
+
["\u22A0", "Squared times"],
|
|
2913
|
+
["\u2297", "Circled times"],
|
|
2914
|
+
["\u29BF", "Circled bullet"],
|
|
2915
|
+
["\u25C9", "Fisheye"],
|
|
2916
|
+
["\u25CB", "White circle"],
|
|
2917
|
+
["\u25CF", "Black circle"],
|
|
2918
|
+
["\u25EF", "Large circle"],
|
|
2919
|
+
["\u25FB", "White medium square"],
|
|
2920
|
+
["\u25FC", "Black medium square"]
|
|
2921
|
+
]),
|
|
2922
|
+
g("bullets", "Bullets & shapes", "Bullets", [
|
|
2923
|
+
["\u2022", "Bullet"],
|
|
2924
|
+
["\u25E6", "White bullet"],
|
|
2925
|
+
["\u25AA", "Black small square"],
|
|
2926
|
+
["\u25AB", "White small square"],
|
|
2927
|
+
["\u25A0", "Black square"],
|
|
2928
|
+
["\u25A1", "White square"],
|
|
2929
|
+
["\u25AC", "Black rectangle"],
|
|
2930
|
+
["\u25C6", "Black diamond"],
|
|
2931
|
+
["\u25C7", "White diamond"],
|
|
2932
|
+
["\u25CA", "Lozenge"],
|
|
2933
|
+
["\u2605", "Black star"],
|
|
2934
|
+
["\u2606", "White star"],
|
|
2935
|
+
["\u2726", "Black four pointed star"],
|
|
2936
|
+
["\u2727", "White four pointed star"],
|
|
2937
|
+
["\u25BA", "Black right-pointing pointer"],
|
|
2938
|
+
["\u25B8", "Black right-pointing small triangle"],
|
|
2939
|
+
["\u25B9", "White right-pointing small triangle"],
|
|
2940
|
+
["\u25B2", "Black up-pointing triangle"],
|
|
2941
|
+
["\u25B3", "White up-pointing triangle"],
|
|
2942
|
+
["\u25BC", "Black down-pointing triangle"],
|
|
2943
|
+
["\u25BD", "White down-pointing triangle"],
|
|
2944
|
+
["\u25C4", "Black left-pointing pointer"],
|
|
2945
|
+
["\u2756", "Black diamond minus white X"],
|
|
2946
|
+
["\u273D", "Heavy teardrop-spoked asterisk"],
|
|
2947
|
+
["\u273F", "Black florette"],
|
|
2948
|
+
["\u2740", "White florette"],
|
|
2949
|
+
["\u27A2", "Three-D top-lighted rightwards arrowhead"],
|
|
2950
|
+
["\u27A4", "Black rightwards arrowhead"],
|
|
2951
|
+
["\u27A3", "Three-D bottom-lighted rightwards arrowhead"],
|
|
2952
|
+
["\u2713", "Check mark"]
|
|
2953
|
+
]),
|
|
2954
|
+
g("arrows", "Arrows", "Arrows", [
|
|
2955
|
+
["\u2190", "Leftwards arrow"],
|
|
2956
|
+
["\u2191", "Upwards arrow"],
|
|
2957
|
+
["\u2192", "Rightwards arrow"],
|
|
2958
|
+
["\u2193", "Downwards arrow"],
|
|
2959
|
+
["\u2194", "Left right arrow"],
|
|
2960
|
+
["\u2195", "Up down arrow"],
|
|
2961
|
+
["\u2196", "North west arrow"],
|
|
2962
|
+
["\u2197", "North east arrow"],
|
|
2963
|
+
["\u2198", "South east arrow"],
|
|
2964
|
+
["\u2199", "South west arrow"],
|
|
2965
|
+
["\u21D0", "Leftwards double arrow"],
|
|
2966
|
+
["\u21D1", "Upwards double arrow"],
|
|
2967
|
+
["\u21D2", "Rightwards double arrow"],
|
|
2968
|
+
["\u21D3", "Downwards double arrow"],
|
|
2969
|
+
["\u21D4", "Left right double arrow"],
|
|
2970
|
+
["\u21D5", "Up down double arrow"],
|
|
2971
|
+
["\u21A9", "Leftwards arrow with hook"],
|
|
2972
|
+
["\u21AA", "Rightwards arrow with hook"],
|
|
2973
|
+
["\u21B5", "Downwards arrow with corner leftwards"],
|
|
2974
|
+
["\u21C4", "Rightwards arrow over leftwards arrow"],
|
|
2975
|
+
["\u21C6", "Leftwards arrow over rightwards arrow"],
|
|
2976
|
+
["\u2794", "Heavy wide-headed rightwards arrow"],
|
|
2977
|
+
["\u279C", "Heavy round-tipped rightwards arrow"],
|
|
2978
|
+
["\u279D", "Triangle-headed rightwards arrow"],
|
|
2979
|
+
["\u279E", "Heavy triangle-headed rightwards arrow"],
|
|
2980
|
+
["\u27A1", "Black rightwards arrow"],
|
|
2981
|
+
["\u2B05", "Leftwards black arrow"],
|
|
2982
|
+
["\u2B06", "Upwards black arrow"],
|
|
2983
|
+
["\u2B07", "Downwards black arrow"],
|
|
2984
|
+
["\u21BB", "Clockwise open circle arrow"],
|
|
2985
|
+
["\u21BA", "Anticlockwise open circle arrow"],
|
|
2986
|
+
["\u2934", "Arrow pointing rightwards then curving upwards"],
|
|
2987
|
+
["\u2935", "Arrow pointing rightwards then curving downwards"]
|
|
2988
|
+
]),
|
|
2989
|
+
g("math", "Math", "Math", [
|
|
2990
|
+
["\xB1", "Plus-minus sign"],
|
|
2991
|
+
["\xD7", "Multiplication sign"],
|
|
2992
|
+
["\xF7", "Division sign"],
|
|
2993
|
+
["\u2212", "Minus sign"],
|
|
2994
|
+
["\u2264", "Less-than or equal to"],
|
|
2995
|
+
["\u2265", "Greater-than or equal to"],
|
|
2996
|
+
["\u2260", "Not equal to"],
|
|
2997
|
+
["\u2248", "Almost equal to"],
|
|
2998
|
+
["\u2261", "Identical to"],
|
|
2999
|
+
["\u221E", "Infinity"],
|
|
3000
|
+
["\u221A", "Square root"],
|
|
3001
|
+
["\u2211", "N-ary summation"],
|
|
3002
|
+
["\u220F", "N-ary product"],
|
|
3003
|
+
["\u222B", "Integral"],
|
|
3004
|
+
["\u2202", "Partial differential"],
|
|
3005
|
+
["\u2206", "Increment"],
|
|
3006
|
+
["\u2207", "Nabla"],
|
|
3007
|
+
["\u2208", "Element of"],
|
|
3008
|
+
["\u2209", "Not an element of"],
|
|
3009
|
+
["\u2229", "Intersection"],
|
|
3010
|
+
["\u222A", "Union"],
|
|
3011
|
+
["\u2282", "Subset of"],
|
|
3012
|
+
["\u2283", "Superset of"],
|
|
3013
|
+
["\u2205", "Empty set"],
|
|
3014
|
+
["\u2200", "For all"],
|
|
3015
|
+
["\u2203", "There exists"],
|
|
3016
|
+
["\xAC", "Not sign"],
|
|
3017
|
+
["\u2227", "Logical and"],
|
|
3018
|
+
["\u2228", "Logical or"],
|
|
3019
|
+
["\xB0", "Degree sign"],
|
|
3020
|
+
["\u2032", "Prime"],
|
|
3021
|
+
["\u2033", "Double prime"],
|
|
3022
|
+
["\u2030", "Per mille sign"],
|
|
3023
|
+
["\xB5", "Micro sign"],
|
|
3024
|
+
["\u03C0", "Greek small letter pi"],
|
|
3025
|
+
["\u221D", "Proportional to"],
|
|
3026
|
+
["\u2220", "Angle"],
|
|
3027
|
+
["\u22A5", "Up tack"],
|
|
3028
|
+
["\u2225", "Parallel to"],
|
|
3029
|
+
["\xB9", "Superscript one"],
|
|
3030
|
+
["\xB2", "Superscript two"],
|
|
3031
|
+
["\xB3", "Superscript three"],
|
|
3032
|
+
["\xBD", "Vulgar fraction one half"],
|
|
3033
|
+
["\u2153", "Vulgar fraction one third"],
|
|
3034
|
+
["\xBC", "Vulgar fraction one quarter"],
|
|
3035
|
+
["\xBE", "Vulgar fraction three quarters"]
|
|
3036
|
+
]),
|
|
3037
|
+
g("currency", "Currency", "Currency", [
|
|
3038
|
+
["\u20AB", "Dong sign"],
|
|
3039
|
+
["$", "Dollar sign"],
|
|
3040
|
+
["\u20AC", "Euro sign"],
|
|
3041
|
+
["\xA3", "Pound sign"],
|
|
3042
|
+
["\xA5", "Yen sign"],
|
|
3043
|
+
["\u20A9", "Won sign"],
|
|
3044
|
+
["\u20B9", "Indian rupee sign"],
|
|
3045
|
+
["\u20BD", "Ruble sign"],
|
|
3046
|
+
["\u0E3F", "Thai currency symbol baht"],
|
|
3047
|
+
["\u20B1", "Peso sign"],
|
|
3048
|
+
["\xA2", "Cent sign"],
|
|
3049
|
+
["\xA4", "Currency sign"],
|
|
3050
|
+
["\u20BF", "Bitcoin sign"]
|
|
3051
|
+
]),
|
|
3052
|
+
g("punct", "Punctuation & typography", "Punct.", [
|
|
3053
|
+
["\u2013", "En dash"],
|
|
3054
|
+
["\u2014", "Em dash"],
|
|
3055
|
+
["\u2026", "Horizontal ellipsis"],
|
|
3056
|
+
["\u2018", "Left single quotation mark"],
|
|
3057
|
+
["\u2019", "Right single quotation mark"],
|
|
3058
|
+
["\u201C", "Left double quotation mark"],
|
|
3059
|
+
["\u201D", "Right double quotation mark"],
|
|
3060
|
+
["\u201A", "Single low-9 quotation mark"],
|
|
3061
|
+
["\u201E", "Double low-9 quotation mark"],
|
|
3062
|
+
["\xAB", "Left-pointing double angle quotation mark"],
|
|
3063
|
+
["\xBB", "Right-pointing double angle quotation mark"],
|
|
3064
|
+
["\u2039", "Single left-pointing angle quotation mark"],
|
|
3065
|
+
["\u203A", "Single right-pointing angle quotation mark"],
|
|
3066
|
+
["\xA7", "Section sign"],
|
|
3067
|
+
["\xB6", "Pilcrow sign"],
|
|
3068
|
+
["\u2020", "Dagger"],
|
|
3069
|
+
["\u2021", "Double dagger"],
|
|
3070
|
+
["\u2022", "Bullet"],
|
|
3071
|
+
["\xB7", "Middle dot"],
|
|
3072
|
+
["\u2116", "Numero sign"],
|
|
3073
|
+
["\xA9", "Copyright sign"],
|
|
3074
|
+
["\xAE", "Registered sign"],
|
|
3075
|
+
["\u2122", "Trade mark sign"],
|
|
3076
|
+
["\u2120", "Service mark"],
|
|
3077
|
+
["\xA1", "Inverted exclamation mark"],
|
|
3078
|
+
["\xBF", "Inverted question mark"],
|
|
3079
|
+
["\u203D", "Interrobang"],
|
|
3080
|
+
["\u02C6", "Modifier letter circumflex accent"],
|
|
3081
|
+
["\u02DC", "Small tilde"],
|
|
3082
|
+
["\xA6", "Broken bar"]
|
|
3083
|
+
]),
|
|
3084
|
+
g("greek", "Greek", "Greek", [
|
|
3085
|
+
["\u03B1", "Alpha"],
|
|
3086
|
+
["\u03B2", "Beta"],
|
|
3087
|
+
["\u03B3", "Gamma"],
|
|
3088
|
+
["\u03B4", "Delta"],
|
|
3089
|
+
["\u03B5", "Epsilon"],
|
|
3090
|
+
["\u03B6", "Zeta"],
|
|
3091
|
+
["\u03B7", "Eta"],
|
|
3092
|
+
["\u03B8", "Theta"],
|
|
3093
|
+
["\u03B9", "Iota"],
|
|
3094
|
+
["\u03BA", "Kappa"],
|
|
3095
|
+
["\u03BB", "Lambda"],
|
|
3096
|
+
["\u03BC", "Mu"],
|
|
3097
|
+
["\u03BD", "Nu"],
|
|
3098
|
+
["\u03BE", "Xi"],
|
|
3099
|
+
["\u03BF", "Omicron"],
|
|
3100
|
+
["\u03C0", "Pi"],
|
|
3101
|
+
["\u03C1", "Rho"],
|
|
3102
|
+
["\u03C3", "Sigma"],
|
|
3103
|
+
["\u03C2", "Final sigma"],
|
|
3104
|
+
["\u03C4", "Tau"],
|
|
3105
|
+
["\u03C5", "Upsilon"],
|
|
3106
|
+
["\u03C6", "Phi"],
|
|
3107
|
+
["\u03C7", "Chi"],
|
|
3108
|
+
["\u03C8", "Psi"],
|
|
3109
|
+
["\u03C9", "Omega"],
|
|
3110
|
+
["\u0391", "Capital alpha"],
|
|
3111
|
+
["\u0392", "Capital beta"],
|
|
3112
|
+
["\u0393", "Capital gamma"],
|
|
3113
|
+
["\u0394", "Capital delta"],
|
|
3114
|
+
["\u0395", "Capital epsilon"],
|
|
3115
|
+
["\u0396", "Capital zeta"],
|
|
3116
|
+
["\u0397", "Capital eta"],
|
|
3117
|
+
["\u0398", "Capital theta"],
|
|
3118
|
+
["\u0399", "Capital iota"],
|
|
3119
|
+
["\u039A", "Capital kappa"],
|
|
3120
|
+
["\u039B", "Capital lambda"],
|
|
3121
|
+
["\u039C", "Capital mu"],
|
|
3122
|
+
["\u039D", "Capital nu"],
|
|
3123
|
+
["\u039E", "Capital xi"],
|
|
3124
|
+
["\u039F", "Capital omicron"],
|
|
3125
|
+
["\u03A0", "Capital pi"],
|
|
3126
|
+
["\u03A1", "Capital rho"],
|
|
3127
|
+
["\u03A3", "Capital sigma"],
|
|
3128
|
+
["\u03A4", "Capital tau"],
|
|
3129
|
+
["\u03A5", "Capital upsilon"],
|
|
3130
|
+
["\u03A6", "Capital phi"],
|
|
3131
|
+
["\u03A7", "Capital chi"],
|
|
3132
|
+
["\u03A8", "Capital psi"],
|
|
3133
|
+
["\u03A9", "Capital omega"]
|
|
3134
|
+
]),
|
|
3135
|
+
g("latin", "Latin (Vietnamese & accents)", "Latin", [
|
|
3136
|
+
["\u0110", "Capital D with stroke"],
|
|
3137
|
+
["\u0111", "Small d with stroke"],
|
|
3138
|
+
["\u0102", "Capital A with breve"],
|
|
3139
|
+
["\u0103", "Small a with breve"],
|
|
3140
|
+
["\xC2", "Capital A with circumflex"],
|
|
3141
|
+
["\xE2", "Small a with circumflex"],
|
|
3142
|
+
["\xCA", "Capital E with circumflex"],
|
|
3143
|
+
["\xEA", "Small e with circumflex"],
|
|
3144
|
+
["\xD4", "Capital O with circumflex"],
|
|
3145
|
+
["\xF4", "Small o with circumflex"],
|
|
3146
|
+
["\u01A0", "Capital O with horn"],
|
|
3147
|
+
["\u01A1", "Small o with horn"],
|
|
3148
|
+
["\u01AF", "Capital U with horn"],
|
|
3149
|
+
["\u01B0", "Small u with horn"],
|
|
3150
|
+
["\xC0", "Capital A with grave"],
|
|
3151
|
+
["\xE0", "Small a with grave"],
|
|
3152
|
+
["\xC1", "Capital A with acute"],
|
|
3153
|
+
["\xE1", "Small a with acute"],
|
|
3154
|
+
["\xC3", "Capital A with tilde"],
|
|
3155
|
+
["\xE3", "Small a with tilde"],
|
|
3156
|
+
["\u1EA2", "Capital A with hook above"],
|
|
3157
|
+
["\u1EA3", "Small a with hook above"],
|
|
3158
|
+
["\u1EA0", "Capital A with dot below"],
|
|
3159
|
+
["\u1EA1", "Small a with dot below"],
|
|
3160
|
+
["\xC8", "Capital E with grave"],
|
|
3161
|
+
["\xE9", "Small e with acute"],
|
|
3162
|
+
["\xCC", "Capital I with grave"],
|
|
3163
|
+
["\xED", "Small i with acute"],
|
|
3164
|
+
["\xD2", "Capital O with grave"],
|
|
3165
|
+
["\xF3", "Small o with acute"],
|
|
3166
|
+
["\xD9", "Capital U with grave"],
|
|
3167
|
+
["\xFA", "Small u with acute"],
|
|
3168
|
+
["\xDD", "Capital Y with acute"],
|
|
3169
|
+
["\u1EF9", "Small y with tilde"],
|
|
3170
|
+
["\xC7", "Capital C with cedilla"],
|
|
3171
|
+
["\xE7", "Small c with cedilla"],
|
|
3172
|
+
["\xD1", "Capital N with tilde"],
|
|
3173
|
+
["\xF1", "Small n with tilde"],
|
|
3174
|
+
["\xD6", "Capital O with diaeresis"],
|
|
3175
|
+
["\xF6", "Small o with diaeresis"],
|
|
3176
|
+
["\xDC", "Capital U with diaeresis"],
|
|
3177
|
+
["\xFC", "Small u with diaeresis"],
|
|
3178
|
+
["\xDF", "Small sharp s"],
|
|
3179
|
+
["\xC6", "Capital AE"],
|
|
3180
|
+
["\xE6", "Small ae"],
|
|
3181
|
+
["\xD8", "Capital O with stroke"],
|
|
3182
|
+
["\xF8", "Small o with stroke"],
|
|
3183
|
+
["\u0152", "Capital ligature OE"],
|
|
3184
|
+
["\u0153", "Small ligature oe"]
|
|
3185
|
+
]),
|
|
3186
|
+
g("misc", "Misc", "Misc", [
|
|
3187
|
+
["\u260E", "Black telephone"],
|
|
3188
|
+
["\u2706", "Telephone location sign"],
|
|
3189
|
+
["\u2709", "Envelope"],
|
|
3190
|
+
["\u2702", "Black scissors"],
|
|
3191
|
+
["\u270E", "Lower right pencil"],
|
|
3192
|
+
["\u270F", "Pencil"],
|
|
3193
|
+
["\u270D", "Writing hand"],
|
|
3194
|
+
["\u263A", "White smiling face"],
|
|
3195
|
+
["\u2639", "White frowning face"],
|
|
3196
|
+
["\u2660", "Black spade suit"],
|
|
3197
|
+
["\u2663", "Black club suit"],
|
|
3198
|
+
["\u2665", "Black heart suit"],
|
|
3199
|
+
["\u2666", "Black diamond suit"],
|
|
3200
|
+
["\u266A", "Eighth note"],
|
|
3201
|
+
["\u266B", "Beamed eighth notes"],
|
|
3202
|
+
["\u2600", "Black sun with rays"],
|
|
3203
|
+
["\u2601", "Cloud"],
|
|
3204
|
+
["\u2602", "Umbrella"],
|
|
3205
|
+
["\u2691", "Black flag"],
|
|
3206
|
+
["\u2690", "White flag"],
|
|
3207
|
+
["\u26A0", "Warning sign"],
|
|
3208
|
+
["\u26D4", "No entry"],
|
|
3209
|
+
["\u2640", "Female sign"],
|
|
3210
|
+
["\u2642", "Male sign"],
|
|
3211
|
+
["\u2318", "Place of interest sign"],
|
|
3212
|
+
["\u2325", "Option key"],
|
|
3213
|
+
["\u21E7", "Upwards white arrow"],
|
|
3214
|
+
["\u23CE", "Return symbol"],
|
|
3215
|
+
["\u232B", "Erase to the left"],
|
|
3216
|
+
["\u23CF", "Eject symbol"],
|
|
3217
|
+
["\u267B", "Black universal recycling symbol"],
|
|
3218
|
+
["\u2708", "Airplane"],
|
|
3219
|
+
["\u2696", "Scales"],
|
|
3220
|
+
["\u2695", "Staff of aesculapius"]
|
|
3221
|
+
])
|
|
3222
|
+
];
|
|
3223
|
+
function autoCorrectHint(char) {
|
|
3224
|
+
return AUTOCORRECT_RULES.find((r) => r.to === char)?.seq ?? "";
|
|
3225
|
+
}
|
|
3226
|
+
var SPECIAL_CHARACTERS = [
|
|
3227
|
+
{ char: "\u2014", name: "Em dash", hint: "" },
|
|
3228
|
+
{ char: "\u2013", name: "En dash", hint: "" },
|
|
3229
|
+
{ char: "\u2011", name: "Nonbreaking hyphen", hint: "" },
|
|
3230
|
+
{ char: "\xAD", name: "Optional hyphen", hint: "" },
|
|
3231
|
+
{ char: "\xA0", name: "Nonbreaking space", hint: "" },
|
|
3232
|
+
{ char: "\u2003", name: "Em space", hint: "" },
|
|
3233
|
+
{ char: "\u2002", name: "En space", hint: "" },
|
|
3234
|
+
{ char: "\u2009", name: "Thin space", hint: "" },
|
|
3235
|
+
{ char: "\xA9", name: "Copyright", hint: autoCorrectHint("\xA9") },
|
|
3236
|
+
{ char: "\xAE", name: "Registered", hint: autoCorrectHint("\xAE") },
|
|
3237
|
+
{ char: "\u2122", name: "Trademark", hint: autoCorrectHint("\u2122") },
|
|
3238
|
+
{ char: "\xA7", name: "Section", hint: "" },
|
|
3239
|
+
{ char: "\xB6", name: "Paragraph", hint: "" },
|
|
3240
|
+
{ char: "\u2026", name: "Ellipsis", hint: autoCorrectHint("\u2026") },
|
|
3241
|
+
{ char: "\u2018", name: "Single opening quote", hint: "" },
|
|
3242
|
+
{ char: "\u2019", name: "Single closing quote", hint: "" },
|
|
3243
|
+
{ char: "\u201C", name: "Double opening quote", hint: "" },
|
|
3244
|
+
{ char: "\u201D", name: "Double closing quote", hint: "" },
|
|
3245
|
+
{ char: "\u20AB", name: "Dong sign", hint: "" },
|
|
3246
|
+
{ char: "\u2116", name: "Numero sign", hint: "" }
|
|
3247
|
+
];
|
|
3248
|
+
var SYMBOL_NAMES = (() => {
|
|
3249
|
+
const m = /* @__PURE__ */ new Map();
|
|
3250
|
+
for (const grp of SYMBOL_GROUPS)
|
|
3251
|
+
for (const e of grp.entries) if (!m.has(e.char)) m.set(e.char, e.name);
|
|
3252
|
+
for (const e of SPECIAL_CHARACTERS) if (!m.has(e.char)) m.set(e.char, e.name);
|
|
3253
|
+
return m;
|
|
3254
|
+
})();
|
|
3255
|
+
function codePointLabel(char) {
|
|
3256
|
+
const cp = char.codePointAt(0) ?? 0;
|
|
3257
|
+
return `U+${cp.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
3258
|
+
}
|
|
3259
|
+
function parseCodePoint(input) {
|
|
3260
|
+
const m = /^\s*(?:u\+|0x)?([0-9a-f]{1,6})\s*$/i.exec(input);
|
|
3261
|
+
if (!m) return null;
|
|
3262
|
+
const cp = parseInt(m[1], 16);
|
|
3263
|
+
if (cp > 1114111 || cp >= 55296 && cp <= 57343) return null;
|
|
3264
|
+
if (cp < 32 || cp >= 127 && cp < 160) return null;
|
|
3265
|
+
return String.fromCodePoint(cp);
|
|
3266
|
+
}
|
|
3267
|
+
var RECENT_MAX = 16;
|
|
3268
|
+
function pushRecent(recent, char, max = RECENT_MAX) {
|
|
3269
|
+
return [char, ...recent.filter((c) => c !== char)].slice(0, max);
|
|
3270
|
+
}
|
|
3271
|
+
function searchSymbols(query) {
|
|
3272
|
+
const q = query.trim().toLowerCase();
|
|
3273
|
+
if (!q) return [];
|
|
3274
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3275
|
+
const ranked = [];
|
|
3276
|
+
for (const grp of SYMBOL_GROUPS) {
|
|
3277
|
+
const groupHit = grp.label.toLowerCase().includes(q);
|
|
3278
|
+
for (const e of grp.entries) {
|
|
3279
|
+
const name = e.name.toLowerCase();
|
|
3280
|
+
if (seen.has(e.char) || !name.includes(q)) continue;
|
|
3281
|
+
seen.add(e.char);
|
|
3282
|
+
ranked.push({ e, rank: groupHit ? 0 : name.startsWith(q) ? 1 : 2 });
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
return ranked.sort((a, b) => a.rank - b.rank).map((r) => r.e);
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
// packages/ui/src/lib/symbol-dialog.ts
|
|
3289
|
+
var STYLE13 = `
|
|
3290
|
+
.bb-sd{display:flex;flex-direction:column;gap:12px;width:412px;max-width:100%;color:var(--bb-ui-fg,#2c2c2a)}
|
|
3291
|
+
.bb-sd *{box-sizing:border-box}
|
|
3292
|
+
.bb-sd-tabs{display:inline-flex;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;overflow:hidden;align-self:flex-start}
|
|
3293
|
+
.bb-sd-tab{height:30px;padding:0 18px;border:0;border-right:0.5px solid var(--bb-ui-border,#e3e3e0);background:transparent;color:inherit;opacity:.65;font:inherit;font-size:13px;cursor:pointer}
|
|
3294
|
+
.bb-sd-tab:last-child{border-right:0}
|
|
3295
|
+
.bb-sd-tab[aria-selected="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);opacity:1}
|
|
3296
|
+
.bb-sd-pane{display:flex;flex-direction:column;gap:12px}
|
|
3297
|
+
.bb-sd-pane[hidden]{display:none}
|
|
3298
|
+
.bb-sd-lbl{font-size:12px;opacity:.7;margin-bottom:5px}
|
|
3299
|
+
.bb-sd-row{display:flex;gap:10px;align-items:flex-end}
|
|
3300
|
+
.bb-sd-ctl::placeholder{color:inherit;opacity:.45}
|
|
3301
|
+
.bb-sd-ctl{width:100%;height:32px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font:inherit;font-size:13px;padding:0 8px}
|
|
3302
|
+
.bb-sd-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:2px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:8px;padding:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));max-height:196px;overflow:auto}
|
|
3303
|
+
.bb-sd-grid.recent{grid-template-columns:repeat(16,1fr);max-height:none;border-color:transparent;padding:0;background:transparent;min-height:26px}
|
|
3304
|
+
.bb-sd-cell{aspect-ratio:1;display:grid;place-items:center;border:0;border-radius:5px;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer;padding:0;font-family:inherit,"Noto Sans Symbols 2"}
|
|
3305
|
+
.bb-sd-grid.recent .bb-sd-cell{border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));font-size:15px}
|
|
3306
|
+
.bb-sd-cell:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
3307
|
+
.bb-sd-cell:focus{outline:none}
|
|
3308
|
+
.bb-sd-cell[aria-selected="true"]{background:var(--bb-ui-active-bg,#e6f1fb);box-shadow:inset 0 0 0 1.5px var(--bb-ui-active-border,#7fb2ec);color:var(--bb-ui-active-fg,#0c447c)}
|
|
3309
|
+
.bb-sd-empty{grid-column:1/-1;font-size:12px;opacity:.55;padding:6px 2px}
|
|
3310
|
+
.bb-sd-meta{display:grid;grid-template-columns:minmax(0,1fr) 140px;gap:10px;align-items:end}
|
|
3311
|
+
.bb-sd-info{min-width:0;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:8px;padding:8px 10px;display:flex;gap:12px;align-items:center;min-height:56px}
|
|
3312
|
+
.bb-sd-big{font-size:30px;line-height:1;width:44px;text-align:center;font-family:inherit,"Noto Sans Symbols 2"}
|
|
3313
|
+
.bb-sd-name{font-size:13px}
|
|
3314
|
+
.bb-sd-cp{font-size:11px;opacity:.55;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
3315
|
+
.bb-sd-code{font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
3316
|
+
.bb-sd-code[aria-invalid="true"]{border-color:#c0392b}
|
|
3317
|
+
.bb-sd-list{border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:8px;max-height:280px;overflow:auto}
|
|
3318
|
+
.bb-sd-list table{width:100%;border-collapse:collapse;font-size:13px}
|
|
3319
|
+
.bb-sd-list td{padding:6px 8px;border-bottom:0.5px solid var(--bb-ui-border,#e3e3e0)}
|
|
3320
|
+
.bb-sd-list tr:last-child td{border-bottom:0}
|
|
3321
|
+
.bb-sd-list tr{cursor:pointer}
|
|
3322
|
+
.bb-sd-list tr:hover td{background:var(--bb-ui-hover,#f1efe8)}
|
|
3323
|
+
.bb-sd-list tr[aria-selected="true"] td{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c)}
|
|
3324
|
+
.bb-sd-list td.ch{width:36px;text-align:center;font-size:16px;font-family:inherit,"Noto Sans Symbols 2"}
|
|
3325
|
+
.bb-sd-list td.k{width:120px;font-size:11px;opacity:.6;font-family:ui-monospace,Menlo,Consolas,monospace;text-align:right}
|
|
3326
|
+
.bb-sd-foot{display:flex;justify-content:space-between;align-items:center;gap:8px}
|
|
3327
|
+
.bb-sd-hint{font-size:11px;opacity:.55}
|
|
3328
|
+
.bb-sd-btns{display:flex;gap:8px}
|
|
3329
|
+
.bb-sd-btn{height:31px;padding:0 17px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
3330
|
+
.bb-sd-btn.primary{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#7fb2ec);color:var(--bb-ui-active-fg,#0c447c)}
|
|
3331
|
+
.bb-sd-btn:disabled{opacity:.5;cursor:default}
|
|
3332
|
+
`;
|
|
3333
|
+
var GRID_COLS = 12;
|
|
3334
|
+
var RECENT_COLS = 16;
|
|
3335
|
+
function el2(tag, cls, text) {
|
|
3336
|
+
const n = document.createElement(tag);
|
|
3337
|
+
if (cls) n.className = cls;
|
|
3338
|
+
if (text !== void 0) n.textContent = text;
|
|
3339
|
+
return n;
|
|
3340
|
+
}
|
|
3341
|
+
function glyphFor(char) {
|
|
3342
|
+
const cp = char.codePointAt(0) ?? 0;
|
|
3343
|
+
if (char === " " || char === "\xA0") return "\u2423";
|
|
3344
|
+
if (cp === 8194 || cp === 8195 || cp === 8201) return "\u2423";
|
|
3345
|
+
if (cp === 173) return "-";
|
|
3346
|
+
if (cp === 8209) return "\u2011";
|
|
3347
|
+
return char;
|
|
3348
|
+
}
|
|
3349
|
+
function createSymbolDialog(options) {
|
|
3350
|
+
injectStyle("bb-symbol-dialog", STYLE13);
|
|
3351
|
+
let recent = [...options.recent ?? []].slice(0, RECENT_MAX);
|
|
3352
|
+
let selected = null;
|
|
3353
|
+
const root = el2("div", "bb-sd");
|
|
3354
|
+
const tabs = el2("div", "bb-sd-tabs");
|
|
3355
|
+
tabs.setAttribute("role", "tablist");
|
|
3356
|
+
const symTab = el2("button", "bb-sd-tab", "Symbols");
|
|
3357
|
+
const spcTab = el2("button", "bb-sd-tab", "Special characters");
|
|
3358
|
+
for (const t of [symTab, spcTab]) {
|
|
3359
|
+
t.type = "button";
|
|
3360
|
+
t.setAttribute("role", "tab");
|
|
3361
|
+
tabs.append(t);
|
|
3362
|
+
}
|
|
3363
|
+
const symPane = el2("div", "bb-sd-pane");
|
|
3364
|
+
const spcPane = el2("div", "bb-sd-pane");
|
|
3365
|
+
const showTab = (special) => {
|
|
3366
|
+
symTab.setAttribute("aria-selected", String(!special));
|
|
3367
|
+
spcTab.setAttribute("aria-selected", String(special));
|
|
3368
|
+
symPane.hidden = special;
|
|
3369
|
+
spcPane.hidden = !special;
|
|
3370
|
+
};
|
|
3371
|
+
symTab.addEventListener("click", () => showTab(false));
|
|
3372
|
+
spcTab.addEventListener("click", () => showTab(true));
|
|
3373
|
+
const recentWrap = el2("div");
|
|
3374
|
+
recentWrap.append(el2("div", "bb-sd-lbl", "Recently used"));
|
|
3375
|
+
const recentGrid = el2("div", "bb-sd-grid recent");
|
|
3376
|
+
recentGrid.setAttribute("role", "grid");
|
|
3377
|
+
recentGrid.setAttribute("aria-label", "Recently used symbols");
|
|
3378
|
+
recentWrap.append(recentGrid);
|
|
3379
|
+
const controls = el2("div", "bb-sd-row");
|
|
3380
|
+
const groupCell = el2("div");
|
|
3381
|
+
groupCell.style.flex = "1";
|
|
3382
|
+
groupCell.append(el2("div", "bb-sd-lbl", "Group"));
|
|
3383
|
+
const groupSel = el2("select", "bb-sd-ctl");
|
|
3384
|
+
groupSel.setAttribute("aria-label", "Group");
|
|
3385
|
+
for (const grp of SYMBOL_GROUPS) {
|
|
3386
|
+
const o = el2("option", void 0, grp.label);
|
|
3387
|
+
o.value = grp.id;
|
|
3388
|
+
groupSel.append(o);
|
|
3389
|
+
}
|
|
3390
|
+
groupCell.append(groupSel);
|
|
3391
|
+
const searchCell = el2("div");
|
|
3392
|
+
searchCell.style.width = "160px";
|
|
3393
|
+
searchCell.append(el2("div", "bb-sd-lbl", "Search name"));
|
|
3394
|
+
const search = el2("input", "bb-sd-ctl");
|
|
3395
|
+
search.type = "search";
|
|
3396
|
+
search.placeholder = "e.g. ballot, arrow";
|
|
3397
|
+
search.setAttribute("aria-label", "Search symbols by name");
|
|
3398
|
+
searchCell.append(search);
|
|
3399
|
+
controls.append(groupCell, searchCell);
|
|
3400
|
+
const grid = el2("div", "bb-sd-grid");
|
|
3401
|
+
grid.setAttribute("role", "grid");
|
|
3402
|
+
grid.setAttribute("aria-label", "Symbols");
|
|
3403
|
+
const meta = el2("div", "bb-sd-meta");
|
|
3404
|
+
const info = el2("div", "bb-sd-info");
|
|
3405
|
+
const big = el2("div", "bb-sd-big", "");
|
|
3406
|
+
const infoText = el2("div");
|
|
3407
|
+
const nameEl = el2("div", "bb-sd-name", "Select a symbol");
|
|
3408
|
+
const cpEl = el2("div", "bb-sd-cp", "");
|
|
3409
|
+
infoText.append(nameEl, cpEl);
|
|
3410
|
+
info.append(big, infoText);
|
|
3411
|
+
const codeCell = el2("div");
|
|
3412
|
+
codeCell.append(el2("div", "bb-sd-lbl", "Character code (hex)"));
|
|
3413
|
+
const code = el2("input", "bb-sd-ctl bb-sd-code");
|
|
3414
|
+
code.placeholder = "e.g. 2611";
|
|
3415
|
+
code.setAttribute("aria-label", "Character code, hexadecimal");
|
|
3416
|
+
code.spellcheck = false;
|
|
3417
|
+
codeCell.append(code);
|
|
3418
|
+
meta.append(info, codeCell);
|
|
3419
|
+
symPane.append(recentWrap, controls, grid, meta);
|
|
3420
|
+
const list = el2("div", "bb-sd-list");
|
|
3421
|
+
const table = el2("table");
|
|
3422
|
+
const tbody = el2("tbody");
|
|
3423
|
+
const rows = [];
|
|
3424
|
+
for (const sc of SPECIAL_CHARACTERS) {
|
|
3425
|
+
const tr = el2("tr");
|
|
3426
|
+
tr.tabIndex = -1;
|
|
3427
|
+
tr.setAttribute("role", "option");
|
|
3428
|
+
const ch = el2("td", "ch", glyphFor(sc.char));
|
|
3429
|
+
const nm = el2("td", void 0, sc.name);
|
|
3430
|
+
const k = el2("td", "k", sc.hint);
|
|
3431
|
+
tr.append(ch, nm, k);
|
|
3432
|
+
tr.addEventListener("click", () => select2(sc.char, tr));
|
|
3433
|
+
tr.addEventListener("dblclick", () => insert(sc.char));
|
|
3434
|
+
tr.addEventListener("keydown", (e) => {
|
|
3435
|
+
const i = rows.indexOf(tr);
|
|
3436
|
+
if (e.key === "ArrowDown" && rows[i + 1]) {
|
|
3437
|
+
e.preventDefault();
|
|
3438
|
+
rows[i + 1].focus();
|
|
3439
|
+
select2(SPECIAL_CHARACTERS[i + 1].char, rows[i + 1]);
|
|
3440
|
+
} else if (e.key === "ArrowUp" && rows[i - 1]) {
|
|
3441
|
+
e.preventDefault();
|
|
3442
|
+
rows[i - 1].focus();
|
|
3443
|
+
select2(SPECIAL_CHARACTERS[i - 1].char, rows[i - 1]);
|
|
3444
|
+
} else if (e.key === "Enter") {
|
|
3445
|
+
e.preventDefault();
|
|
3446
|
+
insert(sc.char);
|
|
3447
|
+
}
|
|
3448
|
+
});
|
|
3449
|
+
tbody.append(tr);
|
|
3450
|
+
rows.push(tr);
|
|
3451
|
+
}
|
|
3452
|
+
table.append(tbody);
|
|
3453
|
+
list.append(table);
|
|
3454
|
+
list.setAttribute("role", "listbox");
|
|
3455
|
+
spcPane.append(list);
|
|
3456
|
+
const foot = el2("div", "bb-sd-foot");
|
|
3457
|
+
const hint = el2(
|
|
3458
|
+
"span",
|
|
3459
|
+
"bb-sd-hint",
|
|
3460
|
+
"Double-click a cell to insert \xB7 Enter inserts the selection"
|
|
3461
|
+
);
|
|
3462
|
+
const btns = el2("div", "bb-sd-btns");
|
|
3463
|
+
const closeBtn = el2("button", "bb-sd-btn", "Close");
|
|
3464
|
+
closeBtn.type = "button";
|
|
3465
|
+
const insertBtn = el2("button", "bb-sd-btn primary", "Insert");
|
|
3466
|
+
insertBtn.type = "button";
|
|
3467
|
+
insertBtn.disabled = true;
|
|
3468
|
+
btns.append(closeBtn, insertBtn);
|
|
3469
|
+
foot.append(hint, btns);
|
|
3470
|
+
root.append(tabs, symPane, spcPane, foot);
|
|
3471
|
+
const dialog = new Dialog({
|
|
3472
|
+
title: "Symbol",
|
|
3473
|
+
modal: false,
|
|
3474
|
+
anchor: options.anchor,
|
|
3475
|
+
className: "bb-symbol-dialog"
|
|
3476
|
+
});
|
|
3477
|
+
dialog.setContent(root);
|
|
3478
|
+
let selectedEl = null;
|
|
3479
|
+
function select2(char, cell) {
|
|
3480
|
+
selected = char;
|
|
3481
|
+
if (selectedEl) selectedEl.removeAttribute("aria-selected");
|
|
3482
|
+
selectedEl = cell ?? null;
|
|
3483
|
+
if (selectedEl) selectedEl.setAttribute("aria-selected", "true");
|
|
3484
|
+
big.textContent = glyphFor(char);
|
|
3485
|
+
nameEl.textContent = SYMBOL_NAMES.get(char) ?? "Character";
|
|
3486
|
+
cpEl.textContent = codePointLabel(char);
|
|
3487
|
+
if (document.activeElement !== code)
|
|
3488
|
+
code.value = codePointLabel(char).slice(2);
|
|
3489
|
+
code.removeAttribute("aria-invalid");
|
|
3490
|
+
insertBtn.disabled = false;
|
|
3491
|
+
}
|
|
3492
|
+
function insert(char) {
|
|
3493
|
+
const active = document.activeElement;
|
|
3494
|
+
options.onInsert(char);
|
|
3495
|
+
recent = pushRecent(recent, char);
|
|
3496
|
+
options.onRecentChange?.([...recent]);
|
|
3497
|
+
renderRecent();
|
|
3498
|
+
if (active instanceof HTMLElement && root.contains(active))
|
|
3499
|
+
queueMicrotask(() => {
|
|
3500
|
+
if (active.isConnected) active.focus();
|
|
3501
|
+
else search.focus();
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3504
|
+
function cellFor(e) {
|
|
3505
|
+
const b = el2("button", "bb-sd-cell", glyphFor(e.char));
|
|
3506
|
+
b.type = "button";
|
|
3507
|
+
b.title = `${e.name} \xB7 ${codePointLabel(e.char)}`;
|
|
3508
|
+
b.setAttribute("aria-label", e.name);
|
|
3509
|
+
b.addEventListener("click", () => select2(e.char, b));
|
|
3510
|
+
b.addEventListener("dblclick", () => insert(e.char));
|
|
3511
|
+
b.addEventListener("keydown", (ev) => {
|
|
3512
|
+
if (ev.key === "Enter" || ev.key === " ") {
|
|
3513
|
+
ev.preventDefault();
|
|
3514
|
+
select2(e.char, b);
|
|
3515
|
+
insert(e.char);
|
|
3516
|
+
}
|
|
3517
|
+
});
|
|
3518
|
+
b.addEventListener("focus", () => select2(e.char, b));
|
|
3519
|
+
return b;
|
|
3520
|
+
}
|
|
3521
|
+
function fillGrid(target, entries, cols, empty) {
|
|
3522
|
+
target.replaceChildren();
|
|
3523
|
+
if (entries.length === 0) {
|
|
3524
|
+
target.append(el2("div", "bb-sd-empty", empty));
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
const cells = [];
|
|
3528
|
+
entries.forEach((e, i) => {
|
|
3529
|
+
const b = cellFor(e);
|
|
3530
|
+
target.append(b);
|
|
3531
|
+
if (i % cols === 0) cells.push([]);
|
|
3532
|
+
cells[cells.length - 1].push(b);
|
|
3533
|
+
});
|
|
3534
|
+
rovingGrid(cells);
|
|
3535
|
+
}
|
|
3536
|
+
function renderRecent() {
|
|
3537
|
+
fillGrid(
|
|
3538
|
+
recentGrid,
|
|
3539
|
+
recent.map((c) => ({
|
|
3540
|
+
char: c,
|
|
3541
|
+
name: SYMBOL_NAMES.get(c) ?? "Character"
|
|
3542
|
+
})),
|
|
3543
|
+
RECENT_COLS,
|
|
3544
|
+
"Nothing yet \u2014 inserted symbols appear here."
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
function renderGroup() {
|
|
3548
|
+
const q = search.value.trim();
|
|
3549
|
+
const entries = q ? searchSymbols(q) : SYMBOL_GROUPS.find((g2) => g2.id === groupSel.value)?.entries ?? [];
|
|
3550
|
+
fillGrid(grid, entries, GRID_COLS, "No symbol matches that name.");
|
|
3551
|
+
const first = grid.querySelector(".bb-sd-cell");
|
|
3552
|
+
if (q && first && entries[0]) select2(entries[0].char, first);
|
|
3553
|
+
else clearSelection();
|
|
3554
|
+
}
|
|
3555
|
+
function clearSelection() {
|
|
3556
|
+
selected = null;
|
|
3557
|
+
selectedEl?.removeAttribute("aria-selected");
|
|
3558
|
+
selectedEl = null;
|
|
3559
|
+
big.textContent = "";
|
|
3560
|
+
nameEl.textContent = "Select a symbol";
|
|
3561
|
+
cpEl.textContent = "";
|
|
3562
|
+
if (document.activeElement !== code) code.value = "";
|
|
3563
|
+
insertBtn.disabled = true;
|
|
3564
|
+
}
|
|
3565
|
+
groupSel.addEventListener("change", () => {
|
|
3566
|
+
search.value = "";
|
|
3567
|
+
renderGroup();
|
|
3568
|
+
});
|
|
3569
|
+
search.addEventListener("input", renderGroup);
|
|
3570
|
+
search.addEventListener("keydown", (e) => {
|
|
3571
|
+
if (e.key === "Enter" && selected) {
|
|
3572
|
+
e.preventDefault();
|
|
3573
|
+
insert(selected);
|
|
3574
|
+
} else if (e.key === "ArrowDown") {
|
|
3575
|
+
e.preventDefault();
|
|
3576
|
+
grid.querySelector(".bb-sd-cell")?.focus();
|
|
3577
|
+
}
|
|
3578
|
+
});
|
|
3579
|
+
code.addEventListener("input", () => {
|
|
3580
|
+
const ch = parseCodePoint(code.value);
|
|
3581
|
+
if (ch) {
|
|
3582
|
+
code.removeAttribute("aria-invalid");
|
|
3583
|
+
const cell = Array.from(
|
|
3584
|
+
grid.querySelectorAll(".bb-sd-cell")
|
|
3585
|
+
).find((c) => c.textContent === glyphFor(ch));
|
|
3586
|
+
select2(ch, cell ?? null);
|
|
3587
|
+
} else {
|
|
3588
|
+
code.setAttribute("aria-invalid", code.value.trim() ? "true" : "false");
|
|
3589
|
+
insertBtn.disabled = true;
|
|
3590
|
+
selected = null;
|
|
3591
|
+
}
|
|
3592
|
+
});
|
|
3593
|
+
code.addEventListener("keydown", (e) => {
|
|
3594
|
+
if (e.key === "Enter" && selected) {
|
|
3595
|
+
e.preventDefault();
|
|
3596
|
+
insert(selected);
|
|
3597
|
+
}
|
|
3598
|
+
});
|
|
3599
|
+
insertBtn.addEventListener("click", () => {
|
|
3600
|
+
if (selected) insert(selected);
|
|
3601
|
+
});
|
|
3602
|
+
closeBtn.addEventListener("click", () => dialog.close());
|
|
3603
|
+
showTab(false);
|
|
3604
|
+
renderRecent();
|
|
3605
|
+
renderGroup();
|
|
3606
|
+
return {
|
|
3607
|
+
open() {
|
|
3608
|
+
dialog.open();
|
|
3609
|
+
showTab(false);
|
|
3610
|
+
queueMicrotask(() => {
|
|
3611
|
+
search.focus();
|
|
3612
|
+
search.select();
|
|
3613
|
+
});
|
|
3614
|
+
},
|
|
3615
|
+
close() {
|
|
3616
|
+
dialog.close();
|
|
3617
|
+
},
|
|
3618
|
+
destroy() {
|
|
3619
|
+
dialog.destroy();
|
|
3620
|
+
},
|
|
3621
|
+
get isOpen() {
|
|
3622
|
+
return dialog.isOpen;
|
|
3623
|
+
}
|
|
3624
|
+
};
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
// packages/ui/src/lib/symbol-popover.ts
|
|
3628
|
+
var STYLE14 = `
|
|
3629
|
+
.bb-spk{position:fixed;margin:0;max-width:none;max-height:none;z-index:1300;width:276px;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);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);font-size:12px;box-sizing:border-box;display:flex;flex-direction:column;gap:8px}
|
|
3630
|
+
.bb-spk *{box-sizing:border-box}
|
|
3631
|
+
.bb-spk::backdrop{background:transparent}
|
|
3632
|
+
.bb-spk-search{width:100%;height:28px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font:inherit;font-size:12px;padding:0 8px}
|
|
3633
|
+
.bb-spk-search::placeholder{color:inherit;opacity:.45}
|
|
3634
|
+
.bb-spk-lbl{font-size:10.5px;opacity:.65;margin:0 0 4px}
|
|
3635
|
+
.bb-spk-recent{display:grid;grid-template-columns:repeat(8,1fr);gap:3px;min-height:26px}
|
|
3636
|
+
.bb-spk-recent .bb-spk-cell{border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));font-size:14px}
|
|
3637
|
+
.bb-spk-empty{grid-column:1/-1;font-size:11px;opacity:.5;padding:4px 2px}
|
|
3638
|
+
.bb-spk-chips{display:flex;flex-wrap:wrap;gap:3px}
|
|
3639
|
+
.bb-spk-chip{padding:2px 6px;border-radius:10px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font:inherit;font-size:10px;opacity:.8;cursor:pointer}
|
|
3640
|
+
.bb-spk-chip:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
3641
|
+
.bb-spk-chip[aria-pressed="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);border-color:var(--bb-ui-active-border,#7fb2ec);opacity:1}
|
|
3642
|
+
.bb-spk-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:2px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:7px;padding:4px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));max-height:150px;overflow:auto}
|
|
3643
|
+
.bb-spk-cell{aspect-ratio:1;display:grid;place-items:center;border:0;border-radius:5px;background:transparent;color:inherit;font-size:16px;line-height:1;padding:0;cursor:pointer;font-family:inherit,"Noto Sans Symbols 2"}
|
|
3644
|
+
.bb-spk-cell:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
3645
|
+
.bb-spk-cell:focus{outline:none;box-shadow:inset 0 0 0 1.5px var(--bb-ui-active-border,#7fb2ec)}
|
|
3646
|
+
.bb-spk-cell[aria-selected="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c)}
|
|
3647
|
+
.bb-spk-foot{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:11px}
|
|
3648
|
+
.bb-spk-name{opacity:.75;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3649
|
+
.bb-spk-more{border:0;background:transparent;color:var(--bb-ui-active-fg,#0c447c);font:inherit;font-size:11px;cursor:pointer;padding:2px 4px;border-radius:4px;white-space:nowrap}
|
|
3650
|
+
.bb-spk-more:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
3651
|
+
`;
|
|
3652
|
+
var COLS = 8;
|
|
3653
|
+
var RECENT_ROW = COLS;
|
|
3654
|
+
function el3(tag, cls, text) {
|
|
3655
|
+
const n = document.createElement(tag);
|
|
3656
|
+
if (cls) n.className = cls;
|
|
3657
|
+
if (text !== void 0) n.textContent = text;
|
|
3658
|
+
return n;
|
|
3659
|
+
}
|
|
3660
|
+
function openSymbolPopover(options) {
|
|
3661
|
+
injectStyle("bb-ui-symbol-popover", STYLE14);
|
|
3662
|
+
const { anchor } = options;
|
|
3663
|
+
let recent = [...options.recent ?? []].slice(0, RECENT_MAX);
|
|
3664
|
+
let groupId = SYMBOL_GROUPS[0].id;
|
|
3665
|
+
let selected = null;
|
|
3666
|
+
const panel = document.createElement("dialog");
|
|
3667
|
+
panel.className = "bb-spk";
|
|
3668
|
+
panel.setAttribute("aria-label", "Insert symbol");
|
|
3669
|
+
let closed = false;
|
|
3670
|
+
const close = () => {
|
|
3671
|
+
if (closed) return;
|
|
3672
|
+
closed = true;
|
|
3673
|
+
window.removeEventListener("scroll", close, true);
|
|
3674
|
+
if (panel.open) panel.close();
|
|
3675
|
+
panel.remove();
|
|
3676
|
+
options.onClose?.();
|
|
3677
|
+
};
|
|
3678
|
+
const search = el3("input", "bb-spk-search");
|
|
3679
|
+
search.type = "search";
|
|
3680
|
+
search.placeholder = "Search name or code (U+2611)";
|
|
3681
|
+
search.setAttribute("aria-label", "Search symbols by name or code");
|
|
3682
|
+
search.spellcheck = false;
|
|
3683
|
+
const recentWrap = el3("div");
|
|
3684
|
+
recentWrap.append(el3("div", "bb-spk-lbl", "Recent"));
|
|
3685
|
+
const recentGrid = el3("div", "bb-spk-recent");
|
|
3686
|
+
recentGrid.setAttribute("role", "grid");
|
|
3687
|
+
recentGrid.setAttribute("aria-label", "Recently used symbols");
|
|
3688
|
+
recentWrap.append(recentGrid);
|
|
3689
|
+
const chips = el3("div", "bb-spk-chips");
|
|
3690
|
+
const chipEls = /* @__PURE__ */ new Map();
|
|
3691
|
+
for (const grp of SYMBOL_GROUPS) {
|
|
3692
|
+
const c = el3("button", "bb-spk-chip", grp.short);
|
|
3693
|
+
c.type = "button";
|
|
3694
|
+
c.title = grp.label;
|
|
3695
|
+
c.addEventListener("click", () => {
|
|
3696
|
+
groupId = grp.id;
|
|
3697
|
+
search.value = "";
|
|
3698
|
+
render();
|
|
3699
|
+
});
|
|
3700
|
+
chips.append(c);
|
|
3701
|
+
chipEls.set(grp.id, c);
|
|
3702
|
+
}
|
|
3703
|
+
const grid = el3("div", "bb-spk-grid");
|
|
3704
|
+
grid.setAttribute("role", "grid");
|
|
3705
|
+
grid.setAttribute("aria-label", "Symbols");
|
|
3706
|
+
const foot = el3("div", "bb-spk-foot");
|
|
3707
|
+
const nameEl = el3("span", "bb-spk-name", "");
|
|
3708
|
+
const more = el3("button", "bb-spk-more", "More symbols\u2026");
|
|
3709
|
+
more.type = "button";
|
|
3710
|
+
more.addEventListener("click", () => {
|
|
3711
|
+
close();
|
|
3712
|
+
options.onMore?.();
|
|
3713
|
+
});
|
|
3714
|
+
foot.append(nameEl, more);
|
|
3715
|
+
if (!options.onMore) more.hidden = true;
|
|
3716
|
+
panel.append(search, recentWrap, chips, grid, foot);
|
|
3717
|
+
let selectedEl = null;
|
|
3718
|
+
const select2 = (char, cell) => {
|
|
3719
|
+
selected = char;
|
|
3720
|
+
selectedEl?.removeAttribute("aria-selected");
|
|
3721
|
+
selectedEl = cell;
|
|
3722
|
+
selectedEl?.setAttribute("aria-selected", "true");
|
|
3723
|
+
nameEl.textContent = `${SYMBOL_NAMES.get(char) ?? "Character"} \xB7 ${codePointLabel(char)}`;
|
|
3724
|
+
};
|
|
3725
|
+
const insert = (char) => {
|
|
3726
|
+
const active = document.activeElement;
|
|
3727
|
+
options.onInsert(char);
|
|
3728
|
+
recent = pushRecent(recent, char);
|
|
3729
|
+
options.onRecentChange?.([...recent]);
|
|
3730
|
+
renderRecent();
|
|
3731
|
+
queueMicrotask(() => {
|
|
3732
|
+
if (active instanceof HTMLElement && active.isConnected) active.focus();
|
|
3733
|
+
else search.focus();
|
|
3734
|
+
});
|
|
3735
|
+
};
|
|
3736
|
+
const cellFor = (e) => {
|
|
3737
|
+
const b = el3("button", "bb-spk-cell", e.char);
|
|
3738
|
+
b.type = "button";
|
|
3739
|
+
b.title = `${e.name} \xB7 ${codePointLabel(e.char)}`;
|
|
3740
|
+
b.setAttribute("aria-label", e.name);
|
|
3741
|
+
b.addEventListener("focus", () => select2(e.char, b));
|
|
3742
|
+
b.addEventListener("click", () => {
|
|
3743
|
+
select2(e.char, b);
|
|
3744
|
+
insert(e.char);
|
|
3745
|
+
});
|
|
3746
|
+
b.addEventListener("keydown", (ev) => {
|
|
3747
|
+
if (ev.key === "Enter" || ev.key === " ") {
|
|
3748
|
+
ev.preventDefault();
|
|
3749
|
+
select2(e.char, b);
|
|
3750
|
+
insert(e.char);
|
|
3751
|
+
}
|
|
3752
|
+
});
|
|
3753
|
+
return b;
|
|
3754
|
+
};
|
|
3755
|
+
const fill = (target, entries, empty) => {
|
|
3756
|
+
target.replaceChildren();
|
|
3757
|
+
if (entries.length === 0) {
|
|
3758
|
+
target.append(el3("div", "bb-spk-empty", empty));
|
|
3759
|
+
return;
|
|
3760
|
+
}
|
|
3761
|
+
const cells = [];
|
|
3762
|
+
entries.forEach((e, i) => {
|
|
3763
|
+
const b = cellFor(e);
|
|
3764
|
+
target.append(b);
|
|
3765
|
+
if (i % COLS === 0) cells.push([]);
|
|
3766
|
+
cells[cells.length - 1].push(b);
|
|
3767
|
+
});
|
|
3768
|
+
rovingGrid(cells);
|
|
3769
|
+
};
|
|
3770
|
+
const renderRecent = () => fill(
|
|
3771
|
+
recentGrid,
|
|
3772
|
+
recent.slice(0, RECENT_ROW).map((c) => ({
|
|
3773
|
+
char: c,
|
|
3774
|
+
name: SYMBOL_NAMES.get(c) ?? "Character"
|
|
3775
|
+
})),
|
|
3776
|
+
"Nothing yet."
|
|
3777
|
+
);
|
|
3778
|
+
const render = () => {
|
|
3779
|
+
const q = search.value.trim();
|
|
3780
|
+
const byCode = q ? parseCodePoint(q) : null;
|
|
3781
|
+
const entries = q ? byCode && !searchSymbols(q).length ? [{ char: byCode, name: SYMBOL_NAMES.get(byCode) ?? "Character" }] : searchSymbols(q) : SYMBOL_GROUPS.find((g2) => g2.id === groupId)?.entries ?? [];
|
|
3782
|
+
for (const [id, c] of chipEls)
|
|
3783
|
+
c.setAttribute("aria-pressed", String(!q && id === groupId));
|
|
3784
|
+
fill(grid, entries, "No match.");
|
|
3785
|
+
const first = grid.querySelector(".bb-spk-cell");
|
|
3786
|
+
if (q && first && entries[0]) select2(entries[0].char, first);
|
|
3787
|
+
else {
|
|
3788
|
+
selected = null;
|
|
3789
|
+
selectedEl?.removeAttribute("aria-selected");
|
|
3790
|
+
selectedEl = null;
|
|
3791
|
+
nameEl.textContent = "";
|
|
3792
|
+
}
|
|
3793
|
+
};
|
|
3794
|
+
search.addEventListener("input", render);
|
|
3795
|
+
search.addEventListener("keydown", (e) => {
|
|
3796
|
+
if (e.key === "Enter" && selected) {
|
|
3797
|
+
e.preventDefault();
|
|
3798
|
+
insert(selected);
|
|
3799
|
+
} else if (e.key === "ArrowDown") {
|
|
3800
|
+
e.preventDefault();
|
|
3801
|
+
grid.querySelector(".bb-spk-cell")?.focus();
|
|
3802
|
+
}
|
|
3803
|
+
});
|
|
3804
|
+
document.body.append(panel);
|
|
3805
|
+
panel.showModal();
|
|
3806
|
+
const r = anchor.getBoundingClientRect();
|
|
3807
|
+
placeFloating(panel, { x: r.left, y: r.top, height: r.height });
|
|
3808
|
+
panel.addEventListener("close", close);
|
|
3809
|
+
panel.addEventListener("pointerdown", (e) => {
|
|
3810
|
+
const b = panel.getBoundingClientRect();
|
|
3811
|
+
const outside = e.clientX < b.left || e.clientX > b.right || e.clientY < b.top || e.clientY > b.bottom;
|
|
3812
|
+
if (outside) close();
|
|
3813
|
+
});
|
|
3814
|
+
window.addEventListener("scroll", close, true);
|
|
3815
|
+
renderRecent();
|
|
3816
|
+
render();
|
|
3817
|
+
search.focus();
|
|
3818
|
+
return { close };
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
// packages/ui/src/lib/shortcuts-dialog.ts
|
|
3822
|
+
import {
|
|
3823
|
+
filterKeybindingRows,
|
|
3824
|
+
formatKey,
|
|
3825
|
+
keybindingRows
|
|
3826
|
+
} from "@shadow-garden/bapbong-contracts";
|
|
3827
|
+
var STYLE15 = `
|
|
3828
|
+
.bb-ks{display:flex;flex-direction:column;gap:10px;width:600px;max-width:100%;color:var(--bb-ui-fg,#2c2c2a);font-size:12px}
|
|
3829
|
+
.bb-ks *{box-sizing:border-box}
|
|
3830
|
+
.bb-ks-top{display:flex;gap:8px;align-items:center}
|
|
3831
|
+
.bb-ks-search{flex:1;height:30px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font:inherit;font-size:12px;padding:0 8px}
|
|
3832
|
+
.bb-ks-search::placeholder{color:inherit;opacity:.45}
|
|
3833
|
+
.bb-ks-count{font-size:11px;opacity:.6;white-space:nowrap}
|
|
3834
|
+
/* No box around the list: a bordered, tinted scroller turns the scrollbar's
|
|
3835
|
+
gutter into a blank column at its edge. Flat on the dialog surface, the
|
|
3836
|
+
rail reads like the app's own (idle-hidden overlay on a plain ground). */
|
|
3837
|
+
.bb-ks-wrap{overflow:auto;max-height:min(60vh,420px);margin:0 -12px;padding:0 12px;scrollbar-gutter:stable}
|
|
3838
|
+
.bb-ks table{width:100%;border-collapse:collapse}
|
|
3839
|
+
.bb-ks th{position:sticky;top:0;z-index:1;text-align:left;font-size:11px;font-weight:600;padding:7px 10px;border-bottom:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));cursor:pointer;user-select:none;white-space:nowrap;background-color:var(--bb-ui-bg,#fff);background-image:linear-gradient(var(--bb-ui-dialog-bg,transparent),var(--bb-ui-dialog-bg,transparent))}
|
|
3840
|
+
/* Opaque surface UNDER the dialog's own tint: a sticky header on the glass
|
|
3841
|
+
alone let the rows scrolling beneath show through it. The dimming goes on
|
|
3842
|
+
the text, not the cell, for the same reason. */
|
|
3843
|
+
.bb-ks th span{opacity:.75}
|
|
3844
|
+
.bb-ks th[aria-sort]{color:var(--bb-ui-active-fg,#0c447c)}
|
|
3845
|
+
.bb-ks th[aria-sort] span{opacity:1}
|
|
3846
|
+
.bb-ks td{padding:7px 10px;border-bottom:0.5px solid var(--bb-ui-border,#e3e3e0);vertical-align:middle}
|
|
3847
|
+
.bb-ks tr:last-child td{border-bottom:0}
|
|
3848
|
+
.bb-ks tbody tr:hover td{background:var(--bb-ui-hover,#f1efe8)}
|
|
3849
|
+
.bb-ks tbody tr:hover td:first-child{border-radius:6px 0 0 6px}
|
|
3850
|
+
.bb-ks tbody tr:hover td:last-child{border-radius:0 6px 6px 0}
|
|
3851
|
+
.bb-ks td.cmd{width:42%}
|
|
3852
|
+
.bb-ks td.cmd small{display:block;font-size:10px;opacity:.5;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
3853
|
+
.bb-ks kbd{display:inline-block;min-width:20px;padding:1px 6px;margin-right:3px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-bottom-width:1.5px;border-radius:5px;background:var(--bb-ui-bg,#fff);font:inherit;font-size:11px;text-align:center}
|
|
3854
|
+
.bb-ks .or{opacity:.5;margin:0 4px 0 1px;font-size:11px}
|
|
3855
|
+
.bb-ks td.when{font-size:11px;opacity:.7;width:24%}
|
|
3856
|
+
.bb-ks td.src{width:12%}
|
|
3857
|
+
.bb-ks-chip{display:inline-block;padding:1px 7px;border-radius:9px;font-size:10px;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));opacity:.85;white-space:nowrap}
|
|
3858
|
+
.bb-ks-chip.core{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#7fb2ec);color:var(--bb-ui-active-fg,#0c447c);opacity:1}
|
|
3859
|
+
.bb-ks-empty{padding:16px;text-align:center;font-size:12px;opacity:.55}
|
|
3860
|
+
.bb-ks-foot{display:flex;justify-content:space-between;font-size:11px;opacity:.6}
|
|
3861
|
+
.bb-ks mark{background:transparent;color:inherit;font-weight:600;text-decoration:underline;text-decoration-color:var(--bb-ui-active-border,#7fb2ec);text-underline-offset:2px}
|
|
3862
|
+
`;
|
|
3863
|
+
function el4(tag, cls, text) {
|
|
3864
|
+
const n = document.createElement(tag);
|
|
3865
|
+
if (cls) n.className = cls;
|
|
3866
|
+
if (text !== void 0) n.textContent = text;
|
|
3867
|
+
return n;
|
|
3868
|
+
}
|
|
3869
|
+
function highlight(text, q) {
|
|
3870
|
+
if (!q) return [text];
|
|
3871
|
+
const out = [];
|
|
3872
|
+
const lower = text.toLowerCase();
|
|
3873
|
+
let i = 0;
|
|
3874
|
+
for (; ; ) {
|
|
3875
|
+
const j = lower.indexOf(q, i);
|
|
3876
|
+
if (j < 0) break;
|
|
3877
|
+
if (j > i) out.push(text.slice(i, j));
|
|
3878
|
+
out.push(el4("mark", void 0, text.slice(j, j + q.length)));
|
|
3879
|
+
i = j + q.length;
|
|
3880
|
+
}
|
|
3881
|
+
if (i < text.length) out.push(text.slice(i));
|
|
3882
|
+
return out;
|
|
3883
|
+
}
|
|
3884
|
+
function openKeyboardShortcutsDialog(options) {
|
|
3885
|
+
injectStyle("bb-ui-shortcuts-dialog", STYLE15);
|
|
3886
|
+
const dialog = new Dialog({
|
|
3887
|
+
title: options.title ?? "Keyboard shortcuts",
|
|
3888
|
+
modal: true,
|
|
3889
|
+
wide: true
|
|
3890
|
+
});
|
|
3891
|
+
const root = el4("div", "bb-ks");
|
|
3892
|
+
const top = el4("div", "bb-ks-top");
|
|
3893
|
+
const search = el4("input", "bb-ks-search");
|
|
3894
|
+
search.type = "search";
|
|
3895
|
+
search.placeholder = "Search command, key, when or source\u2026";
|
|
3896
|
+
search.setAttribute("aria-label", "Search shortcuts");
|
|
3897
|
+
const count = el4("span", "bb-ks-count", "");
|
|
3898
|
+
top.append(search, count);
|
|
3899
|
+
const wrap = el4("div", "bb-ks-wrap");
|
|
3900
|
+
const table = el4("table");
|
|
3901
|
+
const thead = el4("thead");
|
|
3902
|
+
const headRow = el4("tr");
|
|
3903
|
+
const columns = [
|
|
3904
|
+
{ key: "title", label: "Command" },
|
|
3905
|
+
{ key: "keys", label: "Keybinding" },
|
|
3906
|
+
{ key: "when", label: "When" },
|
|
3907
|
+
{ key: "source", label: "Source" }
|
|
3908
|
+
];
|
|
3909
|
+
let sortKey = "title";
|
|
3910
|
+
let sortAsc = true;
|
|
3911
|
+
const ths = /* @__PURE__ */ new Map();
|
|
3912
|
+
for (const c of columns) {
|
|
3913
|
+
const th = el4("th");
|
|
3914
|
+
th.append(el4("span", void 0, c.label));
|
|
3915
|
+
th.addEventListener("click", () => {
|
|
3916
|
+
if (sortKey === c.key) sortAsc = !sortAsc;
|
|
3917
|
+
else {
|
|
3918
|
+
sortKey = c.key;
|
|
3919
|
+
sortAsc = true;
|
|
3920
|
+
}
|
|
3921
|
+
render();
|
|
3922
|
+
});
|
|
3923
|
+
headRow.append(th);
|
|
3924
|
+
ths.set(c.key, th);
|
|
3925
|
+
}
|
|
3926
|
+
thead.append(headRow);
|
|
3927
|
+
const tbody = el4("tbody");
|
|
3928
|
+
table.append(thead, tbody);
|
|
3929
|
+
wrap.append(table);
|
|
3930
|
+
const foot = el4("div", "bb-ks-foot");
|
|
3931
|
+
const footL = el4(
|
|
3932
|
+
"span",
|
|
3933
|
+
void 0,
|
|
3934
|
+
"Sorted by command \xB7 click a header to sort"
|
|
3935
|
+
);
|
|
3936
|
+
const footR = el4("span", void 0, "Esc closes");
|
|
3937
|
+
foot.append(footL, footR);
|
|
3938
|
+
root.append(top, wrap, foot);
|
|
3939
|
+
dialog.setContent(root);
|
|
3940
|
+
const allRows = () => {
|
|
3941
|
+
const rows = [];
|
|
3942
|
+
for (const src of options.sources)
|
|
3943
|
+
rows.push(
|
|
3944
|
+
...keybindingRows(src.keybindings, (c) => src.commands.get(c)?.title)
|
|
3945
|
+
);
|
|
3946
|
+
return rows;
|
|
3947
|
+
};
|
|
3948
|
+
const kbdCell = (keys) => {
|
|
3949
|
+
const td = el4("td");
|
|
3950
|
+
keys.forEach((k, i) => {
|
|
3951
|
+
if (i > 0) td.append(el4("span", "or", "or"));
|
|
3952
|
+
for (const part of formatKey(k, IS_MAC))
|
|
3953
|
+
td.append(el4("kbd", void 0, part));
|
|
3954
|
+
});
|
|
3955
|
+
return td;
|
|
3956
|
+
};
|
|
3957
|
+
const sortRows = (rows) => {
|
|
3958
|
+
const val = (r) => sortKey === "title" ? r.title : sortKey === "keys" ? r.keys.map((k) => formatKey(k, IS_MAC).join("")).join(" ") : sortKey === "when" ? r.when ?? "" : r.source;
|
|
3959
|
+
const sorted = [...rows].sort(
|
|
3960
|
+
(a, b) => val(a).localeCompare(val(b), void 0, { sensitivity: "base" })
|
|
3961
|
+
);
|
|
3962
|
+
return sortAsc ? sorted : sorted.reverse();
|
|
3963
|
+
};
|
|
3964
|
+
const render = () => {
|
|
3965
|
+
const q = search.value.trim().toLowerCase();
|
|
3966
|
+
const rows = allRows();
|
|
3967
|
+
const shown = sortRows(filterKeybindingRows(rows, q, IS_MAC));
|
|
3968
|
+
for (const [key, th] of ths) {
|
|
3969
|
+
if (key === sortKey)
|
|
3970
|
+
th.setAttribute("aria-sort", sortAsc ? "ascending" : "descending");
|
|
3971
|
+
else th.removeAttribute("aria-sort");
|
|
3972
|
+
}
|
|
3973
|
+
tbody.replaceChildren();
|
|
3974
|
+
if (shown.length === 0) {
|
|
3975
|
+
const tr = el4("tr");
|
|
3976
|
+
const td = el4("td", "bb-ks-empty", "No shortcut matches.");
|
|
3977
|
+
td.colSpan = 4;
|
|
3978
|
+
tr.append(td);
|
|
3979
|
+
tbody.append(tr);
|
|
3980
|
+
}
|
|
3981
|
+
for (const r of shown) {
|
|
3982
|
+
const tr = el4("tr");
|
|
3983
|
+
const cmd = el4("td", "cmd");
|
|
3984
|
+
cmd.append(...highlight(r.title, q));
|
|
3985
|
+
const small = el4("small");
|
|
3986
|
+
small.append(...highlight(r.command, q));
|
|
3987
|
+
cmd.append(small);
|
|
3988
|
+
const when = el4("td", "when");
|
|
3989
|
+
when.append(...highlight(r.when ?? "", q));
|
|
3990
|
+
const src = el4("td", "src");
|
|
3991
|
+
const chip = el4(
|
|
3992
|
+
"span",
|
|
3993
|
+
"bb-ks-chip" + (r.source === "core" ? " core" : "")
|
|
3994
|
+
);
|
|
3995
|
+
chip.append(...highlight(r.source, q));
|
|
3996
|
+
src.append(chip);
|
|
3997
|
+
tr.append(cmd, kbdCell(r.keys), when, src);
|
|
3998
|
+
tbody.append(tr);
|
|
3999
|
+
}
|
|
4000
|
+
count.textContent = shown.length === rows.length ? `${rows.length} shortcuts` : `${shown.length} of ${rows.length}`;
|
|
4001
|
+
footL.textContent = `Sorted by ${columns.find((c) => c.key === sortKey)?.label.toLowerCase()} \xB7 click a header to sort`;
|
|
4002
|
+
};
|
|
4003
|
+
search.addEventListener("input", render);
|
|
4004
|
+
dialog.onClose(() => dialog.destroy());
|
|
4005
|
+
render();
|
|
4006
|
+
dialog.open();
|
|
4007
|
+
search.focus();
|
|
4008
|
+
}
|
|
4009
|
+
|
|
4010
|
+
// packages/ui/src/lib/scrollbars.ts
|
|
4011
|
+
var STYLE16 = `
|
|
4012
|
+
@property --bb-scroll-thumb-now {
|
|
4013
|
+
syntax: '<color>';
|
|
4014
|
+
/* MUST inherit: the thumb pseudo-element never matches .bb-scrolling
|
|
4015
|
+
itself \u2014 it only ever INHERITS the value from its owner. */
|
|
4016
|
+
inherits: true;
|
|
4017
|
+
initial-value: transparent;
|
|
4018
|
+
}
|
|
4019
|
+
* { transition: --bb-scroll-thumb-now 0.35s ease; }
|
|
4020
|
+
.bb-scrolling { --bb-scroll-thumb-now: var(--bb-ui-scroll-thumb, rgba(64,52,28,.28)); }
|
|
4021
|
+
::-webkit-scrollbar { width: 13px; height: 13px; background: transparent; }
|
|
4022
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
4023
|
+
::-webkit-scrollbar-corner { background: transparent; }
|
|
4024
|
+
::-webkit-scrollbar-thumb {
|
|
4025
|
+
background-color: var(--bb-scroll-thumb-now);
|
|
4026
|
+
background-clip: content-box;
|
|
4027
|
+
border: 3px solid transparent;
|
|
4028
|
+
border-radius: 99px;
|
|
4029
|
+
min-height: 40px; /* a 277-page doc must not shrink the thumb to a dot */
|
|
4030
|
+
}
|
|
4031
|
+
::-webkit-scrollbar-thumb:hover,
|
|
4032
|
+
::-webkit-scrollbar-thumb:active {
|
|
4033
|
+
background-color: var(--bb-ui-scroll-thumb-hover, rgba(64,52,28,.45));
|
|
4034
|
+
}
|
|
4035
|
+
@supports not selector(::-webkit-scrollbar) {
|
|
4036
|
+
/* non-WebKit (Firefox): native thin overlay, same colours */
|
|
4037
|
+
* { scrollbar-width: thin; scrollbar-color: var(--bb-ui-scroll-thumb, rgba(64,52,28,.28)) transparent; }
|
|
4038
|
+
}
|
|
4039
|
+
`;
|
|
4040
|
+
var installed = null;
|
|
4041
|
+
function installScrollbars(root = document) {
|
|
4042
|
+
if (installed) return installed;
|
|
4043
|
+
injectStyle("bb-ui-scrollbars", STYLE16);
|
|
4044
|
+
const quiet = /* @__PURE__ */ new WeakMap();
|
|
4045
|
+
const onScroll = (ev) => {
|
|
4046
|
+
const el5 = ev.target instanceof HTMLElement ? ev.target : root.documentElement;
|
|
4047
|
+
el5.classList.add("bb-scrolling");
|
|
4048
|
+
clearTimeout(quiet.get(el5));
|
|
4049
|
+
quiet.set(
|
|
4050
|
+
el5,
|
|
4051
|
+
window.setTimeout(() => el5.classList.remove("bb-scrolling"), 1e3)
|
|
4052
|
+
);
|
|
4053
|
+
};
|
|
4054
|
+
root.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
|
4055
|
+
installed = () => {
|
|
4056
|
+
root.removeEventListener("scroll", onScroll, { capture: true });
|
|
4057
|
+
installed = null;
|
|
4058
|
+
};
|
|
4059
|
+
return installed;
|
|
4060
|
+
}
|
|
2804
4061
|
export {
|
|
2805
4062
|
COLOR_PALETTE,
|
|
2806
4063
|
Dialog,
|
|
4064
|
+
RECENT_MAX,
|
|
4065
|
+
SPECIAL_CHARACTERS,
|
|
4066
|
+
SYMBOL_GROUPS,
|
|
4067
|
+
SYMBOL_NAMES,
|
|
2807
4068
|
cmToPx,
|
|
4069
|
+
codePointLabel,
|
|
2808
4070
|
colorButton,
|
|
2809
4071
|
createFindDialog,
|
|
2810
4072
|
createSectionChip,
|
|
4073
|
+
createSymbolDialog,
|
|
2811
4074
|
defaultMenus,
|
|
2812
4075
|
defaultToolbarGroups,
|
|
4076
|
+
installScrollbars,
|
|
2813
4077
|
marginPresetPicker,
|
|
2814
4078
|
mountMenubar,
|
|
2815
4079
|
mountToolbar,
|
|
2816
4080
|
openCellProperties,
|
|
2817
4081
|
openColorPicker,
|
|
2818
4082
|
openFontDialog,
|
|
4083
|
+
openKeyboardShortcutsDialog,
|
|
2819
4084
|
openMarginsDialog,
|
|
2820
4085
|
openPageSizeDialog,
|
|
4086
|
+
openSymbolPopover,
|
|
2821
4087
|
orientationPicker,
|
|
2822
4088
|
pageNumberPicker,
|
|
2823
4089
|
pageSizePicker,
|
|
4090
|
+
panelAnchor,
|
|
4091
|
+
parseCodePoint,
|
|
2824
4092
|
promptDialog,
|
|
4093
|
+
pushRecent,
|
|
2825
4094
|
pxToCm,
|
|
4095
|
+
searchSymbols,
|
|
2826
4096
|
sectionPaperPanel,
|
|
2827
4097
|
showContextMenu,
|
|
2828
4098
|
showLinkPanel,
|