slate#Node TypeScript Examples

The following examples show how to use slate#Node. 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: playground-current.stories.tsx    From remark-slate-transformer with MIT License 6 votes vote down vote up
SlateToMarkdown = () => {
  const [value, setValue] = useState(toSlate(text));
  const [md, setMd] = useState(toMd(value));
  const ref = useRef<Node[]>(null);
  return (
    <Wrapper>
      <SlateEditor ref={ref} initialValue={value} />
      <div style={{ padding: 10 }}>
        <button
          style={{ height: "100%" }}
          onClick={() => {
            if (!ref.current) return;
            setMd(toMd(ref.current));
          }}
        >
          {"slate -> md"}
        </button>
      </div>
      <Text>{md}</Text>
    </Wrapper>
  );
}
Example #3
Source File: code-block-decorate.ts    From fantasy-editor with MIT License 6 votes vote down vote up
codeBlockDecorate = (entry: NodeEntry, editor: Editor) => {
  const ranges: any = [];
  const [node, path] = entry;
  if (node.type === BLOCK_CODE) {
    const text = Node.string(node);
    const langName: any = node.lang || 'markup';
    const lang = languages[langName];
    if (lang) {
      const tokens = tokenize(text, lang);
      let offset = 0;
      const {ranges: r} = buildRange(tokens, path, offset);
      ranges.push(...r);
    }
  }
  return ranges;
}
Example #4
Source File: runtime-util.ts    From slate-vue with MIT License 6 votes vote down vote up
getChildren = (node: Node): any => {
  return Editor.isEditor(node) ? (node as any)._state: (node as any).children
}
Example #5
Source File: Editor.tsx    From react-editor-kit with MIT License 6 votes vote down vote up
isAtEndOfNode = (editor: ReactEditor) => {
  if (editor.selection) {
    const { anchor, focus } = editor.selection;
    const node = getActiveNode(editor);
    if (node) {
      const length = Node.string(node).length;
      return anchor.offset === length && focus.offset === length;
    }
  }
  return false;
}
Example #6
Source File: op-generator.ts    From slate-ot with MIT License 6 votes vote down vote up
getRandomLeafWithPath = (snapshot: Node): TextWithPath | null => {
  const paths = getAllTextPaths(snapshot);
  const path = paths[fuzzer.randomInt(paths.length)];

  if (!path || !path.length) {
    return null;
  }

  const t = Node.leaf(snapshot, path);

  return { ...t, path };
}
Example #7
Source File: LokiQueryFieldForm.tsx    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
constructor(props: LokiQueryFieldFormProps, context: React.Context<any>) {
    super(props, context);

    this.plugins = [
      BracesPlugin(),
      SlatePrism({
        onlyIn: (node: Node) => node.object === 'block' && node.type === 'code_block',
        getSyntax: (node: Node) => 'promql',
      }),
    ];
  }
Example #8
Source File: placeholder.component.ts    From slate-angular with MIT License 6 votes vote down vote up
placeholderDecorate: (editor: Editor) => SlatePlaceholder[] = (editor) => {
        const cursorAnchor = editor.selection?.anchor
        if(cursorAnchor) {
            const parent = Node.parent(editor,cursorAnchor.path)
            if(parent.children.length === 1 &&
                Array.from(Node.texts(parent)).length === 1 && 
                Node.string(parent) === '' ) {
                const start = Editor.start(editor, cursorAnchor)
                return [{
                    placeholder: 'advance placeholder use with placeholderDecoration',
                    anchor: start,
                    focus: start
                }];
            } else {
                return [];
            }
        }
        return [];
    };
Example #9
Source File: RichEditor.tsx    From Full-Stack-React-TypeScript-and-Node with MIT License 5 votes vote down vote up
RichEditor: FC<RichEditorProps> = ({ existingBody }) => {
  const [value, setValue] = useState<Node[]>(initialValue);
  const renderElement = useCallback((props) => <Element {...props} />, []);
  const renderLeaf = useCallback((props) => <Leaf {...props} />, []);
  const editor = useMemo(() => withHistory(withReact(createEditor())), []);

  useEffect(() => {
    if (existingBody) {
      setValue([
        {
          type: "paragraph",
          text: existingBody,
        },
      ]);
    }
  }, []);

  const onChangeEditorValue = (val: Node[]) => {
    setValue(val);
  };

  return (
    <Slate editor={editor} value={value} onChange={onChangeEditorValue}>
      <Toolbar>
        <MarkButton format="bold" icon="bold" />
        <MarkButton format="italic" icon="italic" />
        <MarkButton format="underline" icon="underlined" />
        <MarkButton format="code" icon="code" />
        <BlockButton format="heading-one" icon="header1" />
        <BlockButton format="block-quote" icon="in_quotes" />
        <BlockButton format="numbered-list" icon="list_numbered" />
        <BlockButton format="bulleted-list" icon="list_bulleted" />
      </Toolbar>
      <Editable
        className="editor"
        renderElement={renderElement}
        renderLeaf={renderLeaf}
        placeholder="Enter some rich text…"
        spellCheck
        autoFocus
        onKeyDown={(event) => {
          for (const hotkey in HOTKEYS) {
            if (isHotkey(hotkey, event as any)) {
              event.preventDefault();
              const mark = HOTKEYS[hotkey];
              toggleMark(editor, mark);
            }
          }
        }}
      />
    </Slate>
  );
}
Example #10
Source File: playground-current.stories.tsx    From remark-slate-transformer with MIT License 5 votes vote down vote up
toSlate = (s: string) => toSlateProcessor.processSync(s).result as Node[]
Example #11
Source File: serialize-html.ts    From fantasy-editor with MIT License 5 votes vote down vote up
serializeHtml = (nodes:Node[], formatCodeBlock:boolean = false) => {
  const editor = {
    children: nodes
  }
  return serialize(editor, formatCodeBlock);
}
Example #12
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} />
  }
})
Example #13
Source File: Editor.d.ts    From react-editor-kit with MIT License 5 votes vote down vote up
getSelectionRootNodes: (selection: Range, editor: ReactEditor) => Node[]
Example #14
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 #15
Source File: options.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
readonly onlyIn!: (node: Node) => boolean;
Example #16
Source File: editable.component.ts    From slate-angular with MIT License 5 votes vote down vote up
forceFlush() {
        timeDebug('start data sync');
        this.detectContext();
        this.cdr.detectChanges();
        // repair collaborative editing when Chinese input is interrupted by other users' cursors
        // when the DOMElement where the selection is located is removed
        // the compositionupdate and compositionend events will no longer be fired
        // so isComposing needs to be corrected
        // need exec after this.cdr.detectChanges() to render HTML
        // need exec before this.toNativeSelection() to correct native selection
        if (this.isComposing) {
            // Composition input text be not rendered when user composition input with selection is expanded
            // At this time, the following matching conditions are met, assign isComposing to false, and the status is wrong
            // this time condition is true and isComposiing is assigned false
            // Therefore, need to wait for the composition input text to be rendered before performing condition matching
            setTimeout(() => {
                const textNode = Node.get(this.editor, this.editor.selection.anchor.path);
                const textDOMNode = AngularEditor.toDOMNode(this.editor, textNode);
                let textContent = '';
                // skip decorate text
                textDOMNode.querySelectorAll('[editable-text]').forEach((stringDOMNode) => {
                    let text = stringDOMNode.textContent;
                    const zeroChar = '\uFEFF';
                    // remove zero with char
                    if (text.startsWith(zeroChar)) {
                        text = text.slice(1);
                    }
                    if (text.endsWith(zeroChar)) {
                        text = text.slice(0, text.length - 1);
                    }
                    textContent += text;
                });
                if (Node.string(textNode).endsWith(textContent)) {
                    this.isComposing = false;
                }
            }, 0);
        }
        this.toNativeSelection();
        timeDebug('end data sync');
    }
Example #17
Source File: Client.tsx    From slate-yjs-example with MIT License 4 votes vote down vote up
Client: React.FC<ClientProps> = ({ id, name, slug, removeUser }) => {
  const [value, setValue] = useState<Node[]>([]);
  const [isOnline, setOnlineState] = useState<boolean>(false);

  const color = useMemo(
    () =>
      randomColor({
        luminosity: "dark",
        format: "rgba",
        alpha: 1,
      }),
    []
  );

  const [sharedType, provider] = useMemo(() => {
    const doc = new Y.Doc();
    const sharedType = doc.getArray<SyncElement>("content");
    const provider = new WebsocketProvider(WEBSOCKET_ENDPOINT, slug, doc, {
      connect: false,
    });

    return [sharedType, provider];
  }, [id]);

  const editor = useMemo(() => {
    const editor = withCursor(
      withYjs(withLinks(withReact(withHistory(createEditor()))), sharedType),
      provider.awareness
    );

    return editor;
  }, [sharedType, provider]);

  useEffect(() => {
    provider.on("status", ({ status }: { status: string }) => {
      setOnlineState(status === "connected");
    });

    provider.awareness.setLocalState({
      alphaColor: color.slice(0, -2) + "0.2)",
      color,
      name,
    });

    // Super hacky way to provide a initial value from the client, if
    // you plan to use y-websocket in prod you probably should provide the
    // initial state from the server.
    provider.on("sync", (isSynced: boolean) => {
      if (isSynced && sharedType.length === 0) {
        toSharedType(sharedType, [
          { type: "paragraph", children: [{ text: "Hello world!" }] },
        ]);
      }
    });

    provider.connect();

    return () => {
      provider.disconnect();
    };
  }, [provider]);

  const { decorate } = useCursors(editor);

  const toggleOnline = () => {
    isOnline ? provider.disconnect() : provider.connect();
  };

  return (
    <Instance online={isOnline}>
      <Title>
        <Head>Editor: {name}</Head>
        <div style={{ display: "flex", marginTop: 10, marginBottom: 10 }}>
          <Button type="button" onClick={toggleOnline}>
            Go {isOnline ? "offline" : "online"}
          </Button>
          <Button type="button" onClick={() => removeUser(id)}>
            Remove
          </Button>
        </div>
      </Title>

      <EditorFrame
        editor={editor}
        value={value}
        decorate={decorate}
        onChange={(value: Node[]) => setValue(value)}
      />
    </Instance>
  );
}