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 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 &ZeroWidthSpace; (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">&ZeroWidthSpace;</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 svedit-selectable"><br/></div>
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":";;;;;AAwBA,sFAA6D;yCApBlB,cAAc"}
1
+ {"version":3,"file":"CustomProperty.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CustomProperty.svelte.js"],"names":[],"mappings":";;;;;AA2BA,sFAA6D;yCAvBlB,cAAc"}
@@ -350,7 +350,7 @@
350
350
  doc.redo();
351
351
  e.preventDefault();
352
352
  e.stopPropagation();
353
- } else if (e.key === 'Enter' && (e.shiftKey)) {
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[data-type="after-node-cursor-trap"]
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[data-type="after-node-cursor-trap"]
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: only apply this rule if focus node and anchor node are in the same container
543
- focus_node_depth === anchor_node_depth
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 cursor_trap = el.querySelector('.cursor-trap');
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(cursor_trap);
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
- .svedit-canvas.hide-selection :global(::selection) {
967
- background: transparent;
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":";;;;;;;AA86BA;;OAAqD;iCAl6BgE,cAAc"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svedit",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -16,7 +16,10 @@
16
16
  data-path={path.join('.')}
17
17
  style="anchor-name: --{path.join('-')};"
18
18
  >
19
- <div class="cursor-trap svedit-selectable"><br/></div>
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
 
@@ -350,7 +350,7 @@
350
350
  doc.redo();
351
351
  e.preventDefault();
352
352
  e.stopPropagation();
353
- } else if (e.key === 'Enter' && (e.shiftKey)) {
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[data-type="after-node-cursor-trap"]
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[data-type="after-node-cursor-trap"]
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: only apply this rule if focus node and anchor node are in the same container
543
- focus_node_depth === anchor_node_depth
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 cursor_trap = el.querySelector('.cursor-trap');
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(cursor_trap);
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
- .svedit-canvas.hide-selection :global(::selection) {
967
- background: transparent;
965
+ @media not (pointer: coarse) {
966
+ .svedit-canvas.hide-selection :global(::selection) {
967
+ background: transparent;
968
+ }
968
969
  }
969
970
  </style>