Fix emoji bug on the demo website
This commit is contained in:
parent
c566eb7ef6
commit
55e8177255
3 changed files with 37 additions and 10 deletions
|
|
@ -128,6 +128,24 @@ console.log(result.cursors); // [{ id: 2, position: 0 }, { id: 1, position: 3 }]
|
||||||
|
|
||||||
> The `cursors` list is sorted by character position (not IDs).
|
> The `cursors` list is sorted by character position (not IDs).
|
||||||
|
|
||||||
|
Input and output cursor positions count Unicode scalars (code points), starting
|
||||||
|
at zero and allowing a position just after the last scalar. They do not count
|
||||||
|
UTF-16 code units or grapheme clusters. For example, the position after `😀` is
|
||||||
|
`1`, although JavaScript's `'😀'.length` is `2`.
|
||||||
|
|
||||||
|
DOM `selectionStart` and `selectionEnd` use UTF-16 code units. Convert both
|
||||||
|
endpoints before passing a textarea selection to the library:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const edited = {
|
||||||
|
text: textarea.value,
|
||||||
|
cursors: [
|
||||||
|
{ id: 1, position: [...textarea.value.slice(0, textarea.selectionStart)].length },
|
||||||
|
{ id: 2, position: [...textarea.value.slice(0, textarea.selectionEnd)].length },
|
||||||
|
],
|
||||||
|
} satisfies TextWithOptionalCursors;
|
||||||
|
```
|
||||||
|
|
||||||
## Generic Helpers and Inference
|
## Generic Helpers and Inference
|
||||||
|
|
||||||
The exported merge functions are intentionally small: they merge strings, or
|
The exported merge functions are intentionally small: they merge strings, or
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { reconcileWithHistory } from 'reconcile-text';
|
import { reconcileWithHistory } from 'reconcile-text';
|
||||||
import type { BuiltinTokenizer } from 'reconcile-text';
|
import type { BuiltinTokenizer, CursorPosition } from 'reconcile-text';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
const originalTextArea = document.getElementById('original') as HTMLTextAreaElement;
|
const originalTextArea = document.getElementById('original') as HTMLTextAreaElement;
|
||||||
|
|
@ -157,19 +157,22 @@ function getCursorsFromActiveTextArea() {
|
||||||
let rightCursors = undefined;
|
let rightCursors = undefined;
|
||||||
|
|
||||||
if (activeElement === leftTextArea) {
|
if (activeElement === leftTextArea) {
|
||||||
leftCursors = [
|
leftCursors = getSelectionCursors(leftTextArea);
|
||||||
{ id: 1, position: leftTextArea.selectionStart },
|
|
||||||
{ id: 2, position: leftTextArea.selectionEnd },
|
|
||||||
];
|
|
||||||
} else if (activeElement === rightTextArea) {
|
} else if (activeElement === rightTextArea) {
|
||||||
rightCursors = [
|
rightCursors = getSelectionCursors(rightTextArea);
|
||||||
{ id: 1, position: rightTextArea.selectionStart },
|
|
||||||
{ id: 2, position: rightTextArea.selectionEnd },
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
return { leftCursors, rightCursors };
|
return { leftCursors, rightCursors };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelectionCursors(textarea: HTMLTextAreaElement): CursorPosition[] {
|
||||||
|
const text = textarea.value;
|
||||||
|
// DOM selections count UTF-16 code units; reconcile counts Unicode scalars.
|
||||||
|
return [
|
||||||
|
{ id: 1, position: [...text.slice(0, textarea.selectionStart)].length },
|
||||||
|
{ id: 2, position: [...text.slice(0, textarea.selectionEnd)].length },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function createSelectionOverlay(isLeft: boolean, isSelection: boolean): HTMLSpanElement {
|
function createSelectionOverlay(isLeft: boolean, isSelection: boolean): HTMLSpanElement {
|
||||||
const caretSpan = document.createElement('span');
|
const caretSpan = document.createElement('span');
|
||||||
caretSpan.className = `selection-caret selection-caret-${isLeft ? 'left' : 'right'}`;
|
caretSpan.className = `selection-caret selection-caret-${isLeft ? 'left' : 'right'}`;
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,13 @@ export interface CursorPosition {
|
||||||
/** Unique identifier for the cursor (can be any number, must be unique within the document) */
|
/** Unique identifier for the cursor (can be any number, must be unique within the document) */
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
/** Character position in the text, 0-based index from the beginning of the document */
|
/**
|
||||||
|
* Zero-based offset in Unicode scalars (code points), from 0 through
|
||||||
|
* `[...text].length` inclusive. Applies to both input and output cursors.
|
||||||
|
* These are not UTF-16 code units or grapheme clusters: `😀` counts as one.
|
||||||
|
* Convert DOM selectionStart/selectionEnd offsets before passing them in:
|
||||||
|
* `[...text.slice(0, offset)].length`.
|
||||||
|
*/
|
||||||
position: number;
|
position: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue