@shadow-garden/bapbong-ui 0.7.0 → 0.8.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 +841 -273
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +837 -273
- package/dist/lib/cell-properties.d.ts +1 -1
- package/dist/lib/cell-properties.d.ts.map +1 -1
- package/dist/lib/color-picker.d.ts +51 -0
- package/dist/lib/color-picker.d.ts.map +1 -0
- package/dist/lib/font-dialog.d.ts +15 -0
- package/dist/lib/font-dialog.d.ts.map +1 -0
- package/dist/lib/internal.d.ts +12 -0
- package/dist/lib/internal.d.ts.map +1 -1
- package/dist/lib/link-panel.d.ts.map +1 -1
- package/dist/lib/menubar.d.ts.map +1 -1
- package/dist/lib/toolbar.d.ts +7 -6
- package/dist/lib/toolbar.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
// packages/ui/src/lib/internal.ts
|
|
2
2
|
function injectStyle(id, css) {
|
|
3
3
|
if (document.getElementById(id)) return;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
document.head.appendChild(
|
|
4
|
+
const el2 = document.createElement("style");
|
|
5
|
+
el2.id = id;
|
|
6
|
+
el2.textContent = css;
|
|
7
|
+
document.head.appendChild(el2);
|
|
8
|
+
}
|
|
9
|
+
function placeFloating(el2, anchor) {
|
|
10
|
+
const r = el2.getBoundingClientRect();
|
|
11
|
+
const pad = 6;
|
|
12
|
+
const gap = 6;
|
|
13
|
+
const x = Math.max(
|
|
14
|
+
pad,
|
|
15
|
+
Math.min(anchor.x, window.innerWidth - r.width - pad)
|
|
16
|
+
);
|
|
17
|
+
const below = anchor.y + anchor.height + gap;
|
|
18
|
+
const above = anchor.y - gap - r.height;
|
|
19
|
+
const y = below + r.height <= window.innerHeight - pad || above < pad ? Math.min(below, window.innerHeight - r.height - pad) : above;
|
|
20
|
+
el2.style.left = `${x}px`;
|
|
21
|
+
el2.style.top = `${y}px`;
|
|
8
22
|
}
|
|
9
23
|
|
|
10
24
|
// packages/ui/src/lib/dialog.ts
|
|
@@ -20,9 +34,9 @@ var STYLE = `
|
|
|
20
34
|
.bb-dialog-close:hover{opacity:1;background:var(--bb-ui-hover,#f1efe8)}
|
|
21
35
|
.bb-dialog-body{padding:12px}
|
|
22
36
|
.bb-prompt{display:flex;flex-direction:column;gap:10px;min-width:280px}
|
|
23
|
-
.bb-prompt-input{height:30px;padding:0 9px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;font:inherit;font-size:13px}
|
|
37
|
+
.bb-prompt-input{height:30px;padding:0 9px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;font:inherit;font-size:13px}
|
|
24
38
|
.bb-prompt-actions{display:flex;justify-content:flex-end;gap:8px}
|
|
25
|
-
.bb-prompt-btn{height:30px;padding:0 14px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
39
|
+
.bb-prompt-btn{height:30px;padding:0 14px;border:1px 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}
|
|
26
40
|
.bb-prompt-btn[data-primary]{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#b5d4f4);color:var(--bb-ui-active-fg,#0c447c)}
|
|
27
41
|
.bb-prompt-btn:hover{filter:brightness(.97)}
|
|
28
42
|
`;
|
|
@@ -46,8 +60,8 @@ var Dialog = class {
|
|
|
46
60
|
injectStyle("bb-ui-dialog-styles", STYLE);
|
|
47
61
|
this.modal = options.modal ?? false;
|
|
48
62
|
this.anchor = options.anchor;
|
|
49
|
-
const
|
|
50
|
-
|
|
63
|
+
const el2 = document.createElement("dialog");
|
|
64
|
+
el2.className = `bb-dialog ${this.modal ? "bb-dialog-modal" : "bb-dialog-float"}` + (options.className ? ` ${options.className}` : "");
|
|
51
65
|
const header = document.createElement("div");
|
|
52
66
|
header.className = "bb-dialog-header";
|
|
53
67
|
this.title = document.createElement("span");
|
|
@@ -62,18 +76,18 @@ var Dialog = class {
|
|
|
62
76
|
header.append(this.title, close);
|
|
63
77
|
this.body = document.createElement("div");
|
|
64
78
|
this.body.className = "bb-dialog-body";
|
|
65
|
-
|
|
66
|
-
document.body.appendChild(
|
|
67
|
-
|
|
79
|
+
el2.append(header, this.body);
|
|
80
|
+
document.body.appendChild(el2);
|
|
81
|
+
el2.addEventListener("close", () => {
|
|
68
82
|
this.detachReposition();
|
|
69
83
|
this.closeListeners.forEach((cb) => cb());
|
|
70
84
|
});
|
|
71
85
|
if (!this.modal) {
|
|
72
|
-
|
|
86
|
+
el2.addEventListener("keydown", (e) => {
|
|
73
87
|
if (e.key === "Escape") this.close();
|
|
74
88
|
});
|
|
75
89
|
}
|
|
76
|
-
this.el =
|
|
90
|
+
this.el = el2;
|
|
77
91
|
}
|
|
78
92
|
attachReposition() {
|
|
79
93
|
window.addEventListener("scroll", this.reposition, true);
|
|
@@ -158,6 +172,327 @@ function promptDialog(options) {
|
|
|
158
172
|
});
|
|
159
173
|
}
|
|
160
174
|
|
|
175
|
+
// packages/ui/src/lib/color-picker.ts
|
|
176
|
+
var COLOR_PALETTE = [
|
|
177
|
+
[
|
|
178
|
+
"#000000",
|
|
179
|
+
"#434343",
|
|
180
|
+
"#666666",
|
|
181
|
+
"#999999",
|
|
182
|
+
"#B7B7B7",
|
|
183
|
+
"#CCCCCC",
|
|
184
|
+
"#D9D9D9",
|
|
185
|
+
"#EFEFEF",
|
|
186
|
+
"#F3F3F3",
|
|
187
|
+
"#FFFFFF"
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
"#980000",
|
|
191
|
+
"#FF0000",
|
|
192
|
+
"#FF9900",
|
|
193
|
+
"#FFFF00",
|
|
194
|
+
"#00FF00",
|
|
195
|
+
"#00FFFF",
|
|
196
|
+
"#4A86E8",
|
|
197
|
+
"#0000FF",
|
|
198
|
+
"#9900FF",
|
|
199
|
+
"#FF00FF"
|
|
200
|
+
],
|
|
201
|
+
[
|
|
202
|
+
"#E6B8AF",
|
|
203
|
+
"#F4CCCC",
|
|
204
|
+
"#FCE5CD",
|
|
205
|
+
"#FFF2CC",
|
|
206
|
+
"#D9EAD3",
|
|
207
|
+
"#D0E0E3",
|
|
208
|
+
"#C9DAF8",
|
|
209
|
+
"#CFE2F3",
|
|
210
|
+
"#D9D2E9",
|
|
211
|
+
"#EAD1DC"
|
|
212
|
+
],
|
|
213
|
+
[
|
|
214
|
+
"#DD7E6B",
|
|
215
|
+
"#EA9999",
|
|
216
|
+
"#F9CB9C",
|
|
217
|
+
"#FFE599",
|
|
218
|
+
"#B6D7A8",
|
|
219
|
+
"#A2C4C9",
|
|
220
|
+
"#A4C2F4",
|
|
221
|
+
"#9FC5E8",
|
|
222
|
+
"#B4A7D6",
|
|
223
|
+
"#D5A6BD"
|
|
224
|
+
],
|
|
225
|
+
[
|
|
226
|
+
"#CC4125",
|
|
227
|
+
"#E06666",
|
|
228
|
+
"#F6B26B",
|
|
229
|
+
"#FFD966",
|
|
230
|
+
"#93C47D",
|
|
231
|
+
"#76A5AF",
|
|
232
|
+
"#6D9EEB",
|
|
233
|
+
"#6FA8DC",
|
|
234
|
+
"#8E7CC3",
|
|
235
|
+
"#C27BA0"
|
|
236
|
+
],
|
|
237
|
+
[
|
|
238
|
+
"#A61C00",
|
|
239
|
+
"#CC0000",
|
|
240
|
+
"#E69138",
|
|
241
|
+
"#F1C232",
|
|
242
|
+
"#6AA84F",
|
|
243
|
+
"#45818E",
|
|
244
|
+
"#3C78D8",
|
|
245
|
+
"#3D85C6",
|
|
246
|
+
"#674EA7",
|
|
247
|
+
"#A64D79"
|
|
248
|
+
],
|
|
249
|
+
[
|
|
250
|
+
"#85200C",
|
|
251
|
+
"#990000",
|
|
252
|
+
"#B45F06",
|
|
253
|
+
"#BF9000",
|
|
254
|
+
"#38761D",
|
|
255
|
+
"#134F5C",
|
|
256
|
+
"#1155CC",
|
|
257
|
+
"#0B5394",
|
|
258
|
+
"#351C75",
|
|
259
|
+
"#741B47"
|
|
260
|
+
],
|
|
261
|
+
[
|
|
262
|
+
"#5B0F00",
|
|
263
|
+
"#660000",
|
|
264
|
+
"#783F04",
|
|
265
|
+
"#7F6000",
|
|
266
|
+
"#274E13",
|
|
267
|
+
"#0C343D",
|
|
268
|
+
"#1C4587",
|
|
269
|
+
"#073763",
|
|
270
|
+
"#20124D",
|
|
271
|
+
"#4C1130"
|
|
272
|
+
]
|
|
273
|
+
];
|
|
274
|
+
var customColors = [];
|
|
275
|
+
var MAX_CUSTOM = 8;
|
|
276
|
+
function remember(color) {
|
|
277
|
+
const at = customColors.indexOf(color);
|
|
278
|
+
if (at !== -1) customColors.splice(at, 1);
|
|
279
|
+
customColors.unshift(color);
|
|
280
|
+
if (customColors.length > MAX_CUSTOM) customColors.length = MAX_CUSTOM;
|
|
281
|
+
}
|
|
282
|
+
var isHex = (v) => /^#[0-9a-fA-F]{6}$/.test(v);
|
|
283
|
+
var STYLE2 = `
|
|
284
|
+
.bb-cpk{position:fixed;margin:0;max-width:none;max-height:none;z-index:1300;padding:11px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);color:var(--bb-ui-fg,#2c2c2a);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);box-sizing:border-box}
|
|
285
|
+
.bb-cpk *{box-sizing:border-box}
|
|
286
|
+
.bb-cpk-grid{display:grid;grid-template-columns:repeat(10,20px);gap:5px}
|
|
287
|
+
.bb-cpk-sw{width:20px;height:20px;padding:0;border-radius:50%;border:1px solid rgba(128,128,128,.35);cursor:pointer}
|
|
288
|
+
.bb-cpk-sw:focus-visible{outline:none}
|
|
289
|
+
.bb-cpk-sw.on,.bb-cpk-sw:focus-visible{box-shadow:0 0 0 2px var(--bb-ui-menu-bg,#fff),0 0 0 4px var(--bb-ui-active-fg,#0c447c)}
|
|
290
|
+
.bb-cpk-cap{font-size:11px;letter-spacing:.04em;opacity:.6;margin:13px 0 7px}
|
|
291
|
+
.bb-cpk-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
|
|
292
|
+
.bb-cpk-none{width:100%;display:flex;align-items:center;gap:8px;height:28px;padding:0 7px;margin-bottom:10px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:13px;text-align:left;cursor:pointer}
|
|
293
|
+
.bb-cpk-none:hover,.bb-cpk-none:focus-visible{background:var(--bb-ui-hover,#f1efe8);outline:none}
|
|
294
|
+
.bb-cpk-none-chip{width:18px;height:18px;border-radius:50%;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));display:flex;align-items:center;justify-content:center;font-size:12px;opacity:.6;flex:none}
|
|
295
|
+
.bb-cpk-plus{width:20px;height:20px;padding:0;border-radius:50%;border:1px dashed var(--bb-ui-border,#d8d6cf);background:transparent;color:inherit;font:inherit;font-size:13px;line-height:1;cursor:pointer;opacity:.75;position:relative;overflow:hidden}
|
|
296
|
+
.bb-cpk-plus input{position:absolute;inset:0;opacity:0;cursor:pointer;border:0;padding:0}
|
|
297
|
+
.bb-cpk-hex{height:26px;width:96px;border:1px 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 7px;font-family:var(--bb-ui-mono,ui-monospace,monospace)}
|
|
298
|
+
|
|
299
|
+
.bb-cpk-btn{min-width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;gap:5px;border:1px 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 7px;cursor:pointer}
|
|
300
|
+
.bb-cpk-btn-chip{width:14px;height:14px;border-radius:50%;border:1px solid rgba(128,128,128,.4);flex:none}
|
|
301
|
+
.bb-cpk-btn-chip.none{background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));display:flex;align-items:center;justify-content:center;font-size:10px;opacity:.6}
|
|
302
|
+
.bb-cpk-btn-caret{opacity:.5;font-size:10px}
|
|
303
|
+
/* Toolbar shape: a glyph with the colour as a bar beneath it. */
|
|
304
|
+
.bb-cpk-btn.glyphed{flex-direction:column;gap:1px;min-width:30px;padding:0 6px;border-color:transparent;background:transparent}
|
|
305
|
+
.bb-cpk-btn.glyphed:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
306
|
+
.bb-cpk-btn-glyph{font-size:13px;line-height:1}
|
|
307
|
+
.bb-cpk-btn-bar{width:16px;height:4px;border-radius:1px;border:0.5px solid rgba(128,128,128,.35)}
|
|
308
|
+
.bb-cpk::backdrop{background:transparent}
|
|
309
|
+
`;
|
|
310
|
+
function openColorPicker({
|
|
311
|
+
anchor,
|
|
312
|
+
value,
|
|
313
|
+
clearLabel,
|
|
314
|
+
onPick
|
|
315
|
+
}) {
|
|
316
|
+
injectStyle("bb-ui-colorpicker-styles", STYLE2);
|
|
317
|
+
const panel = document.createElement("dialog");
|
|
318
|
+
panel.className = "bb-cpk";
|
|
319
|
+
panel.setAttribute("aria-label", "Colour");
|
|
320
|
+
let closed = false;
|
|
321
|
+
const close = () => {
|
|
322
|
+
if (closed) return;
|
|
323
|
+
closed = true;
|
|
324
|
+
window.removeEventListener("scroll", close, true);
|
|
325
|
+
if (panel.open) panel.close();
|
|
326
|
+
panel.remove();
|
|
327
|
+
anchor.focus();
|
|
328
|
+
};
|
|
329
|
+
const pick = (c) => {
|
|
330
|
+
if (c && !COLOR_PALETTE.some((row) => row.includes(c))) remember(c);
|
|
331
|
+
onPick(c);
|
|
332
|
+
close();
|
|
333
|
+
};
|
|
334
|
+
if (clearLabel !== void 0) {
|
|
335
|
+
const none = document.createElement("button");
|
|
336
|
+
none.type = "button";
|
|
337
|
+
none.className = "bb-cpk-none";
|
|
338
|
+
const chip = document.createElement("span");
|
|
339
|
+
chip.className = "bb-cpk-none-chip";
|
|
340
|
+
chip.textContent = "\u29B8";
|
|
341
|
+
none.append(chip, document.createTextNode(clearLabel));
|
|
342
|
+
none.addEventListener("click", () => pick(null));
|
|
343
|
+
panel.append(none);
|
|
344
|
+
}
|
|
345
|
+
const grid = document.createElement("div");
|
|
346
|
+
grid.className = "bb-cpk-grid";
|
|
347
|
+
grid.setAttribute("role", "grid");
|
|
348
|
+
const cells = [];
|
|
349
|
+
COLOR_PALETTE.forEach((row, r2) => {
|
|
350
|
+
const rowCells = [];
|
|
351
|
+
row.forEach((color, c) => {
|
|
352
|
+
const sw = document.createElement("button");
|
|
353
|
+
sw.type = "button";
|
|
354
|
+
sw.className = "bb-cpk-sw";
|
|
355
|
+
sw.style.background = color;
|
|
356
|
+
sw.title = color;
|
|
357
|
+
sw.setAttribute("role", "gridcell");
|
|
358
|
+
sw.setAttribute("aria-label", color);
|
|
359
|
+
sw.tabIndex = -1;
|
|
360
|
+
if (color.toUpperCase() === value?.toUpperCase()) sw.classList.add("on");
|
|
361
|
+
sw.addEventListener("click", () => pick(color));
|
|
362
|
+
sw.addEventListener("keydown", (e) => onGridKey(e, r2, c));
|
|
363
|
+
grid.append(sw);
|
|
364
|
+
rowCells.push(sw);
|
|
365
|
+
});
|
|
366
|
+
cells.push(rowCells);
|
|
367
|
+
});
|
|
368
|
+
panel.append(grid);
|
|
369
|
+
function onGridKey(e, r2, c) {
|
|
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
|
+
}
|
|
388
|
+
panel.append(
|
|
389
|
+
Object.assign(document.createElement("div"), {
|
|
390
|
+
className: "bb-cpk-cap",
|
|
391
|
+
textContent: "CUSTOM"
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
const customRow2 = document.createElement("div");
|
|
395
|
+
customRow2.className = "bb-cpk-row";
|
|
396
|
+
for (const c of customColors) {
|
|
397
|
+
const sw = document.createElement("button");
|
|
398
|
+
sw.type = "button";
|
|
399
|
+
sw.className = "bb-cpk-sw";
|
|
400
|
+
sw.style.background = c;
|
|
401
|
+
sw.title = c;
|
|
402
|
+
sw.setAttribute("aria-label", c);
|
|
403
|
+
if (c.toUpperCase() === value?.toUpperCase()) sw.classList.add("on");
|
|
404
|
+
sw.addEventListener("click", () => pick(c));
|
|
405
|
+
customRow2.append(sw);
|
|
406
|
+
}
|
|
407
|
+
const plus = document.createElement("button");
|
|
408
|
+
plus.type = "button";
|
|
409
|
+
plus.className = "bb-cpk-plus";
|
|
410
|
+
plus.title = "Custom colour";
|
|
411
|
+
plus.append(document.createTextNode("+"));
|
|
412
|
+
const native = document.createElement("input");
|
|
413
|
+
native.type = "color";
|
|
414
|
+
native.value = value && isHex(value) ? value : "#000000";
|
|
415
|
+
native.addEventListener("input", () => pick(native.value.toUpperCase()));
|
|
416
|
+
plus.append(native);
|
|
417
|
+
customRow2.append(plus);
|
|
418
|
+
const hex = document.createElement("input");
|
|
419
|
+
hex.type = "text";
|
|
420
|
+
hex.className = "bb-cpk-hex";
|
|
421
|
+
hex.placeholder = "#RRGGBB";
|
|
422
|
+
hex.spellcheck = false;
|
|
423
|
+
hex.value = value ?? "";
|
|
424
|
+
hex.addEventListener("keydown", (e) => {
|
|
425
|
+
if (e.key !== "Enter") return;
|
|
426
|
+
e.preventDefault();
|
|
427
|
+
const v = hex.value.trim();
|
|
428
|
+
if (isHex(v)) pick(v.toUpperCase());
|
|
429
|
+
});
|
|
430
|
+
customRow2.append(hex);
|
|
431
|
+
panel.append(customRow2);
|
|
432
|
+
document.body.append(panel);
|
|
433
|
+
panel.showModal();
|
|
434
|
+
const r = anchor.getBoundingClientRect();
|
|
435
|
+
placeFloating(panel, { x: r.left, y: r.top, height: r.height });
|
|
436
|
+
panel.addEventListener("close", close);
|
|
437
|
+
panel.addEventListener("pointerdown", (e) => {
|
|
438
|
+
const b = panel.getBoundingClientRect();
|
|
439
|
+
const outside = e.clientX < b.left || e.clientX > b.right || e.clientY < b.top || e.clientY > b.bottom;
|
|
440
|
+
if (outside) close();
|
|
441
|
+
});
|
|
442
|
+
window.addEventListener("scroll", close, true);
|
|
443
|
+
const start = cells.flat().find((el2) => el2.classList.contains("on")) ?? cells[0][0];
|
|
444
|
+
start.tabIndex = 0;
|
|
445
|
+
start.focus();
|
|
446
|
+
return { close };
|
|
447
|
+
}
|
|
448
|
+
function colorButton(o) {
|
|
449
|
+
injectStyle("bb-ui-colorpicker-styles", STYLE2);
|
|
450
|
+
const el2 = document.createElement("button");
|
|
451
|
+
el2.type = "button";
|
|
452
|
+
el2.className = `bb-cpk-btn${o.glyph ? " glyphed" : ""}`;
|
|
453
|
+
el2.title = o.title;
|
|
454
|
+
el2.setAttribute("aria-label", o.title);
|
|
455
|
+
el2.setAttribute("aria-haspopup", "dialog");
|
|
456
|
+
let chip;
|
|
457
|
+
if (o.glyph) {
|
|
458
|
+
const g = document.createElement("span");
|
|
459
|
+
g.className = "bb-cpk-btn-glyph";
|
|
460
|
+
g.innerHTML = o.glyph;
|
|
461
|
+
chip = document.createElement("span");
|
|
462
|
+
chip.className = "bb-cpk-btn-bar";
|
|
463
|
+
el2.append(g, chip);
|
|
464
|
+
} else {
|
|
465
|
+
chip = document.createElement("span");
|
|
466
|
+
chip.className = "bb-cpk-btn-chip";
|
|
467
|
+
el2.append(chip);
|
|
468
|
+
if (o.label) el2.append(document.createTextNode(o.label));
|
|
469
|
+
const caret = document.createElement("span");
|
|
470
|
+
caret.className = "bb-cpk-btn-caret";
|
|
471
|
+
caret.textContent = "\u25BE";
|
|
472
|
+
el2.append(caret);
|
|
473
|
+
}
|
|
474
|
+
function sync() {
|
|
475
|
+
const v = o.value();
|
|
476
|
+
chip.style.background = v ?? "transparent";
|
|
477
|
+
chip.classList.toggle("none", !v && !o.glyph);
|
|
478
|
+
chip.textContent = !v && !o.glyph ? "\u29B8" : "";
|
|
479
|
+
}
|
|
480
|
+
el2.addEventListener("mousedown", (e) => e.preventDefault());
|
|
481
|
+
el2.addEventListener("click", () => {
|
|
482
|
+
openColorPicker({
|
|
483
|
+
anchor: el2,
|
|
484
|
+
value: o.value(),
|
|
485
|
+
clearLabel: o.clearLabel,
|
|
486
|
+
onPick: (c) => {
|
|
487
|
+
o.onPick(c);
|
|
488
|
+
sync();
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
sync();
|
|
493
|
+
return { el: el2, sync };
|
|
494
|
+
}
|
|
495
|
+
|
|
161
496
|
// packages/ui/src/lib/toolbar.ts
|
|
162
497
|
var alignSvg = (spans) => `<svg width="15" height="15" viewBox="0 0 16 16" aria-hidden="true"><g stroke="currentColor" stroke-width="1.6" stroke-linecap="round">` + spans.map(
|
|
163
498
|
([x1, x2], i) => `<line x1="${x1}" y1="${4 + i * 4}" x2="${x2}" y2="${4 + i * 4}"/>`
|
|
@@ -228,9 +563,9 @@ var DEFAULT_ITEMS = {
|
|
|
228
563
|
svg: '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M6 4h8M6 8h8M6 12h8"/><text x="0.5" y="5.6" font-size="5.5" fill="currentColor" stroke="none">1</text><text x="0.5" y="9.6" font-size="5.5" fill="currentColor" stroke="none">2</text><text x="0.5" y="13.6" font-size="5.5" fill="currentColor" stroke="none">3</text></svg>'
|
|
229
564
|
}
|
|
230
565
|
};
|
|
231
|
-
var
|
|
566
|
+
var STYLE3 = `
|
|
232
567
|
.bb-toolbar-wrap{position:relative}
|
|
233
|
-
.bb-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:nowrap;overflow:hidden;padding:6px 8px;font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);color:var(--bb-ui-fg,#2c2c2a);background:var(--bb-ui-bg,#fff);border-bottom:1px solid var(--bb-ui-border,#e3e3e0);box-sizing:border-box}
|
|
568
|
+
.bb-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:nowrap;overflow:hidden;padding:6px 8px;font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);color:var(--bb-ui-fg,#2c2c2a);background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));border-bottom:1px solid var(--bb-ui-border,#e3e3e0);box-sizing:border-box}
|
|
234
569
|
.bb-toolbar *{box-sizing:border-box}
|
|
235
570
|
.bb-toolbar-group{display:flex;gap:2px;flex:none}
|
|
236
571
|
.bb-toolbar-group+.bb-toolbar-group{padding-left:10px;border-left:1px solid var(--bb-ui-border,#e3e3e0)}
|
|
@@ -241,16 +576,8 @@ var STYLE2 = `
|
|
|
241
576
|
.bb-toolbar-btn:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
242
577
|
.bb-toolbar-btn.is-active{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);border-color:var(--bb-ui-active-border,#b5d4f4)}
|
|
243
578
|
.bb-toolbar-btn:disabled{opacity:.38;cursor:default}
|
|
244
|
-
.bb-toolbar-select{height:30px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font-family:inherit;font-size:13px;padding:0 6px;cursor:pointer}
|
|
579
|
+
.bb-toolbar-select{height:30px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#e3e3e0));border-radius:6px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit;font-family:inherit;font-size:13px;padding:0 6px;cursor:pointer}
|
|
245
580
|
.bb-toolbar-select:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
246
|
-
.bb-toolbar-color{position:relative}
|
|
247
|
-
.bb-toolbar-color .bb-color-glyph{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;line-height:1;font-size:13px}
|
|
248
|
-
.bb-toolbar-color .bb-color-bar{width:16px;height:3px;border-radius:1px;background:currentColor}
|
|
249
|
-
.bb-color-pop{position:absolute;z-index:1200;display:grid;grid-template-columns:repeat(8,16px);gap:4px;padding:8px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:8px;box-shadow:0 8px 28px rgba(0,0,0,.16)}
|
|
250
|
-
.bb-color-pop[hidden]{display:none}
|
|
251
|
-
.bb-color-swatch{width:16px;height:16px;padding:0;border:1px solid rgba(0,0,0,.18);border-radius:3px;cursor:pointer}
|
|
252
|
-
.bb-color-none{grid-column:1/-1;font:inherit;font-size:12px;padding:3px 0;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:5px;background:var(--bb-ui-bg,#fff);cursor:pointer}
|
|
253
|
-
.bb-color-none:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
254
581
|
.bb-i-bold{font-weight:700}.bb-i-italic{font-style:italic}.bb-i-underline{text-decoration:underline}.bb-i-strike{text-decoration:line-through}
|
|
255
582
|
.bb-toolbar-split{display:flex;align-items:center;gap:0}
|
|
256
583
|
.bb-toolbar-split .bb-toolbar-btn{min-width:26px;padding:0 5px}
|
|
@@ -258,7 +585,7 @@ var STYLE2 = `
|
|
|
258
585
|
.bb-split-arrow svg{display:block}
|
|
259
586
|
.bb-split-pop{position:absolute;z-index:1200;display:grid;grid-template-columns:repeat(3,auto);gap:8px;padding:10px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16)}
|
|
260
587
|
.bb-split-pop[hidden]{display:none}
|
|
261
|
-
.bb-split-card{display:flex;flex-direction:column;gap:6px;width:76px;padding:9px 8px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:6px;background:transparent;cursor:pointer;font-family:inherit}
|
|
588
|
+
.bb-split-card{display:flex;flex-direction:column;gap:6px;width:76px;padding:9px 8px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#e3e3e0));border-radius:6px;background:transparent;cursor:pointer;font-family:inherit}
|
|
262
589
|
.bb-split-card:hover{background:var(--bb-ui-hover,#f1efe8)}
|
|
263
590
|
.bb-split-card.is-selected{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#b5d4f4)}
|
|
264
591
|
.bb-split-row{display:flex;align-items:center;gap:4px}
|
|
@@ -274,7 +601,7 @@ function defaultToolbarGroups(commands) {
|
|
|
274
601
|
return [rest, aligns].filter((g) => g.length > 0);
|
|
275
602
|
}
|
|
276
603
|
function mountToolbar(host, editor, options = {}) {
|
|
277
|
-
injectStyle("bb-ui-toolbar-styles",
|
|
604
|
+
injectStyle("bb-ui-toolbar-styles", STYLE3);
|
|
278
605
|
const items = { ...DEFAULT_ITEMS, ...options.items ?? {} };
|
|
279
606
|
const groups = options.groups ?? defaultToolbarGroups(editor.commands);
|
|
280
607
|
const wrap = document.createElement("div");
|
|
@@ -317,77 +644,20 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
317
644
|
continue;
|
|
318
645
|
}
|
|
319
646
|
if (typeof entry !== "string" && entry.kind === "color") {
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const bar = document.createElement("span");
|
|
331
|
-
bar.className = "bb-color-bar";
|
|
332
|
-
glyph.appendChild(bar);
|
|
333
|
-
btn2.appendChild(glyph);
|
|
334
|
-
const pop2 = document.createElement("div");
|
|
335
|
-
pop2.className = "bb-color-pop";
|
|
336
|
-
pop2.hidden = true;
|
|
337
|
-
const pick = (color) => {
|
|
338
|
-
entry.onSelect(color);
|
|
339
|
-
pop2.hidden = true;
|
|
340
|
-
editor.focus();
|
|
341
|
-
};
|
|
342
|
-
for (const c of entry.swatches) {
|
|
343
|
-
const sw = document.createElement("button");
|
|
344
|
-
sw.type = "button";
|
|
345
|
-
sw.className = "bb-color-swatch";
|
|
346
|
-
sw.style.background = c;
|
|
347
|
-
sw.title = c;
|
|
348
|
-
sw.addEventListener("mousedown", (e) => e.preventDefault());
|
|
349
|
-
sw.addEventListener("click", () => pick(c));
|
|
350
|
-
pop2.appendChild(sw);
|
|
351
|
-
}
|
|
352
|
-
if (entry.allowNone) {
|
|
353
|
-
const none = document.createElement("button");
|
|
354
|
-
none.type = "button";
|
|
355
|
-
none.className = "bb-color-none";
|
|
356
|
-
none.textContent = "None";
|
|
357
|
-
none.addEventListener("mousedown", (e) => e.preventDefault());
|
|
358
|
-
none.addEventListener("click", () => pick(null));
|
|
359
|
-
pop2.appendChild(none);
|
|
360
|
-
}
|
|
361
|
-
btn2.addEventListener("mousedown", (e) => e.preventDefault());
|
|
362
|
-
btn2.addEventListener("click", () => {
|
|
363
|
-
const open = pop2.hidden;
|
|
364
|
-
wrap.querySelectorAll(".bb-color-pop,.bb-split-pop").forEach((p) => p.hidden = true);
|
|
365
|
-
pop2.hidden = !open;
|
|
366
|
-
if (!pop2.hidden) {
|
|
367
|
-
const wrapRect = wrap.getBoundingClientRect();
|
|
368
|
-
const btnRect = btn2.getBoundingClientRect();
|
|
369
|
-
pop2.style.top = `${btnRect.bottom - wrapRect.top + 4}px`;
|
|
370
|
-
const left = Math.max(
|
|
371
|
-
0,
|
|
372
|
-
Math.min(
|
|
373
|
-
btnRect.left - wrapRect.left,
|
|
374
|
-
wrap.clientWidth - pop2.offsetWidth - 4
|
|
375
|
-
)
|
|
376
|
-
);
|
|
377
|
-
pop2.style.left = `${left}px`;
|
|
378
|
-
const onDoc = (ev) => {
|
|
379
|
-
if (!colorWrap.contains(ev.target) && !pop2.contains(ev.target)) {
|
|
380
|
-
pop2.hidden = true;
|
|
381
|
-
document.removeEventListener("pointerdown", onDoc, true);
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
document.addEventListener("pointerdown", onDoc, true);
|
|
647
|
+
const ctl = colorButton({
|
|
648
|
+
title: entry.title,
|
|
649
|
+
glyph: entry.glyph,
|
|
650
|
+
clearLabel: entry.clearLabel,
|
|
651
|
+
// `latest`, not `editor.state`: the toolbar mounts before any
|
|
652
|
+
// document exists, and reading state then throws.
|
|
653
|
+
value: () => latest ? entry.value(latest) : null,
|
|
654
|
+
onPick: (color) => {
|
|
655
|
+
entry.onSelect(color);
|
|
656
|
+
editor.focus();
|
|
385
657
|
}
|
|
386
658
|
});
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
groupEl.appendChild(colorWrap);
|
|
390
|
-
colors.push({ spec: entry, bar });
|
|
659
|
+
groupEl.appendChild(ctl.el);
|
|
660
|
+
colors.push(ctl);
|
|
391
661
|
continue;
|
|
392
662
|
}
|
|
393
663
|
if (typeof entry !== "string" && entry.kind === "split") {
|
|
@@ -449,7 +719,7 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
449
719
|
arrow.addEventListener("mousedown", (e) => e.preventDefault());
|
|
450
720
|
arrow.addEventListener("click", () => {
|
|
451
721
|
const open = pop2.hidden;
|
|
452
|
-
wrap.querySelectorAll(".bb-split-pop
|
|
722
|
+
wrap.querySelectorAll(".bb-split-pop").forEach((p) => p.hidden = true);
|
|
453
723
|
pop2.hidden = !open;
|
|
454
724
|
if (!pop2.hidden) {
|
|
455
725
|
const wrapRect = wrap.getBoundingClientRect();
|
|
@@ -529,11 +799,11 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
529
799
|
while (pop.firstChild) root.insertBefore(pop.firstChild, moreBtn);
|
|
530
800
|
moreBtn.style.display = "none";
|
|
531
801
|
closePop();
|
|
532
|
-
wrap.querySelectorAll(".bb-
|
|
802
|
+
wrap.querySelectorAll(".bb-split-pop").forEach((p) => p.hidden = true);
|
|
533
803
|
if (fits()) return;
|
|
534
804
|
moreBtn.style.display = "";
|
|
535
805
|
const groupEls = Array.from(root.children).filter(
|
|
536
|
-
(
|
|
806
|
+
(el2) => el2.classList.contains("bb-toolbar-group")
|
|
537
807
|
);
|
|
538
808
|
for (let i = groupEls.length - 1; i >= 0 && !fits(); i--) {
|
|
539
809
|
pop.insertBefore(groupEls[i], pop.firstChild);
|
|
@@ -548,17 +818,16 @@ function mountToolbar(host, editor, options = {}) {
|
|
|
548
818
|
layout();
|
|
549
819
|
const refresh = (state) => {
|
|
550
820
|
latest = state;
|
|
551
|
-
for (const { name, el } of buttons) {
|
|
821
|
+
for (const { name, el: el2 } of buttons) {
|
|
552
822
|
const cmd = editor.commands.get(name);
|
|
553
823
|
if (!cmd) continue;
|
|
554
824
|
const active = cmd.isActive?.(state) ?? false;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
825
|
+
el2.classList.toggle("is-active", active);
|
|
826
|
+
el2.setAttribute("aria-pressed", String(active));
|
|
827
|
+
el2.disabled = cmd.isEnabled ? !cmd.isEnabled(state) : false;
|
|
558
828
|
}
|
|
559
|
-
for (const { spec, el } of selects)
|
|
560
|
-
for (const
|
|
561
|
-
bar.style.background = spec.value(state) ?? "";
|
|
829
|
+
for (const { spec, el: el2 } of selects) el2.value = spec.value(state);
|
|
830
|
+
for (const c of colors) c.sync();
|
|
562
831
|
for (const { spec, cards } of splits) {
|
|
563
832
|
const selected = spec.value(state);
|
|
564
833
|
for (const card of cards)
|
|
@@ -592,6 +861,8 @@ var DEFAULT_LABELS = {
|
|
|
592
861
|
strike: "Strikethrough",
|
|
593
862
|
superscript: "Superscript",
|
|
594
863
|
subscript: "Subscript",
|
|
864
|
+
"small-caps": "Small caps",
|
|
865
|
+
"double-strike": "Double strikethrough",
|
|
595
866
|
"align-left": "Align left",
|
|
596
867
|
"align-center": "Center",
|
|
597
868
|
"align-right": "Align right",
|
|
@@ -608,8 +879,8 @@ var DEFAULT_LABELS = {
|
|
|
608
879
|
redo: "Redo",
|
|
609
880
|
"page-break": "Page break"
|
|
610
881
|
};
|
|
611
|
-
var
|
|
612
|
-
.bb-menubar{display:flex;gap:2px;align-items:center;padding:2px 6px;font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);color:var(--bb-ui-fg,#2c2c2a);background:var(--bb-ui-bg,#fff);border-bottom:1px solid var(--bb-ui-border,#e3e3e0);box-sizing:border-box}
|
|
882
|
+
var STYLE4 = `
|
|
883
|
+
.bb-menubar{display:flex;gap:2px;align-items:center;padding:2px 6px;font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);color:var(--bb-ui-fg,#2c2c2a);background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));border-bottom:1px solid var(--bb-ui-border,#e3e3e0);box-sizing:border-box}
|
|
613
884
|
.bb-menubar *{box-sizing:border-box}
|
|
614
885
|
.bb-menubar-menu{position:relative}
|
|
615
886
|
.bb-menubar-title{height:28px;padding:0 10px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
@@ -646,7 +917,7 @@ function defaultMenus(commands) {
|
|
|
646
917
|
return entries.length ? [{ label: "Format", entries }] : [];
|
|
647
918
|
}
|
|
648
919
|
function mountMenubar(host, editor, options = {}) {
|
|
649
|
-
injectStyle("bb-ui-menubar-styles",
|
|
920
|
+
injectStyle("bb-ui-menubar-styles", STYLE4);
|
|
650
921
|
const labels = { ...DEFAULT_LABELS, ...options.labels ?? {} };
|
|
651
922
|
const menus = options.menus ?? defaultMenus(editor.commands);
|
|
652
923
|
const vertical = options.orientation === "vertical";
|
|
@@ -842,18 +1113,18 @@ var DEFAULT_LABELS2 = {
|
|
|
842
1113
|
replaceOne: "Replace",
|
|
843
1114
|
replaceAll: "Replace all"
|
|
844
1115
|
};
|
|
845
|
-
var
|
|
1116
|
+
var STYLE5 = `
|
|
846
1117
|
.bb-find{display:flex;flex-direction:column;gap:8px;min-width:300px}
|
|
847
1118
|
.bb-find *{box-sizing:border-box}
|
|
848
1119
|
.bb-find-row{display:flex;gap:6px;align-items:center}
|
|
849
|
-
.bb-find-input{flex:1 1 auto;height:30px;padding:0 9px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;font:inherit;font-size:13px;background:var(--bb-ui-bg,#fff);color:inherit}
|
|
1120
|
+
.bb-find-input{flex:1 1 auto;height:30px;padding:0 9px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;font:inherit;font-size:13px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));color:inherit}
|
|
850
1121
|
.bb-find-count{min-width:44px;text-align:center;font-size:12px;opacity:.65;font-variant-numeric:tabular-nums}
|
|
851
|
-
.bb-find-btn{height:30px;min-width:30px;padding:0 10px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
1122
|
+
.bb-find-btn{height:30px;min-width:30px;padding:0 10px;border:1px 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}
|
|
852
1123
|
.bb-find-btn:hover:not(:disabled){background:var(--bb-ui-hover,#f1efe8)}
|
|
853
1124
|
.bb-find-btn:disabled{opacity:.4;cursor:default}
|
|
854
1125
|
`;
|
|
855
1126
|
function createFindDialog(handle, options = {}) {
|
|
856
|
-
injectStyle("bb-ui-find-styles",
|
|
1127
|
+
injectStyle("bb-ui-find-styles", STYLE5);
|
|
857
1128
|
const find = typeof handle === "function" ? new Proxy({}, {
|
|
858
1129
|
get: (_t, prop) => handle()[prop]
|
|
859
1130
|
}) : handle;
|
|
@@ -954,7 +1225,7 @@ function createFindDialog(handle, options = {}) {
|
|
|
954
1225
|
}
|
|
955
1226
|
|
|
956
1227
|
// packages/ui/src/lib/context-menu.ts
|
|
957
|
-
var
|
|
1228
|
+
var STYLE6 = `
|
|
958
1229
|
.bb-ctx{position:fixed;z-index:1200;min-width:208px;padding:4px;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:8px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif)}
|
|
959
1230
|
.bb-ctx *{box-sizing:border-box}
|
|
960
1231
|
.bb-ctx-item{display:flex;align-items:center;gap:16px;width:100%;height:30px;padding:0 10px;border:0;border-radius:5px;background:transparent;color:inherit;font:inherit;font-size:13px;text-align:left;white-space:nowrap;cursor:pointer}
|
|
@@ -973,17 +1244,17 @@ function closeCurrent() {
|
|
|
973
1244
|
}
|
|
974
1245
|
}
|
|
975
1246
|
function showContextMenu(entries, at) {
|
|
976
|
-
injectStyle("bb-ui-contextmenu-styles",
|
|
1247
|
+
injectStyle("bb-ui-contextmenu-styles", STYLE6);
|
|
977
1248
|
closeCurrent();
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
|
|
1249
|
+
const el2 = document.createElement("div");
|
|
1250
|
+
el2.className = "bb-ctx";
|
|
1251
|
+
el2.setAttribute("role", "menu");
|
|
981
1252
|
for (const entry of entries) {
|
|
982
1253
|
if (entry === "separator") {
|
|
983
1254
|
const sep = document.createElement("div");
|
|
984
1255
|
sep.className = "bb-ctx-sep";
|
|
985
1256
|
sep.setAttribute("role", "separator");
|
|
986
|
-
|
|
1257
|
+
el2.appendChild(sep);
|
|
987
1258
|
continue;
|
|
988
1259
|
}
|
|
989
1260
|
const item = document.createElement("button");
|
|
@@ -1006,22 +1277,22 @@ function showContextMenu(entries, at) {
|
|
|
1006
1277
|
closeCurrent();
|
|
1007
1278
|
entry.run();
|
|
1008
1279
|
});
|
|
1009
|
-
|
|
1280
|
+
el2.appendChild(item);
|
|
1010
1281
|
}
|
|
1011
|
-
document.body.appendChild(
|
|
1012
|
-
const r =
|
|
1282
|
+
document.body.appendChild(el2);
|
|
1283
|
+
const r = el2.getBoundingClientRect();
|
|
1013
1284
|
const pad = 4;
|
|
1014
1285
|
const fitsX = (v) => v >= pad && v + r.width <= window.innerWidth - pad;
|
|
1015
1286
|
const fitsY = (v) => v >= pad && v + r.height <= window.innerHeight - pad;
|
|
1016
1287
|
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));
|
|
1017
1288
|
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));
|
|
1018
|
-
|
|
1019
|
-
|
|
1289
|
+
el2.style.left = `${x}px`;
|
|
1290
|
+
el2.style.top = `${y}px`;
|
|
1020
1291
|
const items = () => Array.from(
|
|
1021
|
-
|
|
1292
|
+
el2.querySelectorAll(".bb-ctx-item:not(:disabled)")
|
|
1022
1293
|
);
|
|
1023
1294
|
const onDown = (e) => {
|
|
1024
|
-
if (!
|
|
1295
|
+
if (!el2.contains(e.target)) closeCurrent();
|
|
1025
1296
|
};
|
|
1026
1297
|
const onKey = (e) => {
|
|
1027
1298
|
if (e.key === "Escape") return closeCurrent();
|
|
@@ -1043,19 +1314,19 @@ function showContextMenu(entries, at) {
|
|
|
1043
1314
|
document.removeEventListener("pointerdown", onDown, true);
|
|
1044
1315
|
document.removeEventListener("keydown", onKey, true);
|
|
1045
1316
|
window.removeEventListener("scroll", onScroll, true);
|
|
1046
|
-
|
|
1317
|
+
el2.remove();
|
|
1047
1318
|
};
|
|
1048
1319
|
items()[0]?.focus();
|
|
1049
1320
|
return { close: closeCurrent };
|
|
1050
1321
|
}
|
|
1051
1322
|
|
|
1052
1323
|
// packages/ui/src/lib/link-panel.ts
|
|
1053
|
-
var
|
|
1324
|
+
var STYLE7 = `
|
|
1054
1325
|
.bb-linkpanel{position:fixed;z-index:1200;width:300px;padding:8px;background:var(--bb-ui-menu-bg,#fff);-webkit-backdrop-filter:var(--bb-ui-pop-filter,none);backdrop-filter:var(--bb-ui-pop-filter,none);color:var(--bb-ui-fg,#2c2c2a);border:1px solid var(--bb-ui-pop-border,var(--bb-ui-border,#e3e3e0));border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:var(--bb-ui-font,system-ui,-apple-system,sans-serif);box-sizing:border-box}
|
|
1055
1326
|
.bb-linkpanel *{box-sizing:border-box}
|
|
1056
1327
|
.bb-linkpanel-row{display:flex;align-items:center;gap:8px}
|
|
1057
1328
|
.bb-linkpanel-form{display:flex;flex-direction:column;gap:8px}
|
|
1058
|
-
.bb-linkpanel-field{display:flex;align-items:center;gap:7px;border:1px solid var(--bb-ui-border,#e3e3e0);border-radius:7px;padding:0 9px;background:var(--bb-ui-bg,#fff)}
|
|
1329
|
+
.bb-linkpanel-field{display:flex;align-items:center;gap:7px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#e3e3e0));border-radius:7px;padding:0 9px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff))}
|
|
1059
1330
|
.bb-linkpanel-field:focus-within{border-color:var(--bb-ui-accent,#d85a30)}
|
|
1060
1331
|
.bb-linkpanel-field svg{flex:none;opacity:.55}
|
|
1061
1332
|
.bb-linkpanel-input{flex:1;min-width:0;height:30px;border:0;background:transparent;color:inherit;font:inherit;font-size:13px;outline:none}
|
|
@@ -1097,31 +1368,17 @@ function closeCurrent2() {
|
|
|
1097
1368
|
dispose();
|
|
1098
1369
|
}
|
|
1099
1370
|
}
|
|
1100
|
-
function place(el, anchor) {
|
|
1101
|
-
const r = el.getBoundingClientRect();
|
|
1102
|
-
const pad = 6;
|
|
1103
|
-
const gap = 6;
|
|
1104
|
-
const x = Math.max(
|
|
1105
|
-
pad,
|
|
1106
|
-
Math.min(anchor.x, window.innerWidth - r.width - pad)
|
|
1107
|
-
);
|
|
1108
|
-
const below = anchor.y + anchor.height + gap;
|
|
1109
|
-
const above = anchor.y - gap - r.height;
|
|
1110
|
-
const y = below + r.height <= window.innerHeight - pad || above < pad ? Math.min(below, window.innerHeight - r.height - pad) : above;
|
|
1111
|
-
el.style.left = `${x}px`;
|
|
1112
|
-
el.style.top = `${y}px`;
|
|
1113
|
-
}
|
|
1114
1371
|
function showLinkPanel(opts) {
|
|
1115
|
-
injectStyle("bb-ui-linkpanel-styles",
|
|
1372
|
+
injectStyle("bb-ui-linkpanel-styles", STYLE7);
|
|
1116
1373
|
if (opts.key && current2 && currentKey === opts.key && currentHandle) {
|
|
1117
1374
|
cancelPendingClose();
|
|
1118
1375
|
return currentHandle;
|
|
1119
1376
|
}
|
|
1120
1377
|
closeCurrent2();
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1378
|
+
const el2 = document.createElement("div");
|
|
1379
|
+
el2.className = "bb-linkpanel";
|
|
1380
|
+
el2.setAttribute("role", "dialog");
|
|
1381
|
+
el2.setAttribute("aria-label", "Link");
|
|
1125
1382
|
const iconBtn = (svg, label, onClick) => {
|
|
1126
1383
|
const b = document.createElement("button");
|
|
1127
1384
|
b.type = "button";
|
|
@@ -1148,7 +1405,7 @@ function showLinkPanel(opts) {
|
|
|
1148
1405
|
return { wrap, input };
|
|
1149
1406
|
};
|
|
1150
1407
|
const renderView = (existing) => {
|
|
1151
|
-
|
|
1408
|
+
el2.textContent = "";
|
|
1152
1409
|
const row = document.createElement("div");
|
|
1153
1410
|
row.className = "bb-linkpanel-row";
|
|
1154
1411
|
const internal = opts.internal;
|
|
@@ -1195,11 +1452,11 @@ function showLinkPanel(opts) {
|
|
|
1195
1452
|
})
|
|
1196
1453
|
);
|
|
1197
1454
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1455
|
+
el2.appendChild(row);
|
|
1456
|
+
placeFloating(el2, opts.anchor);
|
|
1200
1457
|
};
|
|
1201
1458
|
const renderForm = (prefill) => {
|
|
1202
|
-
|
|
1459
|
+
el2.textContent = "";
|
|
1203
1460
|
const form = document.createElement("div");
|
|
1204
1461
|
form.className = "bb-linkpanel-form";
|
|
1205
1462
|
const withText = !opts.hasSelection;
|
|
@@ -1240,16 +1497,16 @@ function showLinkPanel(opts) {
|
|
|
1240
1497
|
}
|
|
1241
1498
|
row.appendChild(apply);
|
|
1242
1499
|
form.appendChild(row);
|
|
1243
|
-
|
|
1244
|
-
|
|
1500
|
+
el2.appendChild(form);
|
|
1501
|
+
placeFloating(el2, opts.anchor);
|
|
1245
1502
|
u.input.focus();
|
|
1246
1503
|
u.input.select();
|
|
1247
1504
|
};
|
|
1248
|
-
document.body.appendChild(
|
|
1505
|
+
document.body.appendChild(el2);
|
|
1249
1506
|
if (opts.existing) renderView(opts.existing);
|
|
1250
1507
|
else renderForm(null);
|
|
1251
1508
|
const onDown = (e) => {
|
|
1252
|
-
if (
|
|
1509
|
+
if (el2.contains(e.target)) return;
|
|
1253
1510
|
if (opts.key) {
|
|
1254
1511
|
cancelPendingClose();
|
|
1255
1512
|
pendingClose = setTimeout(closeCurrent2, 80);
|
|
@@ -1270,7 +1527,7 @@ function showLinkPanel(opts) {
|
|
|
1270
1527
|
document.removeEventListener("pointerdown", onDown, true);
|
|
1271
1528
|
document.removeEventListener("keydown", onKey, true);
|
|
1272
1529
|
window.removeEventListener("scroll", onScroll, true);
|
|
1273
|
-
|
|
1530
|
+
el2.remove();
|
|
1274
1531
|
};
|
|
1275
1532
|
current2 = dispose;
|
|
1276
1533
|
currentKey = opts.key ?? null;
|
|
@@ -1285,8 +1542,6 @@ function showLinkPanel(opts) {
|
|
|
1285
1542
|
}
|
|
1286
1543
|
|
|
1287
1544
|
// packages/ui/src/lib/cell-properties.ts
|
|
1288
|
-
var FILL_PRESETS = ["#FCEBEB", "#FAEEDA", "#EAF3DE", "#E6F1FB", "#EEEDFE", "#FBEAF0", "#E1F5EE", "#F1EFE8", "#D3D1C7"];
|
|
1289
|
-
var PEN_COLORS = ["#000000", "#5F5E5A", "#B0B0B0", "#E24B4A", "#185FA5", "#1D9E75", "#BA7517"];
|
|
1290
1545
|
var PT_WIDTHS = [0.5, 0.75, 1, 1.5, 2.25, 3];
|
|
1291
1546
|
var STYLES = [
|
|
1292
1547
|
{ key: "solid", label: "Solid" },
|
|
@@ -1300,43 +1555,53 @@ var VALIGNS = [
|
|
|
1300
1555
|
{ key: "bottom", label: "Bottom" }
|
|
1301
1556
|
];
|
|
1302
1557
|
var PRESETS = [
|
|
1303
|
-
{
|
|
1558
|
+
{
|
|
1559
|
+
key: "all",
|
|
1560
|
+
label: "All borders",
|
|
1561
|
+
sides: [1, 1, 1, 1, 1, 1],
|
|
1562
|
+
inside: false
|
|
1563
|
+
},
|
|
1304
1564
|
{ key: "none", label: "No border", sides: [0, 0, 0, 0, 0, 0], inside: false },
|
|
1305
|
-
{
|
|
1565
|
+
{
|
|
1566
|
+
key: "outside",
|
|
1567
|
+
label: "Outside",
|
|
1568
|
+
sides: [1, 1, 1, 1, 0, 0],
|
|
1569
|
+
inside: false
|
|
1570
|
+
},
|
|
1306
1571
|
{ key: "inside", label: "Inside", sides: [0, 0, 0, 0, 1, 1], inside: true },
|
|
1307
1572
|
{ key: "top", label: "Top", sides: [1, 0, 0, 0, 0, 0], inside: false },
|
|
1308
1573
|
{ key: "bottom", label: "Bottom", sides: [0, 0, 1, 0, 0, 0], inside: false },
|
|
1309
1574
|
{ key: "left", label: "Left", sides: [0, 0, 0, 1, 0, 0], inside: false },
|
|
1310
1575
|
{ key: "right", label: "Right", sides: [0, 1, 0, 0, 0, 0], inside: false },
|
|
1311
|
-
{
|
|
1312
|
-
|
|
1576
|
+
{
|
|
1577
|
+
key: "insideH",
|
|
1578
|
+
label: "Inside horizontal",
|
|
1579
|
+
sides: [0, 0, 0, 0, 1, 0],
|
|
1580
|
+
inside: true
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
key: "insideV",
|
|
1584
|
+
label: "Inside vertical",
|
|
1585
|
+
sides: [0, 0, 0, 0, 0, 1],
|
|
1586
|
+
inside: true
|
|
1587
|
+
}
|
|
1313
1588
|
];
|
|
1314
|
-
var
|
|
1589
|
+
var STYLE8 = `
|
|
1315
1590
|
.bb-cp{display:flex;flex-direction:column;gap:16px;min-width:320px;color:var(--bb-ui-fg,#2c2c2a)}
|
|
1316
1591
|
.bb-cp *{box-sizing:border-box}
|
|
1317
1592
|
.bb-cp-label{font-size:12px;opacity:.7;margin-bottom:8px}
|
|
1318
|
-
.bb-cp-
|
|
1319
|
-
.bb-cp-swatch{width:26px;height:26px;border-radius:6px;border:0.5px solid var(--bb-ui-border,#d8d6cf);padding:0;cursor:pointer}
|
|
1320
|
-
.bb-cp-swatch.on{box-shadow:0 0 0 2px var(--bb-ui-active-fg,#0c447c)}
|
|
1321
|
-
.bb-cp-none{display:flex;align-items:center;justify-content:center;background:var(--bb-ui-bg,#fff);font-size:15px;color:#b4b2a9}
|
|
1322
|
-
.bb-cp-hexrow{display:flex;align-items:center;gap:8px;margin-top:10px}
|
|
1323
|
-
.bb-cp-hexpreview{width:26px;height:26px;border-radius:6px;border:0.5px solid var(--bb-ui-border,#d8d6cf);flex:none}
|
|
1324
|
-
.bb-cp-hex{height:30px;width:120px;font-size:13px;padding:0 8px;border:0.5px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit}
|
|
1325
|
-
.bb-cp-seg{display:inline-flex;border:0.5px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;overflow:hidden}
|
|
1593
|
+
.bb-cp-seg{display:inline-flex;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;overflow:hidden}
|
|
1326
1594
|
.bb-cp-segbtn{height:32px;padding:0 16px;border:0;border-right:0.5px solid var(--bb-ui-border,#e3e3e0);background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
1327
1595
|
.bb-cp-segbtn:last-child{border-right:0}
|
|
1328
1596
|
.bb-cp-segbtn.on{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c)}
|
|
1329
1597
|
.bb-cp-presets{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;margin-bottom:12px}
|
|
1330
|
-
.bb-cp-preset{display:flex;align-items:center;justify-content:center;height:32px;border:0.5px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);cursor:pointer;padding:0}
|
|
1598
|
+
.bb-cp-preset{display:flex;align-items:center;justify-content:center;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));cursor:pointer;padding:0}
|
|
1331
1599
|
.bb-cp-preset.on{border-color:var(--bb-ui-active-border,#7fb2ec);background:var(--bb-ui-active-bg,#e6f1fb)}
|
|
1332
1600
|
.bb-cp-preset:disabled{opacity:.35;cursor:default}
|
|
1333
1601
|
.bb-cp-penrow{display:flex;gap:10px;margin-bottom:10px}
|
|
1334
|
-
.bb-cp-select{flex:1;height:32px;border:0.5px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px;padding:0 8px}
|
|
1335
|
-
.bb-cp-colors{display:flex;gap:7px}
|
|
1336
|
-
.bb-cp-color{width:24px;height:24px;border-radius:6px;border:0.5px solid var(--bb-ui-border,#d8d6cf);padding:0;cursor:pointer}
|
|
1337
|
-
.bb-cp-color.on{box-shadow:0 0 0 2px var(--bb-ui-active-fg,#0c447c)}
|
|
1602
|
+
.bb-cp-select{flex:1;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}
|
|
1338
1603
|
.bb-cp-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:2px}
|
|
1339
|
-
.bb-cp-btn{height:32px;padding:0 16px;border:0.5px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
1604
|
+
.bb-cp-btn{height:32px;padding:0 16px;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}
|
|
1340
1605
|
.bb-cp-primary{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#b5d4f4);color:var(--bb-ui-active-fg,#0c447c)}
|
|
1341
1606
|
`;
|
|
1342
1607
|
var ACTIVE = "#378ADD";
|
|
@@ -1351,8 +1616,12 @@ var section = (label) => {
|
|
|
1351
1616
|
sec.appendChild(l);
|
|
1352
1617
|
return sec;
|
|
1353
1618
|
};
|
|
1354
|
-
function openCellProperties({
|
|
1355
|
-
|
|
1619
|
+
function openCellProperties({
|
|
1620
|
+
initial,
|
|
1621
|
+
singleCell,
|
|
1622
|
+
onApply
|
|
1623
|
+
}) {
|
|
1624
|
+
injectStyle("bb-ui-cellprops-styles", STYLE8);
|
|
1356
1625
|
let background = initial.background;
|
|
1357
1626
|
let vAlign = initial.vAlign;
|
|
1358
1627
|
let penWidthPt = 1;
|
|
@@ -1362,48 +1631,16 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1362
1631
|
const root = document.createElement("div");
|
|
1363
1632
|
root.className = "bb-cp";
|
|
1364
1633
|
const fill = section("Fill");
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
});
|
|
1373
|
-
swatches.appendChild(el);
|
|
1374
|
-
swatchEls.push({ el, color });
|
|
1375
|
-
};
|
|
1376
|
-
const none = document.createElement("button");
|
|
1377
|
-
none.type = "button";
|
|
1378
|
-
none.className = "bb-cp-swatch bb-cp-none";
|
|
1379
|
-
none.title = "No fill";
|
|
1380
|
-
none.textContent = "\u29B8";
|
|
1381
|
-
addSwatch(none, null);
|
|
1382
|
-
for (const color of FILL_PRESETS) {
|
|
1383
|
-
const sw = document.createElement("button");
|
|
1384
|
-
sw.type = "button";
|
|
1385
|
-
sw.className = "bb-cp-swatch";
|
|
1386
|
-
sw.style.background = color;
|
|
1387
|
-
sw.title = color;
|
|
1388
|
-
addSwatch(sw, color);
|
|
1389
|
-
}
|
|
1390
|
-
const hexRow = document.createElement("div");
|
|
1391
|
-
hexRow.className = "bb-cp-hexrow";
|
|
1392
|
-
const hexPreview = document.createElement("span");
|
|
1393
|
-
hexPreview.className = "bb-cp-hexpreview";
|
|
1394
|
-
const hex = document.createElement("input");
|
|
1395
|
-
hex.type = "text";
|
|
1396
|
-
hex.className = "bb-cp-hex";
|
|
1397
|
-
hex.placeholder = "#RRGGBB";
|
|
1398
|
-
hex.addEventListener("input", () => {
|
|
1399
|
-
const v = hex.value.trim();
|
|
1400
|
-
if (/^#[0-9a-fA-F]{6}$/.test(v)) {
|
|
1401
|
-
background = v;
|
|
1402
|
-
syncFill();
|
|
1634
|
+
const fillBtn = colorButton({
|
|
1635
|
+
title: "Cell fill",
|
|
1636
|
+
label: "Fill",
|
|
1637
|
+
clearLabel: "No fill",
|
|
1638
|
+
value: () => background,
|
|
1639
|
+
onPick: (c) => {
|
|
1640
|
+
background = c;
|
|
1403
1641
|
}
|
|
1404
1642
|
});
|
|
1405
|
-
|
|
1406
|
-
fill.append(swatches, hexRow);
|
|
1643
|
+
fill.append(fillBtn.el);
|
|
1407
1644
|
const valign = section("Vertical alignment");
|
|
1408
1645
|
const seg = document.createElement("div");
|
|
1409
1646
|
seg.className = "bb-cp-seg";
|
|
@@ -1433,7 +1670,10 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1433
1670
|
if (pt === penWidthPt) o.selected = true;
|
|
1434
1671
|
widthSel.appendChild(o);
|
|
1435
1672
|
}
|
|
1436
|
-
widthSel.addEventListener(
|
|
1673
|
+
widthSel.addEventListener(
|
|
1674
|
+
"change",
|
|
1675
|
+
() => penWidthPt = Number(widthSel.value)
|
|
1676
|
+
);
|
|
1437
1677
|
const styleSel = document.createElement("select");
|
|
1438
1678
|
styleSel.className = "bb-cp-select";
|
|
1439
1679
|
for (const { key, label } of STYLES) {
|
|
@@ -1442,24 +1682,19 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1442
1682
|
o.textContent = label;
|
|
1443
1683
|
styleSel.appendChild(o);
|
|
1444
1684
|
}
|
|
1445
|
-
styleSel.addEventListener(
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
c
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
syncColors();
|
|
1459
|
-
});
|
|
1460
|
-
colors.appendChild(c);
|
|
1461
|
-
colorEls.push({ el: c, color });
|
|
1462
|
-
}
|
|
1685
|
+
styleSel.addEventListener(
|
|
1686
|
+
"change",
|
|
1687
|
+
() => penStyle = styleSel.value
|
|
1688
|
+
);
|
|
1689
|
+
const penBtn = colorButton({
|
|
1690
|
+
title: "Border colour",
|
|
1691
|
+
label: "Colour",
|
|
1692
|
+
value: () => penColor,
|
|
1693
|
+
onPick: (c) => {
|
|
1694
|
+
if (c) penColor = c;
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
penRow.append(widthSel, styleSel, penBtn.el);
|
|
1463
1698
|
const grid = document.createElement("div");
|
|
1464
1699
|
grid.className = "bb-cp-presets";
|
|
1465
1700
|
const presetEls = [];
|
|
@@ -1478,7 +1713,7 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1478
1713
|
grid.appendChild(btn);
|
|
1479
1714
|
presetEls.push({ el: btn, key: p.key });
|
|
1480
1715
|
}
|
|
1481
|
-
borders.append(grid, penRow
|
|
1716
|
+
borders.append(grid, penRow);
|
|
1482
1717
|
const footer = document.createElement("div");
|
|
1483
1718
|
footer.className = "bb-cp-footer";
|
|
1484
1719
|
const cancel = document.createElement("button");
|
|
@@ -1491,23 +1726,14 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1491
1726
|
apply.textContent = "Apply";
|
|
1492
1727
|
footer.append(cancel, apply);
|
|
1493
1728
|
root.append(fill, valign, borders, footer);
|
|
1494
|
-
function syncFill() {
|
|
1495
|
-
for (const { el, color } of swatchEls) el.classList.toggle("on", color === background);
|
|
1496
|
-
hexPreview.style.background = background ?? "transparent";
|
|
1497
|
-
if (document.activeElement !== hex) hex.value = background ?? "";
|
|
1498
|
-
}
|
|
1499
1729
|
function syncVAlign() {
|
|
1500
1730
|
for (const [key, b] of vaBtns) b.classList.toggle("on", vAlign === key);
|
|
1501
1731
|
}
|
|
1502
|
-
function syncColors() {
|
|
1503
|
-
for (const { el, color } of colorEls) el.classList.toggle("on", color === penColor);
|
|
1504
|
-
}
|
|
1505
1732
|
function syncPresets() {
|
|
1506
|
-
for (const { el, key } of presetEls)
|
|
1733
|
+
for (const { el: el2, key } of presetEls)
|
|
1734
|
+
el2.classList.toggle("on", preset === key);
|
|
1507
1735
|
}
|
|
1508
|
-
syncFill();
|
|
1509
1736
|
syncVAlign();
|
|
1510
|
-
syncColors();
|
|
1511
1737
|
syncPresets();
|
|
1512
1738
|
const dialog = new Dialog({ title: "Cell properties", modal: true });
|
|
1513
1739
|
dialog.setContent(root);
|
|
@@ -1516,7 +1742,12 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1516
1742
|
apply.addEventListener("click", () => {
|
|
1517
1743
|
const result = { background, vAlign };
|
|
1518
1744
|
if (preset) {
|
|
1519
|
-
result.border = {
|
|
1745
|
+
result.border = {
|
|
1746
|
+
preset,
|
|
1747
|
+
width: penWidthPt * 96 / 72,
|
|
1748
|
+
style: penStyle,
|
|
1749
|
+
color: penColor
|
|
1750
|
+
};
|
|
1520
1751
|
}
|
|
1521
1752
|
onApply(result);
|
|
1522
1753
|
dialog.close();
|
|
@@ -1525,15 +1756,15 @@ function openCellProperties({ initial, singleCell, onApply }) {
|
|
|
1525
1756
|
}
|
|
1526
1757
|
|
|
1527
1758
|
// packages/ui/src/lib/table-grid.ts
|
|
1528
|
-
var
|
|
1759
|
+
var STYLE9 = `
|
|
1529
1760
|
.bb-grid{display:flex;flex-direction:column;gap:6px;user-select:none}
|
|
1530
1761
|
.bb-grid-cells{display:grid;gap:2px}
|
|
1531
|
-
.bb-grid-cell{width:15px;height:15px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:2px;background:var(--bb-ui-bg,#fff);cursor:pointer;padding:0}
|
|
1762
|
+
.bb-grid-cell{width:15px;height:15px;border:1px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:2px;background:var(--bb-ui-bg,#fff);cursor:pointer;padding:0}
|
|
1532
1763
|
.bb-grid-cell.on{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#7fb2ec)}
|
|
1533
1764
|
.bb-grid-label{font-size:12px;text-align:center;opacity:.7}
|
|
1534
1765
|
`;
|
|
1535
1766
|
function tableGridPicker(options) {
|
|
1536
|
-
injectStyle("bb-ui-grid-styles",
|
|
1767
|
+
injectStyle("bb-ui-grid-styles", STYLE9);
|
|
1537
1768
|
const maxRows = options.maxRows ?? 8;
|
|
1538
1769
|
const maxCols = options.maxCols ?? 10;
|
|
1539
1770
|
const root = document.createElement("div");
|
|
@@ -1576,7 +1807,7 @@ var cmToPx = (cm) => Math.round(cm * PX_PER_CM);
|
|
|
1576
1807
|
function fmtCm(cm) {
|
|
1577
1808
|
return `${Number(cm.toFixed(3))} cm`;
|
|
1578
1809
|
}
|
|
1579
|
-
var
|
|
1810
|
+
var STYLE10 = `
|
|
1580
1811
|
.bb-ps{display:flex;flex-direction:column;min-width:250px;max-height:min(70vh,460px);overflow-y:auto}
|
|
1581
1812
|
.bb-ps-row{display:flex;align-items:center;gap:11px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
|
|
1582
1813
|
.bb-ps-row:hover,.bb-ps-row:focus{background:var(--bb-ui-hover,#f1efe8);outline:none}
|
|
@@ -1591,15 +1822,15 @@ var STYLE9 = `
|
|
|
1591
1822
|
.bb-pd-fields{display:grid;gap:10px 18px}
|
|
1592
1823
|
.bb-pd-field{display:flex;align-items:center;gap:8px}
|
|
1593
1824
|
.bb-pd-label{flex:0 0 74px;font-size:13px}
|
|
1594
|
-
.bb-pd-input{flex:1 1 auto;min-width:0;width:100%;height:30px;padding:0 8px;border:1px solid var(--bb-ui-border,#d8d6cf);border-radius:6px;background:var(--bb-ui-bg,#fff);color:inherit;font:inherit;font-size:13px}
|
|
1825
|
+
.bb-pd-input{flex:1 1 auto;min-width:0;width:100%;height:30px;padding:0 8px;border:1px 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}
|
|
1595
1826
|
.bb-pd-input:focus{outline:2px solid var(--bb-ui-active-border,#7fb2ec);outline-offset:-1px}
|
|
1596
1827
|
.bb-pd-actions{display:flex;justify-content:flex-end;gap:8px}
|
|
1597
1828
|
`;
|
|
1598
1829
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
1599
1830
|
function svgEl(name, attrs) {
|
|
1600
|
-
const
|
|
1601
|
-
for (const [k, v] of Object.entries(attrs))
|
|
1602
|
-
return
|
|
1831
|
+
const el2 = document.createElementNS(SVG_NS, name);
|
|
1832
|
+
for (const [k, v] of Object.entries(attrs)) el2.setAttribute(k, String(v));
|
|
1833
|
+
return el2;
|
|
1603
1834
|
}
|
|
1604
1835
|
function pagePreview(ratio) {
|
|
1605
1836
|
const svg = svgEl("svg", { viewBox: "0 0 34 42", class: "bb-ps-icon" });
|
|
@@ -1666,11 +1897,11 @@ function presetRow(icon, name, caption, active, onPick) {
|
|
|
1666
1897
|
return row;
|
|
1667
1898
|
}
|
|
1668
1899
|
function captionLine(text) {
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
return
|
|
1900
|
+
const el2 = document.createElement("span");
|
|
1901
|
+
el2.className = "bb-ps-dims";
|
|
1902
|
+
el2.style.display = "block";
|
|
1903
|
+
el2.textContent = text;
|
|
1904
|
+
return el2;
|
|
1674
1905
|
}
|
|
1675
1906
|
function customRow(icon, title, description, onPick) {
|
|
1676
1907
|
icon.appendChild(
|
|
@@ -1691,7 +1922,7 @@ function customRow(icon, title, description, onPick) {
|
|
|
1691
1922
|
return presetRow(icon, title, captionLine(description), false, onPick);
|
|
1692
1923
|
}
|
|
1693
1924
|
function pageSizePicker(options) {
|
|
1694
|
-
injectStyle("bb-ui-pagesetup-styles",
|
|
1925
|
+
injectStyle("bb-ui-pagesetup-styles", STYLE10);
|
|
1695
1926
|
const root = document.createElement("div");
|
|
1696
1927
|
root.className = "bb-ps";
|
|
1697
1928
|
root.setAttribute("role", "menu");
|
|
@@ -1720,7 +1951,7 @@ function pageSizePicker(options) {
|
|
|
1720
1951
|
return root;
|
|
1721
1952
|
}
|
|
1722
1953
|
function orientationPicker(options) {
|
|
1723
|
-
injectStyle("bb-ui-pagesetup-styles",
|
|
1954
|
+
injectStyle("bb-ui-pagesetup-styles", STYLE10);
|
|
1724
1955
|
const root = document.createElement("div");
|
|
1725
1956
|
root.className = "bb-ps";
|
|
1726
1957
|
root.setAttribute("role", "menu");
|
|
@@ -1743,7 +1974,7 @@ function orientationPicker(options) {
|
|
|
1743
1974
|
return root;
|
|
1744
1975
|
}
|
|
1745
1976
|
function marginPresetPicker(options) {
|
|
1746
|
-
injectStyle("bb-ui-pagesetup-styles",
|
|
1977
|
+
injectStyle("bb-ui-pagesetup-styles", STYLE10);
|
|
1747
1978
|
const root = document.createElement("div");
|
|
1748
1979
|
root.className = "bb-ps";
|
|
1749
1980
|
root.setAttribute("role", "menu");
|
|
@@ -1799,8 +2030,8 @@ function cmField(label, valuePx, min) {
|
|
|
1799
2030
|
return { row, input };
|
|
1800
2031
|
}
|
|
1801
2032
|
function dialogActions() {
|
|
1802
|
-
const
|
|
1803
|
-
|
|
2033
|
+
const el2 = document.createElement("div");
|
|
2034
|
+
el2.className = "bb-pd-actions";
|
|
1804
2035
|
const cancel = document.createElement("button");
|
|
1805
2036
|
cancel.type = "button";
|
|
1806
2037
|
cancel.className = "bb-prompt-btn";
|
|
@@ -1810,15 +2041,15 @@ function dialogActions() {
|
|
|
1810
2041
|
ok.className = "bb-prompt-btn";
|
|
1811
2042
|
ok.dataset["primary"] = "true";
|
|
1812
2043
|
ok.textContent = "OK";
|
|
1813
|
-
|
|
1814
|
-
return { el, ok, cancel };
|
|
2044
|
+
el2.append(cancel, ok);
|
|
2045
|
+
return { el: el2, ok, cancel };
|
|
1815
2046
|
}
|
|
1816
2047
|
function readPx(input, fallbackPx) {
|
|
1817
2048
|
const n = Number(input.value);
|
|
1818
2049
|
return Number.isFinite(n) && n > 0 ? cmToPx(n) : fallbackPx;
|
|
1819
2050
|
}
|
|
1820
2051
|
function openPageSizeDialog(options) {
|
|
1821
|
-
injectStyle("bb-ui-pagesetup-styles",
|
|
2052
|
+
injectStyle("bb-ui-pagesetup-styles", STYLE10);
|
|
1822
2053
|
const dialog = new Dialog({ title: "Paper size", modal: true });
|
|
1823
2054
|
const form = document.createElement("form");
|
|
1824
2055
|
form.className = "bb-pd";
|
|
@@ -1845,7 +2076,7 @@ function openPageSizeDialog(options) {
|
|
|
1845
2076
|
width.input.select();
|
|
1846
2077
|
}
|
|
1847
2078
|
function openMarginsDialog(options) {
|
|
1848
|
-
injectStyle("bb-ui-pagesetup-styles",
|
|
2079
|
+
injectStyle("bb-ui-pagesetup-styles", STYLE10);
|
|
1849
2080
|
const dialog = new Dialog({ title: "Margins", modal: true });
|
|
1850
2081
|
const form = document.createElement("form");
|
|
1851
2082
|
form.className = "bb-pd";
|
|
@@ -1880,9 +2111,340 @@ function openMarginsDialog(options) {
|
|
|
1880
2111
|
top.input.focus();
|
|
1881
2112
|
top.input.select();
|
|
1882
2113
|
}
|
|
2114
|
+
|
|
2115
|
+
// packages/ui/src/lib/font-dialog.ts
|
|
2116
|
+
var twipsToPt = (tw) => tw / 20;
|
|
2117
|
+
var ptToTwips = (pt) => Math.round(pt * 20);
|
|
2118
|
+
var halfToPt = (hp) => hp / 2;
|
|
2119
|
+
var ptToHalf = (pt) => Math.round(pt * 2);
|
|
2120
|
+
var SCALE_PRESETS = [200, 150, 100, 90, 80, 66, 50, 33];
|
|
2121
|
+
var SUPERSUB_SCALE = 0.66;
|
|
2122
|
+
var STYLE11 = `
|
|
2123
|
+
.bb-fd{display:flex;flex-direction:column;gap:15px;min-width:396px;max-width:430px;color:var(--bb-ui-fg,#2c2c2a)}
|
|
2124
|
+
.bb-fd *{box-sizing:border-box}
|
|
2125
|
+
/* A segmented control, not a "tab joined to its pane": that pattern needs the
|
|
2126
|
+
active tab's background to equal the pane's, and this dialog floats on
|
|
2127
|
+
frosted glass whose colour the widget cannot know. Same shape cell-properties
|
|
2128
|
+
already uses, so the two dialogs read as one product. */
|
|
2129
|
+
.bb-fd-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}
|
|
2130
|
+
.bb-fd-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}
|
|
2131
|
+
.bb-fd-tab:last-child{border-right:0}
|
|
2132
|
+
.bb-fd-tab[aria-selected="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c);opacity:1}
|
|
2133
|
+
.bb-fd-pane{display:flex;flex-direction:column;gap:13px}
|
|
2134
|
+
/* A class-level display beats the UA's [hidden]{display:none}; without this
|
|
2135
|
+
both tabs render at once. */
|
|
2136
|
+
.bb-fd-pane[hidden]{display:none}
|
|
2137
|
+
.bb-fd-lbl{font-size:12px;opacity:.7;margin-bottom:5px}
|
|
2138
|
+
.bb-fd-sec{font-size:12px;opacity:.7;margin-bottom:9px}
|
|
2139
|
+
.bb-fd-grid{display:grid;gap:10px}
|
|
2140
|
+
.bb-fd-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}
|
|
2141
|
+
.bb-fd-row{display:grid;grid-template-columns:96px 1fr 88px;gap:10px;align-items:center;margin-bottom:10px}
|
|
2142
|
+
.bb-fd-row label{font-size:13px}
|
|
2143
|
+
.bb-fd-state{font-size:12px;color:var(--bb-ui-active-fg,#0c447c)}
|
|
2144
|
+
.bb-fd-state.off{color:inherit;opacity:.5}
|
|
2145
|
+
.bb-fd-seg{display:inline-flex;border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:6px;overflow:hidden;height:32px}
|
|
2146
|
+
.bb-fd-segbtn{width:40px;border:0;border-right:0.5px solid var(--bb-ui-border,#e3e3e0);background:transparent;color:inherit;font:inherit;font-size:13px;cursor:pointer}
|
|
2147
|
+
.bb-fd-segbtn:last-child{border-right:0}
|
|
2148
|
+
.bb-fd-segbtn[aria-pressed="true"]{background:var(--bb-ui-active-bg,#e6f1fb);color:var(--bb-ui-active-fg,#0c447c)}
|
|
2149
|
+
.bb-fd-segbtn[aria-pressed="mixed"]{background:repeating-linear-gradient(135deg,transparent 0 4px,var(--bb-ui-active-bg,#e6f1fb) 4px 8px)}
|
|
2150
|
+
.bb-fd-swatch-custom{background:conic-gradient(#c0392b,#ba7517,#1d9e75,#185fa5,#7f77dd,#c0392b)}
|
|
2151
|
+
.bb-fd-swatch-custom::-webkit-color-swatch-wrapper{padding:0}
|
|
2152
|
+
.bb-fd-swatch-custom::-webkit-color-swatch{border:0;border-radius:5px;opacity:0}
|
|
2153
|
+
.bb-fd-eff{display:grid;grid-template-columns:1fr 1fr;gap:8px 14px}
|
|
2154
|
+
.bb-fd-cb{display:flex;align-items:center;gap:7px;font-size:13px;cursor:pointer}
|
|
2155
|
+
.bb-fd-prev{border:0.5px solid var(--bb-ui-control-border,var(--bb-ui-border,#d8d6cf));border-radius:8px;background:var(--bb-ui-control-bg,var(--bb-ui-bg,#fff));padding:13px 14px;overflow:hidden}
|
|
2156
|
+
.bb-fd-prev-cap{font-size:11px;opacity:.55;margin-bottom:9px;text-align:center}
|
|
2157
|
+
/* The surrounding sentence stays at a fixed size and weight \u2014 it is the ruler
|
|
2158
|
+
the sample is read against, so it must not move when the sample does. */
|
|
2159
|
+
.bb-fd-prev-line{font-size:15px;line-height:2;text-align:center}
|
|
2160
|
+
.bb-fd-prev-txt{display:inline-block}
|
|
2161
|
+
.bb-fd-dim{opacity:.4}
|
|
2162
|
+
.bb-fd-note{font-size:11px;opacity:.55;margin-top:5px}
|
|
2163
|
+
.bb-fd-foot{display:flex;justify-content:flex-end;gap:8px}
|
|
2164
|
+
.bb-fd-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}
|
|
2165
|
+
.bb-fd-btn.primary{background:var(--bb-ui-active-bg,#e6f1fb);border-color:var(--bb-ui-active-border,#7fb2ec);color:var(--bb-ui-active-fg,#0c447c)}
|
|
2166
|
+
`;
|
|
2167
|
+
function el(tag, cls, text) {
|
|
2168
|
+
const n = document.createElement(tag);
|
|
2169
|
+
if (cls) n.className = cls;
|
|
2170
|
+
if (text) n.textContent = text;
|
|
2171
|
+
return n;
|
|
2172
|
+
}
|
|
2173
|
+
function select(options, value) {
|
|
2174
|
+
const s = el("select", "bb-fd-ctl");
|
|
2175
|
+
for (const [v, label] of options) {
|
|
2176
|
+
const o = document.createElement("option");
|
|
2177
|
+
o.value = v;
|
|
2178
|
+
o.textContent = label;
|
|
2179
|
+
s.append(o);
|
|
2180
|
+
}
|
|
2181
|
+
s.value = value;
|
|
2182
|
+
return s;
|
|
2183
|
+
}
|
|
2184
|
+
function checkbox(label, on) {
|
|
2185
|
+
const wrap = el("label", "bb-fd-cb");
|
|
2186
|
+
const input = document.createElement("input");
|
|
2187
|
+
input.type = "checkbox";
|
|
2188
|
+
input.checked = on === true;
|
|
2189
|
+
input.indeterminate = on === void 0;
|
|
2190
|
+
input.addEventListener("change", () => {
|
|
2191
|
+
input.indeterminate = false;
|
|
2192
|
+
});
|
|
2193
|
+
wrap.append(input, document.createTextNode(label));
|
|
2194
|
+
input.wrap = wrap;
|
|
2195
|
+
return input;
|
|
2196
|
+
}
|
|
2197
|
+
var wrapOf = (i) => i.wrap ?? i;
|
|
2198
|
+
function openFontDialog({
|
|
2199
|
+
initial,
|
|
2200
|
+
families,
|
|
2201
|
+
sizes,
|
|
2202
|
+
onApply
|
|
2203
|
+
}) {
|
|
2204
|
+
injectStyle("bb-font-dialog", STYLE11);
|
|
2205
|
+
const dialog = new Dialog({ title: "Font", modal: true });
|
|
2206
|
+
const root = el("div", "bb-fd");
|
|
2207
|
+
const tabs = el("div", "bb-fd-tabs");
|
|
2208
|
+
tabs.setAttribute("role", "tablist");
|
|
2209
|
+
const fontTab = el("button", "bb-fd-tab", "Font");
|
|
2210
|
+
const advTab = el("button", "bb-fd-tab", "Advanced");
|
|
2211
|
+
for (const t of [fontTab, advTab]) {
|
|
2212
|
+
t.type = "button";
|
|
2213
|
+
t.setAttribute("role", "tab");
|
|
2214
|
+
tabs.append(t);
|
|
2215
|
+
}
|
|
2216
|
+
const fontPane = el("div", "bb-fd-pane");
|
|
2217
|
+
const advPane = el("div", "bb-fd-pane");
|
|
2218
|
+
const showTab = (advanced) => {
|
|
2219
|
+
fontTab.setAttribute("aria-selected", String(!advanced));
|
|
2220
|
+
advTab.setAttribute("aria-selected", String(advanced));
|
|
2221
|
+
fontPane.hidden = advanced;
|
|
2222
|
+
advPane.hidden = !advanced;
|
|
2223
|
+
};
|
|
2224
|
+
fontTab.addEventListener("click", () => showTab(false));
|
|
2225
|
+
advTab.addEventListener("click", () => showTab(true));
|
|
2226
|
+
const famList = families.includes(initial.family ?? "") ? families : initial.family ? [initial.family, ...families] : families;
|
|
2227
|
+
const familySel = select(
|
|
2228
|
+
[["", "Font"], ...famList.map((f) => [f, f])],
|
|
2229
|
+
initial.family ?? ""
|
|
2230
|
+
);
|
|
2231
|
+
const sizeSel = select(
|
|
2232
|
+
[["", "Size"], ...sizes.map((n) => [String(n), String(n)])],
|
|
2233
|
+
initial.sizePt == null ? "" : String(initial.sizePt)
|
|
2234
|
+
);
|
|
2235
|
+
const topGrid = el("div", "bb-fd-grid");
|
|
2236
|
+
topGrid.style.gridTemplateColumns = "1fr 100px";
|
|
2237
|
+
const famCell = el("div");
|
|
2238
|
+
famCell.append(el("div", "bb-fd-lbl", "Font"), familySel);
|
|
2239
|
+
const sizeCell = el("div");
|
|
2240
|
+
sizeCell.append(el("div", "bb-fd-lbl", "Size"), sizeSel);
|
|
2241
|
+
topGrid.append(famCell, sizeCell);
|
|
2242
|
+
const seg = el("div", "bb-fd-seg");
|
|
2243
|
+
const mkSeg = (text, on, style) => {
|
|
2244
|
+
const b = el("button", "bb-fd-segbtn", text);
|
|
2245
|
+
b.type = "button";
|
|
2246
|
+
b.setAttribute("aria-pressed", on === void 0 ? "mixed" : String(on));
|
|
2247
|
+
if (style) b.setAttribute("style", style);
|
|
2248
|
+
b.addEventListener("click", () => {
|
|
2249
|
+
b.setAttribute(
|
|
2250
|
+
"aria-pressed",
|
|
2251
|
+
b.getAttribute("aria-pressed") === "true" ? "false" : "true"
|
|
2252
|
+
);
|
|
2253
|
+
paint();
|
|
2254
|
+
});
|
|
2255
|
+
seg.append(b);
|
|
2256
|
+
return b;
|
|
2257
|
+
};
|
|
2258
|
+
const boldBtn = mkSeg("B", initial.bold, "font-weight:600");
|
|
2259
|
+
const italicBtn = mkSeg("I", initial.italic, "font-style:italic");
|
|
2260
|
+
const underBtn = mkSeg("U", initial.underline, "text-decoration:underline");
|
|
2261
|
+
let color = initial.color ?? null;
|
|
2262
|
+
let highlight = initial.highlight ?? null;
|
|
2263
|
+
const colorBtn = colorButton({
|
|
2264
|
+
title: "Text color",
|
|
2265
|
+
label: "Text color",
|
|
2266
|
+
clearLabel: "Automatic",
|
|
2267
|
+
value: () => color,
|
|
2268
|
+
onPick: (c) => {
|
|
2269
|
+
color = c;
|
|
2270
|
+
paint();
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2273
|
+
const hlBtn = colorButton({
|
|
2274
|
+
title: "Highlight",
|
|
2275
|
+
label: "Highlight",
|
|
2276
|
+
clearLabel: "No highlight",
|
|
2277
|
+
value: () => highlight,
|
|
2278
|
+
onPick: (c) => {
|
|
2279
|
+
highlight = c;
|
|
2280
|
+
paint();
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
const styleRow = el("div", "bb-fd-grid");
|
|
2284
|
+
styleRow.style.gridTemplateColumns = "auto 1fr 1fr";
|
|
2285
|
+
styleRow.style.alignItems = "end";
|
|
2286
|
+
const styleCell = el("div");
|
|
2287
|
+
styleCell.append(el("div", "bb-fd-lbl", "Style"), seg);
|
|
2288
|
+
const colorCell = el("div");
|
|
2289
|
+
colorCell.append(el("div", "bb-fd-lbl", "Colour"), colorBtn.el);
|
|
2290
|
+
const hlCell = el("div");
|
|
2291
|
+
hlCell.append(el("div", "bb-fd-lbl", "Highlight"), hlBtn.el);
|
|
2292
|
+
styleRow.append(styleCell, colorCell, hlCell);
|
|
2293
|
+
const effWrap = el("div");
|
|
2294
|
+
effWrap.append(el("div", "bb-fd-sec", "Effects"));
|
|
2295
|
+
const effGrid = el("div", "bb-fd-eff");
|
|
2296
|
+
const strikeCb = checkbox("Strikethrough", initial.strike);
|
|
2297
|
+
const dstrikeCb = checkbox("Double strikethrough", initial.doubleStrike);
|
|
2298
|
+
const smallCapsCb = checkbox("Small caps", initial.smallCaps);
|
|
2299
|
+
const supCb = checkbox("Superscript", initial.vertAlign === "super");
|
|
2300
|
+
const subCb = checkbox("Subscript", initial.vertAlign === "sub");
|
|
2301
|
+
const exclusive = (a, b) => {
|
|
2302
|
+
a.addEventListener("change", () => {
|
|
2303
|
+
if (a.checked) b.checked = false;
|
|
2304
|
+
paint();
|
|
2305
|
+
});
|
|
2306
|
+
};
|
|
2307
|
+
exclusive(strikeCb, dstrikeCb);
|
|
2308
|
+
exclusive(dstrikeCb, strikeCb);
|
|
2309
|
+
exclusive(supCb, subCb);
|
|
2310
|
+
exclusive(subCb, supCb);
|
|
2311
|
+
smallCapsCb.addEventListener("change", () => paint());
|
|
2312
|
+
effGrid.append(
|
|
2313
|
+
wrapOf(strikeCb),
|
|
2314
|
+
wrapOf(dstrikeCb),
|
|
2315
|
+
wrapOf(supCb),
|
|
2316
|
+
wrapOf(subCb),
|
|
2317
|
+
wrapOf(smallCapsCb)
|
|
2318
|
+
);
|
|
2319
|
+
effWrap.append(effGrid);
|
|
2320
|
+
fontPane.append(topGrid, styleRow, effWrap);
|
|
2321
|
+
const advWrap = el("div");
|
|
2322
|
+
advWrap.append(el("div", "bb-fd-sec", "Character spacing"));
|
|
2323
|
+
const mkRow = (label, control, stateText, dim = false) => {
|
|
2324
|
+
const row = el("div", "bb-fd-row");
|
|
2325
|
+
if (dim) row.classList.add("bb-fd-dim");
|
|
2326
|
+
const l = el("label", void 0, label);
|
|
2327
|
+
const st = el("span", "bb-fd-state off", stateText);
|
|
2328
|
+
row.append(l, control, st);
|
|
2329
|
+
advWrap.append(row);
|
|
2330
|
+
return st;
|
|
2331
|
+
};
|
|
2332
|
+
const scaleSel = select(
|
|
2333
|
+
SCALE_PRESETS.map((n) => [String(n), `${n}%`]),
|
|
2334
|
+
String(initial.scalePercent)
|
|
2335
|
+
);
|
|
2336
|
+
const scaleState = mkRow("Scale", scaleSel, "");
|
|
2337
|
+
const spacingInput = el("input", "bb-fd-ctl");
|
|
2338
|
+
spacingInput.type = "number";
|
|
2339
|
+
spacingInput.step = "0.05";
|
|
2340
|
+
spacingInput.value = String(twipsToPt(initial.letterSpacingTwips));
|
|
2341
|
+
const spacingState = mkRow("Spacing", spacingInput, "");
|
|
2342
|
+
const positionInput = el("input", "bb-fd-ctl");
|
|
2343
|
+
positionInput.type = "number";
|
|
2344
|
+
positionInput.step = "0.5";
|
|
2345
|
+
positionInput.value = String(halfToPt(initial.positionHalfPoints));
|
|
2346
|
+
const positionState = mkRow("Position", positionInput, "");
|
|
2347
|
+
const kernInput = el("input", "bb-fd-ctl");
|
|
2348
|
+
kernInput.type = "number";
|
|
2349
|
+
kernInput.value = "12";
|
|
2350
|
+
kernInput.disabled = true;
|
|
2351
|
+
mkRow("Kerning", kernInput, "not supported", true);
|
|
2352
|
+
const kernNote = el(
|
|
2353
|
+
"div",
|
|
2354
|
+
"bb-fd-note",
|
|
2355
|
+
"Kerning is read from the file but not reproduced when drawing, so it is shown here rather than silently dropped."
|
|
2356
|
+
);
|
|
2357
|
+
advWrap.append(kernNote);
|
|
2358
|
+
advPane.append(advWrap);
|
|
2359
|
+
const prev = el("div", "bb-fd-prev");
|
|
2360
|
+
const prevLine = el("div", "bb-fd-prev-line");
|
|
2361
|
+
const prevTxt = el("span", "bb-fd-prev-txt", "The quick brown fox");
|
|
2362
|
+
prevLine.append(
|
|
2363
|
+
document.createTextNode("Text before "),
|
|
2364
|
+
prevTxt,
|
|
2365
|
+
document.createTextNode(" and text after.")
|
|
2366
|
+
);
|
|
2367
|
+
prev.append(el("div", "bb-fd-prev-cap", "Preview"), prevLine);
|
|
2368
|
+
const num = (i) => {
|
|
2369
|
+
const v = Number(i.value);
|
|
2370
|
+
return Number.isFinite(v) ? v : 0;
|
|
2371
|
+
};
|
|
2372
|
+
const pressed = (b) => b.getAttribute("aria-pressed") === "true";
|
|
2373
|
+
const pressedOrMixed = (b) => b.getAttribute("aria-pressed") === "mixed" ? void 0 : pressed(b);
|
|
2374
|
+
const checkedOrMixed = (i) => i.indeterminate ? void 0 : i.checked;
|
|
2375
|
+
function paint() {
|
|
2376
|
+
const spacingPt = num(spacingInput);
|
|
2377
|
+
const positionPt = num(positionInput);
|
|
2378
|
+
const scale = Number(scaleSel.value) || 100;
|
|
2379
|
+
scaleState.textContent = scale === 100 ? "normal" : `${scale}%`;
|
|
2380
|
+
scaleState.classList.toggle("off", scale === 100);
|
|
2381
|
+
spacingState.textContent = spacingPt === 0 ? "normal" : spacingPt > 0 ? "expanded" : "condensed";
|
|
2382
|
+
spacingState.classList.toggle("off", spacingPt === 0);
|
|
2383
|
+
positionState.textContent = positionPt === 0 ? "on baseline" : positionPt > 0 ? "raised" : "lowered";
|
|
2384
|
+
positionState.classList.toggle("off", positionPt === 0);
|
|
2385
|
+
const deco = [
|
|
2386
|
+
pressed(underBtn) ? "underline" : "",
|
|
2387
|
+
strikeCb.checked || dstrikeCb.checked ? "line-through" : ""
|
|
2388
|
+
].filter(Boolean).join(" ");
|
|
2389
|
+
const s = prevTxt.style;
|
|
2390
|
+
s.fontFamily = familySel.value || "inherit";
|
|
2391
|
+
const shrink = supCb.checked || subCb.checked ? SUPERSUB_SCALE : 1;
|
|
2392
|
+
s.fontSize = sizeSel.value ? `${Number(sizeSel.value) * shrink}pt` : `${15 * shrink}px`;
|
|
2393
|
+
s.fontWeight = pressed(boldBtn) ? "700" : "400";
|
|
2394
|
+
s.fontStyle = pressed(italicBtn) ? "italic" : "normal";
|
|
2395
|
+
s.textDecoration = deco || "none";
|
|
2396
|
+
s.textDecorationStyle = dstrikeCb.checked ? "double" : "solid";
|
|
2397
|
+
s.fontVariant = smallCapsCb.checked ? "small-caps" : "normal";
|
|
2398
|
+
s.color = color ?? "inherit";
|
|
2399
|
+
s.background = highlight ?? "transparent";
|
|
2400
|
+
s.letterSpacing = `${spacingPt}pt`;
|
|
2401
|
+
s.transform = scale === 100 ? "none" : `scaleX(${scale / 100})`;
|
|
2402
|
+
s.verticalAlign = supCb.checked ? "super" : subCb.checked ? "sub" : `${positionPt}pt`;
|
|
2403
|
+
}
|
|
2404
|
+
for (const c of [familySel, sizeSel, scaleSel])
|
|
2405
|
+
c.addEventListener("change", paint);
|
|
2406
|
+
for (const i of [spacingInput, positionInput])
|
|
2407
|
+
i.addEventListener("input", paint);
|
|
2408
|
+
const foot = el("div", "bb-fd-foot");
|
|
2409
|
+
const cancel = el("button", "bb-fd-btn", "Cancel");
|
|
2410
|
+
const ok = el("button", "bb-fd-btn primary", "OK");
|
|
2411
|
+
cancel.type = ok.type = "button";
|
|
2412
|
+
cancel.addEventListener("click", () => dialog.close());
|
|
2413
|
+
ok.addEventListener("click", () => {
|
|
2414
|
+
const spacingPt = num(spacingInput);
|
|
2415
|
+
const positionPt = num(positionInput);
|
|
2416
|
+
const scale = Number(scaleSel.value) || 100;
|
|
2417
|
+
onApply({
|
|
2418
|
+
family: familySel.value || void 0,
|
|
2419
|
+
sizePt: sizeSel.value ? Number(sizeSel.value) : void 0,
|
|
2420
|
+
bold: pressedOrMixed(boldBtn),
|
|
2421
|
+
italic: pressedOrMixed(italicBtn),
|
|
2422
|
+
underline: pressedOrMixed(underBtn),
|
|
2423
|
+
strike: checkedOrMixed(strikeCb),
|
|
2424
|
+
doubleStrike: checkedOrMixed(dstrikeCb),
|
|
2425
|
+
smallCaps: checkedOrMixed(smallCapsCb),
|
|
2426
|
+
vertAlign: supCb.checked ? "super" : subCb.checked ? "sub" : null,
|
|
2427
|
+
color,
|
|
2428
|
+
highlight,
|
|
2429
|
+
scalePercent: scale,
|
|
2430
|
+
letterSpacingTwips: ptToTwips(spacingPt),
|
|
2431
|
+
positionHalfPoints: ptToHalf(positionPt)
|
|
2432
|
+
});
|
|
2433
|
+
dialog.close();
|
|
2434
|
+
});
|
|
2435
|
+
foot.append(cancel, ok);
|
|
2436
|
+
root.append(tabs, fontPane, advPane, prev, foot);
|
|
2437
|
+
dialog.body.append(root);
|
|
2438
|
+
dialog.onClose(() => dialog.destroy());
|
|
2439
|
+
showTab(false);
|
|
2440
|
+
paint();
|
|
2441
|
+
dialog.open();
|
|
2442
|
+
}
|
|
1883
2443
|
export {
|
|
2444
|
+
COLOR_PALETTE,
|
|
1884
2445
|
Dialog,
|
|
1885
2446
|
cmToPx,
|
|
2447
|
+
colorButton,
|
|
1886
2448
|
createFindDialog,
|
|
1887
2449
|
defaultMenus,
|
|
1888
2450
|
defaultToolbarGroups,
|
|
@@ -1890,6 +2452,8 @@ export {
|
|
|
1890
2452
|
mountMenubar,
|
|
1891
2453
|
mountToolbar,
|
|
1892
2454
|
openCellProperties,
|
|
2455
|
+
openColorPicker,
|
|
2456
|
+
openFontDialog,
|
|
1893
2457
|
openMarginsDialog,
|
|
1894
2458
|
openPageSizeDialog,
|
|
1895
2459
|
orientationPicker,
|