svedit 0.1.1 → 0.2.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 +36 -0
- package/dist/CustomProperty.svelte +4 -1
- package/dist/CustomProperty.svelte.d.ts.map +1 -1
- package/dist/Svedit.svelte +13 -12
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/lib/CustomProperty.svelte +4 -1
- package/src/lib/Svedit.svelte +13 -12
package/README.md
CHANGED
|
@@ -286,6 +286,42 @@ Now you can start making your Svelte pages in-place editable by wrapping your de
|
|
|
286
286
|
<Svedit {doc} path={[doc.document_id]} editable={true} />
|
|
287
287
|
```
|
|
288
288
|
|
|
289
|
+
## Mastering contenteditable
|
|
290
|
+
|
|
291
|
+
Svedit relies on the contenteditable attribute to make elements editable. The below example shows you
|
|
292
|
+
a simplified version of the markup of `<NodeCursorTrap>` and why it is implemented the way it is.
|
|
293
|
+
|
|
294
|
+
```html
|
|
295
|
+
<div contenteditable="true">
|
|
296
|
+
<div class="some-wrapper">
|
|
297
|
+
<!--
|
|
298
|
+
Putting a <br> tag into a div gives you a single addressable cursor position.
|
|
299
|
+
|
|
300
|
+
Adding a ​ (or any character) here will lead to 2 cursor
|
|
301
|
+
positions (one before, and one after the character)
|
|
302
|
+
|
|
303
|
+
Svedit uses this behavior for node-cursor-traps, and when an
|
|
304
|
+
<AnnotatedTextProperty> is empty.
|
|
305
|
+
-->
|
|
306
|
+
<div class="cursor-trap"><br></div>
|
|
307
|
+
<!--
|
|
308
|
+
If you create a contenteditable="false" island, there needs to be some content in it,
|
|
309
|
+
otherwise it will create two additional cursor positions. One before, and another one
|
|
310
|
+
after the island.
|
|
311
|
+
|
|
312
|
+
The Svedit demo uses this technique in `<NodeCursorTrap>` to create a node-cursor
|
|
313
|
+
visualization, that doesn't mess with the contenteditable cursor positions.
|
|
314
|
+
-->
|
|
315
|
+
<div contenteditable="false" class="node-cursor">​</div>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Further things to consider:
|
|
321
|
+
|
|
322
|
+
- If you make a sub-tree `contenteditable="false"`, be aware that you can't create a `contenteditable="true"` segment somewhere inside it. Svedit can only work reliably when there's one contenteditable="true" at root (it's set by `<Svedit`>)
|
|
323
|
+
- `<AnnotatedString>` and `<CustomProperty>` must not be wrapped in `contenteditable="false"` to work properly.
|
|
324
|
+
|
|
289
325
|
## Full API docs?
|
|
290
326
|
|
|
291
327
|
Not yet. Please just [read the code](./src) for now. It's only a couple of files with less than 3000 LOC in total. The files in `routes` are considered example code (copy them and adapt them to your needs), while files in `lib` are considered library code. Read them to understand the API and what's happening behind the scences.
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
data-path={path.join('.')}
|
|
17
17
|
style="anchor-name: --{path.join('-')};"
|
|
18
18
|
>
|
|
19
|
-
<div class="cursor-trap
|
|
19
|
+
<div class="cursor-trap">
|
|
20
|
+
<div class="svedit-selectable"><br/></div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
20
23
|
<div class="property-content" contenteditable="false">{@render children()}</div>
|
|
21
24
|
</div>
|
|
22
25
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CustomProperty.svelte.js"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"CustomProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CustomProperty.svelte.js"],"names":[],"mappings":";;;;;AA2BA,sFAA6D;yCAvBlB,cAAc"}
|
package/dist/Svedit.svelte
CHANGED
|
@@ -350,7 +350,7 @@
|
|
|
350
350
|
doc.redo();
|
|
351
351
|
e.preventDefault();
|
|
352
352
|
e.stopPropagation();
|
|
353
|
-
} else if (e.key === 'Enter' && (e.
|
|
353
|
+
} else if (e.key === 'Enter' && (e.altKey)) {
|
|
354
354
|
doc.apply(doc.tr.insert_text('\n'));
|
|
355
355
|
e.preventDefault();
|
|
356
356
|
e.stopPropagation();
|
|
@@ -524,23 +524,23 @@
|
|
|
524
524
|
focus_offset += 1;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
// Exclude first node when anchor_node
|
|
527
|
+
// EDGE CASE: Exclude first node when anchor_node is an afer-node-cursor-trap
|
|
528
528
|
// in a non-collapsed forward selection
|
|
529
529
|
if (
|
|
530
|
-
anchor_node.dataset.type === 'after-node-cursor-trap' &&
|
|
530
|
+
anchor_node.parentElement?.dataset.type === 'after-node-cursor-trap' &&
|
|
531
531
|
!is_backwards &&
|
|
532
532
|
anchor_offset !== focus_offset
|
|
533
533
|
) {
|
|
534
534
|
anchor_offset += 1;
|
|
535
535
|
}
|
|
536
|
-
// Exclude first node when focus_node
|
|
536
|
+
// EDGE CASE: Exclude first node when focus_node is an afer-node-cursor-trap
|
|
537
537
|
// in a non-collapsed backward selection
|
|
538
538
|
else if (
|
|
539
|
-
focus_node.dataset.type === 'after-node-cursor-trap' &&
|
|
539
|
+
focus_node.parentElement?.dataset.type === 'after-node-cursor-trap' &&
|
|
540
540
|
is_backwards &&
|
|
541
541
|
anchor_offset !== focus_offset &&
|
|
542
|
-
// EDGE CASE:
|
|
543
|
-
|
|
542
|
+
// EDGE CASE: Only do correction when drag started from a deeper or equally deep anchor node
|
|
543
|
+
anchor_node_depth >= focus_node_depth
|
|
544
544
|
) {
|
|
545
545
|
focus_offset += 1;
|
|
546
546
|
}
|
|
@@ -553,7 +553,6 @@
|
|
|
553
553
|
};
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
-
|
|
557
556
|
/**
|
|
558
557
|
* Extracts a PropertySelection from the current DOM selection.
|
|
559
558
|
*
|
|
@@ -754,13 +753,13 @@
|
|
|
754
753
|
const selection = doc.selection;
|
|
755
754
|
// The element that holds the property
|
|
756
755
|
const el = canvas_ref.querySelector(`[data-path="${selection.path.join('.')}"][data-type="property"]`);
|
|
757
|
-
const
|
|
756
|
+
const cursor_trap_selectable = el.querySelector('.svedit-selectable');
|
|
758
757
|
|
|
759
758
|
const range = window.document.createRange();
|
|
760
759
|
const dom_selection = window.getSelection();
|
|
761
760
|
|
|
762
761
|
// Select the entire cursor trap element contents and collapse to start
|
|
763
|
-
range.selectNodeContents(
|
|
762
|
+
range.selectNodeContents(cursor_trap_selectable);
|
|
764
763
|
range.collapse(true); // Collapse to start position
|
|
765
764
|
dom_selection.removeAllRanges();
|
|
766
765
|
dom_selection.addRange(range);
|
|
@@ -963,7 +962,9 @@
|
|
|
963
962
|
background: var(--editing-fill-color);
|
|
964
963
|
}
|
|
965
964
|
|
|
966
|
-
|
|
967
|
-
|
|
965
|
+
@media not (pointer: coarse) {
|
|
966
|
+
.svedit-canvas.hide-selection :global(::selection) {
|
|
967
|
+
background: transparent;
|
|
968
|
+
}
|
|
968
969
|
}
|
|
969
970
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Svedit.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Svedit.svelte.js"],"names":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"Svedit.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Svedit.svelte.js"],"names":[],"mappings":";;;;;;;AA66BA;;OAAqD;iCAj6BgE,cAAc"}
|
package/package.json
CHANGED
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
data-path={path.join('.')}
|
|
17
17
|
style="anchor-name: --{path.join('-')};"
|
|
18
18
|
>
|
|
19
|
-
<div class="cursor-trap
|
|
19
|
+
<div class="cursor-trap">
|
|
20
|
+
<div class="svedit-selectable"><br/></div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
20
23
|
<div class="property-content" contenteditable="false">{@render children()}</div>
|
|
21
24
|
</div>
|
|
22
25
|
|
package/src/lib/Svedit.svelte
CHANGED
|
@@ -350,7 +350,7 @@
|
|
|
350
350
|
doc.redo();
|
|
351
351
|
e.preventDefault();
|
|
352
352
|
e.stopPropagation();
|
|
353
|
-
} else if (e.key === 'Enter' && (e.
|
|
353
|
+
} else if (e.key === 'Enter' && (e.altKey)) {
|
|
354
354
|
doc.apply(doc.tr.insert_text('\n'));
|
|
355
355
|
e.preventDefault();
|
|
356
356
|
e.stopPropagation();
|
|
@@ -524,23 +524,23 @@
|
|
|
524
524
|
focus_offset += 1;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
// Exclude first node when anchor_node
|
|
527
|
+
// EDGE CASE: Exclude first node when anchor_node is an afer-node-cursor-trap
|
|
528
528
|
// in a non-collapsed forward selection
|
|
529
529
|
if (
|
|
530
|
-
anchor_node.dataset.type === 'after-node-cursor-trap' &&
|
|
530
|
+
anchor_node.parentElement?.dataset.type === 'after-node-cursor-trap' &&
|
|
531
531
|
!is_backwards &&
|
|
532
532
|
anchor_offset !== focus_offset
|
|
533
533
|
) {
|
|
534
534
|
anchor_offset += 1;
|
|
535
535
|
}
|
|
536
|
-
// Exclude first node when focus_node
|
|
536
|
+
// EDGE CASE: Exclude first node when focus_node is an afer-node-cursor-trap
|
|
537
537
|
// in a non-collapsed backward selection
|
|
538
538
|
else if (
|
|
539
|
-
focus_node.dataset.type === 'after-node-cursor-trap' &&
|
|
539
|
+
focus_node.parentElement?.dataset.type === 'after-node-cursor-trap' &&
|
|
540
540
|
is_backwards &&
|
|
541
541
|
anchor_offset !== focus_offset &&
|
|
542
|
-
// EDGE CASE:
|
|
543
|
-
|
|
542
|
+
// EDGE CASE: Only do correction when drag started from a deeper or equally deep anchor node
|
|
543
|
+
anchor_node_depth >= focus_node_depth
|
|
544
544
|
) {
|
|
545
545
|
focus_offset += 1;
|
|
546
546
|
}
|
|
@@ -553,7 +553,6 @@
|
|
|
553
553
|
};
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
-
|
|
557
556
|
/**
|
|
558
557
|
* Extracts a PropertySelection from the current DOM selection.
|
|
559
558
|
*
|
|
@@ -754,13 +753,13 @@
|
|
|
754
753
|
const selection = doc.selection;
|
|
755
754
|
// The element that holds the property
|
|
756
755
|
const el = canvas_ref.querySelector(`[data-path="${selection.path.join('.')}"][data-type="property"]`);
|
|
757
|
-
const
|
|
756
|
+
const cursor_trap_selectable = el.querySelector('.svedit-selectable');
|
|
758
757
|
|
|
759
758
|
const range = window.document.createRange();
|
|
760
759
|
const dom_selection = window.getSelection();
|
|
761
760
|
|
|
762
761
|
// Select the entire cursor trap element contents and collapse to start
|
|
763
|
-
range.selectNodeContents(
|
|
762
|
+
range.selectNodeContents(cursor_trap_selectable);
|
|
764
763
|
range.collapse(true); // Collapse to start position
|
|
765
764
|
dom_selection.removeAllRanges();
|
|
766
765
|
dom_selection.addRange(range);
|
|
@@ -963,7 +962,9 @@
|
|
|
963
962
|
background: var(--editing-fill-color);
|
|
964
963
|
}
|
|
965
964
|
|
|
966
|
-
|
|
967
|
-
|
|
965
|
+
@media not (pointer: coarse) {
|
|
966
|
+
.svedit-canvas.hide-selection :global(::selection) {
|
|
967
|
+
background: transparent;
|
|
968
|
+
}
|
|
968
969
|
}
|
|
969
970
|
</style>
|