slate#Path TypeScript Examples

The following examples show how to use slate#Path. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From slate-ot with MIT License 7 votes vote down vote up
checkOp = (snapshot: Editor, op: Operation) => {
  switch (op.type) {
    case 'remove_text': {
      const leaf = Node.leaf(snapshot, op.path);
      const textToRemove = leaf.text.slice(
        op.offset,
        op.offset + op.text.length
      );

      expect(textToRemove).toBe(op.text);

      break;
    }

    case 'merge_node': {
      const prev = Node.get(snapshot, Path.previous(op.path));

      const prevLen = Text.isText(prev)
        ? prev.text.length
        : prev.children.length;

      expect(prevLen).toBe(op.position);

      break;
    }

    case 'remove_node': {
      // op.node needs to be checked
      break;
    }

    default:
      return;
  }
}
Example #2
Source File: on-block-keydown.ts    From fantasy-editor with MIT License 6 votes vote down vote up
onKeyDownBlockquote = (e: KeyboardEvent, editor: Editor) => {
  if (isHotkey('Enter', e)) {
    const match = Editor.above(editor, {
      match: n => n.type === BLOCK_QUOTE,
    });
    if (match) {
      const { selection } = editor;
      const [node, path] = match;
      if (selection) {
        const childPath = Editor.path(editor, selection);
        if (childPath.length > path.length) {
          const idx = childPath[path.length];
          if (idx === node.children.length - 1) {
            const lastPath = childPath.slice(0, path.length + 1);
            const text = Editor.string(editor, lastPath);
            if (!text) {
              e.preventDefault();
              const newPath = Path.next(path);
              Transforms.moveNodes(editor, {
                at: lastPath,
                to: newPath,
              });
            }
          }
        }
      }
    }
  }
}
Example #3
Source File: op-generator.ts    From slate-ot with MIT License 6 votes vote down vote up
getRandomPathFrom = (snapshot: Node): Path => {
  if (Text.isText(snapshot) || snapshot.children.length === 0) {
    return [];
  }

  const path: Path = [];
  // let currentNode = snapshot;
  while (1) {
    // stop when you get to a leaf
    if (Text.isText(snapshot)) {
      return path;
    }

    if (snapshot.children.length === 0) {
      return path;
    }

    if (fuzzer.randomInt(3) === 0 && path.length > 0) {
      return path;
    }

    // continue
    const index = <number>fuzzer.randomInt(snapshot.children.length);
    path.push(index);
    snapshot = snapshot.children[index];
  }
  return path;
}
Example #4
Source File: op-generator.ts    From slate-ot with MIT License 6 votes vote down vote up
getRandomPathTo = (snapshot: Node): Path => {
  const path: Path = [];
  // let currentNode = snapshot;
  while (1) {
    // stop when you get to a leaf
    if (Text.isText(snapshot)) {
      return path;
    }

    if (snapshot.children.length === 0) {
      return [...path, 0];
    }

    // randomly stop at the next level
    if (fuzzer.randomInt(3) === 0) {
      const index = <number>fuzzer.randomInt(snapshot.children.length + 1);
      return [...path, index];
    }

    // continue
    const index = <number>fuzzer.randomInt(snapshot.children.length);
    path.push(index);
    snapshot = snapshot.children[index];
  }
  return path;
}
Example #5
Source File: op-generator.ts    From slate-ot with MIT License 6 votes vote down vote up
generateRandomInsertNodeOp = (snapshot): Operation => {
  const randomPath = getRandomPathTo(snapshot);

  const parent = <Ancestor>Node.get(snapshot, Path.parent(randomPath));

  let node;

  if (parent.children[0] && Text.isText(parent.children[0])) {
    node = { text: fuzzer.randomWord() };
  } else if (!parent.children[0] && fuzzer.randomInt(3) === 0) {
    node = { text: fuzzer.randomWord() };
  } else if (fuzzer.randomInt(2) === 0) {
    node = {
      type: BLOCKS[fuzzer.randomInt(BLOCKS.length)],
      children: [{ text: fuzzer.randomWord() }, { text: fuzzer.randomWord() }],
    };
  } else {
    node = {
      type: BLOCKS[fuzzer.randomInt(BLOCKS.length)],
      children: [
        {
          type: BLOCKS[fuzzer.randomInt(BLOCKS.length)],
          children: [
            { text: fuzzer.randomWord() },
            { text: fuzzer.randomWord() },
          ],
        },
      ],
    };
  }

  return {
    type: 'insert_node',
    path: randomPath,
    node,
  };
}
Example #6
Source File: transMoveNode.ts    From slate-ot with MIT License 6 votes vote down vote up
reverseMove = (
  rem: RemoveNodeOperation,
  ins: InsertNodeOperation
): MoveNodeOperation => {
  let path = ins.path;
  let newPath = Path.transform(rem.path, ins)!;

  if (Path.isSibling(path, newPath) && Path.endsBefore(path, newPath)) {
    newPath = Path.previous(newPath);
  }

  return {
    type: 'move_node',
    path,
    newPath,
  };
}
Example #7
Source File: transMoveNode.ts    From slate-ot with MIT License 6 votes vote down vote up
composeMove = (
  rem: RemoveNodeOperation,
  ins: InsertNodeOperation
): MoveNodeOperation => {
  let path = rem.path;
  let newPath = Path.transform(ins.path, {
    ...rem,
    type: 'insert_node',
  })!;

  // this is a trick in slate:
  //   normally moving destination is right BEFORE newPath,
  //   however when the condition holds, it becomes right AFTER newPath
  if (Path.isSibling(path, newPath) && Path.endsBefore(path, newPath)) {
    newPath = Path.previous(newPath);
  }

  return {
    type: 'move_node',
    path,
    newPath,
  };
}
Example #8
Source File: transMoveNode.ts    From slate-ot with MIT License 6 votes vote down vote up
decomposeMove = (
  op: MoveNodeOperation
): [RemoveNodeOperation, InsertNodeOperation] => {
  const rem: RemoveNodeOperation = {
    type: 'remove_node',
    path: op.path.slice(),
    node: { text: '' },
  };

  const ins: InsertNodeOperation = {
    type: 'insert_node',
    path: Path.transform(op.path, op)!,
    node: { text: '' },
  };

  return [rem, ins];
}
Example #9
Source File: op-generator.ts    From slate-ot with MIT License 6 votes vote down vote up
generateRandomMoveNodeOp = (snapshot): Operation | null => {
  let count = 0;
  while (count < 10) {
    count++;
    const path = getRandomPathFrom(snapshot);
    const newPath = getRandomPathTo(snapshot);

    if (Path.isSibling(path, newPath)) {
      const parent = <Ancestor>Node.get(snapshot, Path.parent(newPath));
      if (newPath[newPath.length - 1] == parent.children.length) {
        newPath[newPath.length - 1]--;
      }
    }

    if (!Path.isAncestor(path, newPath)) {
      return {
        type: 'move_node',
        path,
        newPath,
      };
    }
  }
  return null;
}
Example #10
Source File: utils.ts    From slate-ot with MIT License 6 votes vote down vote up
makeOp = {
  insertText: (
    path: Path,
    offset: number,
    text: string
  ): InsertTextOperation => {
    return {
      type: 'insert_text',
      path,
      offset,
      text,
    };
  },

  removeText: (
    path: Path,
    offset: number,
    text: string
  ): RemoveTextOperation => {
    return {
      type: 'remove_text',
      path,
      offset,
      text,
    };
  },

  insertNode: (path: Path, node: Node): InsertNodeOperation => {
    return {
      type: 'insert_node',
      path,
      node,
    };
  },

  removeNode: (path: Path, node: Node): RemoveNodeOperation => {
    return {
      type: 'remove_node',
      path,
      node,
    };
  },

  splitNode: (path: Path, position: number): SplitNodeOperation => {
    return {
      type: 'split_node',
      path,
      position,
      target: null,
      properties: {},
    };
  },

  mergeNode: (path: Path, position: number): MergeNodeOperation => {
    return {
      type: 'merge_node',
      path,
      position,
      target: null,
      properties: {},
    };
  },

  moveNode: (path: Path, newPath: Path): MoveNodeOperation => {
    return {
      type: 'move_node',
      path,
      newPath,
    };
  },

  setNode: (path: Path, newProperties: Partial<Node>): SetNodeOperation => {
    return {
      type: 'set_node',
      path,
      properties: {},
      newProperties,
    };
  },
}
Example #11
Source File: OT.ts    From slate-ot with MIT License 6 votes vote down vote up
pathTransform = (
  leftOp: NodeOperation | TextOperation,
  rightOp: Operation
): (NodeOperation | TextOperation)[] => {
  let path = Path.transform(leftOp.path, rightOp);

  return path
    ? [
        {
          ...leftOp,
          path,
        },
      ]
    : [];
}
Example #12
Source File: InitialLetterPluginAction.ts    From react-editor-kit with MIT License 6 votes vote down vote up
getTextNodeOffset = (element: Element, childPath: Path) => {
  const childIndex = childPath[childPath.length - 1];
  let offset = 0;
  element.children
    .filter((child: Node, index: number) => index < childIndex)
    .forEach((node) => {
      offset += Node.string(node).length;
    });
  return offset;
}
Example #13
Source File: Focus.ts    From react-editor-kit with MIT License 6 votes vote down vote up
useFocused = (node?: Node) => {
  const { editor } = useEditorKit();
  const { selection } = editor;
  const [focus, setFocus] = useState({
    isFocused: false,
    isFocusedWithin: false,
  });
  useEffect(() => {
    if (!node) {
      return;
    }
    const path = ReactEditor.findPath(editor, node);
    let isFocused = false;
    let isFocusedWithin = false;
    if (selection) {
      const { focus } = selection;
      isFocusedWithin = Path.isDescendant(focus.path, path);
      isFocused = Path.equals(focus.path, path);
    }
    setFocus({ isFocused, isFocusedWithin });
  }, [node, selection]);

  return focus;
}
Example #14
Source File: Editor.tsx    From react-editor-kit with MIT License 6 votes vote down vote up
getSelectionRootNodes = (
  selection: Range,
  editor: ReactEditor
) => {
  const anchor = selection.anchor.path.slice();
  //Move to Element
  anchor.pop();
  const focus = selection.focus.path.slice(0, anchor.length);
  const nodes: Node[] = [];
  for (let [node, path] of SlateEditor.nodes(editor, {
    at: selection,
  })) {
    //Ignore text nodes, the editor node and nodes below the selection
    if (!node.type || !path.length || !(path.length <= focus.length)) {
      continue;
    }
    const hasParent = nodes.find((_node) =>
      Path.isParent(ReactEditor.findPath(editor, _node), path)
    );
    //Don't add a node if it's parent (root) has been added already
    if (hasParent) {
      continue;
    }
    const equal = Path.equals(path, anchor) || Path.equals(path, focus);
    const within = Path.isAfter(path, anchor) && Path.isBefore(path, focus);
    //Is same path or within the range of the selection
    if (equal || within) {
      nodes.push(node);
    }
  }
  return nodes;
}
Example #15
Source File: editable.component.ts    From slate-angular with MIT License 6 votes vote down vote up
private onDOMClick(event: MouseEvent) {
        if (
            !this.readonly &&
            hasTarget(this.editor, event.target) &&
            !this.isDOMEventHandled(event, this.click) &&
            isDOMNode(event.target)
        ) {
            const node = AngularEditor.toSlateNode(this.editor, event.target);
            const path = AngularEditor.findPath(this.editor, node);
            const start = Editor.start(this.editor, path);
            const end = Editor.end(this.editor, path);

            const startVoid = Editor.void(this.editor, { at: start });
            const endVoid = Editor.void(this.editor, { at: end });

            if (startVoid && endVoid && Path.equals(startVoid[1], endVoid[1])) {
                const range = Editor.range(this.editor, start);
                Transforms.select(this.editor, range);
            }
        }
    }
Example #16
Source File: string.component.ts    From slate-angular with MIT License 6 votes vote down vote up
getViewType() {
        const path = AngularEditor.findPath(this.viewContext.editor, this.context.text);
        const parentPath = Path.parent(path);

        // COMPAT: Render text inside void nodes with a zero-width space.
        // So the node can contain selection but the text is not visible.
        if (this.viewContext.editor.isVoid(this.context.parent)) {
            return this.viewContext.templateComponent.emptyStringTemplate;
        }

        // COMPAT: If this is the last text node in an empty block, render a zero-
        // width space that will convert into a line break when copying and pasting
        // to support expected plain text.
        if (
            this.context.leaf.text === '' &&
            this.context.parent.children[this.context.parent.children.length - 1] === this.context.text &&
            !this.viewContext.editor.isInline(this.context.parent) &&
            Editor.string(this.viewContext.editor, parentPath) === ''
        ) {
            return this.viewContext.templateComponent.lineBreakEmptyStringTemplate;
        }

        // COMPAT: If the text is empty, it's because it's on the edge of an inline
        // node, so we render a zero-width space so that the selection can be
        // inserted next to it still.
        if (this.context.leaf.text === '') {
            return this.viewContext.templateComponent.emptyTextTemplate;
        }

        // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
        // so we need to add an extra trailing new lines to prevent that.
        if (this.context.isLast && this.context.leaf.text.slice(-1) === '\n') {
            return this.viewContext.templateComponent.compatStringTemplate;
        }

        return this.viewContext.templateComponent.stringTemplate;
    }
Example #17
Source File: on-code-block-keydown.ts    From fantasy-editor with MIT License 6 votes vote down vote up
onKeyDownCodeBlock = (e: KeyboardEvent, editor: Editor) => {
  if (isHotkey('Enter', e)) {
    const match = Editor.above(editor, {
      match: n => n.type === BLOCK_CODE,
    });
    if (match) {
      const [, path] = match;
      const text = Editor.string(editor, path);
      e.preventDefault();
      const {selection} = editor;
      if(selection && Range.isCollapsed(selection)){
        const start = Editor.end(editor, path);
        if(text.endsWith('\n')&&Point.equals(selection.anchor, start)){
          Transforms.delete(editor, {
            at: {
              anchor: {
                offset: selection.anchor.offset-1,
                path: selection.anchor.path
              },
              focus: selection.focus
            }
          })
          let nextPath = Path.next(path);
          Transforms.insertNodes(editor, {
            type: BLOCK_PARAGRAPH,
            children: [{
              text: ''
            }]
          }, {
            at: nextPath
          });
          Transforms.select(editor, nextPath);
          return;
        }
      }
      editor.insertText('\n');
    }
  }
}
Example #18
Source File: on-list-keydown.ts    From fantasy-editor with MIT License 6 votes vote down vote up
addNew = (editor: Editor) => {
  editor.insertBreak();
  const { selection } = editor;
  if (selection) {
    const {
      anchor: { path },
    } = selection;
    const match = Editor.above(editor, {
      match: (n: any) => n.type === BLOCK_LI,
    });
    if (match) {
      const [, liPath] = match;
      const itemPath = path.slice(0, liPath.length + 1);
      const nextPath = Path.next(liPath);
      Transforms.moveNodes(editor, {
        at: itemPath,
        to: nextPath,
      });
      Transforms.wrapNodes(editor, {
        type: BLOCK_LI,
        children: [],
      });
    }
  }
}
Example #19
Source File: on-table-keydown.ts    From fantasy-editor with MIT License 6 votes vote down vote up
getLastNode = (editor: Editor, lastPath: Path): NodeEntry => {
  let i = lastPath.length;
  while (i > 0) {
    const path = lastPath.slice(0, i);
    const node = Editor.node(editor, path);
    if (!!node[0].type) {
      return node;
    }
    i--;
  }
  return Editor.node(editor, lastPath.slice(0, 1));
}
Example #20
Source File: with-trailing-node.ts    From fantasy-editor with MIT License 6 votes vote down vote up
withTrailingNode = () => <T extends Editor>(editor: T) => {
  const { normalizeNode } = editor;
  editor.normalizeNode = ([currentNode, currentPath]) => {
    if (!currentPath.length) {
      const [lastNode, lastPath] = getLastNode(editor, 1);
      if ([BLOCK_IMAGE].includes(lastNode.type as string)) {
        Transforms.insertNodes(
          editor,
          {
            type: BLOCK_PARAGRAPH,
            children: [{ text: '' }],
          },
          { at: Path.next(lastPath) },
        );
      }
      const path = lastPath.slice(0, 1);
      const [node] = Editor.node(editor, path);
      if (node.type === BLOCK_TABLE) {
        Transforms.insertNodes(
          editor,
          {
            type: BLOCK_PARAGRAPH,
            children: [{ text: '' }],
          },
          { at: Path.next(path) },
        );
      }
    }
    return normalizeNode([currentNode, currentPath]);
  };
  return editor;
}
Example #21
Source File: insert-row.ts    From fantasy-editor with MIT License 6 votes vote down vote up
insertRow = (editor: Editor, position: 'top' | 'bottom') => {
  const currentRow = Editor.above(editor, {
    match: n => n.type === BLOCK_TABLE_ROW,
  });
  if (currentRow) {
    const [, rowPath] = currentRow;
    const [tableNode, tablePath] = Editor.parent(editor, rowPath);
    const col = tableNode.col as number;
    Transforms.insertNodes(editor, getEmptyTableRow(col), {
      at: position === 'top' ? rowPath : Path.next(rowPath),
      select: true,
    });
    const row = (tableNode.row as number) + 1;
    Transforms.setNodes(
      editor,
      {
        row,
      },
      {
        at: tablePath,
      },
    );
  }
}
Example #22
Source File: insert-column.ts    From fantasy-editor with MIT License 6 votes vote down vote up
insertColumn = (editor: Editor, position: 'left' | 'right') => {
  const currentCell = Editor.above(editor, {
    match: n => n.type === BLOCK_TABLE_CELL,
  });
  if (currentCell) {
    const currentTable = Editor.above(editor, {
      match: n => n.type === BLOCK_TABLE,
    });
    if (currentTable) {
      const [, cellPath] = currentCell;
      const [tableNode, tablePath] = currentTable;
      const nextCellPath = position === 'left' ? cellPath : Path.next(cellPath);
      const newCellPath = nextCellPath.slice();
      const replacePathPos = newCellPath.length - 2;
      const currentRowIdx = newCellPath[replacePathPos];
      tableNode.children.forEach((row, rowIdx) => {
        newCellPath[replacePathPos] = rowIdx;
        Transforms.insertNodes(editor, getEmptyTableCell(), {
          at: newCellPath,
          select: rowIdx === currentRowIdx,
        });
      });
      const col = (tableNode.col as number) + 1;
      Transforms.setNodes(
        editor,
        {
          col,
        },
        {
          at: tablePath,
        },
      );
    }
  }
}
Example #23
Source File: MessageFormatEditor.tsx    From project-loved-web with MIT License 6 votes vote down vote up
function addRanges(ranges: Range[], path: Path, elements: MessageFormatElement[]): void {
  for (const element of elements) {
    ranges.push({
      anchor: { path, offset: element.location!.start.offset },
      focus: { path, offset: element.location!.end.offset },
      type: element.type,
    } as Range);

    if (element.type === TYPE.select || element.type === TYPE.plural) {
      for (const option of Object.values(element.options)) {
        addRanges(ranges, path, option.value);
      }
    }

    if (element.type === TYPE.tag) {
      addRanges(ranges, path, element.children);
    }
  }
}
Example #24
Source File: op-generator.ts    From slate-ot with MIT License 5 votes vote down vote up
getAllTextPaths = (node: Node): Path[] => {
  let array: Path[] = [];
  for (let [, p] of Node.texts(node)) {
    array.push(p);
  }
  return array;
}
Example #25
Source File: transSetNode.ts    From slate-ot with MIT License 5 votes vote down vote up
transSetNode = (
  leftOp: SetNodeOperation,
  rightOp: Operation,
  side: 'left' | 'right'
): SetNodeOperation[] => {
  switch (rightOp.type) {
    case 'split_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [
          leftOp,
          {
            ...leftOp,
            path: Path.next(leftOp.path),
          },
        ];
      }

      return <SetNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'merge_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [];
      }

      return <SetNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'set_node': {
      if (!Path.equals(leftOp.path, rightOp.path)) {
        return [leftOp];
      }

      return side === 'left'
        ? [
            {
              ...leftOp,
              newProperties: {
                ...rightOp.newProperties,
                ...leftOp.newProperties,
              },
            },
          ]
        : [
            {
              ...leftOp,
              newProperties: {
                ...leftOp.newProperties,
                ...rightOp.newProperties,
              },
            },
          ];
    }

    // insert_text
    // remove_text
    // insert_node
    // remove_node
    // move_node
    default:
      return <SetNodeOperation[]>pathTransform(leftOp, rightOp);
  }
}
Example #26
Source File: on-list-keydown.ts    From fantasy-editor with MIT License 5 votes vote down vote up
moveDown = (editor: Editor) => {
  const { selection } = editor;
  if (selection) {
    const match = Editor.above(editor, {
      match: (n: any) => n.type === BLOCK_LI,
    });
    if (match) {
      const [, liPath] = match;
      const prevPath = Path.previous(liPath);
      const [prevNode] = Editor.node(editor, prevPath);
      const [lastNode, lastNodePath] = Editor.node(editor, prevPath.concat([(prevNode as any).children.length - 1]));
      if ([BLOCK_OL, BLOCK_UL].includes(lastNode.type as string)) {
        const newPath = lastNodePath.concat([(lastNode as any).children.length]);
        Transforms.moveNodes(editor, {
          at: liPath,
          to: newPath,
        });
      } else {
        const newPath = prevPath.concat([(prevNode as any).children.length]);
        Transforms.moveNodes(editor, {
          at: liPath,
          to: newPath,
        });
        const outerMatch = Editor.above(editor, {
          match: (n: any) => [BLOCK_UL, BLOCK_OL].includes(n.type),
        });
        if (outerMatch) {
          Transforms.wrapNodes(
            editor,
            {
              type: outerMatch[0].type,
              children: [],
            },
            {
              at: newPath,
            },
          );
        }
      }
    }
  }
}
Example #27
Source File: on-list-keydown.ts    From fantasy-editor with MIT License 5 votes vote down vote up
moveUp = (editor: Editor) => {
  const { selection } = editor;
  if (selection) {
    const match = Editor.above(editor, {
      match: (n: any) => n.type === BLOCK_LI,
    });
    if (match) {
      const [, liPath] = match;
      let nextPath = Path.next(liPath.slice(0, liPath.length - 1));
      let nest = false;
      if (nextPath.length > 1) {
        const [parent] = Editor.node(editor, nextPath.slice(0, nextPath.length - 1));
        if (parent.type === BLOCK_LI) {
          nest = true;
        }
      }
      if (nest) {
        nextPath = Path.next(nextPath.slice(0, nextPath.length - 1));
        const [pNode, pPath] = Editor.node(editor, liPath.slice(0, liPath.length - 1));
        if ((pNode as any).children.length === 1) {
          Transforms.unwrapNodes(editor, {
            at: pPath,
          });
          Transforms.moveNodes(editor, {
            at: pPath,
            to: nextPath,
          });
        } else {
          Transforms.moveNodes(editor, {
            at: liPath,
            to: nextPath,
          });
        }
      } else {
        Transforms.moveNodes(editor, {
          at: liPath,
          to: nextPath,
        });
        Transforms.unwrapNodes(editor, {
          at: nextPath,
        });
      }
    }
  }
}
Example #28
Source File: op-generator.ts    From slate-ot with MIT License 5 votes vote down vote up
generateRandomMergeNodeOp = (snapshot): Operation | null => {
  const randomPath = getRandomPathFrom(snapshot);

  if (randomPath.length == 0 || randomPath[randomPath.length - 1] == 0) {
    return null;
  }

  const prev = Node.get(snapshot, Path.previous(randomPath));

  const node = Node.get(snapshot, randomPath);

  const properties = {};

  // Object.keys(prev).forEach((key) => {
  //   if (key !== 'text' && key !== 'children') {
  //     properties[key] = null;
  //   }
  // });
  // Object.keys(node).forEach((key) => {
  //   if (key !== 'text' && key !== 'children') {
  //     properties[key] = node[key];
  //   }
  // });

  if (Text.isText(prev) && Text.isText(node)) {
    return {
      type: 'merge_node',
      path: randomPath,
      position: prev.text.length,
      target: null,
      properties,
    };
  }

  if (!Text.isText(prev) && !Text.isText(node)) {
    return {
      type: 'merge_node',
      path: randomPath,
      position: prev.children.length,
      target: null,
      properties,
    };
  }

  return null;
}
Example #29
Source File: string.tsx    From slate-vue with MIT License 5 votes vote down vote up
string = tsx.component({
  props: {
    leaf: {
      type: Object as PropType<Text>
    },
    editor: Object
  },
  inject: ['isLast', 'parent', 'text'],
  components: {
    TextString
  },
  data(): providedByText {
    return {}
  },
  render() {
    const { leaf, editor, isLast, parent, text } = this
    const path = VueEditor.findPath(editor, text as Node)
    const parentPath = Path.parent(path)

    // COMPAT: Render text inside void nodes with a zero-width space.
    // So the node can contain selection but the text is not visible.
    if (editor.isVoid(parent)) {
      return <ZeroWidthString length={Node.string(parent as Node).length} />
    }

    // COMPAT: If this is the last text node in an empty block, render a zero-
    // width space that will convert into a line break when copying and pasting
    // to support expected plain text.
    if (
      leaf.text === '' &&
      (parent as Element & Editor).children[(parent as Element & Editor).children.length - 1] === text &&
      !editor.isInline(parent) &&
      Editor.string(editor, parentPath) === ''
    ) {
      return <ZeroWidthString isLineBreak={true} />
    }

    // COMPAT: If the text is empty, it's because it's on the edge of an inline
    // node, so we render a zero-width space so that the selection can be
    // inserted next to it still.
    if (leaf.text === '') {
      return <ZeroWidthString />
    }

    // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
    // so we need to add an extra trailing new lines to prevent that.
    if (isLast && leaf.text.slice(-1) === '\n') {
      return <TextString isTrailing={true} text={leaf.text} />
    }

    return <TextString text={leaf.text} />
  }
})