svedit 0.7.1 → 0.9.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 +115 -2
- package/dist/Node.svelte +4 -25
- package/dist/Node.svelte.d.ts.map +1 -1
- package/dist/NodeArrayProperty.svelte +17 -3
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/NodeGap.svelte +64 -27
- package/dist/NodeGap.svelte.d.ts +6 -6
- package/dist/NodeGap.svelte.d.ts.map +1 -1
- package/dist/NodeGapMarkers.svelte +36 -10
- package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
- package/dist/Svedit.svelte +102 -119
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/node_gap_computation.svelte.js +1 -1
- package/package.json +1 -1
- package/src/lib/Node.svelte +4 -25
- package/src/lib/NodeArrayProperty.svelte +17 -3
- package/src/lib/NodeGap.svelte +64 -27
- package/src/lib/NodeGapMarkers.svelte +36 -10
- package/src/lib/Svedit.svelte +102 -119
- package/src/lib/node_gap_computation.svelte.js +1 -1
|
@@ -66,6 +66,12 @@
|
|
|
66
66
|
* --node-caret-blink-duration
|
|
67
67
|
* --node-caret-animation
|
|
68
68
|
* --node-caret-row-inline-position
|
|
69
|
+
* --node-caret-boundary (anchor-name of a parent element; edge
|
|
70
|
+
* gaps clamp to its edges instead of 0px.
|
|
71
|
+
* Prevents overlap when the node array has
|
|
72
|
+
* neighbors. See NodeGap.svelte for details.)
|
|
73
|
+
* --node-caret-boundary-x (per-axis override; clamps left/right only)
|
|
74
|
+
* --node-caret-boundary-y (per-axis override; clamps top/bottom only)
|
|
69
75
|
*
|
|
70
76
|
* Row/column detection uses var(--row, 1) with the * 99999 multiplier
|
|
71
77
|
* trick throughout. Shorthand:
|
|
@@ -262,9 +268,11 @@
|
|
|
262
268
|
|
|
263
269
|
/* Edge first: column = above first node, row = left of first node */
|
|
264
270
|
.gap-edge.first {
|
|
271
|
+
--_b-t: anchor(var(--node-caret-boundary-y, var(--node-caret-boundary, --_no-boundary)) top, 0px);
|
|
272
|
+
--_b-l: anchor(var(--node-caret-boundary-x, var(--node-caret-boundary, --_no-boundary)) left, 0px);
|
|
265
273
|
top: min(
|
|
266
274
|
calc(anchor(var(--_a) top) + var(--_C) * 99999px),
|
|
267
|
-
calc(max(
|
|
275
|
+
calc(max(var(--_b-t), calc(anchor(var(--_a) top) - var(--_gm))) + var(--_R) * 99999px)
|
|
268
276
|
);
|
|
269
277
|
bottom: min(
|
|
270
278
|
calc(anchor(var(--_a) bottom) + var(--_C) * 99999px),
|
|
@@ -272,7 +280,7 @@
|
|
|
272
280
|
);
|
|
273
281
|
left: min(
|
|
274
282
|
calc(anchor(var(--_a) left) + var(--_R) * 99999px),
|
|
275
|
-
calc(max(
|
|
283
|
+
calc(max(var(--_b-l), calc(anchor(var(--_a) left) - var(--_gm))) + var(--_C) * 99999px)
|
|
276
284
|
);
|
|
277
285
|
right: min(
|
|
278
286
|
calc(anchor(var(--_a) right) + var(--_R) * 99999px),
|
|
@@ -280,23 +288,40 @@
|
|
|
280
288
|
);
|
|
281
289
|
}
|
|
282
290
|
|
|
283
|
-
/* Edge last: column = below last node, row = right of last node
|
|
291
|
+
/* Edge last: column = below last node, row = right of last node.
|
|
292
|
+
top/left also clamp to boundary - gm so that min-height/min-width
|
|
293
|
+
(which win over bottom/right in overconstrained abs-pos) cannot
|
|
294
|
+
push the element past the boundary. */
|
|
284
295
|
.gap-edge.last {
|
|
296
|
+
--_b-b: anchor(var(--node-caret-boundary-y, var(--node-caret-boundary, --_no-boundary)) bottom, 0px);
|
|
297
|
+
--_b-r: anchor(var(--node-caret-boundary-x, var(--node-caret-boundary, --_no-boundary)) right, 0px);
|
|
298
|
+
--_b-bt: anchor(var(--node-caret-boundary-y, var(--node-caret-boundary, --_no-boundary)) bottom, 99999px);
|
|
299
|
+
--_b-rl: anchor(var(--node-caret-boundary-x, var(--node-caret-boundary, --_no-boundary)) right, 99999px);
|
|
285
300
|
top: min(
|
|
286
301
|
calc(anchor(var(--_a) top) + var(--_C) * 99999px),
|
|
287
|
-
calc(
|
|
302
|
+
calc(
|
|
303
|
+
min(
|
|
304
|
+
anchor(var(--_a) bottom),
|
|
305
|
+
calc(var(--_b-bt) - var(--_gm))
|
|
306
|
+
) + var(--_R) * 99999px
|
|
307
|
+
)
|
|
288
308
|
);
|
|
289
309
|
bottom: min(
|
|
290
310
|
calc(anchor(var(--_a) bottom) + var(--_C) * 99999px),
|
|
291
|
-
calc(anchor(var(--_a) bottom) - var(--_gm) + var(--_R) * 99999px)
|
|
311
|
+
calc(max(var(--_b-b), calc(anchor(var(--_a) bottom) - var(--_gm))) + var(--_R) * 99999px)
|
|
292
312
|
);
|
|
293
313
|
left: min(
|
|
294
314
|
calc(anchor(var(--_a) left) + var(--_R) * 99999px),
|
|
295
|
-
calc(
|
|
315
|
+
calc(
|
|
316
|
+
min(
|
|
317
|
+
anchor(var(--_a) right),
|
|
318
|
+
calc(var(--_b-rl) - var(--_gm))
|
|
319
|
+
) + var(--_C) * 99999px
|
|
320
|
+
),
|
|
296
321
|
calc(100% - var(--_gm) + var(--_C) * 99999px)
|
|
297
322
|
);
|
|
298
323
|
right: max(
|
|
299
|
-
calc(
|
|
324
|
+
calc(var(--_b-r) + var(--_C) * -99999px),
|
|
300
325
|
calc(anchor(var(--_a) right) + var(--_R) * -99999px),
|
|
301
326
|
calc(anchor(var(--_a) right) - var(--_gm) + var(--_C) * -99999px),
|
|
302
327
|
calc(anchor(var(--_c) right) + var(--_C) * -99999px)
|
|
@@ -314,6 +339,7 @@
|
|
|
314
339
|
left: min(
|
|
315
340
|
calc(anchor(var(--_a) left) + var(--_R) * 99999px),
|
|
316
341
|
calc(anchor(var(--_a) right) + var(--_C) * 99999px),
|
|
342
|
+
calc(var(--_b-rl) - var(--_gm) + var(--_C) * 99999px),
|
|
317
343
|
calc(
|
|
318
344
|
anchor(var(--_a) right)
|
|
319
345
|
+ (max(0px, anchor(var(--_s) left) - anchor(var(--_f) right))) / 2
|
|
@@ -341,7 +367,7 @@
|
|
|
341
367
|
);
|
|
342
368
|
right: max(
|
|
343
369
|
calc(anchor(var(--_a) right) + var(--_R) * -99999px),
|
|
344
|
-
calc(
|
|
370
|
+
calc(var(--_b-r) + var(--_C) * -99999px),
|
|
345
371
|
calc(
|
|
346
372
|
anchor(var(--_a) right)
|
|
347
373
|
- (
|
|
@@ -450,7 +476,7 @@
|
|
|
450
476
|
outline: 0.1px solid green;
|
|
451
477
|
}
|
|
452
478
|
.gap-marker {
|
|
453
|
-
outline:
|
|
479
|
+
outline: 1px solid blue;
|
|
454
480
|
outline-offset: 0.5px;
|
|
455
481
|
} */
|
|
456
|
-
</style>
|
|
482
|
+
</style>
|
package/src/lib/Svedit.svelte
CHANGED
|
@@ -145,14 +145,14 @@
|
|
|
145
145
|
if (
|
|
146
146
|
['deleteContentBackward', 'deleteWordBackward', 'deleteContent'].includes(event.inputType)
|
|
147
147
|
) {
|
|
148
|
-
|
|
148
|
+
delete_at_selection('backward');
|
|
149
149
|
event.preventDefault();
|
|
150
150
|
event.stopPropagation();
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
if (['deleteContentForward', 'deleteWordForward'].includes(event.inputType)) {
|
|
155
|
-
|
|
155
|
+
delete_at_selection('forward');
|
|
156
156
|
event.preventDefault();
|
|
157
157
|
event.stopPropagation();
|
|
158
158
|
return;
|
|
@@ -269,6 +269,10 @@
|
|
|
269
269
|
if (!canvas_el?.contains(range.commonAncestorContainer)) return;
|
|
270
270
|
let selection = __get_selection_from_dom();
|
|
271
271
|
if (selection) {
|
|
272
|
+
// Avoid assigning a new object reference when the selection is
|
|
273
|
+
// structurally identical — prevents a redundant $effect cycle
|
|
274
|
+
// (render_selection → scrollIntoView) on every DOM layout change.
|
|
275
|
+
if (JSON.stringify(selection) === JSON.stringify(session.selection)) return;
|
|
272
276
|
session.selection = selection;
|
|
273
277
|
}
|
|
274
278
|
}
|
|
@@ -490,7 +494,24 @@ ${fallback_html}`;
|
|
|
490
494
|
}
|
|
491
495
|
|
|
492
496
|
if (delete_selection) {
|
|
493
|
-
|
|
497
|
+
delete_at_selection();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Shared delete logic for cut, backspace, and forward-delete.
|
|
503
|
+
* For property selections, delegates to the config's handle_property_deletion hook.
|
|
504
|
+
* For text/node selections, uses the standard transaction delete_selection.
|
|
505
|
+
*
|
|
506
|
+
* @param {'backward' | 'forward'} [direction]
|
|
507
|
+
*/
|
|
508
|
+
function delete_at_selection(direction = 'backward') {
|
|
509
|
+
if (session.selection?.type === 'property') {
|
|
510
|
+
if (session.config.handle_property_deletion) {
|
|
511
|
+
session.config.handle_property_deletion(session, session.selection.path);
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
session.apply(session.tr.delete_selection(direction));
|
|
494
515
|
}
|
|
495
516
|
}
|
|
496
517
|
|
|
@@ -764,6 +785,23 @@ ${fallback_html}`;
|
|
|
764
785
|
canvas_el?.focus();
|
|
765
786
|
}
|
|
766
787
|
|
|
788
|
+
/**
|
|
789
|
+
* When a DOM selection endpoint lands in a sibling NodeGap (not inside
|
|
790
|
+
* a node), resolves the adjacent node element for the walk-up algorithm.
|
|
791
|
+
* @param {HTMLElement} el
|
|
792
|
+
* @returns {HTMLElement | null}
|
|
793
|
+
*/
|
|
794
|
+
function __resolve_node_from_gap(el) {
|
|
795
|
+
const gap = /** @type {HTMLElement | null} */ (el.closest('[data-gap-array-path]'));
|
|
796
|
+
if (!gap) return null;
|
|
797
|
+
const array_path = gap.dataset.gapArrayPath;
|
|
798
|
+
const offset = parseInt(gap.dataset.gapOffset, 10);
|
|
799
|
+
const node_idx = offset > 0 ? offset - 1 : 0;
|
|
800
|
+
return /** @type {HTMLElement | null} */ (
|
|
801
|
+
canvas_el.querySelector(`[data-path="${array_path}.${node_idx}"][data-type="node"]`)
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
767
805
|
/**
|
|
768
806
|
* Extracts a NodeSelection from the current DOM selection.
|
|
769
807
|
*
|
|
@@ -783,56 +821,30 @@ ${fallback_html}`;
|
|
|
783
821
|
if (focus_node.nodeType !== Node.ELEMENT_NODE) focus_node = focus_node.parentElement;
|
|
784
822
|
if (anchor_node.nodeType !== Node.ELEMENT_NODE) anchor_node = anchor_node.parentElement;
|
|
785
823
|
|
|
786
|
-
// EDGE CASE:
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
return null;
|
|
795
|
-
}
|
|
796
|
-
const node_path = /** @type {DocumentPath} */ (node.dataset.path.split('.'));
|
|
797
|
-
const node_index = parseInt(String(node_path.at(-1)), 10);
|
|
798
|
-
|
|
799
|
-
return {
|
|
800
|
-
type: 'node',
|
|
801
|
-
path: node_path.slice(0, -1),
|
|
802
|
-
anchor_offset: node_index + 1,
|
|
803
|
-
focus_offset: node_index + 1
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// EDGE CASE: Let's check if we are in a gap-before for node_arrays.
|
|
808
|
-
let gap_before_el = focus_node.closest('[data-type="gap-before"]');
|
|
809
|
-
if (gap_before_el && focus_node === anchor_node) {
|
|
810
|
-
const node_array_el = /** @type {HTMLElement} */ (
|
|
811
|
-
gap_before_el.closest('[data-type="node_array"]')
|
|
812
|
-
);
|
|
813
|
-
const node_array_path = node_array_el.dataset.path.split('.');
|
|
824
|
+
// EDGE CASE: Collapsed selection inside a node gap (gap-after or gap-before).
|
|
825
|
+
// Gaps are siblings of nodes with data-gap-array-path and data-gap-offset.
|
|
826
|
+
const gap_el = /** @type {HTMLElement | null} */ (
|
|
827
|
+
focus_node.closest('[data-gap-array-path]')
|
|
828
|
+
);
|
|
829
|
+
if (gap_el && focus_node === anchor_node) {
|
|
830
|
+
const array_path = gap_el.dataset.gapArrayPath.split('.');
|
|
831
|
+
const gap_offset = parseInt(gap_el.dataset.gapOffset, 10);
|
|
814
832
|
return {
|
|
815
833
|
type: 'node',
|
|
816
|
-
path:
|
|
817
|
-
anchor_offset:
|
|
818
|
-
focus_offset:
|
|
834
|
+
path: array_path,
|
|
835
|
+
anchor_offset: gap_offset,
|
|
836
|
+
focus_offset: gap_offset
|
|
819
837
|
};
|
|
820
838
|
}
|
|
821
839
|
|
|
822
|
-
let focus_root =
|
|
823
|
-
focus_node.closest('[data-path][data-type="node"]')
|
|
824
|
-
);
|
|
840
|
+
let focus_root = __resolve_node_from_gap(focus_node)
|
|
841
|
+
?? /** @type {HTMLElement} */ (focus_node.closest('[data-path][data-type="node"]'));
|
|
825
842
|
if (!focus_root) return null;
|
|
826
843
|
|
|
827
|
-
let anchor_root =
|
|
828
|
-
anchor_node.closest('[data-path][data-type="node"]')
|
|
829
|
-
);
|
|
844
|
+
let anchor_root = __resolve_node_from_gap(anchor_node)
|
|
845
|
+
?? /** @type {HTMLElement} */ (anchor_node.closest('[data-path][data-type="node"]'));
|
|
830
846
|
if (!anchor_root) return null;
|
|
831
847
|
|
|
832
|
-
if (!(focus_root && anchor_root)) {
|
|
833
|
-
return null;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
848
|
let focus_root_path = focus_root.dataset.path.split('.');
|
|
837
849
|
let anchor_root_path = anchor_root.dataset.path.split('.');
|
|
838
850
|
let focus_node_depth = focus_root_path.length;
|
|
@@ -872,6 +884,15 @@ ${fallback_html}`;
|
|
|
872
884
|
}
|
|
873
885
|
}
|
|
874
886
|
|
|
887
|
+
// If both paths walked up to the root node, there's no common parent node_array
|
|
888
|
+
// (e.g. selecting between nav and page body). The resulting path would be empty.
|
|
889
|
+
if (anchor_root_path.length <= 1) return null;
|
|
890
|
+
|
|
891
|
+
const parent_array_path = anchor_root_path.slice(0, -1);
|
|
892
|
+
// A node selection is only valid inside a node_array property.
|
|
893
|
+
const parent_property = session.inspect(parent_array_path);
|
|
894
|
+
if (!parent_property || parent_property.type !== 'node_array') return null;
|
|
895
|
+
|
|
875
896
|
let anchor_offset = parseInt(anchor_root_path.at(-1));
|
|
876
897
|
let focus_offset = parseInt(focus_root_path.at(-1));
|
|
877
898
|
|
|
@@ -911,7 +932,7 @@ ${fallback_html}`;
|
|
|
911
932
|
|
|
912
933
|
return {
|
|
913
934
|
type: 'node',
|
|
914
|
-
path:
|
|
935
|
+
path: parent_array_path,
|
|
915
936
|
anchor_offset: anchor_offset,
|
|
916
937
|
focus_offset: focus_offset
|
|
917
938
|
};
|
|
@@ -1094,106 +1115,68 @@ ${fallback_html}`;
|
|
|
1094
1115
|
}
|
|
1095
1116
|
|
|
1096
1117
|
function __get_node_element(node_array_path, node_offset) {
|
|
1097
|
-
|
|
1098
|
-
`[data-path="${node_array_path}"][data-type="
|
|
1118
|
+
return canvas_el.querySelector(
|
|
1119
|
+
`[data-path="${node_array_path}.${node_offset}"][data-type="node"]`
|
|
1099
1120
|
);
|
|
1100
|
-
if (!node_array_el) return null;
|
|
1101
|
-
|
|
1102
|
-
const node_elements = node_array_el.children;
|
|
1103
|
-
if (node_elements.length === 0) return null;
|
|
1104
|
-
|
|
1105
|
-
return node_elements[node_offset];
|
|
1106
1121
|
}
|
|
1107
1122
|
|
|
1108
1123
|
function __render_node_selection() {
|
|
1109
1124
|
const selection = /** @type {NodeSelection} */ (session.selection);
|
|
1110
1125
|
const node_array_path = selection.path.join('.');
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
// We need to translate the cusor offset to node offsets now
|
|
1115
|
-
let anchor_node_offset, focus_node_offset;
|
|
1116
|
-
|
|
1117
|
-
if (is_collapsed) {
|
|
1118
|
-
anchor_node_offset = Math.max(0, selection.anchor_offset - 1);
|
|
1119
|
-
focus_node_offset = Math.max(0, selection.focus_offset - 1);
|
|
1120
|
-
} else if (is_backward) {
|
|
1121
|
-
anchor_node_offset = selection.anchor_offset - 1;
|
|
1122
|
-
focus_node_offset = selection.focus_offset;
|
|
1123
|
-
} else {
|
|
1124
|
-
anchor_node_offset = selection.anchor_offset;
|
|
1125
|
-
focus_node_offset = selection.focus_offset - 1;
|
|
1126
|
-
}
|
|
1126
|
+
const is_collapsed = selection.anchor_offset === selection.focus_offset;
|
|
1127
|
+
const is_backward = !is_collapsed && selection.anchor_offset > selection.focus_offset;
|
|
1127
1128
|
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1129
|
+
const node_array_el = canvas_el.querySelector(
|
|
1130
|
+
`[data-path="${node_array_path}"][data-type="node_array"]`
|
|
1131
|
+
);
|
|
1132
|
+
if (!node_array_el) return;
|
|
1130
1133
|
|
|
1131
|
-
if (!anchor_node || !focus_node) return;
|
|
1132
1134
|
const dom_selection = window.getSelection();
|
|
1133
1135
|
const range = window.document.createRange();
|
|
1134
1136
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
// IMPORTANT: We need to look for direct children of anchor_node to find the right node gap.
|
|
1138
|
-
const gap_selector = selection.anchor_offset === 0
|
|
1139
|
-
? '.gap-before'
|
|
1140
|
-
: '.gap-after';
|
|
1141
|
-
// In empty arrays the node gap is a sibling of the placeholder, not a child
|
|
1142
|
-
const gap_el =
|
|
1143
|
-
anchor_node.querySelector(`:scope > ${gap_selector}`) ??
|
|
1144
|
-
anchor_node.parentElement?.querySelector(`:scope > ${gap_selector}`);
|
|
1137
|
+
const gap_selector = (offset) =>
|
|
1138
|
+
`[data-gap-array-path="${node_array_path}"][data-gap-offset="${offset}"]`;
|
|
1145
1139
|
|
|
1140
|
+
if (is_collapsed) {
|
|
1141
|
+
const gap_el = node_array_el.querySelector(gap_selector(selection.anchor_offset));
|
|
1146
1142
|
if (!gap_el) return;
|
|
1147
|
-
|
|
1148
|
-
|
|
1143
|
+
// Target .svedit-selectable (has a box), not gap_el which is
|
|
1144
|
+
// display:contents and would cause the browser to normalize
|
|
1145
|
+
// the range into the parent, breaking read-back.
|
|
1146
|
+
const selectable = gap_el.querySelector('.svedit-selectable');
|
|
1147
|
+
if (!selectable) return;
|
|
1148
|
+
range.setStart(selectable, 1);
|
|
1149
|
+
range.setEnd(selectable, 1);
|
|
1149
1150
|
dom_selection.removeAllRanges();
|
|
1150
1151
|
dom_selection.addRange(range);
|
|
1151
1152
|
} else {
|
|
1152
|
-
|
|
1153
|
+
const anchor_gap = node_array_el.querySelector(gap_selector(selection.anchor_offset));
|
|
1154
|
+
const focus_gap = node_array_el.querySelector(gap_selector(selection.focus_offset));
|
|
1155
|
+
if (!anchor_gap || !focus_gap) return;
|
|
1156
|
+
const anchor_sel = anchor_gap.querySelector('.svedit-selectable');
|
|
1157
|
+
const focus_sel = focus_gap.querySelector('.svedit-selectable');
|
|
1158
|
+
if (!anchor_sel || !focus_sel) return;
|
|
1159
|
+
|
|
1153
1160
|
if (is_backward) {
|
|
1154
|
-
// Use the last selectable you find
|
|
1155
|
-
const anchor_node_selectable = [...anchor_node.querySelectorAll('.svedit-selectable')].at(
|
|
1156
|
-
-1
|
|
1157
|
-
);
|
|
1158
|
-
// Use the first selectable you find
|
|
1159
|
-
const focus_node_selectable = [...focus_node.querySelectorAll('.svedit-selectable')].at(0);
|
|
1160
|
-
|
|
1161
|
-
range.setStart(focus_node_selectable, 1);
|
|
1162
|
-
range.setEnd(anchor_node_selectable, 1);
|
|
1163
1161
|
dom_selection.removeAllRanges();
|
|
1164
|
-
dom_selection.
|
|
1165
|
-
// Phew, this was a hard nut to crack. But with that code the direction can be reversed.
|
|
1166
|
-
dom_selection.setBaseAndExtent(anchor_node_selectable, 1, focus_node_selectable, 1);
|
|
1162
|
+
dom_selection.setBaseAndExtent(anchor_sel, 1, focus_sel, 1);
|
|
1167
1163
|
} else {
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
0
|
|
1171
|
-
);
|
|
1172
|
-
// Use the last selectable you find
|
|
1173
|
-
const focus_node_selectable = [...focus_node.querySelectorAll('.svedit-selectable')].at(-1);
|
|
1174
|
-
|
|
1175
|
-
range.setStart(anchor_node_selectable, 1);
|
|
1176
|
-
range.setEnd(focus_node_selectable, 1);
|
|
1164
|
+
range.setStart(anchor_sel, 1);
|
|
1165
|
+
range.setEnd(focus_sel, 1);
|
|
1177
1166
|
dom_selection.removeAllRanges();
|
|
1178
1167
|
dom_selection.addRange(range);
|
|
1179
1168
|
}
|
|
1180
1169
|
}
|
|
1181
1170
|
|
|
1182
|
-
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
// Scroll the selection into view
|
|
1171
|
+
node_array_el.focus();
|
|
1172
|
+
const scroll_node_offset = is_collapsed
|
|
1173
|
+
? Math.max(0, selection.anchor_offset - 1)
|
|
1174
|
+
: (is_backward ? selection.focus_offset : selection.anchor_offset);
|
|
1175
|
+
const scroll_node = __get_node_element(node_array_path, scroll_node_offset);
|
|
1176
|
+
if (scroll_node) {
|
|
1189
1177
|
setTimeout(() => {
|
|
1190
|
-
(
|
|
1191
|
-
block: 'nearest',
|
|
1192
|
-
inline: 'nearest'
|
|
1193
|
-
});
|
|
1178
|
+
scroll_node.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
1194
1179
|
}, 0);
|
|
1195
|
-
} else {
|
|
1196
|
-
console.log('No container element found!');
|
|
1197
1180
|
}
|
|
1198
1181
|
}
|
|
1199
1182
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* The viewport tracker drives **lazy positioning**: only near-viewport
|
|
28
28
|
* gaps receive CSS anchor positioning (via NodeGap's `positioned`
|
|
29
|
-
* prop). Off-viewport gaps remain as zero-size
|
|
29
|
+
* prop). Off-viewport gaps remain as zero-size absolute elements with
|
|
30
30
|
* no layout cost. Gap markers (NodeGapMarkers) are only computed for
|
|
31
31
|
* visible indices.
|
|
32
32
|
*
|