I'm trying to implement a collaborative text editor using Slate.js, with React and Typescript. For applying text styling and broadcasting to other clients, I need the exact selection of the text that the user wants to change style, in the following format:
type Cursor = {
line: number;
column: number;
};
type Selection = {
start: Cursor;
end: Cursor;
};
With this approach, the current styles of the text affect the output, because it is dependent on the tree structure of the slate document. What I would like to get is the current selection no matter the document structure, but the returning columns are incorrect, in the following way:
function getSelection(editor: Editor): Selection {
const { selection } = editor;
if (!selection) {
return {
start: { line: 0, column: 0 },
end: { line: 0, column: 0 },
};
}
const [start, end] = Range.edges(selection);
return {
start: pointToCursor(start),
end: pointToCursor(end),
};
}
function pointToCursor(point: Point): Cursor {
return {
line: point.path[0],
column: point.offset, // wrong
};
}
For example, with the text abcdef
, if I select def
it returns the columns as [0, 3], instead of [3, 6], but if abc
is in a different style (in a another node of the document tree) it returns [3, 6].
Thanks for the help in advance.
Answers
To accurately calculate the cursor position within the text regardless of the document structure in Slate.js, you need to consider the cumulative offset of the text nodes leading up to the selected point. Here's how you can modify the pointToCursor
function to achieve this:
import { Editor, Point, Range } from 'slate';
type Cursor = {
line: number;
column: number;
};
type Selection = {
start: Cursor;
end: Cursor;
};
function getSelection(editor: Editor): Selection {
const { selection } = editor;
if (!selection) {
return {
start: { line: 0, column: 0 },
end: { line: 0, column: 0 },
};
}
const [start, end] = Range.edges(selection);
return {
start: pointToCursor(editor, start),
end: pointToCursor(editor, end),
};
}
function pointToCursor(editor: Editor, point: Point): Cursor {
const path = editor.findPath(point);
const offset = editor.getPointOffset(point, path);
return {
line: path[0],
column: offset,
};
}
In this modified pointToCursor
function:
- We use
editor.findPath(point)
to find the path of the text node containing the point. - We then use
editor.getPointOffset(point, path)
to calculate the cumulative offset of the text nodes leading up to the selected point within the path. - This approach ensures that the column value accurately reflects the position within the text, regardless of the document structure in Slate.js.