@k2b/ui 0.34.0 → 0.35.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.
Files changed (82) hide show
  1. package/dist/browser/actions/Dropdown.js +1 -0
  2. package/dist/browser/actions/Dropdown.js.map +1 -1
  3. package/dist/browser/content/MatchText.js +61 -0
  4. package/dist/browser/content/MatchText.js.map +1 -0
  5. package/dist/browser/content/index.js +1 -0
  6. package/dist/browser/content/index.js.map +1 -1
  7. package/dist/browser/text-editor/TextEditor.js +1358 -0
  8. package/dist/browser/text-editor/TextEditor.js.map +1 -0
  9. package/dist/browser/text-editor/clipboard-html.js +152 -0
  10. package/dist/browser/text-editor/clipboard-html.js.map +1 -0
  11. package/dist/browser/text-editor/clipboard-loading.js +68 -0
  12. package/dist/browser/text-editor/clipboard-loading.js.map +1 -0
  13. package/dist/browser/text-editor/clipboard.js +51 -0
  14. package/dist/browser/text-editor/clipboard.js.map +1 -0
  15. package/dist/browser/text-editor/engine.js +125 -0
  16. package/dist/browser/text-editor/engine.js.map +1 -0
  17. package/dist/browser/text-editor/format.js +218 -0
  18. package/dist/browser/text-editor/format.js.map +1 -0
  19. package/dist/browser/text-editor/formatted.js +164 -0
  20. package/dist/browser/text-editor/formatted.js.map +1 -0
  21. package/dist/browser/text-editor/language.js +19 -0
  22. package/dist/browser/text-editor/language.js.map +1 -0
  23. package/dist/browser/text-editor/markdown-table.js +563 -0
  24. package/dist/browser/text-editor/markdown-table.js.map +1 -0
  25. package/dist/browser/text-editor/messages.js +233 -0
  26. package/dist/browser/text-editor/messages.js.map +1 -0
  27. package/dist/browser/text-editor/paste.js +115 -0
  28. package/dist/browser/text-editor/paste.js.map +1 -0
  29. package/dist/browser/text-editor/table.js +1760 -0
  30. package/dist/browser/text-editor/table.js.map +1 -0
  31. package/dist/browser/text-editor.js +5 -0
  32. package/dist/browser/text-editor.js.map +1 -0
  33. package/dist/ssr/actions/Dropdown.js +1 -0
  34. package/dist/ssr/actions/Dropdown.js.map +1 -1
  35. package/dist/ssr/content/MatchText.js +56 -0
  36. package/dist/ssr/content/MatchText.js.map +1 -0
  37. package/dist/ssr/content/index.js +1 -0
  38. package/dist/ssr/content/index.js.map +1 -1
  39. package/dist/ssr/text-editor/TextEditor.js +1155 -0
  40. package/dist/ssr/text-editor/TextEditor.js.map +1 -0
  41. package/dist/ssr/text-editor/clipboard-html.js +152 -0
  42. package/dist/ssr/text-editor/clipboard-html.js.map +1 -0
  43. package/dist/ssr/text-editor/clipboard-loading.js +68 -0
  44. package/dist/ssr/text-editor/clipboard-loading.js.map +1 -0
  45. package/dist/ssr/text-editor/clipboard.js +51 -0
  46. package/dist/ssr/text-editor/clipboard.js.map +1 -0
  47. package/dist/ssr/text-editor/engine.js +125 -0
  48. package/dist/ssr/text-editor/engine.js.map +1 -0
  49. package/dist/ssr/text-editor/format.js +218 -0
  50. package/dist/ssr/text-editor/format.js.map +1 -0
  51. package/dist/ssr/text-editor/formatted.js +164 -0
  52. package/dist/ssr/text-editor/formatted.js.map +1 -0
  53. package/dist/ssr/text-editor/language.js +19 -0
  54. package/dist/ssr/text-editor/language.js.map +1 -0
  55. package/dist/ssr/text-editor/markdown-table.js +563 -0
  56. package/dist/ssr/text-editor/markdown-table.js.map +1 -0
  57. package/dist/ssr/text-editor/messages.js +233 -0
  58. package/dist/ssr/text-editor/messages.js.map +1 -0
  59. package/dist/ssr/text-editor/paste.js +115 -0
  60. package/dist/ssr/text-editor/paste.js.map +1 -0
  61. package/dist/ssr/text-editor/table.js +1760 -0
  62. package/dist/ssr/text-editor/table.js.map +1 -0
  63. package/dist/ssr/text-editor.js +5 -0
  64. package/dist/ssr/text-editor.js.map +1 -0
  65. package/dist/styles.css +1 -1
  66. package/dist/types/actions/Dropdown.d.ts +2 -0
  67. package/dist/types/content/MatchText.d.ts +21 -0
  68. package/dist/types/content/index.d.ts +1 -0
  69. package/dist/types/text-editor/TextEditor.d.ts +69 -0
  70. package/dist/types/text-editor/clipboard-html.d.ts +9 -0
  71. package/dist/types/text-editor/clipboard-loading.d.ts +8 -0
  72. package/dist/types/text-editor/clipboard.d.ts +20 -0
  73. package/dist/types/text-editor/engine.d.ts +34 -0
  74. package/dist/types/text-editor/format.d.ts +34 -0
  75. package/dist/types/text-editor/formatted.d.ts +1 -0
  76. package/dist/types/text-editor/language.d.ts +8 -0
  77. package/dist/types/text-editor/markdown-table.d.ts +105 -0
  78. package/dist/types/text-editor/messages.d.ts +113 -0
  79. package/dist/types/text-editor/paste.d.ts +6 -0
  80. package/dist/types/text-editor/table.d.ts +81 -0
  81. package/dist/types/text-editor.d.ts +4 -0
  82. package/package.json +23 -3
@@ -0,0 +1,1358 @@
1
+ import { template as _$template } from "solid-js/web";
2
+ import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
+ import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
4
+ import { style as _$style } from "solid-js/web";
5
+ import { className as _$className } from "solid-js/web";
6
+ import { setProperty as _$setProperty } from "solid-js/web";
7
+ import { effect as _$effect } from "solid-js/web";
8
+ import { runHydrationEvents as _$runHydrationEvents } from "solid-js/web";
9
+ import { addEventListener as _$addEventListener } from "solid-js/web";
10
+ import { setAttribute as _$setAttribute } from "solid-js/web";
11
+ import { use as _$use } from "solid-js/web";
12
+ import { memo as _$memo } from "solid-js/web";
13
+ import { getNextElement as _$getNextElement } from "solid-js/web";
14
+ import { getNextMarker as _$getNextMarker } from "solid-js/web";
15
+ import { insert as _$insert } from "solid-js/web";
16
+ import { createComponent as _$createComponent } from "solid-js/web";
17
+ var _tmpl$ = /*#__PURE__*/_$template(`<div class=k2b-ste-size-picker><!$><!/><!$><!/>`),
18
+ _tmpl$2 = /*#__PURE__*/_$template(`<i class="ti ti-table"aria-hidden=true>`),
19
+ _tmpl$3 = /*#__PURE__*/_$template(`<div popover=auto class=k2b-ste-size-popover role=dialog tabindex=-1><div class=k2b-ste-size-grid role=group></div><div class=k2b-ste-size-caption role=status>`),
20
+ _tmpl$4 = /*#__PURE__*/_$template(`<div class=k2b-ste-size-row>`),
21
+ _tmpl$5 = /*#__PURE__*/_$template(`<button type=button tabindex=-1>`),
22
+ _tmpl$6 = /*#__PURE__*/_$template(`<label class=k2b-ste-recovery-label><!$><!/><textarea class=k2b-ste-recovery readonly>`),
23
+ _tmpl$7 = /*#__PURE__*/_$template(`<i aria-hidden=true>`),
24
+ _tmpl$8 = /*#__PURE__*/_$template(`<i class="ti ti-chevron-down"aria-hidden=true>`),
25
+ _tmpl$9 = /*#__PURE__*/_$template(`<div class=k2b-ste-table-sheet-heading><span></span><!$><!/>`),
26
+ _tmpl$0 = /*#__PURE__*/_$template(`<span class=k2b-ste-check><i class="ti ti-alert-triangle"aria-hidden=true></i><!$><!/>`),
27
+ _tmpl$1 = /*#__PURE__*/_$template(`<div class=k2b-ste-status role=status><span></span><!$><!/>`),
28
+ _tmpl$10 = /*#__PURE__*/_$template(`<div class=k2b-ste-toolbar-slot>`),
29
+ _tmpl$11 = /*#__PURE__*/_$template(`<div class=k2b-ste-loading role=status>`),
30
+ _tmpl$12 = /*#__PURE__*/_$template(`<div class=k2b-ste-floating-menu>`),
31
+ _tmpl$13 = /*#__PURE__*/_$template(`<i class="ti ti-x"aria-hidden=true>`),
32
+ _tmpl$14 = /*#__PURE__*/_$template(`<div class=k2b-ste-notice><div class=k2b-ste-notice-content><!$><!/><!$><!/></div><!$><!/>`),
33
+ _tmpl$15 = /*#__PURE__*/_$template(`<div><span class=k2b-ste-announcement role=status aria-live=polite aria-atomic=true></span><!$><!/><!$><!/><!$><!/><div></div><!$><!/><div role=status aria-live=polite aria-atomic=true>`),
34
+ _tmpl$16 = /*#__PURE__*/_$template(`<div><span>`),
35
+ _tmpl$17 = /*#__PURE__*/_$template(`<span>`);
36
+ import { history, historyKeymap, redo, undo } from "@codemirror/commands";
37
+ import { EditorView, keymap } from "@codemirror/view";
38
+ import { clipboard } from "@k2b/stdlib/solid";
39
+ import { createEffect, createSignal, createUniqueId, For, onCleanup, onMount, Show, untrack } from "solid-js";
40
+ import { Button, IconButton } from "../actions/Button";
41
+ import { Dropdown } from "../actions/Dropdown";
42
+ import { SegmentedControl } from "../actions/SegmentedControl";
43
+ import { Toolbar } from "../actions/Toolbar";
44
+ import { Kbd } from "../content/Kbd";
45
+ import { dialogCore } from "../feedback/dialog-core";
46
+ import { NumberInput } from "../inputs/NumberInput";
47
+ import { LocaleProvider, useLocale } from "../intl/locale";
48
+ import { formatShortcut, keyboardPlatform } from "../intl/shortcut";
49
+ import BottomSheet, { bottomSheetAdaptiveOptions } from "../layout/BottomSheet";
50
+ import { createTextEditor } from "./engine";
51
+ import { commands, formatLabels } from "./format";
52
+ import { findTables, inspectTableStructure } from "./markdown-table";
53
+ import { textEditorMessages } from "./messages";
54
+ import { copyTable, editTableSource, formatActiveCell, runMenuAction, runTableAction, tableActionEnabled } from "./table";
55
+ /** Shared table-size control: pointer grid on desktop, large fields in a phone sheet. */
56
+ export function TableSizePicker(props) {
57
+ const locale = useLocale();
58
+ const t = () => textEditorMessages.resolve([locale()]).t;
59
+ const [rows, setRows] = createSignal(3);
60
+ const [columns, setColumns] = createSignal(3);
61
+ const [expanded, setExpanded] = createSignal(false);
62
+ const id = createUniqueId();
63
+ const abort = new AbortController();
64
+ let disposed = false;
65
+ onCleanup(() => {
66
+ disposed = true;
67
+ abort.abort();
68
+ });
69
+ let trigger;
70
+ let popup;
71
+ const reset = () => {
72
+ setRows(Math.min(3, props.maxRows));
73
+ setColumns(Math.min(3, props.maxColumns));
74
+ };
75
+ const choose = (r, c) => {
76
+ popup.hidePopover();
77
+ if (!disposed && !props.disabled) props.onPick({
78
+ rows: r,
79
+ columns: c
80
+ });
81
+ };
82
+ const open = async () => {
83
+ if (disposed || props.disabled || expanded()) return;
84
+ reset();
85
+ if (!window.matchMedia("(max-width: 47.999rem)").matches) {
86
+ popup.showPopover();
87
+ const anchor = trigger.getBoundingClientRect(),
88
+ box = popup.getBoundingClientRect();
89
+ popup.style.left = `${Math.max(8, Math.min(anchor.left, innerWidth - box.width - 8))}px`;
90
+ popup.style.top = `${Math.max(8, anchor.top > box.height + 8 ? anchor.top - box.height - 8 : Math.min(anchor.bottom + 8, innerHeight - box.height - 8))}px`;
91
+ popup.focus();
92
+ return;
93
+ }
94
+ setExpanded(true);
95
+ const chosen = await dialogCore.open((close, context) => _$createComponent(LocaleProvider, {
96
+ get locale() {
97
+ return locale();
98
+ },
99
+ get children() {
100
+ return _$createComponent(BottomSheet, {
101
+ get onDismiss() {
102
+ return context.requestDismiss;
103
+ },
104
+ get children() {
105
+ return [_$createComponent(BottomSheet.Header, {
106
+ get title() {
107
+ return t().insertTable;
108
+ },
109
+ get close() {
110
+ return context.requestDismiss;
111
+ }
112
+ }), _$createComponent(BottomSheet.Body, {
113
+ get children() {
114
+ var _el$ = _$getNextElement(_tmpl$),
115
+ _el$2 = _el$.firstChild,
116
+ [_el$3, _co$] = _$getNextMarker(_el$2.nextSibling),
117
+ _el$4 = _el$3.nextSibling,
118
+ [_el$5, _co$2] = _$getNextMarker(_el$4.nextSibling);
119
+ _$insert(_el$, _$createComponent(NumberInput, {
120
+ get label() {
121
+ return t().rows;
122
+ },
123
+ get value() {
124
+ return rows();
125
+ },
126
+ onValueChange: setRows,
127
+ min: 1,
128
+ get max() {
129
+ return props.maxRows;
130
+ },
131
+ step: 1
132
+ }), _el$3, _co$);
133
+ _$insert(_el$, _$createComponent(NumberInput, {
134
+ get label() {
135
+ return t().columns;
136
+ },
137
+ get value() {
138
+ return columns();
139
+ },
140
+ onValueChange: setColumns,
141
+ min: 1,
142
+ get max() {
143
+ return props.maxColumns;
144
+ },
145
+ step: 1
146
+ }), _el$5, _co$2);
147
+ return _el$;
148
+ }
149
+ }), _$createComponent(BottomSheet.Footer, {
150
+ get children() {
151
+ return _$createComponent(Button, {
152
+ get disabled() {
153
+ return !rows() || !columns();
154
+ },
155
+ onClick: () => {
156
+ const r = rows(),
157
+ c = columns();
158
+ if (r && c) close({
159
+ rows: r,
160
+ columns: c
161
+ });
162
+ },
163
+ get children() {
164
+ return t().insertSize({
165
+ rows: rows() ?? 0,
166
+ columns: columns() ?? 0
167
+ });
168
+ }
169
+ });
170
+ }
171
+ })];
172
+ }
173
+ });
174
+ }
175
+ }), {
176
+ ...bottomSheetAdaptiveOptions,
177
+ ariaLabel: t().insertTable,
178
+ history: true,
179
+ signal: abort.signal
180
+ });
181
+ setExpanded(false);
182
+ if (chosen && !disposed && !props.disabled) props.onPick(chosen);
183
+ };
184
+ onMount(() => props.onReady?.(() => void open()));
185
+ const keyboard = event => {
186
+ const directions = {
187
+ ArrowUp: [-1, 0],
188
+ ArrowDown: [1, 0],
189
+ ArrowLeft: [0, -1],
190
+ ArrowRight: [0, 1]
191
+ };
192
+ const direction = directions[event.key];
193
+ if (direction) {
194
+ event.preventDefault();
195
+ setRows(Math.max(1, Math.min(Math.min(9, props.maxRows), (rows() ?? 1) + direction[0])));
196
+ setColumns(Math.max(1, Math.min(Math.min(10, props.maxColumns), (columns() ?? 1) + direction[1])));
197
+ } else if (event.key === "Enter" || event.key === " ") {
198
+ event.preventDefault();
199
+ choose(rows() ?? 1, columns() ?? 1);
200
+ } else if (event.key === "Escape") trigger.focus();
201
+ };
202
+ return [_$createComponent(IconButton, {
203
+ ref(r$) {
204
+ var _ref$ = trigger;
205
+ typeof _ref$ === "function" ? _ref$(r$) : trigger = r$;
206
+ },
207
+ variant: "ghost",
208
+ get ["class"]() {
209
+ return props.class;
210
+ },
211
+ get size() {
212
+ return props.size ?? "sm";
213
+ },
214
+ get disabled() {
215
+ return props.disabled;
216
+ },
217
+ get label() {
218
+ return t().insertTable;
219
+ },
220
+ "aria-haspopup": "dialog",
221
+ "aria-controls": id,
222
+ get ["aria-expanded"]() {
223
+ return expanded();
224
+ },
225
+ get tooltip() {
226
+ return t().insertTable;
227
+ },
228
+ onMouseDown: event => event.preventDefault(),
229
+ onClick: () => void open(),
230
+ get children() {
231
+ return _$getNextElement(_tmpl$2);
232
+ }
233
+ }), (() => {
234
+ var _el$7 = _$getNextElement(_tmpl$3),
235
+ _el$8 = _el$7.firstChild,
236
+ _el$9 = _el$8.nextSibling;
237
+ _$addEventListener(_el$7, "toggle", () => setExpanded(popup.matches(":popover-open")));
238
+ _el$7.$$keydown = keyboard;
239
+ var _ref$2 = popup;
240
+ typeof _ref$2 === "function" ? _$use(_ref$2, _el$7) : popup = _el$7;
241
+ _$setAttribute(_el$7, "id", id);
242
+ _$insert(_el$8, _$createComponent(For, {
243
+ get each() {
244
+ return Array.from({
245
+ length: Math.min(9, props.maxRows)
246
+ }, (_, index) => index + 1);
247
+ },
248
+ children: r => (() => {
249
+ var _el$0 = _$getNextElement(_tmpl$4);
250
+ _$insert(_el$0, _$createComponent(For, {
251
+ get each() {
252
+ return Array.from({
253
+ length: Math.min(10, props.maxColumns)
254
+ }, (_, index) => index + 1);
255
+ },
256
+ children: c => (() => {
257
+ var _el$1 = _$getNextElement(_tmpl$5);
258
+ _el$1.$$click = () => choose(r, c);
259
+ _el$1.addEventListener("pointerenter", () => {
260
+ setRows(r);
261
+ setColumns(c);
262
+ });
263
+ _$effect(_p$ => {
264
+ var _v$3 = t().insertSize({
265
+ rows: r,
266
+ columns: c
267
+ }),
268
+ _v$4 = r <= (rows() ?? 0) && c <= (columns() ?? 0) ? "true" : undefined;
269
+ _v$3 !== _p$.e && _$setAttribute(_el$1, "aria-label", _p$.e = _v$3);
270
+ _v$4 !== _p$.t && _$setAttribute(_el$1, "data-selected", _p$.t = _v$4);
271
+ return _p$;
272
+ }, {
273
+ e: undefined,
274
+ t: undefined
275
+ });
276
+ _$runHydrationEvents();
277
+ return _el$1;
278
+ })()
279
+ }));
280
+ return _el$0;
281
+ })()
282
+ }));
283
+ _$insert(_el$9, () => t().insertSize({
284
+ rows: rows() ?? 0,
285
+ columns: columns() ?? 0
286
+ }));
287
+ _$effect(_p$ => {
288
+ var _v$ = t().insertTable,
289
+ _v$2 = t().insertTable;
290
+ _v$ !== _p$.e && _$setAttribute(_el$7, "aria-label", _p$.e = _v$);
291
+ _v$2 !== _p$.t && _$setAttribute(_el$8, "aria-label", _p$.t = _v$2);
292
+ return _p$;
293
+ }, {
294
+ e: undefined,
295
+ t: undefined
296
+ });
297
+ _$runHydrationEvents();
298
+ return _el$7;
299
+ })()];
300
+ }
301
+
302
+ /** Shared table controls for a host-owned toolbar (`toolbar={false}`). */
303
+
304
+ const toolbar = [["bold", "bold"], ["italic", "italic"], ["heading", "heading"], ["bulletList", "list"], ["orderedList", "list-numbers"], ["quote", "quote"], ["link", "link"], ["code", "code"], ["table", "table"], ["rule", "separator"]];
305
+ const actionIcons = {
306
+ rowAbove: "row-insert-top",
307
+ rowBelow: "row-insert-bottom",
308
+ colLeft: "column-insert-left",
309
+ colRight: "column-insert-right",
310
+ alignLeft: "align-left",
311
+ alignCenter: "align-center",
312
+ alignRight: "align-right",
313
+ deleteRow: "row-remove",
314
+ deleteCol: "column-remove",
315
+ moveRowUp: "arrow-up",
316
+ moveRowDown: "arrow-down",
317
+ moveColLeft: "arrow-left",
318
+ moveColRight: "arrow-right",
319
+ toggleHeader: "table-row",
320
+ deleteTable: "trash"
321
+ };
322
+ const isInlineCommand = command => inlineCommands.has(command);
323
+ const shortcuts = {
324
+ bold: "mod+b",
325
+ italic: "mod+i",
326
+ link: "mod+k"
327
+ };
328
+ const inlineCommands = new Set(["bold", "italic", "code", "link", "strike"]);
329
+
330
+ /** A Markdown text field with app-injected collaboration and a directly editable table view. */
331
+ export function TextEditor(props) {
332
+ const locale = useLocale();
333
+ const t = () => textEditorMessages.resolve([locale()]).t;
334
+ const [mode, setMode] = createSignal(props.mode ?? "formatted");
335
+ const [ready, setReady] = createSignal(false);
336
+ const [recovery, setRecovery] = createSignal();
337
+ const [notice, setNotice] = createSignal();
338
+ const [cell, setCell] = createSignal();
339
+ const [localSource, setLocalSource] = createSignal(false);
340
+ const [announcement, setAnnouncement] = createSignal("");
341
+ const [check, setCheck] = createSignal(false);
342
+ const [missingSeparator, setMissingSeparator] = createSignal(false);
343
+ const [active, setActive] = createSignal(false);
344
+ const [documentRevision, setDocumentRevision] = createSignal(0);
345
+ const [phoneMenu, setPhoneMenu] = createSignal(false);
346
+ const [platform, setPlatform] = createSignal("other");
347
+ createEffect(() => {
348
+ const target = cell();
349
+ target?.setAttribute("data-active", "");
350
+ onCleanup(() => target?.removeAttribute("data-active"));
351
+ });
352
+ let disposed = false;
353
+ onCleanup(() => {
354
+ disposed = true;
355
+ const text = recovery();
356
+ if (text !== undefined) detachedRecovery(text);
357
+ });
358
+ // A local draft can outlive an island during navigation. Keep its recovery
359
+ // surface in the shared dialog root so disposal never silently drops it.
360
+ const detachedRecovery = text => {
361
+ const language = locale();
362
+ const labels = t();
363
+ const beforeUnload = event => {
364
+ event.preventDefault();
365
+ event.returnValue = "";
366
+ };
367
+ window.addEventListener("beforeunload", beforeUnload);
368
+ const show = () => void dialogCore.open((close, context) => {
369
+ context.setDismissHandler(() => {});
370
+ return _$createComponent(LocaleProvider, {
371
+ locale: language,
372
+ get children() {
373
+ return _$createComponent(BottomSheet, {
374
+ handle: false,
375
+ onDismiss: () => {},
376
+ get children() {
377
+ return [_$createComponent(BottomSheet.Header, {
378
+ get title() {
379
+ return labels.recoveredInput;
380
+ }
381
+ }), _$createComponent(BottomSheet.Body, {
382
+ get children() {
383
+ var _el$10 = _$getNextElement(_tmpl$6),
384
+ _el$12 = _el$10.firstChild,
385
+ [_el$13, _co$3] = _$getNextMarker(_el$12.nextSibling),
386
+ _el$11 = _el$13.nextSibling;
387
+ _$insert(_el$10, () => labels.recoveredInput, _el$13, _co$3);
388
+ _$setProperty(_el$11, "value", text);
389
+ return _el$10;
390
+ }
391
+ }), _$createComponent(BottomSheet.Footer, {
392
+ get children() {
393
+ return _$createComponent(Button, {
394
+ variant: "ghost",
395
+ onClick: () => close("discard"),
396
+ get children() {
397
+ return labels.discardRecovery;
398
+ }
399
+ });
400
+ }
401
+ })];
402
+ }
403
+ });
404
+ }
405
+ });
406
+ }, {
407
+ ...bottomSheetAdaptiveOptions,
408
+ ariaLabel: labels.recoveredInput
409
+ }).then(result => {
410
+ if (result === "discard") window.removeEventListener("beforeunload", beforeUnload);
411
+ // The shared dialog root closes on each island disposal. Retain the
412
+ // draft across subsequent navigation until the person explicitly discards it.
413
+ else queueMicrotask(show);
414
+ });
415
+ show();
416
+ };
417
+ let menuOpen = false;
418
+ let pointerAnchor;
419
+ let floatingTrigger;
420
+ const tableTriggers = new Set();
421
+ let frame = 0;
422
+ const [menuPosition, setMenuPosition] = createSignal({
423
+ left: "0px",
424
+ top: "0px"
425
+ });
426
+ let root;
427
+ let mount;
428
+ let editor;
429
+ const menuBlocked = () => !!props.loading || !ready() || recovery() !== undefined;
430
+ const blocked = () => !!props.readOnly || menuBlocked();
431
+ const updateCheck = () => {
432
+ const issue = mode() === "plain" ? {
433
+ extraCells: false,
434
+ missingSeparator: false
435
+ } : inspectTableStructure(editor?.view.state.doc.toString() ?? props.initialValue ?? "");
436
+ const needsCheck = issue.extraCells || issue.missingSeparator;
437
+ setMissingSeparator(issue.missingSeparator);
438
+ setCheck(needsCheck);
439
+ props.onTableCheck?.(needsCheck);
440
+ };
441
+ const positionMenu = () => {
442
+ const target = cell();
443
+ if (!target) return;
444
+ const wrap = target.closest(".k2b-ste-table");
445
+ const table = wrap?.querySelector("table");
446
+ if (!target.isConnected || !wrap || !table || mode() !== "formatted") {
447
+ menuOpen = false;
448
+ setCell(undefined);
449
+ checkFocus();
450
+ return;
451
+ }
452
+ const rect = target.getBoundingClientRect();
453
+ const bounds = root.getBoundingClientRect();
454
+ const right = Math.min(table.getBoundingClientRect().right, wrap.getBoundingClientRect().right);
455
+ const position = props.readOnly ? {
456
+ left: `${wrap.getBoundingClientRect().left - bounds.left}px`,
457
+ top: `${wrap.getBoundingClientRect().bottom - bounds.top - (window.matchMedia("(max-width: 47.999rem)").matches ? 44 : 0)}px`
458
+ } : {
459
+ left: `${right - bounds.left - 24}px`,
460
+ top: `${rect.top - bounds.top + (rect.height - 24) / 2}px`
461
+ };
462
+ setMenuPosition(pointerAnchor ?? position);
463
+ };
464
+ const schedulePosition = () => {
465
+ if (!frame) frame = requestAnimationFrame(() => {
466
+ frame = 0;
467
+ positionMenu();
468
+ });
469
+ };
470
+ const unavailable = () => !!props.host?.structure && !props.host.structure.available();
471
+ const actionDisabled = action => {
472
+ documentRevision();
473
+ const wrap = cell()?.closest(".k2b-ste-table");
474
+ return blocked() || !editor || !wrap || !tableActionEnabled(editor.view, wrap, action);
475
+ };
476
+ const isMove = action => action.startsWith("move");
477
+ const runAction = action => {
478
+ const wrap = cell()?.closest(".k2b-ste-table");
479
+ const focused = document.activeElement;
480
+ const menuElement = focused instanceof HTMLElement ? focused.closest('[role="menu"]') : null;
481
+ if (editor && wrap && !actionDisabled(action)) runMenuAction(editor.view, wrap, action);
482
+ // The phone move sheet remains usable for another move. Local structural
483
+ // focus belongs to the cell only after the menu has closed.
484
+ if (isMove(action) && focused instanceof HTMLElement && menuElement) {
485
+ const restore = () => {
486
+ if (disposed || !menuElement.isConnected) return;
487
+ const target = focused.isConnected && !focused.matches(":disabled") ? focused : menuElement.querySelector('[role^="menuitem"]:not(:disabled)');
488
+ target?.focus({
489
+ preventScroll: true
490
+ });
491
+ };
492
+ restore();
493
+ queueMicrotask(restore);
494
+ }
495
+ };
496
+ const moveKeys = {
497
+ moveRowUp: "alt+arrowup",
498
+ moveRowDown: "alt+arrowdown",
499
+ moveColLeft: "alt+arrowleft",
500
+ moveColRight: "alt+arrowright"
501
+ };
502
+ const shortActionLabels = {
503
+ rowAbove: "menuAbove",
504
+ rowBelow: "menuBelow",
505
+ moveRowUp: "menuUp",
506
+ moveRowDown: "menuDown",
507
+ deleteRow: "menuDelete",
508
+ colLeft: "menuInsertLeft",
509
+ colRight: "menuInsertRight",
510
+ moveColLeft: "menuMoveLeft",
511
+ moveColRight: "menuMoveRight",
512
+ deleteCol: "menuDelete"
513
+ };
514
+ const menuRow = () => {
515
+ documentRevision();
516
+ return t().menuRow(Number(cell()?.dataset.r ?? 0));
517
+ };
518
+ const menuColumn = () => {
519
+ documentRevision();
520
+ const column = Number(cell()?.dataset.c ?? 0);
521
+ const header = cell()?.closest(".k2b-ste-table")?.querySelector(`.k2b-ste-cell[data-r="0"][data-c="${column}"]`);
522
+ return t().menuColumn(header?.dataset.raw || t().column(column + 1));
523
+ };
524
+ const menuHeading = () => `${menuRow()} · ${menuColumn()}`;
525
+ const actionItem = (action, choice) => ({
526
+ get label() {
527
+ return phoneMenu() && action in shortActionLabels ? t()[shortActionLabels[action]] : t()[action];
528
+ },
529
+ icon: `ti ti-${actionIcons[action]}`,
530
+ get disabled() {
531
+ return actionDisabled(action);
532
+ },
533
+ immediate: true,
534
+ trailing: moveKeys[action] ? _$createComponent(Kbd, {
535
+ get keys() {
536
+ return moveKeys[action];
537
+ }
538
+ }) : action === "rowBelow" ? _$createComponent(Kbd, {
539
+ keys: "enter"
540
+ }) : undefined,
541
+ variant: action === "deleteRow" || action === "deleteCol" || action === "deleteTable" ? "danger" : undefined,
542
+ closeOnSelect: !isMove(action),
543
+ action: () => runAction(action),
544
+ ...choice
545
+ });
546
+ const [copying, setCopying] = createSignal(false);
547
+ const tableCopy = clipboard.createWriter({
548
+ write: ({
549
+ view,
550
+ wrap,
551
+ format
552
+ }) => copyTable(view, wrap, format)
553
+ });
554
+ const copy = async format => {
555
+ const wrap = cell()?.closest(".k2b-ste-table");
556
+ if (!editor || !wrap || copying()) return;
557
+ setCopying(true);
558
+ setNotice({
559
+ message: t().copyingTable
560
+ });
561
+ const success = await tableCopy.copy({
562
+ view: editor.view,
563
+ wrap,
564
+ format
565
+ });
566
+ if (disposed) return;
567
+ setCopying(false);
568
+ setNotice({
569
+ message: success ? format === "table" ? t().copiedTable : t().copiedMarkdown : t().copyFailed
570
+ });
571
+ };
572
+ const copyItems = () => [{
573
+ get label() {
574
+ return phoneMenu() && !props.readOnly ? t().menuCopy : t().copyTable;
575
+ },
576
+ icon: "ti ti-copy",
577
+ get disabled() {
578
+ return copying();
579
+ },
580
+ immediate: true,
581
+ action: () => {
582
+ void copy("table");
583
+ }
584
+ }, {
585
+ get label() {
586
+ return phoneMenu() && !props.readOnly ? t().menuMarkdown : t().copyMarkdown;
587
+ },
588
+ icon: "ti ti-markdown",
589
+ get disabled() {
590
+ return copying();
591
+ },
592
+ immediate: true,
593
+ action: () => {
594
+ void copy("markdown");
595
+ }
596
+ }];
597
+ const items = () => {
598
+ if (props.readOnly) return copyItems();
599
+ const align = () => {
600
+ documentRevision();
601
+ return cell()?.parentElement?.style.textAlign || "left";
602
+ };
603
+ return [{
604
+ get sectionLabel() {
605
+ return menuHeading();
606
+ },
607
+ items: []
608
+ }, {
609
+ get sectionLabel() {
610
+ return menuRow();
611
+ },
612
+ items: ["rowAbove", "rowBelow", "moveRowUp", "moveRowDown", "deleteRow"].map(action => actionItem(action))
613
+ }, {
614
+ get sectionLabel() {
615
+ return menuColumn();
616
+ },
617
+ items: ["colLeft", "colRight", "moveColLeft", "moveColRight"].map(action => actionItem(action))
618
+ }, {
619
+ sectionLabel: t().alignment,
620
+ layout: "row",
621
+ items: ["alignLeft", "alignCenter", "alignRight"].map((action, index) => actionItem(action, {
622
+ choice: "radio",
623
+ class: "k2b-ste-align-choice",
624
+ ariaLabel: t()[action],
625
+ checked: () => align() === ["left", "center", "right"][index]
626
+ }))
627
+ }, {
628
+ items: [actionItem("deleteCol")]
629
+ }, {
630
+ sectionLabel: t().table,
631
+ items: [actionItem("toggleHeader", {
632
+ choice: "checkbox",
633
+ class: "k2b-ste-header-choice",
634
+ checked: () => {
635
+ documentRevision();
636
+ return cell()?.closest(".k2b-ste-table")?.dataset.header === "true";
637
+ }
638
+ }), {
639
+ label: t().editMarkdown,
640
+ icon: "ti ti-markdown",
641
+ action: () => {
642
+ const wrap = cell()?.closest(".k2b-ste-table");
643
+ if (editor && wrap) editTableSource(editor.view, wrap);
644
+ }
645
+ }, ...copyItems(), actionItem("deleteTable"), ...(unavailable() ? [{
646
+ label: t().structureUnavailable,
647
+ disabled: true
648
+ }] : [])]
649
+ }];
650
+ };
651
+ const preserveSelection = event => event.preventDefault();
652
+ const menuChange = open => {
653
+ menuOpen = open;
654
+ };
655
+ const menuClose = () => {
656
+ menuOpen = false;
657
+ pointerAnchor = undefined;
658
+ schedulePosition();
659
+ queueMicrotask(() => {
660
+ if (disposed) return;
661
+ const focused = document.activeElement;
662
+ if (focused instanceof HTMLElement && focused.matches(".k2b-ste-cell-menu, .k2b-ste-table-tool")) cell()?.focus();
663
+ checkFocus();
664
+ });
665
+ };
666
+ const menu = (floating, trigger) => // A sheet owns its item inventory until it closes. Replace that owner when
667
+ // edit access changes so an already open sheet cannot retain edit entries.
668
+ _$createComponent(Show, {
669
+ get when() {
670
+ return props.readOnly ? "read" : "write";
671
+ },
672
+ keyed: true,
673
+ children: _access => _$createComponent(Dropdown, {
674
+ get items() {
675
+ return items();
676
+ },
677
+ phone: "sheet",
678
+ get sheetTop() {
679
+ return props.readOnly ? undefined : close => (() => {
680
+ var _el$16 = _$getNextElement(_tmpl$9),
681
+ _el$17 = _el$16.firstChild,
682
+ _el$18 = _el$17.nextSibling,
683
+ [_el$19, _co$4] = _$getNextMarker(_el$18.nextSibling);
684
+ _$insert(_el$17, menuHeading);
685
+ _$insert(_el$16, _$createComponent(Button, {
686
+ variant: "ghost",
687
+ onClick: close,
688
+ get children() {
689
+ return t().done;
690
+ }
691
+ }), _el$19, _co$4);
692
+ _$effect(() => _$setAttribute(_el$17, "title", menuHeading()));
693
+ return _el$16;
694
+ })();
695
+ },
696
+ menuClass: "k2b-ste-table-menu",
697
+ get position() {
698
+ return floating ? "right-start" : trigger?.position ?? "bottom-right";
699
+ },
700
+ width: "18rem",
701
+ get label() {
702
+ return _$memo(() => !!props.readOnly)() ? t().copyTable : t().tableMenu;
703
+ },
704
+ get disabled() {
705
+ return menuBlocked() || localSource() || !cell()?.isConnected;
706
+ },
707
+ onOpenChange: menuChange,
708
+ onClose: menuClose,
709
+ get children() {
710
+ return _$createComponent(Dropdown.Trigger, {
711
+ ref: element => {
712
+ if (floating) floatingTrigger = element;else {
713
+ tableTriggers.add(element);
714
+ onCleanup(() => tableTriggers.delete(element));
715
+ }
716
+ },
717
+ get ["class"]() {
718
+ return floating ? props.readOnly ? "k2b-ste-cell-menu k2b-ste-copy-menu" : "k2b-ste-cell-menu" : `k2b-ste-tool k2b-ste-table-tool ${trigger?.class ?? ""}`;
719
+ },
720
+ variant: "ghost",
721
+ get size() {
722
+ return trigger?.size ?? "sm";
723
+ },
724
+ get iconOnly() {
725
+ return _$memo(() => !!!trigger?.text)() && !props.readOnly;
726
+ },
727
+ get label() {
728
+ return trigger?.text ?? (props.readOnly ? t().copyTable : t().tableMenu);
729
+ },
730
+ "aria-pressed": !floating ? true : undefined,
731
+ onMouseDown: preserveSelection,
732
+ get children() {
733
+ return [(() => {
734
+ var _el$14 = _$getNextElement(_tmpl$7);
735
+ _$effect(() => _$className(_el$14, props.readOnly ? "ti ti-copy" : floating ? "ti ti-dots" : "ti ti-table"));
736
+ return _el$14;
737
+ })(), _$memo(() => trigger?.text ?? (props.readOnly ? t().copyTable : undefined)), _$createComponent(Show, {
738
+ get when() {
739
+ return trigger?.text;
740
+ },
741
+ get children() {
742
+ return _$getNextElement(_tmpl$8);
743
+ }
744
+ })];
745
+ }
746
+ });
747
+ }
748
+ })
749
+ });
750
+ const runFormat = command => {
751
+ if (!editor || blocked()) return;
752
+ if (cell()?.isConnected && isInlineCommand(command) && formatActiveCell(editor.view, command)) return;
753
+ commands[command](editor.view);
754
+ };
755
+ const checkFocus = () => {
756
+ if (disposed) return;
757
+ // A host toolbar sits outside this root. Native popover focus moves before
758
+ // its toggle callback, but Dropdown already exposes its synchronous state.
759
+ if (menuOpen || [...tableTriggers, floatingTrigger].some(trigger => trigger?.isConnected && trigger.getAttribute("aria-expanded") === "true")) return;
760
+ const focused = document.activeElement;
761
+ setLocalSource(focused instanceof HTMLElement && !!focused.closest(".k2b-ste-table-source"));
762
+ const inside = focused instanceof Node && root.contains(focused);
763
+ setActive(inside);
764
+ if (!inside || focused === editor?.view.contentDOM) setCell(undefined);else if (!cell()?.isConnected) setCell(undefined);
765
+ };
766
+ const formatMenu = command => _$createComponent(Dropdown, {
767
+ phone: "sheet",
768
+ get label() {
769
+ return t()[command];
770
+ },
771
+ get disabled() {
772
+ return blocked() || command === "heading" && !!cell();
773
+ },
774
+ onOpenChange: menuChange,
775
+ onClose: menuClose,
776
+ get items() {
777
+ return command === "heading" ? [{
778
+ items: ["heading1", "heading2", "heading3"].map((name, index) => ({
779
+ label: t()[name],
780
+ icon: `ti ti-h-${index + 1}`,
781
+ action: () => runFormat(name)
782
+ }))
783
+ }] : [{
784
+ label: t().inlineCode,
785
+ icon: "ti ti-code",
786
+ action: () => runFormat("code")
787
+ }, {
788
+ label: t().codeBlock,
789
+ icon: "ti ti-source-code",
790
+ disabled: !!cell(),
791
+ action: () => runFormat("codeBlock")
792
+ }, {
793
+ label: t().strike,
794
+ icon: "ti ti-strikethrough",
795
+ trailing: _$createComponent(Kbd, {
796
+ keys: "mod+shift+x"
797
+ }),
798
+ action: () => runFormat("strike")
799
+ }];
800
+ },
801
+ get children() {
802
+ return _$createComponent(Dropdown.Trigger, {
803
+ "class": "k2b-ste-tool",
804
+ variant: "ghost",
805
+ size: "sm",
806
+ iconOnly: true,
807
+ get label() {
808
+ return t()[command];
809
+ },
810
+ onMouseDown: preserveSelection,
811
+ get children() {
812
+ var _el$20 = _$getNextElement(_tmpl$7);
813
+ _$className(_el$20, `ti ti-${command}`);
814
+ return _el$20;
815
+ }
816
+ });
817
+ }
818
+ });
819
+ const selectMode = next => {
820
+ const restoreEditorFocus = !!editor?.view.contentDOM.contains(document.activeElement);
821
+ menuOpen = false;
822
+ setCell(undefined);
823
+ setMode(next);
824
+ props.onModeChange?.(next);
825
+ if (restoreEditorFocus) queueMicrotask(() => editor?.view.focus());
826
+ };
827
+ onMount(() => {
828
+ setPlatform(keyboardPlatform());
829
+ const menuMedia = window.matchMedia("(max-width: 47.999rem)");
830
+ const menuViewport = () => setPhoneMenu(menuMedia.matches);
831
+ menuViewport();
832
+ menuMedia.addEventListener("change", menuViewport);
833
+ onCleanup(() => menuMedia.removeEventListener("change", menuViewport));
834
+ // A standalone editor has no binding history; collaboration supplies its own instead.
835
+ const nativeHistory = !props.host?.undo;
836
+ editor = createTextEditor({
837
+ parent: mount,
838
+ doc: props.initialValue,
839
+ mode: mode(),
840
+ label: props.label,
841
+ readOnly: props.readOnly,
842
+ placeholder: props.placeholder,
843
+ presentation: props.presentation,
844
+ extensions: [nativeHistory ? [history(), keymap.of(historyKeymap)] : [], formatLabels.of({
845
+ get link() {
846
+ return t().link;
847
+ },
848
+ column: column => t().column(column)
849
+ }), EditorView.updateListener.of(update => {
850
+ schedulePosition();
851
+ if (update.docChanged) queueMicrotask(() => setDocumentRevision(revision => revision + 1));
852
+ }), props.extensions ?? []],
853
+ host: {
854
+ ...props.host,
855
+ undo: props.host?.undo ?? (() => editor && undo(editor.view)),
856
+ redo: props.host?.redo ?? (() => editor && redo(editor.view)),
857
+ boundary: props.host?.boundary ?? (() => {}),
858
+ cellLabel: (row, column, name) => t().cellLabel({
859
+ row,
860
+ column,
861
+ name
862
+ }),
863
+ actionLabel: name => t()[name],
864
+ sourceLabels: () => ({
865
+ edit: t().editMarkdown,
866
+ valid: t().markdownValid,
867
+ invalid: t().markdownInvalid,
868
+ changed: t().markdownChanged,
869
+ structural: t().markdownStructural,
870
+ applying: t().applyingTable,
871
+ applyFailed: t().markdownApplyFailed,
872
+ done: phoneMenu() ? t().done : t().showTable,
873
+ discard: t().discardMarkdown,
874
+ continue: t().continueMarkdown,
875
+ line: t().markdownLine,
876
+ separator: (header, separator) => t().markdownSeparator({
877
+ header,
878
+ separator
879
+ }),
880
+ extra: t().markdownExtra,
881
+ complete: t().completeColumns
882
+ }),
883
+ maxLength: props.maxLength,
884
+ maxPasteCells: props.maxPasteCells ?? 2000,
885
+ onRecovery: text => {
886
+ if (disposed) detachedRecovery(text);else setRecovery(previous => previous === undefined ? text : `${previous}\n\n${text}`);
887
+ },
888
+ onError: code => setNotice({
889
+ message: t()[code]
890
+ })
891
+ },
892
+ onChange: text => {
893
+ props.onChange?.(text);
894
+ queueMicrotask(updateCheck);
895
+ },
896
+ onDroppedImages: (count, files) => {
897
+ if (props.onDroppedImages?.(count, files) !== true) setNotice({
898
+ message: t().imageHint,
899
+ images: true
900
+ });
901
+ }
902
+ });
903
+ const focus = event => {
904
+ if (!(event instanceof CustomEvent) || !(event.detail.cell instanceof HTMLElement)) return;
905
+ setActive(true);
906
+ if (!menuOpen) pointerAnchor = undefined;
907
+ setCell(event.detail.cell);
908
+ positionMenu();
909
+ };
910
+ const structureAnnouncement = event => {
911
+ if (!(event instanceof CustomEvent)) return;
912
+ const detail = event.detail;
913
+ if (detail && typeof detail === "object" && "rows" in detail && "columns" in detail && typeof detail.rows === "number" && typeof detail.columns === "number") setAnnouncement(t().tableChanged({
914
+ rows: detail.rows,
915
+ columns: detail.columns
916
+ }));
917
+ };
918
+ const focusChange = () => queueMicrotask(checkFocus);
919
+ let pointerCheck;
920
+ const pointer = event => {
921
+ if (event.target instanceof Node && root.contains(event.target)) setActive(true);
922
+ // After the press moved focus: a host toolbar control that keeps focus in the cell keeps it active.
923
+ else if (!menuOpen) {
924
+ clearTimeout(pointerCheck);
925
+ pointerCheck = setTimeout(checkFocus);
926
+ }
927
+ };
928
+ const keyboardMenu = event => {
929
+ if (menuBlocked()) return;
930
+ focus(event);
931
+ if (event instanceof CustomEvent) {
932
+ const bounds = root.getBoundingClientRect();
933
+ const anchor = event.detail.anchor instanceof HTMLElement ? event.detail.anchor.getBoundingClientRect() : undefined;
934
+ if (anchor || typeof event.detail.x === "number") {
935
+ pointerAnchor = {
936
+ left: `${(anchor?.left ?? event.detail.x) - bounds.left}px`,
937
+ top: `${(anchor?.bottom ?? event.detail.y) - bounds.top}px`
938
+ };
939
+ positionMenu();
940
+ }
941
+ }
942
+ queueMicrotask(() => {
943
+ if (disposed || !cell()?.isConnected) return;
944
+ const tableTrigger = [...tableTriggers].find(trigger => trigger.isConnected);
945
+ const trigger = (window.matchMedia("(max-width: 47.999rem)").matches && tableTrigger?.isConnected ? tableTrigger : undefined) ?? floatingTrigger;
946
+ trigger?.click();
947
+ });
948
+ };
949
+ const mutations = new MutationObserver(schedulePosition);
950
+ mutations.observe(mount, {
951
+ childList: true,
952
+ subtree: true,
953
+ characterData: true
954
+ });
955
+ const resize = new ResizeObserver(schedulePosition);
956
+ resize.observe(mount);
957
+ // Safari blurs a contenteditable on mouse-down without focusing the radio.
958
+ // Keep the caret and phone toolbar stationary until the view switch runs.
959
+ const viewMouseDown = event => {
960
+ if (event.target instanceof Element && event.target.closest(".k2b-ste-views")) event.preventDefault();
961
+ };
962
+ root.addEventListener("mousedown", viewMouseDown);
963
+ document.addEventListener("focusin", focusChange);
964
+ document.addEventListener("pointerdown", pointer);
965
+ root.addEventListener("k2b-ste-table-structure", structureAnnouncement);
966
+ root.addEventListener("k2b-ste-cell-focus", focus);
967
+ root.addEventListener("k2b-ste-cell-blur", focusChange);
968
+ root.addEventListener("k2b-ste-cell-menu", keyboardMenu);
969
+ const tableScroll = event => {
970
+ if (menuOpen && event.target instanceof HTMLElement && event.target.classList.contains("k2b-ste-table") && floatingTrigger?.getAttribute("aria-expanded") === "true") floatingTrigger.click();
971
+ schedulePosition();
972
+ };
973
+ window.addEventListener("scroll", tableScroll, true);
974
+ window.addEventListener("resize", schedulePosition);
975
+ const viewport = window.visualViewport;
976
+ const keyboard = () => root.style.setProperty("--k2b-ste-keyboard", `${Math.max(0, window.innerHeight - (viewport?.height ?? window.innerHeight) - (viewport?.offsetTop ?? 0))}px`);
977
+ viewport?.addEventListener("resize", keyboard);
978
+ viewport?.addEventListener("scroll", keyboard);
979
+ keyboard();
980
+ setReady(true);
981
+ updateCheck();
982
+ props.onReady?.(editor, {
983
+ tableActive: () => mode() === "formatted" && !!cell()?.isConnected,
984
+ TableMenu: trigger => menu(false, trigger),
985
+ formatCell: format => !!editor && !blocked() && !!cell()?.isConnected && formatActiveCell(editor.view, format),
986
+ notify: message => setNotice({
987
+ message
988
+ })
989
+ });
990
+ onCleanup(() => {
991
+ clearTimeout(pointerCheck);
992
+ mutations.disconnect();
993
+ resize.disconnect();
994
+ cancelAnimationFrame(frame);
995
+ root.removeEventListener("mousedown", viewMouseDown);
996
+ document.removeEventListener("focusin", focusChange);
997
+ document.removeEventListener("pointerdown", pointer);
998
+ root.removeEventListener("k2b-ste-table-structure", structureAnnouncement);
999
+ root.removeEventListener("k2b-ste-cell-focus", focus);
1000
+ root.removeEventListener("k2b-ste-cell-blur", focusChange);
1001
+ root.removeEventListener("k2b-ste-cell-menu", keyboardMenu);
1002
+ window.removeEventListener("scroll", tableScroll, true);
1003
+ window.removeEventListener("resize", schedulePosition);
1004
+ viewport?.removeEventListener("resize", keyboard);
1005
+ viewport?.removeEventListener("scroll", keyboard);
1006
+ editor?.destroy();
1007
+ });
1008
+ });
1009
+ createEffect(() => {
1010
+ if (props.mode !== undefined) setMode(props.mode);
1011
+ });
1012
+ createEffect(() => {
1013
+ if (ready()) {
1014
+ const next = mode();
1015
+ // Rendering the grid consults reactive host availability. Those reads
1016
+ // must not turn a history notification into a nested mode reconfigure.
1017
+ untrack(() => {
1018
+ menuOpen = false;
1019
+ setCell(undefined);
1020
+ editor?.setMode(next);
1021
+ updateCheck();
1022
+ });
1023
+ }
1024
+ });
1025
+ createEffect(() => {
1026
+ const label = props.label;
1027
+ if (ready()) untrack(() => editor?.setLabel(label));
1028
+ });
1029
+ createEffect(() => {
1030
+ const readOnly = !!props.readOnly || !!props.loading || recovery() !== undefined;
1031
+ if (ready()) untrack(() => editor?.setReadOnly(readOnly));
1032
+ });
1033
+ createEffect(() => {
1034
+ documentRevision();
1035
+ const disabled = blocked() || unavailable();
1036
+ if (ready()) root.querySelectorAll(".k2b-ste-addColumn").forEach(button => {
1037
+ button.disabled = disabled;
1038
+ });
1039
+ });
1040
+ return (() => {
1041
+ var _el$21 = _$getNextElement(_tmpl$15),
1042
+ _el$22 = _el$21.firstChild,
1043
+ _el$49 = _el$22.nextSibling,
1044
+ [_el$50, _co$1] = _$getNextMarker(_el$49.nextSibling),
1045
+ _el$51 = _el$50.nextSibling,
1046
+ [_el$52, _co$10] = _$getNextMarker(_el$51.nextSibling),
1047
+ _el$53 = _el$52.nextSibling,
1048
+ [_el$54, _co$11] = _$getNextMarker(_el$53.nextSibling),
1049
+ _el$33 = _el$54.nextSibling,
1050
+ _el$55 = _el$33.nextSibling,
1051
+ [_el$56, _co$12] = _$getNextMarker(_el$55.nextSibling),
1052
+ _el$35 = _el$56.nextSibling;
1053
+ var _ref$3 = root;
1054
+ typeof _ref$3 === "function" ? _$use(_ref$3, _el$21) : root = _el$21;
1055
+ _$insert(_el$22, announcement);
1056
+ _$insert(_el$21, _$createComponent(Show, {
1057
+ get when() {
1058
+ return !props.presentation || "status" in props || check();
1059
+ },
1060
+ get children() {
1061
+ var _el$23 = _$getNextElement(_tmpl$1),
1062
+ _el$24 = _el$23.firstChild,
1063
+ _el$29 = _el$24.nextSibling,
1064
+ [_el$30, _co$6] = _$getNextMarker(_el$29.nextSibling);
1065
+ _$insert(_el$24, () => props.status);
1066
+ _$insert(_el$23, _$createComponent(Show, {
1067
+ get when() {
1068
+ return check();
1069
+ },
1070
+ get children() {
1071
+ return [(() => {
1072
+ var _el$25 = _$getNextElement(_tmpl$0),
1073
+ _el$26 = _el$25.firstChild,
1074
+ _el$27 = _el$26.nextSibling,
1075
+ [_el$28, _co$5] = _$getNextMarker(_el$27.nextSibling);
1076
+ _$insert(_el$25, () => t().checkTable, _el$28, _co$5);
1077
+ _$effect(() => _$setAttribute(_el$25, "title", missingSeparator() ? t().checkSeparatorDetail : t().checkTableDetail));
1078
+ return _el$25;
1079
+ })(), _$createComponent(Button, {
1080
+ size: "sm",
1081
+ variant: "ghost",
1082
+ get disabled() {
1083
+ return blocked() || !missingSeparator() && unavailable();
1084
+ },
1085
+ get hidden() {
1086
+ return props.readOnly;
1087
+ },
1088
+ get tooltip() {
1089
+ return _$memo(() => !!missingSeparator())() ? t().checkSeparatorDetail : _$memo(() => !!unavailable())() ? t().structureUnavailable : t().checkTableDetail;
1090
+ },
1091
+ onMouseDown: preserveSelection,
1092
+ onClick: () => {
1093
+ if (!editor) return;
1094
+ if (missingSeparator()) {
1095
+ selectMode("source");
1096
+ queueMicrotask(() => editor?.view.focus());
1097
+ return;
1098
+ }
1099
+ const table = findTables(editor.view.state.doc.toString()).find(table => table.lines.some(row => row.cells.length > table.columns));
1100
+ if (table) runTableAction(editor.view, table.from, 0, 0, "completeColumns");
1101
+ },
1102
+ get children() {
1103
+ return _$memo(() => !!missingSeparator())() ? t().reviewMarkdown : t().completeColumns;
1104
+ }
1105
+ })];
1106
+ }
1107
+ }), _el$30, _co$6);
1108
+ return _el$23;
1109
+ }
1110
+ }), _el$50, _co$1);
1111
+ _$insert(_el$21, _$createComponent(Show, {
1112
+ get when() {
1113
+ return _$memo(() => !!(!props.readOnly && mode() !== "plain"))() && props.toolbar !== false;
1114
+ },
1115
+ get children() {
1116
+ var _el$31 = _$getNextElement(_tmpl$10);
1117
+ _$insert(_el$31, _$createComponent(Toolbar, {
1118
+ "class": "k2b-ste-toolbar",
1119
+ get label() {
1120
+ return t().formatting;
1121
+ },
1122
+ get children() {
1123
+ return [_$createComponent(For, {
1124
+ get each() {
1125
+ return [toolbar.slice(0, 3), toolbar.slice(3, 6), toolbar.slice(6)];
1126
+ },
1127
+ children: group => _$createComponent(Toolbar.Group, {
1128
+ "class": "k2b-ste-tool-group",
1129
+ get children() {
1130
+ return _$createComponent(For, {
1131
+ each: group,
1132
+ children: ([command, icon]) => command === "heading" || command === "code" ? formatMenu(command) : _$createComponent(Show, {
1133
+ get when() {
1134
+ return _$memo(() => !!(command === "table" && mode() === "formatted"))() && cell()?.isConnected;
1135
+ },
1136
+ get fallback() {
1137
+ return _$createComponent(IconButton, {
1138
+ "class": "k2b-ste-tool",
1139
+ variant: "ghost",
1140
+ size: "sm",
1141
+ get label() {
1142
+ return t()[command];
1143
+ },
1144
+ get tooltip() {
1145
+ return _$memo(() => !!shortcuts[command])() ? `${t()[command]} (${formatShortcut(shortcuts[command] ?? "", {
1146
+ platform: platform(),
1147
+ locale: locale()
1148
+ }).label})` : t()[command];
1149
+ },
1150
+ get disabled() {
1151
+ return blocked() || !!cell() && !inlineCommands.has(command) && command !== "table";
1152
+ },
1153
+ onMouseDown: preserveSelection,
1154
+ onClick: () => runFormat(command),
1155
+ get children() {
1156
+ var _el$57 = _$getNextElement(_tmpl$7);
1157
+ _$className(_el$57, `ti ti-${icon}`);
1158
+ return _el$57;
1159
+ }
1160
+ });
1161
+ },
1162
+ get children() {
1163
+ return menu(false);
1164
+ }
1165
+ })
1166
+ });
1167
+ }
1168
+ })
1169
+ }), _$createComponent(SegmentedControl, {
1170
+ "class": "k2b-ste-views",
1171
+ size: "sm",
1172
+ get ariaLabel() {
1173
+ return t().view;
1174
+ },
1175
+ get value() {
1176
+ return mode() === "source" ? "source" : "formatted";
1177
+ },
1178
+ get options() {
1179
+ return [{
1180
+ value: "formatted",
1181
+ label: t().formatted,
1182
+ icon: "ti ti-typography"
1183
+ }, {
1184
+ value: "source",
1185
+ label: t().source,
1186
+ icon: "ti ti-markdown"
1187
+ }];
1188
+ },
1189
+ onValueChange: selectMode
1190
+ })];
1191
+ }
1192
+ }));
1193
+ return _el$31;
1194
+ }
1195
+ }), _el$52, _co$10);
1196
+ _$insert(_el$21, _$createComponent(Show, {
1197
+ get when() {
1198
+ return props.loading || !ready();
1199
+ },
1200
+ get children() {
1201
+ var _el$32 = _$getNextElement(_tmpl$11);
1202
+ _$insert(_el$32, _$createComponent(For, {
1203
+ each: [82, 100, 68, 0, 96, 74],
1204
+ children: width => (() => {
1205
+ var _el$58 = _$getNextElement(_tmpl$16),
1206
+ _el$59 = _el$58.firstChild;
1207
+ _$setStyleProperty(_el$59, "width", `${width}%`);
1208
+ return _el$58;
1209
+ })()
1210
+ }));
1211
+ _$effect(() => _$setAttribute(_el$32, "aria-label", t().loading));
1212
+ return _el$32;
1213
+ }
1214
+ }), _el$54, _co$11);
1215
+ var _ref$4 = mount;
1216
+ typeof _ref$4 === "function" ? _$use(_ref$4, _el$33) : mount = _el$33;
1217
+ _$insert(_el$21, _$createComponent(Show, {
1218
+ get when() {
1219
+ return _$memo(() => !!(cell() && mode() === "formatted"))() && !menuBlocked();
1220
+ },
1221
+ get children() {
1222
+ var _el$34 = _$getNextElement(_tmpl$12);
1223
+ _$insert(_el$34, () => menu(true));
1224
+ _$effect(_p$ => {
1225
+ var _v$5 = props.readOnly ? "true" : undefined,
1226
+ _v$6 = menuPosition();
1227
+ _v$5 !== _p$.e && _$setAttribute(_el$34, "data-readonly", _p$.e = _v$5);
1228
+ _p$.t = _$style(_el$34, _v$6, _p$.t);
1229
+ return _p$;
1230
+ }, {
1231
+ e: undefined,
1232
+ t: undefined
1233
+ });
1234
+ return _el$34;
1235
+ }
1236
+ }), _el$56, _co$12);
1237
+ _$insert(_el$35, _$createComponent(Show, {
1238
+ get when() {
1239
+ return notice() || recovery() !== undefined;
1240
+ },
1241
+ get children() {
1242
+ var _el$36 = _$getNextElement(_tmpl$14),
1243
+ _el$37 = _el$36.firstChild,
1244
+ _el$42 = _el$37.firstChild,
1245
+ [_el$43, _co$8] = _$getNextMarker(_el$42.nextSibling),
1246
+ _el$44 = _el$43.nextSibling,
1247
+ [_el$45, _co$9] = _$getNextMarker(_el$44.nextSibling),
1248
+ _el$47 = _el$37.nextSibling,
1249
+ [_el$48, _co$0] = _$getNextMarker(_el$47.nextSibling);
1250
+ _$insert(_el$37, _$createComponent(Show, {
1251
+ get when() {
1252
+ return notice();
1253
+ },
1254
+ get children() {
1255
+ return _$createComponent(Show, {
1256
+ get when() {
1257
+ return _$memo(() => !!notice()?.images)() && props.onAttachImages;
1258
+ },
1259
+ get fallback() {
1260
+ return (() => {
1261
+ var _el$60 = _$getNextElement(_tmpl$17);
1262
+ _$insert(_el$60, () => notice()?.message);
1263
+ return _el$60;
1264
+ })();
1265
+ },
1266
+ get children() {
1267
+ return _$createComponent(Button, {
1268
+ size: "sm",
1269
+ variant: "ghost",
1270
+ get disabled() {
1271
+ return blocked();
1272
+ },
1273
+ onClick: () => props.onAttachImages?.(),
1274
+ get children() {
1275
+ return notice()?.message;
1276
+ }
1277
+ });
1278
+ }
1279
+ });
1280
+ }
1281
+ }), _el$43, _co$8);
1282
+ _$insert(_el$37, _$createComponent(Show, {
1283
+ get when() {
1284
+ return recovery() !== undefined;
1285
+ },
1286
+ get children() {
1287
+ return [(() => {
1288
+ var _el$38 = _$getNextElement(_tmpl$6),
1289
+ _el$40 = _el$38.firstChild,
1290
+ [_el$41, _co$7] = _$getNextMarker(_el$40.nextSibling),
1291
+ _el$39 = _el$41.nextSibling;
1292
+ _$insert(_el$38, () => t().recoveredInput, _el$41, _co$7);
1293
+ _$effect(() => _$setProperty(_el$39, "value", recovery()));
1294
+ return _el$38;
1295
+ })(), _$createComponent(Button, {
1296
+ size: "sm",
1297
+ variant: "ghost",
1298
+ onClick: () => {
1299
+ setRecovery(undefined);
1300
+ if (notice()?.message === t().cellRemoved) setNotice(undefined);
1301
+ },
1302
+ get children() {
1303
+ return t().discardRecovery;
1304
+ }
1305
+ })];
1306
+ }
1307
+ }), _el$45, _co$9);
1308
+ _$insert(_el$36, _$createComponent(Show, {
1309
+ get when() {
1310
+ return notice();
1311
+ },
1312
+ get children() {
1313
+ return _$createComponent(IconButton, {
1314
+ size: "sm",
1315
+ variant: "ghost",
1316
+ get label() {
1317
+ return t().dismiss;
1318
+ },
1319
+ onClick: () => setNotice(undefined),
1320
+ get children() {
1321
+ return _$getNextElement(_tmpl$13);
1322
+ }
1323
+ });
1324
+ }
1325
+ }), _el$48, _co$0);
1326
+ return _el$36;
1327
+ }
1328
+ }));
1329
+ _$effect(_p$ => {
1330
+ var _v$7 = `k2b-text-editor ${props.class ?? ""}`,
1331
+ _v$8 = mode(),
1332
+ _v$9 = props.readOnly ? "true" : undefined,
1333
+ _v$0 = active() ? "true" : undefined,
1334
+ _v$1 = props.loading || !ready(),
1335
+ _v$10 = props.presentation ? "k2b-ste-app-host" : "k2b-ste-host",
1336
+ _v$11 = props.loading || !ready();
1337
+ _v$7 !== _p$.e && _$className(_el$21, _p$.e = _v$7);
1338
+ _v$8 !== _p$.t && _$setAttribute(_el$21, "data-mode", _p$.t = _v$8);
1339
+ _v$9 !== _p$.a && _$setAttribute(_el$21, "data-readonly", _p$.a = _v$9);
1340
+ _v$0 !== _p$.o && _$setAttribute(_el$21, "data-active", _p$.o = _v$0);
1341
+ _v$1 !== _p$.i && _$setAttribute(_el$21, "aria-busy", _p$.i = _v$1);
1342
+ _v$10 !== _p$.n && _$className(_el$33, _p$.n = _v$10);
1343
+ _v$11 !== _p$.s && _$setProperty(_el$33, "hidden", _p$.s = _v$11);
1344
+ return _p$;
1345
+ }, {
1346
+ e: undefined,
1347
+ t: undefined,
1348
+ a: undefined,
1349
+ o: undefined,
1350
+ i: undefined,
1351
+ n: undefined,
1352
+ s: undefined
1353
+ });
1354
+ return _el$21;
1355
+ })();
1356
+ }
1357
+ _$delegateEvents(["keydown", "click"]);
1358
+ //# sourceMappingURL=TextEditor.js.map