edytor 2.0.0 → 3.0.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/README.md +19 -4
- package/dist/block/block.svelte.js +2 -4
- package/dist/clipboard/htmlFlow.d.ts +20 -7
- package/dist/clipboard/htmlFlow.js +23 -15
- package/dist/clipboard/serializeClipboardFragment.d.ts +37 -4
- package/dist/clipboard/serializeClipboardFragment.js +6 -4
- package/dist/clipboard/textBlocks.d.ts +8 -0
- package/dist/clipboard/textBlocks.js +6 -2
- package/dist/cloudflare/room/admission.js +3 -3
- package/dist/cloudflare/room/context.d.ts +1 -1
- package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
- package/dist/cloudflare/routeDocumentSocket.js +53 -0
- package/dist/collaboration/comments/client.d.ts +1 -1
- package/dist/collaboration/comments/client.js +1 -1
- package/dist/collaboration/remoteSelection.js +3 -3
- package/dist/components/Edytor.svelte +2 -10
- package/dist/components/Edytor.svelte.d.ts +1 -3
- package/dist/crdt/admission.d.ts +5 -10
- package/dist/crdt/admission.js +5 -11
- package/dist/crdt/doc/funnel.d.ts +7 -6
- package/dist/crdt/doc/funnel.js +10 -9
- package/dist/crdt/doc/gate.d.ts +0 -17
- package/dist/crdt/doc/gate.js +2 -41
- package/dist/crdt/doc/history.js +2 -2
- package/dist/crdt/document.d.ts +5 -14
- package/dist/crdt/document.js +5 -19
- package/dist/crdt/edytor-doc.d.ts +4 -4
- package/dist/crdt/edytor-doc.js +3 -7
- package/dist/crdt/index.d.ts +3 -3
- package/dist/crdt/index.js +2 -2
- package/dist/crdt/protocol.d.ts +2 -2
- package/dist/crdt/protocol.js +2 -2
- package/dist/crdt/providers/index.d.ts +1 -1
- package/dist/crdt/providers/room.d.ts +10 -9
- package/dist/crdt/providers/room.js +13 -10
- package/dist/crdt/providers/websocket.d.ts +1 -1
- package/dist/crdt/providers/websocket.js +28 -29
- package/dist/crdt/semantics.js +3 -1
- package/dist/dnd/autoScroll.d.ts +15 -7
- package/dist/dnd/autoScroll.js +10 -15
- package/dist/dnd/dropIndicator.js +2 -5
- package/dist/dnd/external.d.ts +6 -0
- package/dist/dnd/external.js +17 -0
- package/dist/dnd/geometry.d.ts +2 -0
- package/dist/dnd/geometry.js +3 -4
- package/dist/dnd/placement.d.ts +1 -1
- package/dist/dnd/pragmatic.d.ts +3 -1
- package/dist/dnd/pragmatic.js +12 -2
- package/dist/dnd/vendor/pragmatic/UPSTREAM.md +51 -18
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.d.ts +4 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.js +6 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +0 -2
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +4 -22
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter-types.d.ts +26 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.d.ts +30 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.js +203 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-items.js +13 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-types.js +9 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/is-an-available-type.js +19 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.d.ts +4 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.js +6 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +4 -16
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +1 -8
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +5 -6
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +2 -3
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.d.ts +1 -1
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +6 -24
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.d.ts +2 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.js +5 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.d.ts +3 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.js +12 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/native-types.d.ts +12 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window-in-safari.js +10 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window.js +55 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.d.ts +4 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.js +8 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +0 -4
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +16 -72
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +1 -9
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +4 -29
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +2 -3
- package/dist/edytor.svelte.d.ts +2 -5
- package/dist/edytor.svelte.js +17 -31
- package/dist/events/onBeforeInput.js +1 -1
- package/dist/events/onDrop.d.ts +0 -6
- package/dist/events/onDrop.js +10 -23
- package/dist/events/onInput.d.ts +0 -12
- package/dist/events/onInput.js +1 -67
- package/dist/export/forms.d.ts +627 -0
- package/dist/export/forms.js +749 -0
- package/dist/export/index.d.ts +170 -0
- package/dist/export/index.js +161 -0
- package/dist/export/markdown.d.ts +41 -0
- package/dist/export/markdown.js +585 -0
- package/dist/export/markdownWriter.d.ts +9 -0
- package/dist/export/markdownWriter.js +133 -0
- package/dist/export/mdx.d.ts +15 -0
- package/dist/export/mdx.js +33 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/kinds.js +1 -1
- package/dist/plugins/anchored.svelte.d.ts +21 -25
- package/dist/plugins/anchored.svelte.js +23 -39
- package/dist/plugins/blockHandles/BlockHandleController.svelte.js +24 -32
- package/dist/plugins/blockHandles/dragPreview.d.ts +26 -7
- package/dist/plugins/blockHandles/dragPreview.js +33 -0
- package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +5 -0
- package/dist/plugins/blockMenu/BlockMenuController.svelte.js +7 -2
- package/dist/plugins/blockMenu/blockMenuPlugin.js +5 -50
- package/dist/plugins/chrome.d.ts +27 -5
- package/dist/plugins/chrome.js +24 -9
- package/dist/plugins/code/CodePlugin.svelte +12 -9
- package/dist/plugins/code/header.svelte.js +7 -11
- package/dist/plugins/code/languageMenu.svelte.d.ts +5 -2
- package/dist/plugins/code/languageMenu.svelte.js +13 -20
- package/dist/plugins/columns/ColumnsPlugin.svelte +7 -20
- package/dist/plugins/columns/resize.svelte.d.ts +2 -3
- package/dist/plugins/columns/resize.svelte.js +2 -5
- package/dist/plugins/comments/CommentsController.svelte.d.ts +6 -0
- package/dist/plugins/comments/CommentsController.svelte.js +20 -0
- package/dist/plugins/equation/EquationPlugin.svelte +12 -22
- package/dist/plugins/equation/equation.svelte.d.ts +7 -15
- package/dist/plugins/equation/equation.svelte.js +13 -50
- package/dist/plugins/equation/tex.d.ts +17 -0
- package/dist/plugins/equation/tex.js +42 -0
- package/dist/plugins/fileDrop.d.ts +38 -23
- package/dist/plugins/fileDrop.js +82 -76
- package/dist/plugins/find/FindController.svelte.d.ts +5 -0
- package/dist/plugins/find/FindController.svelte.js +14 -14
- package/dist/plugins/find/findPlugin.js +1 -15
- package/dist/plugins/image/ImageChrome.svelte +8 -2
- package/dist/plugins/image/ImagePlugin.svelte +15 -52
- package/dist/plugins/image/controls.svelte.js +16 -2
- package/dist/plugins/image/toolbar.svelte.d.ts +5 -4
- package/dist/plugins/image/toolbar.svelte.js +21 -31
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/list.svelte.d.ts +9 -0
- package/dist/plugins/list.svelte.js +18 -0
- package/dist/plugins/marquee/marqueePlugin.js +0 -4
- package/dist/plugins/media/AudioPlugin.svelte +1 -1
- package/dist/plugins/media/BookmarkPlugin.svelte +6 -97
- package/dist/plugins/media/EmbedPlugin.svelte +5 -90
- package/dist/plugins/media/EmptySlot.svelte +36 -20
- package/dist/plugins/media/EmptySlot.svelte.d.ts +6 -15
- package/dist/plugins/media/FilePlugin.svelte +1 -19
- package/dist/plugins/media/MediaEmpty.svelte +2 -55
- package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
- package/dist/plugins/media/SourceBlock.svelte +142 -52
- package/dist/plugins/media/SourceBlock.svelte.d.ts +10 -4
- package/dist/plugins/media/VideoPlugin.svelte +1 -1
- package/dist/plugins/media/empty.svelte.js +2 -7
- package/dist/plugins/media/media.d.ts +2 -63
- package/dist/plugins/media/media.js +1 -194
- package/dist/plugins/media/sources.d.ts +60 -0
- package/dist/plugins/media/sources.js +165 -0
- package/dist/plugins/media/urlPaste.svelte.d.ts +2 -1
- package/dist/plugins/media/urlPaste.svelte.js +12 -21
- package/dist/plugins/mention/MentionPlugin.svelte +8 -35
- package/dist/plugins/page/PagePlugin.svelte +2 -9
- package/dist/plugins/page/page.js +1 -1
- package/dist/plugins/pageLink/PageLinkPlugin.svelte +6 -13
- package/dist/plugins/richtext/RichTextPlugin.svelte +85 -197
- package/dist/plugins/richtext/body.js +1 -1
- package/dist/plugins/richtext/calloutIcons.svelte.d.ts +2 -2
- package/dist/plugins/richtext/calloutIcons.svelte.js +5 -7
- package/dist/plugins/richtext/richTextOperations.d.ts +1 -19
- package/dist/plugins/richtext/richTextOperations.js +3 -49
- package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +2 -0
- package/dist/plugins/slashMenu/SlashMenuController.svelte.js +7 -0
- package/dist/plugins/slashMenu/slashMenuPlugin.js +5 -14
- package/dist/plugins/table/TableChrome.svelte +2 -3
- package/dist/plugins/table/TablePlugin.svelte +20 -54
- package/dist/plugins/table/chrome.svelte.d.ts +5 -2
- package/dist/plugins/table/chrome.svelte.js +30 -54
- package/dist/plugins/table/table.js +3 -3
- package/dist/plugins/toc/TocPlugin.svelte +2 -4
- package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +7 -0
- package/dist/plugins/toolbar/ToolbarController.svelte.js +20 -13
- package/dist/plugins/toolbar/toolbarPlugin.js +19 -53
- package/dist/plugins/triggers/TriggerController.svelte.d.ts +7 -2
- package/dist/plugins/triggers/TriggerController.svelte.js +11 -4
- package/dist/plugins/triggers/place.js +1 -2
- package/dist/plugins/triggers/triggers.d.ts +6 -1
- package/dist/plugins/triggers/triggers.js +12 -13
- package/dist/plugins/uploads.svelte.d.ts +5 -2
- package/dist/plugins/uploads.svelte.js +25 -15
- package/dist/plugins.d.ts +26 -0
- package/dist/plugins.js +0 -24
- package/dist/selection/replaceSelection.js +2 -2
- package/dist/selection/selection.svelte.d.ts +4 -10
- package/dist/selection/selection.svelte.js +7 -11
- package/dist/session/bindings.js +1 -1
- package/dist/session/commands.d.ts +7 -11
- package/dist/session/commands.js +13 -19
- package/dist/session/inputRules.js +1 -1
- package/dist/session/navigation.js +1 -1
- package/dist/session/suggestions.svelte.d.ts +0 -11
- package/dist/session/suggestions.svelte.js +15 -3
- package/dist/session/uploads.d.ts +8 -8
- package/dist/surface/lines.js +1 -3
- package/dist/surface/observer.svelte.d.ts +17 -1
- package/dist/surface/observer.svelte.js +72 -17
- package/dist/surface/overlay.d.ts +0 -14
- package/dist/surface/overlay.js +0 -25
- package/dist/surface/projector.svelte.js +1 -1
- package/dist/text/text.svelte.js +3 -13
- package/dist/utils/sanitize.d.ts +38 -0
- package/dist/utils/sanitize.js +82 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +5 -0
- package/package.json +18 -1
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +0 -3
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +0 -16
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +0 -3
- package/dist/plugins/code/LanguageMenu.svelte +0 -25
- package/dist/plugins/code/LanguageMenu.svelte.d.ts +0 -9
- package/dist/plugins/equation/EquationEditor.svelte +0 -27
- package/dist/plugins/equation/EquationEditor.svelte.d.ts +0 -9
- package/dist/plugins/image/ImageToolbarHost.svelte +0 -37
- package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +0 -9
- package/dist/plugins/richtext/CalloutIconPicker.svelte +0 -26
- package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +0 -9
|
@@ -72,6 +72,13 @@ export class SlashMenuController extends TextTriggerController {
|
|
|
72
72
|
constructor(edytor) {
|
|
73
73
|
super(edytor, '/', 'slash-menu');
|
|
74
74
|
}
|
|
75
|
+
/** A `+`'s menu stands under the `+` (its block while the `+` is gone); a `/` menu at the caret. */
|
|
76
|
+
anchorRect() {
|
|
77
|
+
const { addition } = this;
|
|
78
|
+
const anchor = addition?.anchor?.isConnected ? addition.anchor : addition?.block.node;
|
|
79
|
+
const box = anchor?.getBoundingClientRect();
|
|
80
|
+
return box && (box.width || box.height) ? box : super.anchorRect();
|
|
81
|
+
}
|
|
75
82
|
/** A `+`'s menu names its highlighted row from its own field, and tells the `+` it opened it. */
|
|
76
83
|
popupOf(active) {
|
|
77
84
|
const { addition } = this;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { caretRect } from '../triggers/place.js';
|
|
2
|
-
import { placeNear } from '../../surface/overlay.js';
|
|
3
1
|
import { SlashMenuController } from './SlashMenuController.svelte.js';
|
|
4
2
|
import { handleMenuEvents, mountChrome } from '../chrome.js';
|
|
5
3
|
import { withTriggerMenus } from '../triggers/triggers.js';
|
|
@@ -7,21 +5,14 @@ import { withTriggerMenus } from '../triggers/triggers.js';
|
|
|
7
5
|
export const createSlashMenuPlugin = (options = {}) => (edytor) => {
|
|
8
6
|
const controller = new SlashMenuController(edytor);
|
|
9
7
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* Measured in the overlay's frame, written after: the close of a `+`
|
|
9
|
+
* menu whose block is gone (a peer's or a command's delete), as the
|
|
10
|
+
* block menu does. The menu places itself by `controller.anchor`.
|
|
13
11
|
*/
|
|
14
|
-
const positionMenu = (
|
|
15
|
-
if (!edytor.node || !controller.isOpen)
|
|
16
|
-
return;
|
|
12
|
+
const positionMenu = () => {
|
|
17
13
|
const { addition } = controller;
|
|
18
|
-
|
|
19
|
-
if (addition && !addition.block.isInTree)
|
|
14
|
+
if (controller.isOpen && addition && !addition.block.isInTree)
|
|
20
15
|
return () => controller.dismiss();
|
|
21
|
-
const anchor = addition?.anchor?.isConnected ? addition.anchor : addition?.block.node;
|
|
22
|
-
const box = anchor?.getBoundingClientRect();
|
|
23
|
-
const rect = box && (box.width || box.height) ? box : caretRect(edytor);
|
|
24
|
-
return rect && placeNear(host, rect, [310, 350]);
|
|
25
16
|
};
|
|
26
17
|
const plugin = {
|
|
27
18
|
onEdytorAttached: ({ node }) => handleMenuEvents(edytor, node,
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
const labels = $derived(labelsOf(chrome.edytor, 'table', tableEnglish));
|
|
30
30
|
const layout = $derived(chrome.layout);
|
|
31
31
|
const line = $derived(chrome.line);
|
|
32
|
-
const menuPlace = $derived(chrome.menuPlace);
|
|
33
32
|
|
|
34
33
|
// The view turning readonly mid-drag drops the preview at once.
|
|
35
34
|
$effect(() => {
|
|
@@ -114,8 +113,8 @@
|
|
|
114
113
|
{/if}
|
|
115
114
|
{/each}
|
|
116
115
|
{/if}
|
|
117
|
-
{#if menu && chrome.menu
|
|
118
|
-
<div data-edytor-table-menu-host
|
|
116
|
+
{#if menu && chrome.menu}
|
|
117
|
+
<div data-edytor-table-menu-host>
|
|
119
118
|
{@render menu(chrome)}
|
|
120
119
|
</div>
|
|
121
120
|
{/if}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
+
import { tableForms } from '../../export/forms.js';
|
|
2
3
|
import './table.css';
|
|
3
4
|
import type { BlockSnippetPayload, BlockView, KindMenuAction, Plugin } from '../../plugins.js';
|
|
4
5
|
import type { Block } from '../../block/block.svelte.js';
|
|
5
6
|
import type { Edytor } from '../../edytor.svelte.js';
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
import { listedColumns, tableBlock, unlistedOrder } from '../../crdt/tables.js';
|
|
8
9
|
import { onPress } from '../../events/onFocus.js';
|
|
9
10
|
import { all } from '../../utils/listen.js';
|
|
@@ -161,6 +162,8 @@
|
|
|
161
162
|
...switches
|
|
162
163
|
];
|
|
163
164
|
};
|
|
165
|
+
/** The kinds' export and import forms. */
|
|
166
|
+
const forms = tableForms();
|
|
164
167
|
/** Tab and Shift+Tab (`step`) walk the cells, unless blocks are selected. */
|
|
165
168
|
const tab =
|
|
166
169
|
(step: 1 | -1): HotKey =>
|
|
@@ -181,7 +184,7 @@
|
|
|
181
184
|
},
|
|
182
185
|
blocks: {
|
|
183
186
|
table: {
|
|
184
|
-
...
|
|
187
|
+
...forms.table,
|
|
185
188
|
element: (data) => ({
|
|
186
189
|
tag: 'div',
|
|
187
190
|
attributes: {
|
|
@@ -208,26 +211,17 @@
|
|
|
208
211
|
group: 'Advanced blocks'
|
|
209
212
|
}
|
|
210
213
|
],
|
|
211
|
-
menu: menuOf
|
|
212
|
-
html: (block, _, children) => tableHtml(block.data ?? {}, children),
|
|
213
|
-
plain: (_, __, children) => children,
|
|
214
|
-
parse: (element) => (element.localName === 'table' ? parseTable(element) : undefined)
|
|
214
|
+
menu: menuOf
|
|
215
215
|
},
|
|
216
216
|
tableRow: {
|
|
217
|
-
...
|
|
217
|
+
...forms.tableRow,
|
|
218
218
|
element: { tag: 'div', attributes: { 'data-edytor-table-row': '', role: 'row' } },
|
|
219
|
-
snippet: row
|
|
220
|
-
html: (_, __, children) => `<tr>${children}</tr>`,
|
|
221
|
-
plain: (_, __, children) => children.split('\n').join('\t'),
|
|
222
|
-
parse: (element) => (element.localName === 'tr' ? {} : undefined)
|
|
219
|
+
snippet: row
|
|
223
220
|
},
|
|
224
221
|
tableCell: {
|
|
225
|
-
...
|
|
222
|
+
...forms.tableCell,
|
|
226
223
|
element: cellElement,
|
|
227
|
-
snippet: cell
|
|
228
|
-
html: (_, content) => `<td>${content}</td>`,
|
|
229
|
-
plain: (_, content) => content.replace(/\n/g, ' '),
|
|
230
|
-
parse: (element) => (/^t[dh]$/.test(element.localName) ? parseCell(element) : undefined)
|
|
224
|
+
snippet: cell
|
|
231
225
|
}
|
|
232
226
|
},
|
|
233
227
|
hotkeys: {
|
|
@@ -278,13 +272,21 @@
|
|
|
278
272
|
return plugin;
|
|
279
273
|
};
|
|
280
274
|
|
|
281
|
-
/**
|
|
275
|
+
/**
|
|
276
|
+
* `table`'s grid template: each column's width (the resize preview while a
|
|
277
|
+
* band drags it). In a suggestion's preview, the widths its data lists.
|
|
278
|
+
*/
|
|
282
279
|
const templateOf = (view: BlockView) => {
|
|
283
280
|
const edytor = view.handle?.edytor;
|
|
284
281
|
const chrome = edytor && chromes.get(edytor);
|
|
285
282
|
const listed = view.data.columns;
|
|
286
283
|
const fallback = chrome?.columnWidth ?? 120;
|
|
287
|
-
|
|
284
|
+
// A suggestion's preview has no handle (no document grid): its listed columns, in order.
|
|
285
|
+
const columns = edytor
|
|
286
|
+
? columnsOfTable(edytor, view.id)
|
|
287
|
+
: Array.isArray(listed)
|
|
288
|
+
? listed.map((c, i) => String((c as { id?: unknown } | null)?.id ?? i))
|
|
289
|
+
: [];
|
|
288
290
|
if (Array.isArray(listed)) {
|
|
289
291
|
const entry = (id: string) => listed.find((c) => (c as { id?: unknown } | null)?.id === id);
|
|
290
292
|
return columns
|
|
@@ -322,42 +324,6 @@
|
|
|
322
324
|
};
|
|
323
325
|
|
|
324
326
|
/** The table's HTML (`<table>`): its header row's and header column's cells are `th`. */
|
|
325
|
-
const tableHtml = (data: Record<string, unknown>, rows: string) => {
|
|
326
|
-
let html = rows;
|
|
327
|
-
if (data.headerColumn === true)
|
|
328
|
-
html = html.replace(/<tr><td>([\s\S]*?)<\/td>/g, '<tr><th>$1</th>');
|
|
329
|
-
if (data.headerRow === true)
|
|
330
|
-
html = html.replace(
|
|
331
|
-
/^<tr>([\s\S]*?)<\/tr>/,
|
|
332
|
-
(_, row: string) => `<tr>${row.replace(/<td>/g, '<th>').replace(/<\/td>/g, '</th>')}</tr>`
|
|
333
|
-
);
|
|
334
|
-
return `<table><tbody>${html}</tbody></table>`;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
/** The rows of an imported `<table>` (its own, not a nested table's). */
|
|
338
|
-
const rowsOf = (table: HTMLElement): HTMLTableRowElement[] =>
|
|
339
|
-
[...table.querySelectorAll('tr')].filter((tr) => tr.closest('table') === table);
|
|
340
|
-
|
|
341
|
-
/** An imported `<table>`'s data: a column per cell of its widest row, its header flags. */
|
|
342
|
-
const parseTable = (table: HTMLElement) => {
|
|
343
|
-
const rows = rowsOf(table);
|
|
344
|
-
const width = Math.max(1, ...rows.map((tr) => tr.cells.length));
|
|
345
|
-
const head = (cell: Element | undefined) => cell?.localName === 'th';
|
|
346
|
-
const headerRow = rows.length > 1 && [...(rows[0]?.cells ?? [])].every((c) => head(c));
|
|
347
|
-
const headerColumn =
|
|
348
|
-
rows.length > 0 && rows.slice(headerRow ? 1 : 0).every((tr) => head(tr.cells[0]));
|
|
349
|
-
return {
|
|
350
|
-
columns: Array.from({ length: width }, (_, i) => ({ id: `c${i + 1}` })),
|
|
351
|
-
...(headerRow && { headerRow: true }),
|
|
352
|
-
...(headerColumn && !headerRow && { headerColumn: true })
|
|
353
|
-
};
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
/** An imported cell's column: its position in its row (`c1`, `c2`, … as its table's). */
|
|
357
|
-
const parseCell = (cell: HTMLElement) => {
|
|
358
|
-
const index = (cell as HTMLTableCellElement).cellIndex;
|
|
359
|
-
return { column: `c${(Number.isInteger(index) && index >= 0 ? index : 0) + 1}` };
|
|
360
|
-
};
|
|
361
327
|
</script>
|
|
362
328
|
|
|
363
329
|
<!--
|
|
@@ -95,8 +95,11 @@ export declare class TableChrome extends ChromeList {
|
|
|
95
95
|
/** @internal */
|
|
96
96
|
minColumnWidth: number);
|
|
97
97
|
protected get count(): number;
|
|
98
|
-
/**
|
|
99
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Where the open menu stands (`ChromeAnchor`): its grip, which your
|
|
100
|
+
* `menu` places itself under.
|
|
101
|
+
*/
|
|
102
|
+
readonly anchor: import("../chrome.js").ChromeAnchor;
|
|
100
103
|
/**
|
|
101
104
|
* Open the `kind` grip's menu on the row or column it stands for, its
|
|
102
105
|
* first row that can run the keyboard's, or close it when it is open
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { untrack } from 'svelte';
|
|
2
2
|
import { autoScrollFor } from '../../dnd/autoScroll.js';
|
|
3
|
-
import {
|
|
4
|
-
import { EDYTOR_BLOCK_DRAG_MIME } from '../../events/onDrop.js';
|
|
3
|
+
import { dropTargetForElements, elementScrollers } from '../../dnd/pragmatic.js';
|
|
5
4
|
import { takeKeys } from '../../events/onFocus.js';
|
|
6
5
|
import { pin } from '../../surface/overlay.js';
|
|
7
|
-
import { attribute } from '../chrome.js';
|
|
8
|
-
import { ChromeList, holdKeys, menuKey, optionAttachment } from '../list.svelte.js';
|
|
6
|
+
import { attribute, virtualAnchor } from '../chrome.js';
|
|
7
|
+
import { ChromeList, holdKeys, keysOf, menuKey, optionAttachment } from '../list.svelte.js';
|
|
9
8
|
import { BandDrag, bandKey } from '../bands.svelte.js';
|
|
10
9
|
import { all, listen } from '../../utils/listen.js';
|
|
11
|
-
import {
|
|
10
|
+
import { dragSource, ghost } from '../blockHandles/dragPreview.js';
|
|
12
11
|
import { caretCell, deleteColumn, deleteRows, gridOf, insertColumn, insertRow, moveColumn, moveRows, widthOf } from './table.js';
|
|
13
12
|
/** The drag data key naming the chrome a row's or a column's drag is from. */
|
|
14
13
|
const MOVE = Symbol('edytor table move');
|
|
@@ -107,10 +106,6 @@ export class TableChrome extends ChromeList {
|
|
|
107
106
|
get count() {
|
|
108
107
|
return this.#rows.length;
|
|
109
108
|
}
|
|
110
|
-
/** The view is readonly (reactive): the chrome shows nothing. */
|
|
111
|
-
get readonly() {
|
|
112
|
-
return this.edytor.readonly;
|
|
113
|
-
}
|
|
114
109
|
/**
|
|
115
110
|
* The column resize in progress.
|
|
116
111
|
* @internal
|
|
@@ -238,22 +233,16 @@ export class TableChrome extends ChromeList {
|
|
|
238
233
|
];
|
|
239
234
|
}
|
|
240
235
|
/**
|
|
241
|
-
* Where the open menu stands:
|
|
242
|
-
*
|
|
236
|
+
* Where the open menu stands (`ChromeAnchor`): its grip, which your
|
|
237
|
+
* `menu` places itself under.
|
|
243
238
|
*/
|
|
244
|
-
|
|
239
|
+
anchor = virtualAnchor(() => this.#gripAnchor()?.getBoundingClientRect(), () => this.#gripAnchor());
|
|
240
|
+
/** The grip elements in the page, by kind. */
|
|
241
|
+
#gripNodes = {};
|
|
242
|
+
#gripAnchor = () => {
|
|
245
243
|
const kind = this.menu?.kind;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return null;
|
|
249
|
-
const { layout, index } = at;
|
|
250
|
-
if (kind === 'row') {
|
|
251
|
-
const row = layout.rows[index];
|
|
252
|
-
return { left: this.span.left - 9, top: row.top + Math.min(row.height, 28) };
|
|
253
|
-
}
|
|
254
|
-
const column = layout.columns[index];
|
|
255
|
-
return { left: column.left + column.width / 2 - 12, top: layout.top + 10 };
|
|
256
|
-
}
|
|
244
|
+
return kind ? this.#gripNodes[kind] : undefined;
|
|
245
|
+
};
|
|
257
246
|
/**
|
|
258
247
|
* The drop line of a grip's drag, layer-relative: between two rows
|
|
259
248
|
* (columns), where the drop would land.
|
|
@@ -550,43 +539,37 @@ export class TableChrome extends ChromeList {
|
|
|
550
539
|
#grips = {};
|
|
551
540
|
#grip = (node, kind) => {
|
|
552
541
|
node.setAttribute('aria-haspopup', 'menu');
|
|
542
|
+
this.#gripNodes[kind] = node;
|
|
553
543
|
$effect(() => {
|
|
554
544
|
const open = this.menu?.kind === kind;
|
|
555
545
|
attribute(node, 'aria-expanded', String(open));
|
|
556
546
|
attribute(node, 'aria-controls', open ? this.listId : undefined);
|
|
557
547
|
attribute(node, 'data-dragging', this.moving?.kind === kind ? 'true' : undefined);
|
|
558
548
|
});
|
|
559
|
-
const off = all(listen(node, 'click', () => this.toggle(kind)), listen(node, 'keydown', (
|
|
549
|
+
const off = all(listen(node, 'click', () => this.toggle(kind)), listen(node, 'keydown', keysOf({
|
|
560
550
|
// Escape gives the keys back to the editor (closing the menu if open).
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
this.toggle(kind);
|
|
565
|
-
else
|
|
566
|
-
return;
|
|
567
|
-
event.preventDefault();
|
|
568
|
-
event.stopPropagation();
|
|
569
|
-
}), draggable({
|
|
570
|
-
element: node,
|
|
551
|
+
Escape: this.close,
|
|
552
|
+
ArrowDown: () => this.menu?.kind !== kind && this.toggle(kind)
|
|
553
|
+
}, true)), dragSource(node, {
|
|
571
554
|
canDrag: () => !this.edytor.readonly && this.edytor.dispatcher.permits() && this.#source(kind) !== null,
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
getInitialDataForExternal: () => {
|
|
555
|
+
data: () => ({ [MOVE]: this }),
|
|
556
|
+
id: () => {
|
|
575
557
|
const source = this.#source(kind);
|
|
576
|
-
return
|
|
577
|
-
[EDYTOR_BLOCK_DRAG_MIME]: (kind === 'row' ? source?.id : source?.table.id) ?? ''
|
|
578
|
-
};
|
|
558
|
+
return (kind === 'row' ? source?.id : source?.table.id) ?? '';
|
|
579
559
|
},
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
onDrop: () => queueMicrotask(() => {
|
|
560
|
+
begin: (pointer, setImage) => this.#begin(kind, node, pointer, setImage),
|
|
561
|
+
end: () => {
|
|
583
562
|
for (const release of this.#releases.splice(0))
|
|
584
563
|
release();
|
|
585
564
|
this.moving = null;
|
|
586
565
|
this.edytor.overlay.invalidate();
|
|
587
|
-
}
|
|
566
|
+
}
|
|
588
567
|
}));
|
|
589
|
-
return
|
|
568
|
+
return () => {
|
|
569
|
+
off();
|
|
570
|
+
if (this.#gripNodes[kind] === node)
|
|
571
|
+
delete this.#gripNodes[kind];
|
|
572
|
+
};
|
|
590
573
|
};
|
|
591
574
|
/**
|
|
592
575
|
* The `+` under the table (`row`: a row at its end, the caret in its
|
|
@@ -675,14 +658,7 @@ export class TableChrome extends ChromeList {
|
|
|
675
658
|
this.moving = { kind, table: table.id, id, gap: null };
|
|
676
659
|
const widths = this.#widthsOf(table);
|
|
677
660
|
const cells = (kind === 'row' ? grid.rows[index].cells : grid.rows.map((r) => r.cells[index])).flatMap((cell) => (cell && this.edytor.idToBlock.get(cell)?.node) || []);
|
|
678
|
-
|
|
679
|
-
const preview = dragPreview(root, cells, pointer, kind === 'row' ? widths : [widths[index]]);
|
|
680
|
-
setCustomNativeDragPreview({
|
|
681
|
-
nativeSetDragImage,
|
|
682
|
-
getOffset: preview.offset,
|
|
683
|
-
render: ({ container }) => container.append(preview.element)
|
|
684
|
-
});
|
|
685
|
-
}
|
|
661
|
+
ghost(nativeSetDragImage, root, cells, pointer, kind === 'row' ? widths : [widths[index]]);
|
|
686
662
|
const ours = (data) => data[MOVE] === this;
|
|
687
663
|
// The page is the target: the line follows the pointer from the grips, outside the grid.
|
|
688
664
|
this.#releases.push(dropTargetForElements({
|
|
@@ -695,7 +671,7 @@ export class TableChrome extends ChromeList {
|
|
|
695
671
|
}));
|
|
696
672
|
const scroller = table.node?.querySelector('[data-edytor-table-scroll]');
|
|
697
673
|
if (scroller)
|
|
698
|
-
this.#releases.push(autoScrollFor(scroller, ours));
|
|
674
|
+
this.#releases.push(autoScrollFor(scroller, elementScrollers(ours)));
|
|
699
675
|
this.edytor.overlay.invalidate();
|
|
700
676
|
}
|
|
701
677
|
/**
|
|
@@ -45,7 +45,7 @@ export const insertRow = (table, index, caret = false) => {
|
|
|
45
45
|
const plan = run(table, 'insertTableRow', { index }, (p) => ops.insertTableRow(edytor.facade, table.id, p.index));
|
|
46
46
|
const row = plan ? (edytor.idToBlock.get(plan.ids[0]) ?? null) : null;
|
|
47
47
|
if (row && caret)
|
|
48
|
-
edytor.dispatcher.
|
|
48
|
+
edytor.dispatcher.caretIn(row.children[0]?.firstText, 0);
|
|
49
49
|
return row;
|
|
50
50
|
};
|
|
51
51
|
/** Delete `rows` with their cells (`table.delete-row`). */
|
|
@@ -61,7 +61,7 @@ export const insertColumn = (table, index, width, caret) => {
|
|
|
61
61
|
const at = Math.max(0, Math.min(Math.trunc(index) || 0, grid.columns.length - 1));
|
|
62
62
|
const text = cellAt(table, grid, caret, at)?.firstText;
|
|
63
63
|
if (text)
|
|
64
|
-
table.edytor.dispatcher.
|
|
64
|
+
table.edytor.dispatcher.caretIn(text, 0);
|
|
65
65
|
}
|
|
66
66
|
return applied;
|
|
67
67
|
};
|
|
@@ -80,7 +80,7 @@ export const fillCell = (table, row, column) => {
|
|
|
80
80
|
const plan = run(table, 'fillTableCell', { row, column }, (p) => ops.fillTableCell(edytor.facade, p.row, p.column));
|
|
81
81
|
const cell = plan ? (edytor.idToBlock.get(plan.ids[0]) ?? null) : null;
|
|
82
82
|
if (cell)
|
|
83
|
-
edytor.dispatcher.
|
|
83
|
+
edytor.dispatcher.caretIn(cell.firstText, 0);
|
|
84
84
|
return cell;
|
|
85
85
|
};
|
|
86
86
|
/** Toggle `flag` (`headerRow` or `headerColumn`) of `table`'s data (`table.header`). */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
+
import { tocForms } from '../../export/forms.js';
|
|
2
3
|
import type { Plugin, BlockSnippetPayload } from '../../plugins.js';
|
|
3
4
|
import { tocKinds } from '../../crdt/semantics.js';
|
|
4
5
|
import { tocEnglish, keywordsOf, labelsOf } from '../../labels.js';
|
|
@@ -53,11 +54,8 @@
|
|
|
53
54
|
group: 'Advanced blocks'
|
|
54
55
|
}
|
|
55
56
|
],
|
|
56
|
-
html: () => '<nav data-edytor-toc></nav>',
|
|
57
|
-
plain: () => '',
|
|
58
57
|
// Its own export: the list is drawn from the document, never carried.
|
|
59
|
-
|
|
60
|
-
el.localName === 'nav' && el.hasAttribute('data-edytor-toc') ? {} : undefined
|
|
58
|
+
...tocForms().toc
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
};
|
|
@@ -76,6 +76,13 @@ export declare class ToolbarController {
|
|
|
76
76
|
get card(): boolean;
|
|
77
77
|
/** The hovered link's URL, as stored (the card shows it). */
|
|
78
78
|
get hoveredHref(): string;
|
|
79
|
+
/**
|
|
80
|
+
* Where the bar stands (`ChromeAnchor`): the selection's box, which your
|
|
81
|
+
* `toolbar` places itself above (below when there is no room).
|
|
82
|
+
*/
|
|
83
|
+
readonly anchor: import("../chrome.js").ChromeAnchor;
|
|
84
|
+
/** Where the link card stands (`ChromeAnchor`): the hovered link, which your `card` places itself under. */
|
|
85
|
+
readonly cardAnchor: import("../chrome.js").ChromeAnchor;
|
|
79
86
|
/** Show the card for `anchor`, or hide it (`null`). */
|
|
80
87
|
hover(anchor: HTMLAnchorElement | null): void;
|
|
81
88
|
/** The card's Open: the hovered link in a new tab. */
|
|
@@ -4,9 +4,10 @@ import { selectedTextSpans } from '../../selection/visibility.js';
|
|
|
4
4
|
import { getYIndex, SUGGESTION, SYNTHETIC_TEXT_OVERLAY_SELECTOR } from '../../selection/selection.utils.js';
|
|
5
5
|
import { convertBlocks, convertibleKinds, rowOf } from '../../kinds.js';
|
|
6
6
|
import { getSelectionBlocks } from '../../selection/replaceSelection.js';
|
|
7
|
-
import { identify } from '../chrome.js';
|
|
7
|
+
import { identify, virtualAnchor } from '../chrome.js';
|
|
8
8
|
import { all, listen } from '../../utils/listen.js';
|
|
9
9
|
import { takeKeys } from '../../events/onFocus.js';
|
|
10
|
+
import { keysOf } from '../list.svelte.js';
|
|
10
11
|
/** The bar's controls the arrows walk (a disabled one is skipped). */
|
|
11
12
|
const STOPS = 'button:not([disabled]), input:not([disabled]), select:not([disabled])';
|
|
12
13
|
const getLinkHref = (value) => isRecord(value) && typeof value.href === 'string' ? value.href : null;
|
|
@@ -113,20 +114,13 @@ export class ToolbarController {
|
|
|
113
114
|
node.id = this.linkFieldId;
|
|
114
115
|
if (this.takeFieldFocus())
|
|
115
116
|
node.focus({ preventScroll: true });
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return;
|
|
119
|
-
if (event.key === 'Enter') {
|
|
120
|
-
event.preventDefault();
|
|
117
|
+
return listen(node, 'keydown', keysOf({
|
|
118
|
+
Enter: () => {
|
|
121
119
|
this.applyLink();
|
|
122
120
|
this.closePanel(true);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.closePanel(true);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
return listen(node, 'keydown', keydown);
|
|
121
|
+
},
|
|
122
|
+
Escape: () => this.closePanel(true)
|
|
123
|
+
}));
|
|
130
124
|
};
|
|
131
125
|
/** Give the keyboard back to the editor (Escape in the bar), over the held selection. */
|
|
132
126
|
release() {
|
|
@@ -232,6 +226,19 @@ export class ToolbarController {
|
|
|
232
226
|
get hoveredHref() {
|
|
233
227
|
return this.hovered?.getAttribute('href') ?? '';
|
|
234
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Where the bar stands (`ChromeAnchor`): the selection's box, which your
|
|
231
|
+
* `toolbar` places itself above (below when there is no room).
|
|
232
|
+
*/
|
|
233
|
+
anchor = virtualAnchor(() => {
|
|
234
|
+
const editor = this.edytor.node;
|
|
235
|
+
const selection = editor?.ownerDocument.getSelection();
|
|
236
|
+
if (!selection?.rangeCount || !editor?.contains(selection.anchorNode))
|
|
237
|
+
return;
|
|
238
|
+
return selection.getRangeAt(0).getBoundingClientRect();
|
|
239
|
+
}, () => this.edytor.node);
|
|
240
|
+
/** Where the link card stands (`ChromeAnchor`): the hovered link, which your `card` places itself under. */
|
|
241
|
+
cardAnchor = virtualAnchor(() => this.hovered?.getBoundingClientRect(), () => this.hovered);
|
|
235
242
|
/** Show the card for `anchor`, or hide it (`null`). */
|
|
236
243
|
hover(anchor) {
|
|
237
244
|
if (this.hovered === anchor)
|
|
@@ -1,42 +1,14 @@
|
|
|
1
1
|
import { ToolbarController } from './ToolbarController.svelte.js';
|
|
2
|
-
import { mountChrome } from '../chrome.js';
|
|
3
|
-
import { placeNear } from '../../surface/overlay.js';
|
|
2
|
+
import { keepFocus, mountChrome } from '../chrome.js';
|
|
4
3
|
import { all, listen } from '../../utils/listen.js';
|
|
5
|
-
/** A press in a field of the chrome (an input, a select): it takes focus. */
|
|
6
|
-
const isNativeFieldEvent = (event) => event
|
|
7
|
-
.composedPath()
|
|
8
|
-
.some((target) => target instanceof Element && target.matches('input, textarea, select'));
|
|
9
4
|
/** The selection toolbar, drawn by your `toolbar` snippet, and the link card by your `card`. */
|
|
10
5
|
export const createToolbarPlugin = (options = {}) => (edytor) => {
|
|
11
6
|
const controller = new ToolbarController(edytor);
|
|
12
|
-
/**
|
|
13
|
-
const
|
|
14
|
-
const editor = edytor.node;
|
|
15
|
-
if (!editor || !controller.isVisible)
|
|
16
|
-
return;
|
|
17
|
-
const selection = editor.ownerDocument.getSelection();
|
|
18
|
-
if (!selection?.rangeCount || !editor.contains(selection.anchorNode))
|
|
19
|
-
return;
|
|
20
|
-
const range = selection.getRangeAt(0);
|
|
21
|
-
if (typeof range.getBoundingClientRect !== 'function')
|
|
22
|
-
return;
|
|
23
|
-
return placeNear(host, range.getBoundingClientRect(), [460, 40], {
|
|
24
|
-
above: true,
|
|
25
|
-
center: true,
|
|
26
|
-
// The bar, not its open panel: panels drop over the text below it.
|
|
27
|
-
box: host.querySelector('[data-edytor-toolbar-bar]') ?? host.firstElementChild
|
|
28
|
-
});
|
|
29
|
-
};
|
|
30
|
-
/** Just below the hovered link, flush with it (the host's top padding bridges the two). */
|
|
31
|
-
const positionCard = (host) => {
|
|
7
|
+
/** A hovered link that left the page hides its card (measured in the overlay's frame). */
|
|
8
|
+
const positionCard = () => {
|
|
32
9
|
const anchor = controller.hovered;
|
|
33
|
-
if (
|
|
34
|
-
return;
|
|
35
|
-
if (!anchor.isConnected)
|
|
10
|
+
if (anchor && controller.card && !anchor.isConnected)
|
|
36
11
|
return () => controller.hover(null);
|
|
37
|
-
const rect = anchor.getBoundingClientRect();
|
|
38
|
-
const [left, top] = [`${Math.max(8, rect.left)}px`, `${rect.bottom}px`];
|
|
39
|
-
return () => Object.assign(host.style, { left, top, paddingTop: '4px' });
|
|
40
12
|
};
|
|
41
13
|
/**
|
|
42
14
|
* The link card follows the pointer: over a link in the host it shows,
|
|
@@ -97,32 +69,26 @@ export const createToolbarPlugin = (options = {}) => (edytor) => {
|
|
|
97
69
|
edytor.overlay.invalidate();
|
|
98
70
|
},
|
|
99
71
|
onEdytorAttached: () => {
|
|
100
|
-
const offs = [];
|
|
101
72
|
// No press on the chrome takes focus, its background and a custom
|
|
102
73
|
// snippet's markup included (not only its buttons): the editor keeps
|
|
103
74
|
// its focus and its selection. A field (the link panel's input) takes
|
|
104
75
|
// its own. Every press has a `mousedown`, WebKit's lone one too
|
|
105
76
|
// (`onFocus.ts`), so no `pointerdown` is cancelled.
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
offs.push(() => controller.hover(null));
|
|
122
|
-
return () => {
|
|
123
|
-
for (const off of offs.reverse())
|
|
124
|
-
off();
|
|
125
|
-
};
|
|
77
|
+
const host = (name) => edytor.overlay.layer?.querySelector(`[data-${name}]`);
|
|
78
|
+
const offs = [
|
|
79
|
+
mountChrome(edytor, options.toolbar, controller, () => controller.isVisible, {
|
|
80
|
+
name: 'edytor-toolbar-host',
|
|
81
|
+
zIndex: 60
|
|
82
|
+
}),
|
|
83
|
+
mountChrome(edytor, options.card, controller, () => controller.card, {
|
|
84
|
+
name: 'edytor-link-card-host',
|
|
85
|
+
zIndex: 61,
|
|
86
|
+
measure: positionCard
|
|
87
|
+
})
|
|
88
|
+
];
|
|
89
|
+
const bar = options.toolbar && host('edytor-toolbar-host');
|
|
90
|
+
const card = options.card && host('edytor-link-card-host');
|
|
91
|
+
return all(() => controller.hover(null), edytor.node && card ? trackLinks(edytor.node, card) : () => { }, listen(card, 'mousedown', keepFocus), listen(bar, 'mousedown', keepFocus), ...offs.reverse());
|
|
126
92
|
}
|
|
127
93
|
};
|
|
128
94
|
};
|
|
@@ -34,8 +34,13 @@ export declare abstract class TextTriggerController extends ChromeList {
|
|
|
34
34
|
char: string,
|
|
35
35
|
/** The popup's owner name (`edytor.popups`), unique per view. */
|
|
36
36
|
owner: string);
|
|
37
|
-
/**
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Where the menu stands (`ChromeAnchor`): the caret, which your `menu`
|
|
39
|
+
* places itself under (above it when there is no room).
|
|
40
|
+
*/
|
|
41
|
+
readonly anchor: import("../chrome.js").ChromeAnchor;
|
|
42
|
+
/** The anchor's box, read now: the caret's. */
|
|
43
|
+
protected anchorRect(): DOMRect | undefined;
|
|
39
44
|
/** The record `popup` publishes, `active` the highlighted row's id when it is in the page. */
|
|
40
45
|
protected popupOf(active: string | undefined): Popup;
|
|
41
46
|
/** Whether a trigger typed in `block` opens the menu. */
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { inCodeLines } from '../../session/inputRules.js';
|
|
2
2
|
import { ChromeList, inPage } from '../list.svelte.js';
|
|
3
|
+
import { virtualAnchor } from '../chrome.js';
|
|
4
|
+
import { caretRect } from './place.js';
|
|
3
5
|
/** A trigger opens at a text's start or after whitespace, never inside a word (`1/2`, `a@b`). */
|
|
4
6
|
const startsTrigger = (text, offset) => offset === 0 || /\s/.test(text.stringContent[offset - 1] ?? '');
|
|
5
7
|
/** What `items` and `onPick` read: the trigger's block, its range in block offsets, the query. */
|
|
@@ -30,9 +32,14 @@ export class TextTriggerController extends ChromeList {
|
|
|
30
32
|
super(edytor, owner, 'listbox', (node) => this.isOpen && this.popupOf(inPage(node, this.rowId(this.index))));
|
|
31
33
|
this.char = char;
|
|
32
34
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Where the menu stands (`ChromeAnchor`): the caret, which your `menu`
|
|
37
|
+
* places itself under (above it when there is no room).
|
|
38
|
+
*/
|
|
39
|
+
anchor = virtualAnchor(() => this.anchorRect(), () => this.edytor.node);
|
|
40
|
+
/** The anchor's box, read now: the caret's. */
|
|
41
|
+
anchorRect() {
|
|
42
|
+
return caretRect(this.edytor);
|
|
36
43
|
}
|
|
37
44
|
/** The record `popup` publishes, `active` the highlighted row's id when it is in the page. */
|
|
38
45
|
popupOf(active) {
|
|
@@ -139,7 +146,7 @@ export class TextTriggerController extends ChromeList {
|
|
|
139
146
|
const kept = run.out === false || (run.taken && dispatcher.last?.status === 'refused');
|
|
140
147
|
const out = await run.out;
|
|
141
148
|
if (owns(text, held))
|
|
142
|
-
dispatcher.
|
|
149
|
+
dispatcher.caretIn(text, kept ? end : triggerStart);
|
|
143
150
|
return out;
|
|
144
151
|
}
|
|
145
152
|
finally {
|
|
@@ -11,8 +11,7 @@ export const caretRect = (edytor) => {
|
|
|
11
11
|
if (selection?.rangeCount && editor.contains(selection.anchorNode)) {
|
|
12
12
|
const range = selection.getRangeAt(0).cloneRange();
|
|
13
13
|
range.collapse(false);
|
|
14
|
-
|
|
15
|
-
rect = range.getBoundingClientRect();
|
|
14
|
+
rect = range.getBoundingClientRect();
|
|
16
15
|
}
|
|
17
16
|
if (!rect || (!rect.width && !rect.height)) {
|
|
18
17
|
rect = edytor.selection.textRange().startText?.node?.getBoundingClientRect();
|