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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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} />
}
})