123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- import { Editor, Element, Text, Location } from 'slate';
- import { SelectionPoint, TextDelta, TextSelection } from '$app/interfaces/document';
- export function getDelta(editor: Editor, at: Location): TextDelta[] {
- const baseElement = Editor.fragment(editor, at)[0] as Element;
- return baseElement.children.map((item) => {
- const { text, ...attributes } = item as Text;
- return {
- insert: text,
- attributes,
- };
- });
- }
- /**
- * get the selection between the beginning of the editor and the point
- * form 0 to point
- * @param editor
- * @param at
- */
- export function getBeforeRangeAt(editor: Editor, at: Location) {
- const start = Editor.start(editor, at);
- return {
- anchor: { path: [0, 0], offset: 0 },
- focus: start,
- };
- }
- /**
- * get the selection between the point and the end of the editor
- * from point to end
- * @param editor
- * @param at
- */
- export function getAfterRangeAt(editor: Editor, at: Location) {
- const end = Editor.end(editor, at);
- const fragment = (editor.children[0] as Element).children;
- const lastIndex = fragment.length - 1;
- const lastNode = fragment[lastIndex] as Text;
- return {
- anchor: end,
- focus: { path: [0, lastIndex], offset: lastNode.text.length },
- };
- }
- /**
- * check if the point is in the beginning of the editor
- * @param editor
- * @param at
- */
- export function pointInBegin(editor: Editor, at: Location) {
- const start = Editor.start(editor, at);
- return Editor.before(editor, start) === undefined;
- }
- /**
- * check if the point is in the end of the editor
- * @param editor
- * @param at
- */
- export function pointInEnd(editor: Editor, at: Location) {
- const end = Editor.end(editor, at);
- return Editor.after(editor, end) === undefined;
- }
- /**
- * get the selection of the beginning of the node
- */
- export function getNodeBeginSelection(): TextSelection {
- const point: SelectionPoint = {
- path: [0, 0],
- offset: 0,
- };
- const selection: TextSelection = {
- anchor: clonePoint(point),
- focus: clonePoint(point),
- };
- return selection;
- }
- /**
- * get the selection of the end of the node
- * @param delta
- */
- export function getNodeEndSelection(delta: TextDelta[]) {
- const len = delta.length;
- const offset = len > 0 ? delta[len - 1].insert.length : 0;
- const cursorPoint: SelectionPoint = {
- path: [0, Math.max(len - 1, 0)],
- offset,
- };
- const selection: TextSelection = {
- anchor: clonePoint(cursorPoint),
- focus: clonePoint(cursorPoint),
- };
- return selection;
- }
- /**
- * get lines by delta
- * @param delta
- */
- export function getLinesByDelta(delta: TextDelta[]): string[] {
- const text = delta.map((item) => item.insert).join('');
- return text.split('\n');
- }
- /**
- * get the offset of the last line
- * @param delta
- */
- export function getLastLineOffsetByDelta(delta: TextDelta[]): number {
- const text = delta.map((item) => item.insert).join('');
- const index = text.lastIndexOf('\n');
- return index === -1 ? 0 : index + 1;
- }
- /**
- * get the selection of the end line by offset
- * @param delta
- * @param offset relative offset of the end line
- */
- export function getEndLineSelectionByOffset(delta: TextDelta[], offset: number) {
- const lines = getLinesByDelta(delta);
- const endLine = lines[lines.length - 1];
- // if the offset is greater than the length of the end line, set cursor to the end of prev line
- if (offset >= endLine.length) {
- return getNodeEndSelection(delta);
- }
- const textOffset = getLastLineOffsetByDelta(delta) + offset;
- return getSelectionByTextOffset(delta, textOffset);
- }
- /**
- * get the selection of the start line by offset
- * @param delta
- * @param offset relative offset of the start line
- */
- export function getStartLineSelectionByOffset(delta: TextDelta[], offset: number) {
- const lines = getLinesByDelta(delta);
- if (lines.length === 0) {
- return getNodeBeginSelection();
- }
- const startLine = lines[0];
- // if the offset is greater than the length of the end line, set cursor to the end of prev line
- if (offset >= startLine.length) {
- return getSelectionByTextOffset(delta, startLine.length);
- }
- return getSelectionByTextOffset(delta, offset);
- }
- /**
- * get the selection by text offset
- * @param delta
- * @param offset absolute offset
- */
- export function getSelectionByTextOffset(delta: TextDelta[], offset: number) {
- const point = getPointByTextOffset(delta, offset);
- const selection: TextSelection = {
- anchor: clonePoint(point),
- focus: clonePoint(point),
- };
- return selection;
- }
- /**
- * get the point by text offset
- * @param delta
- * @param offset absolute offset
- */
- export function getPointByTextOffset(delta: TextDelta[], offset: number): SelectionPoint {
- let textOffset = 0;
- let path: [number, number] = [0, 0];
- let textLength = 0;
- for (let i = 0; i < delta.length; i++) {
- const item = delta[i];
- if (textOffset + item.insert.length >= offset) {
- path = [0, i];
- textLength = offset - textOffset;
- break;
- }
- textOffset += item.insert.length;
- }
- return {
- path,
- offset: textLength,
- };
- }
- export function clonePoint(point: SelectionPoint): SelectionPoint {
- return {
- path: [...point.path],
- offset: point.offset,
- };
- }
|