@k2b/ui 0.38.0 → 0.39.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/browser/actions/Dropdown.js +42 -6
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/SelectionToolbar.js +11 -6
- package/dist/browser/actions/SelectionToolbar.js.map +1 -1
- package/dist/browser/actions/Toolbar.js +5 -1
- package/dist/browser/actions/Toolbar.js.map +1 -1
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +11 -2
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/text-editor/TextEditor.js +331 -214
- package/dist/browser/text-editor/TextEditor.js.map +1 -1
- package/dist/browser/text-editor/table.js +43 -6
- package/dist/browser/text-editor/table.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +38 -6
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/SelectionToolbar.js +11 -6
- package/dist/ssr/actions/SelectionToolbar.js.map +1 -1
- package/dist/ssr/actions/Toolbar.js +5 -1
- package/dist/ssr/actions/Toolbar.js.map +1 -1
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +10 -2
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/text-editor/TextEditor.js +225 -96
- package/dist/ssr/text-editor/TextEditor.js.map +1 -1
- package/dist/ssr/text-editor/table.js +43 -6
- package/dist/ssr/text-editor/table.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Dropdown.d.ts +15 -1
- package/dist/types/actions/Toolbar.d.ts +6 -1
- package/dist/types/actions/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -11,20 +11,19 @@ var _tmpl$ = ["<div class=\"k2b-ste-size-picker\">", "", "</div>"],
|
|
|
11
11
|
_tmpl$4 = ["<div class=\"k2b-ste-size-row\">", "</div>"],
|
|
12
12
|
_tmpl$5 = ["<button type=\"button\" tabindex=\"-1\"", "></button>"],
|
|
13
13
|
_tmpl$6 = ["<label class=\"k2b-ste-recovery-label\">", "<textarea class=\"k2b-ste-recovery\" readonly", "></textarea></label>"],
|
|
14
|
-
_tmpl$7 = ["<
|
|
14
|
+
_tmpl$7 = ["<div class=\"k2b-ste-table-sheet-heading\"><span", ">", "</span>", "</div>"],
|
|
15
15
|
_tmpl$8 = "<i class=\"ti ti-chevron-down\" aria-hidden=\"true\"></i>",
|
|
16
|
-
_tmpl$9 =
|
|
16
|
+
_tmpl$9 = "<i class=\"ti ti-copy\" aria-hidden=\"true\"></i>",
|
|
17
17
|
_tmpl$0 = ["<i class=\"", "\" aria-hidden=\"true\"></i>"],
|
|
18
18
|
_tmpl$1 = ["<span class=\"k2b-ste-check\"", "><i class=\"ti ti-alert-triangle\" aria-hidden=\"true\"></i>", "</span>"],
|
|
19
19
|
_tmpl$10 = ["<div class=\"k2b-ste-status\" role=\"status\"><span>", "</span>", "</div>"],
|
|
20
20
|
_tmpl$11 = ["<div class=\"k2b-ste-toolbar-slot\">", "</div>"],
|
|
21
21
|
_tmpl$12 = ["<div class=\"k2b-ste-loading\" role=\"status\"", ">", "</div>"],
|
|
22
|
-
_tmpl$13 =
|
|
23
|
-
_tmpl$14 = "<
|
|
24
|
-
_tmpl$15 = ["<div class=\"k2b-ste-
|
|
25
|
-
_tmpl$16 = ["<div
|
|
26
|
-
_tmpl$17 = ["<
|
|
27
|
-
_tmpl$18 = ["<span>", "</span>"];
|
|
22
|
+
_tmpl$13 = "<i class=\"ti ti-x\" aria-hidden=\"true\"></i>",
|
|
23
|
+
_tmpl$14 = ["<div class=\"k2b-ste-notice\"><div class=\"k2b-ste-notice-content\">", "", "</div>", "</div>"],
|
|
24
|
+
_tmpl$15 = ["<div class=\"", "\"", "><span class=\"k2b-ste-announcement\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">", "</span>", "", "", "<div", "", "></div><div class=\"k2b-ste-floating-menu\" style=\"", "\">", "</div><div role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">", "</div></div>"],
|
|
25
|
+
_tmpl$16 = ["<div><span style=\"", "\"></span></div>"],
|
|
26
|
+
_tmpl$17 = ["<span>", "</span>"];
|
|
28
27
|
import { history, historyKeymap, redo, undo } from "@codemirror/commands";
|
|
29
28
|
import { EditorView, keymap } from "@codemirror/view";
|
|
30
29
|
import { clipboard } from "@k2b/stdlib/solid";
|
|
@@ -279,10 +278,27 @@ export function TextEditor(props) {
|
|
|
279
278
|
const [documentRevision, setDocumentRevision] = createSignal(0);
|
|
280
279
|
const [phoneMenu, setPhoneMenu] = createSignal(false);
|
|
281
280
|
const [platform, setPlatform] = createSignal("other");
|
|
281
|
+
/** What the open cell menu acts on: a row grip, a column grip, or the cell (right-click, keys, toolbar). */
|
|
282
|
+
const [scope, setScope] = createSignal("cell");
|
|
283
|
+
let grip;
|
|
282
284
|
createEffect(() => {
|
|
283
285
|
const target = cell();
|
|
284
|
-
|
|
285
|
-
|
|
286
|
+
const kind = scope();
|
|
287
|
+
if (!target) return;
|
|
288
|
+
if (kind === "cell") {
|
|
289
|
+
target.setAttribute("data-active", "");
|
|
290
|
+
onCleanup(() => target.removeAttribute("data-active"));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
// A grip's menu marks its whole row or column, and the grip stays visible while the menu is open.
|
|
294
|
+
const marked = kind === "row" ? [target.closest("tr")] : [...(target.closest(".k2b-ste-table")?.querySelectorAll(`.k2b-ste-cell[data-c="${target.dataset.c}"]`) ?? [])].map(element => element.parentElement);
|
|
295
|
+
const opener = grip;
|
|
296
|
+
for (const element of marked) element?.setAttribute("data-marked", "");
|
|
297
|
+
opener?.setAttribute("data-open", "");
|
|
298
|
+
onCleanup(() => {
|
|
299
|
+
for (const element of marked) element?.removeAttribute("data-marked");
|
|
300
|
+
opener?.removeAttribute("data-open");
|
|
301
|
+
});
|
|
286
302
|
});
|
|
287
303
|
let disposed = false;
|
|
288
304
|
onCleanup(() => {
|
|
@@ -344,8 +360,9 @@ export function TextEditor(props) {
|
|
|
344
360
|
show();
|
|
345
361
|
};
|
|
346
362
|
let menuOpen = false;
|
|
347
|
-
let
|
|
348
|
-
let
|
|
363
|
+
let openCellMenu;
|
|
364
|
+
let closeCellMenu;
|
|
365
|
+
let copyTrigger;
|
|
349
366
|
const tableTriggers = new Set();
|
|
350
367
|
let frame = 0;
|
|
351
368
|
const [menuPosition, setMenuPosition] = createSignal({
|
|
@@ -373,22 +390,19 @@ export function TextEditor(props) {
|
|
|
373
390
|
const wrap = target.closest(".k2b-ste-table");
|
|
374
391
|
const table = wrap?.querySelector("table");
|
|
375
392
|
if (!target.isConnected || !wrap || !table || mode() !== "formatted") {
|
|
393
|
+
closeCellMenu?.();
|
|
376
394
|
menuOpen = false;
|
|
377
395
|
setCell(undefined);
|
|
378
396
|
checkFocus();
|
|
379
397
|
return;
|
|
380
398
|
}
|
|
381
|
-
|
|
399
|
+
// Only the read-only phone copy control sits below its table; every other menu opens beside its anchor.
|
|
400
|
+
if (!props.readOnly) return;
|
|
382
401
|
const bounds = root.getBoundingClientRect();
|
|
383
|
-
|
|
384
|
-
const position = props.readOnly ? {
|
|
402
|
+
setMenuPosition({
|
|
385
403
|
left: `${wrap.getBoundingClientRect().left - bounds.left}px`,
|
|
386
404
|
top: `${wrap.getBoundingClientRect().bottom - bounds.top - (window.matchMedia("(max-width: 47.999rem)").matches ? 44 : 0)}px`
|
|
387
|
-
}
|
|
388
|
-
left: `${right - bounds.left - 24}px`,
|
|
389
|
-
top: `${rect.top - bounds.top + (rect.height - 24) / 2}px`
|
|
390
|
-
};
|
|
391
|
-
setMenuPosition(pointerAnchor ?? position);
|
|
405
|
+
});
|
|
392
406
|
};
|
|
393
407
|
const schedulePosition = () => {
|
|
394
408
|
if (!frame) frame = requestAnimationFrame(() => {
|
|
@@ -450,10 +464,12 @@ export function TextEditor(props) {
|
|
|
450
464
|
const header = cell()?.closest(".k2b-ste-table")?.querySelector(`.k2b-ste-cell[data-r="0"][data-c="${column}"]`);
|
|
451
465
|
return t().menuColumn(header?.dataset.raw || t().column(column + 1));
|
|
452
466
|
};
|
|
453
|
-
const menuHeading = () => `${menuRow()} · ${menuColumn()}`;
|
|
454
|
-
const actionItem = (action, choice) => ({
|
|
467
|
+
const menuHeading = (kind = scope()) => kind === "row" ? menuRow() : kind === "column" ? menuColumn() : `${menuRow()} · ${menuColumn()}`;
|
|
468
|
+
const actionItem = (action, choice, kind = "cell") => ({
|
|
455
469
|
get label() {
|
|
456
|
-
|
|
470
|
+
// A grip's sheet has no row or column heading over its entries, so deleting keeps its full name.
|
|
471
|
+
const short = phoneMenu() && action in shortActionLabels && !(kind !== "cell" && action.startsWith("delete"));
|
|
472
|
+
return short ? t()[shortActionLabels[action]] : t()[action];
|
|
457
473
|
},
|
|
458
474
|
icon: `ti ti-${actionIcons[action]}`,
|
|
459
475
|
get disabled() {
|
|
@@ -523,25 +539,22 @@ export function TextEditor(props) {
|
|
|
523
539
|
void copy("markdown");
|
|
524
540
|
}
|
|
525
541
|
}];
|
|
526
|
-
|
|
542
|
+
/** The row grip shows row and table actions, the column grip column, alignment and table actions; the rest everything. */
|
|
543
|
+
const items = kind => {
|
|
527
544
|
if (props.readOnly) return copyItems();
|
|
528
545
|
const align = () => {
|
|
529
546
|
documentRevision();
|
|
530
547
|
return cell()?.parentElement?.style.textAlign || "left";
|
|
531
548
|
};
|
|
532
|
-
|
|
549
|
+
const row = [{
|
|
533
550
|
get sectionLabel() {
|
|
534
|
-
return
|
|
551
|
+
return kind === "cell" ? menuRow() : undefined;
|
|
535
552
|
},
|
|
536
|
-
items: []
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
return menuRow();
|
|
540
|
-
},
|
|
541
|
-
items: ["rowAbove", "rowBelow", "moveRowUp", "moveRowDown", "deleteRow"].map(action => actionItem(action))
|
|
542
|
-
}, {
|
|
553
|
+
items: ["rowAbove", "rowBelow", "moveRowUp", "moveRowDown", "deleteRow"].map(action => actionItem(action, undefined, kind))
|
|
554
|
+
}];
|
|
555
|
+
const column = [{
|
|
543
556
|
get sectionLabel() {
|
|
544
|
-
return menuColumn();
|
|
557
|
+
return kind === "cell" ? menuColumn() : undefined;
|
|
545
558
|
},
|
|
546
559
|
items: ["colLeft", "colRight", "moveColLeft", "moveColRight"].map(action => actionItem(action))
|
|
547
560
|
}, {
|
|
@@ -554,8 +567,14 @@ export function TextEditor(props) {
|
|
|
554
567
|
checked: () => align() === ["left", "center", "right"][index]
|
|
555
568
|
}))
|
|
556
569
|
}, {
|
|
557
|
-
items: [actionItem("deleteCol")]
|
|
558
|
-
}
|
|
570
|
+
items: [actionItem("deleteCol", undefined, kind)]
|
|
571
|
+
}];
|
|
572
|
+
return [{
|
|
573
|
+
get sectionLabel() {
|
|
574
|
+
return menuHeading(kind);
|
|
575
|
+
},
|
|
576
|
+
items: []
|
|
577
|
+
}, ...(kind === "column" ? [] : row), ...(kind === "row" ? [] : column), {
|
|
559
578
|
sectionLabel: t().table,
|
|
560
579
|
items: [actionItem("toggleHeader", {
|
|
561
580
|
choice: "checkbox",
|
|
@@ -581,70 +600,119 @@ export function TextEditor(props) {
|
|
|
581
600
|
const menuChange = open => {
|
|
582
601
|
menuOpen = open;
|
|
583
602
|
};
|
|
603
|
+
// A press beside an open table menu only closes it: no cell takes focus, no
|
|
604
|
+
// text is selected and no column is marked. The menu's light dismiss closes it.
|
|
605
|
+
let tableMenuOpen = false;
|
|
606
|
+
const tableMenuChange = open => {
|
|
607
|
+
menuOpen = open;
|
|
608
|
+
tableMenuOpen = open && !phoneMenu();
|
|
609
|
+
};
|
|
610
|
+
let swallow;
|
|
611
|
+
const pressBesideMenu = event => {
|
|
612
|
+
swallow?.abort();
|
|
613
|
+
if (!tableMenuOpen || event.target instanceof Element && event.target.closest(".k2b-ste-table-menu")) return;
|
|
614
|
+
event.stopPropagation();
|
|
615
|
+
const current = new AbortController();
|
|
616
|
+
swallow = current;
|
|
617
|
+
const block = blocked => {
|
|
618
|
+
blocked.preventDefault();
|
|
619
|
+
blocked.stopPropagation();
|
|
620
|
+
if (blocked.type === "click" || blocked.type === "contextmenu") current.abort();
|
|
621
|
+
};
|
|
622
|
+
for (const type of ["mousedown", "mouseup", "click", "contextmenu"]) document.addEventListener(type, block, {
|
|
623
|
+
capture: true,
|
|
624
|
+
signal: current.signal
|
|
625
|
+
});
|
|
626
|
+
// Only this press is swallowed: its click follows the release in the same task, and a cancelled press never
|
|
627
|
+
// clicks. A key other than a modifier, which may come with the press, starts new input.
|
|
628
|
+
const end = () => current.abort();
|
|
629
|
+
document.addEventListener("pointerup", () => setTimeout(end), {
|
|
630
|
+
capture: true,
|
|
631
|
+
once: true,
|
|
632
|
+
signal: current.signal
|
|
633
|
+
});
|
|
634
|
+
document.addEventListener("pointercancel", end, {
|
|
635
|
+
capture: true,
|
|
636
|
+
once: true,
|
|
637
|
+
signal: current.signal
|
|
638
|
+
});
|
|
639
|
+
document.addEventListener("keydown", key => {
|
|
640
|
+
if (!["Shift", "Control", "Alt", "Meta"].includes(key.key)) end();
|
|
641
|
+
}, {
|
|
642
|
+
capture: true,
|
|
643
|
+
signal: current.signal
|
|
644
|
+
});
|
|
645
|
+
};
|
|
584
646
|
const menuClose = () => {
|
|
585
647
|
menuOpen = false;
|
|
586
|
-
|
|
648
|
+
tableMenuOpen = false;
|
|
649
|
+
setScope("cell");
|
|
650
|
+
grip = undefined;
|
|
587
651
|
schedulePosition();
|
|
588
652
|
queueMicrotask(() => {
|
|
589
653
|
if (disposed) return;
|
|
590
654
|
const focused = document.activeElement;
|
|
591
|
-
if (focused instanceof HTMLElement && focused.matches(".k2b-ste-
|
|
655
|
+
if (focused instanceof HTMLElement && focused.matches(".k2b-ste-copy-menu, .k2b-ste-table-tool")) cell()?.focus();
|
|
592
656
|
checkFocus();
|
|
593
657
|
});
|
|
594
658
|
};
|
|
595
|
-
const
|
|
659
|
+
const sheetTop = kind => props.readOnly ? undefined : close => _$ssr(_tmpl$7, _$ssrAttribute("title", _$escape(menuHeading(kind()), true), false), _$escape(menuHeading(kind())), _$escape(_$createComponent(Button, {
|
|
660
|
+
variant: "ghost",
|
|
661
|
+
onClick: close,
|
|
662
|
+
get children() {
|
|
663
|
+
return t().done;
|
|
664
|
+
}
|
|
665
|
+
})));
|
|
666
|
+
const menuLabel = () => props.readOnly ? t().copyTable : t().tableMenu;
|
|
667
|
+
const menuDisabled = () => menuBlocked() || localSource() || !cell()?.isConnected;
|
|
668
|
+
// A sheet owns its item inventory until it closes. Replace that owner when
|
|
596
669
|
// edit access changes so an already open sheet cannot retain edit entries.
|
|
597
|
-
|
|
670
|
+
/** The toolbar's table button: the whole menu for the focused cell. */
|
|
671
|
+
const menu = trigger => _$createComponent(Show, {
|
|
598
672
|
get when() {
|
|
599
673
|
return props.readOnly ? "read" : "write";
|
|
600
674
|
},
|
|
601
675
|
keyed: true,
|
|
602
676
|
children: _access => _$createComponent(Dropdown, {
|
|
603
677
|
get items() {
|
|
604
|
-
return items();
|
|
678
|
+
return items("cell");
|
|
605
679
|
},
|
|
606
680
|
phone: "sheet",
|
|
607
681
|
get sheetTop() {
|
|
608
|
-
return
|
|
609
|
-
variant: "ghost",
|
|
610
|
-
onClick: close,
|
|
611
|
-
get children() {
|
|
612
|
-
return t().done;
|
|
613
|
-
}
|
|
614
|
-
})));
|
|
682
|
+
return sheetTop(() => "cell");
|
|
615
683
|
},
|
|
616
684
|
menuClass: "k2b-ste-table-menu",
|
|
617
685
|
get position() {
|
|
618
|
-
return
|
|
686
|
+
return trigger?.position ?? "bottom-right";
|
|
619
687
|
},
|
|
620
688
|
width: "18rem",
|
|
621
689
|
get label() {
|
|
622
|
-
return
|
|
690
|
+
return menuLabel();
|
|
623
691
|
},
|
|
624
692
|
get disabled() {
|
|
625
|
-
return
|
|
693
|
+
return menuDisabled();
|
|
626
694
|
},
|
|
627
|
-
onOpenChange:
|
|
695
|
+
onOpenChange: tableMenuChange,
|
|
628
696
|
onClose: menuClose,
|
|
629
697
|
get children() {
|
|
630
698
|
return _$createComponent(Dropdown.Trigger, {
|
|
631
699
|
get ["class"]() {
|
|
632
|
-
return
|
|
700
|
+
return `k2b-ste-tool k2b-ste-table-tool ${trigger?.class ?? ""}`;
|
|
633
701
|
},
|
|
634
702
|
variant: "ghost",
|
|
635
703
|
get size() {
|
|
636
704
|
return trigger?.size ?? "sm";
|
|
637
705
|
},
|
|
638
706
|
get iconOnly() {
|
|
639
|
-
return
|
|
707
|
+
return !trigger?.text;
|
|
640
708
|
},
|
|
641
709
|
get label() {
|
|
642
|
-
return trigger?.text ??
|
|
710
|
+
return trigger?.text ?? t().tableMenu;
|
|
643
711
|
},
|
|
644
|
-
"aria-pressed":
|
|
712
|
+
"aria-pressed": true,
|
|
645
713
|
onMouseDown: preserveSelection,
|
|
646
714
|
get children() {
|
|
647
|
-
return [_$ssr(_tmpl$
|
|
715
|
+
return [_$ssr(_tmpl$2), trigger?.text, _$createComponent(Show, {
|
|
648
716
|
get when() {
|
|
649
717
|
return trigger?.text;
|
|
650
718
|
},
|
|
@@ -657,6 +725,60 @@ export function TextEditor(props) {
|
|
|
657
725
|
}
|
|
658
726
|
})
|
|
659
727
|
});
|
|
728
|
+
/** Grips, right-click and Shift+F10 open this menu beside its anchor; it has no button of its own. */
|
|
729
|
+
const cellMenu = () => _$createComponent(Show, {
|
|
730
|
+
get when() {
|
|
731
|
+
return props.readOnly ? "read" : "write";
|
|
732
|
+
},
|
|
733
|
+
keyed: true,
|
|
734
|
+
children: _access => _$createComponent(Dropdown, {
|
|
735
|
+
get items() {
|
|
736
|
+
return items(scope());
|
|
737
|
+
},
|
|
738
|
+
phone: "sheet",
|
|
739
|
+
get sheetTop() {
|
|
740
|
+
return sheetTop(scope);
|
|
741
|
+
},
|
|
742
|
+
menuClass: "k2b-ste-table-menu",
|
|
743
|
+
position: "bottom-right",
|
|
744
|
+
width: "18rem",
|
|
745
|
+
get label() {
|
|
746
|
+
return menuLabel();
|
|
747
|
+
},
|
|
748
|
+
get disabled() {
|
|
749
|
+
return menuDisabled();
|
|
750
|
+
},
|
|
751
|
+
onOpenChange: tableMenuChange,
|
|
752
|
+
onClose: menuClose,
|
|
753
|
+
openAt: open => {
|
|
754
|
+
openCellMenu = open;
|
|
755
|
+
},
|
|
756
|
+
closeWith: close => {
|
|
757
|
+
closeCellMenu = close;
|
|
758
|
+
},
|
|
759
|
+
get children() {
|
|
760
|
+
return _$createComponent(Show, {
|
|
761
|
+
get when() {
|
|
762
|
+
return _$memo(() => !!(props.readOnly && cell() && mode() === "formatted"))() && !menuBlocked();
|
|
763
|
+
},
|
|
764
|
+
get children() {
|
|
765
|
+
return _$createComponent(Dropdown.Trigger, {
|
|
766
|
+
"class": "k2b-ste-copy-menu",
|
|
767
|
+
variant: "ghost",
|
|
768
|
+
size: "sm",
|
|
769
|
+
get label() {
|
|
770
|
+
return t().copyTable;
|
|
771
|
+
},
|
|
772
|
+
onMouseDown: preserveSelection,
|
|
773
|
+
get children() {
|
|
774
|
+
return [_$ssr(_tmpl$9), t().copyTable];
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
})
|
|
781
|
+
});
|
|
660
782
|
const runFormat = command => {
|
|
661
783
|
if (!editor || blocked()) return;
|
|
662
784
|
if (cell()?.isConnected && isInlineCommand(command) && formatActiveCell(editor.view, command)) return;
|
|
@@ -666,7 +788,7 @@ export function TextEditor(props) {
|
|
|
666
788
|
if (disposed) return;
|
|
667
789
|
// A host toolbar sits outside this root. Native popover focus moves before
|
|
668
790
|
// its toggle callback, but Dropdown already exposes its synchronous state.
|
|
669
|
-
if (menuOpen || [...tableTriggers,
|
|
791
|
+
if (menuOpen || [...tableTriggers, copyTrigger].some(trigger => trigger?.isConnected && trigger.getAttribute("aria-expanded") === "true")) return;
|
|
670
792
|
const focused = document.activeElement;
|
|
671
793
|
setLocalSource(focused instanceof HTMLElement && !!focused.closest(".k2b-ste-table-source"));
|
|
672
794
|
const inside = focused instanceof Node && root.contains(focused);
|
|
@@ -811,7 +933,6 @@ export function TextEditor(props) {
|
|
|
811
933
|
const focus = event => {
|
|
812
934
|
if (!(event instanceof CustomEvent) || !(event.detail.cell instanceof HTMLElement)) return;
|
|
813
935
|
setActive(true);
|
|
814
|
-
if (!menuOpen) pointerAnchor = undefined;
|
|
815
936
|
setCell(event.detail.cell);
|
|
816
937
|
positionMenu();
|
|
817
938
|
};
|
|
@@ -833,25 +954,41 @@ export function TextEditor(props) {
|
|
|
833
954
|
pointerCheck = setTimeout(checkFocus);
|
|
834
955
|
}
|
|
835
956
|
};
|
|
957
|
+
// A grip opens its row's or column's menu below itself; right-click opens
|
|
958
|
+
// at the pointer and a key below the cell. Focus returns to the cell.
|
|
836
959
|
const keyboardMenu = event => {
|
|
837
|
-
if (menuBlocked()) return;
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
960
|
+
if (menuBlocked() || menuOpen || !(event instanceof CustomEvent) || !(event.detail.cell instanceof HTMLElement)) return;
|
|
961
|
+
const target = event.detail.cell;
|
|
962
|
+
const opener = event.detail.anchor instanceof HTMLElement ? event.detail.anchor : undefined;
|
|
963
|
+
const kind = event.detail.scope === "row" || event.detail.scope === "column" ? event.detail.scope : "cell";
|
|
964
|
+
const {
|
|
965
|
+
x,
|
|
966
|
+
y
|
|
967
|
+
} = event.detail;
|
|
968
|
+
let anchor = opener ?? target;
|
|
969
|
+
if (!opener && typeof x === "number" && typeof y === "number") {
|
|
970
|
+
// Keep the pointer's place inside the cell, so the menu follows it while the page scrolls.
|
|
971
|
+
const start = target.getBoundingClientRect();
|
|
972
|
+
anchor = {
|
|
973
|
+
getBoundingClientRect: () => {
|
|
974
|
+
const box = target.getBoundingClientRect();
|
|
975
|
+
const left = box.left + x - start.left;
|
|
976
|
+
const top = box.top + y - start.top;
|
|
977
|
+
return {
|
|
978
|
+
left,
|
|
979
|
+
top,
|
|
980
|
+
right: left,
|
|
981
|
+
bottom: top
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
};
|
|
849
985
|
}
|
|
986
|
+
grip = opener;
|
|
987
|
+
setScope(kind);
|
|
988
|
+
focus(event);
|
|
989
|
+
// Focus returns to the menu's cell as it closes; moving a row or column moves that cell.
|
|
850
990
|
queueMicrotask(() => {
|
|
851
|
-
if (disposed
|
|
852
|
-
const tableTrigger = [...tableTriggers].find(trigger => trigger.isConnected);
|
|
853
|
-
const trigger = (window.matchMedia("(max-width: 47.999rem)").matches && tableTrigger?.isConnected ? tableTrigger : undefined) ?? floatingTrigger;
|
|
854
|
-
trigger?.click();
|
|
991
|
+
if (!disposed && cell()?.isConnected) openCellMenu?.(anchor, () => cell());
|
|
855
992
|
});
|
|
856
993
|
};
|
|
857
994
|
const mutations = new MutationObserver(schedulePosition);
|
|
@@ -870,15 +1007,12 @@ export function TextEditor(props) {
|
|
|
870
1007
|
root.addEventListener("mousedown", viewMouseDown);
|
|
871
1008
|
document.addEventListener("focusin", focusChange);
|
|
872
1009
|
document.addEventListener("pointerdown", pointer);
|
|
1010
|
+
document.addEventListener("pointerdown", pressBesideMenu, true);
|
|
873
1011
|
root.addEventListener("k2b-ste-table-structure", structureAnnouncement);
|
|
874
1012
|
root.addEventListener("k2b-ste-cell-focus", focus);
|
|
875
1013
|
root.addEventListener("k2b-ste-cell-blur", focusChange);
|
|
876
1014
|
root.addEventListener("k2b-ste-cell-menu", keyboardMenu);
|
|
877
|
-
|
|
878
|
-
if (menuOpen && event.target instanceof HTMLElement && event.target.classList.contains("k2b-ste-table") && floatingTrigger?.getAttribute("aria-expanded") === "true") floatingTrigger.click();
|
|
879
|
-
schedulePosition();
|
|
880
|
-
};
|
|
881
|
-
window.addEventListener("scroll", tableScroll, true);
|
|
1015
|
+
window.addEventListener("scroll", schedulePosition, true);
|
|
882
1016
|
window.addEventListener("resize", schedulePosition);
|
|
883
1017
|
const viewport = window.visualViewport;
|
|
884
1018
|
const keyboard = () => root.style.setProperty("--k2b-ste-keyboard", `${Math.max(0, window.innerHeight - (viewport?.height ?? window.innerHeight) - (viewport?.offsetTop ?? 0))}px`);
|
|
@@ -889,7 +1023,7 @@ export function TextEditor(props) {
|
|
|
889
1023
|
updateCheck();
|
|
890
1024
|
props.onReady?.(editor, {
|
|
891
1025
|
tableActive: () => mode() === "formatted" && !!cell()?.isConnected,
|
|
892
|
-
TableMenu: trigger => menu(
|
|
1026
|
+
TableMenu: trigger => menu(trigger),
|
|
893
1027
|
formatCell: format => !!editor && !blocked() && !!cell()?.isConnected && formatActiveCell(editor.view, format),
|
|
894
1028
|
notify: message => setNotice({
|
|
895
1029
|
message
|
|
@@ -903,11 +1037,13 @@ export function TextEditor(props) {
|
|
|
903
1037
|
root.removeEventListener("mousedown", viewMouseDown);
|
|
904
1038
|
document.removeEventListener("focusin", focusChange);
|
|
905
1039
|
document.removeEventListener("pointerdown", pointer);
|
|
1040
|
+
document.removeEventListener("pointerdown", pressBesideMenu, true);
|
|
1041
|
+
swallow?.abort();
|
|
906
1042
|
root.removeEventListener("k2b-ste-table-structure", structureAnnouncement);
|
|
907
1043
|
root.removeEventListener("k2b-ste-cell-focus", focus);
|
|
908
1044
|
root.removeEventListener("k2b-ste-cell-blur", focusChange);
|
|
909
1045
|
root.removeEventListener("k2b-ste-cell-menu", keyboardMenu);
|
|
910
|
-
window.removeEventListener("scroll",
|
|
1046
|
+
window.removeEventListener("scroll", schedulePosition, true);
|
|
911
1047
|
window.removeEventListener("resize", schedulePosition);
|
|
912
1048
|
viewport?.removeEventListener("resize", keyboard);
|
|
913
1049
|
viewport?.removeEventListener("scroll", keyboard);
|
|
@@ -945,7 +1081,7 @@ export function TextEditor(props) {
|
|
|
945
1081
|
button.disabled = disabled;
|
|
946
1082
|
});
|
|
947
1083
|
});
|
|
948
|
-
return _$ssr(_tmpl$
|
|
1084
|
+
return _$ssr(_tmpl$15, `k2b-text-editor ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-mode", _$escape(mode(), true), false) + _$ssrAttribute("data-readonly", props.readOnly ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-active", active() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("aria-busy", _$escape(props.loading || !ready(), true), false), _$escape(announcement()), _$escape(_$createComponent(Show, {
|
|
949
1085
|
get when() {
|
|
950
1086
|
return !props.presentation || "status" in props || check();
|
|
951
1087
|
},
|
|
@@ -1034,7 +1170,7 @@ export function TextEditor(props) {
|
|
|
1034
1170
|
});
|
|
1035
1171
|
},
|
|
1036
1172
|
get children() {
|
|
1037
|
-
return menu(
|
|
1173
|
+
return menu();
|
|
1038
1174
|
}
|
|
1039
1175
|
})
|
|
1040
1176
|
});
|
|
@@ -1072,22 +1208,15 @@ export function TextEditor(props) {
|
|
|
1072
1208
|
get children() {
|
|
1073
1209
|
return _$ssr(_tmpl$12, _$ssrAttribute("aria-label", _$escape(t().loading, true), false), _$escape(_$createComponent(For, {
|
|
1074
1210
|
each: [82, 100, 68, 0, 96, 74],
|
|
1075
|
-
children: width => _$ssr(_tmpl$
|
|
1211
|
+
children: width => _$ssr(_tmpl$16, _$ssrStyleProperty("width:", `${_$escape(width, true)}%`))
|
|
1076
1212
|
})));
|
|
1077
1213
|
}
|
|
1078
|
-
})), _$ssrAttribute("class", props.presentation ? "k2b-ste-app-host" : "k2b-ste-host", false), _$ssrAttribute("hidden", props.loading || !ready(), true), _$escape(_$createComponent(Show, {
|
|
1079
|
-
get when() {
|
|
1080
|
-
return _$memo(() => !!(cell() && mode() === "formatted"))() && !menuBlocked();
|
|
1081
|
-
},
|
|
1082
|
-
get children() {
|
|
1083
|
-
return _$ssr(_tmpl$13, _$ssrAttribute("data-readonly", props.readOnly ? "true" : _$escape(undefined, true), false), _$ssrStyle(menuPosition()), _$escape(menu(true)));
|
|
1084
|
-
}
|
|
1085
|
-
})), _$escape(_$createComponent(Show, {
|
|
1214
|
+
})), _$ssrAttribute("class", props.presentation ? "k2b-ste-app-host" : "k2b-ste-host", false), _$ssrAttribute("hidden", props.loading || !ready(), true), _$ssrStyle(menuPosition()), _$escape(cellMenu()), _$escape(_$createComponent(Show, {
|
|
1086
1215
|
get when() {
|
|
1087
1216
|
return notice() || recovery() !== undefined;
|
|
1088
1217
|
},
|
|
1089
1218
|
get children() {
|
|
1090
|
-
return _$ssr(_tmpl$
|
|
1219
|
+
return _$ssr(_tmpl$14, _$escape(_$createComponent(Show, {
|
|
1091
1220
|
get when() {
|
|
1092
1221
|
return notice();
|
|
1093
1222
|
},
|
|
@@ -1097,7 +1226,7 @@ export function TextEditor(props) {
|
|
|
1097
1226
|
return _$memo(() => !!notice()?.images)() && props.onAttachImages;
|
|
1098
1227
|
},
|
|
1099
1228
|
get fallback() {
|
|
1100
|
-
return _$ssr(_tmpl$
|
|
1229
|
+
return _$ssr(_tmpl$17, _$escape(notice()?.message));
|
|
1101
1230
|
},
|
|
1102
1231
|
get children() {
|
|
1103
1232
|
return _$createComponent(Button, {
|
|
@@ -1144,7 +1273,7 @@ export function TextEditor(props) {
|
|
|
1144
1273
|
},
|
|
1145
1274
|
onClick: () => setNotice(undefined),
|
|
1146
1275
|
get children() {
|
|
1147
|
-
return _$ssr(_tmpl$
|
|
1276
|
+
return _$ssr(_tmpl$13);
|
|
1148
1277
|
}
|
|
1149
1278
|
});
|
|
1150
1279
|
}
|