5

I am currently working on a slate text editor where the user can add images and text. I would also like to have a hovering toolbar which serves different buttons depending on what type of element the user has selected.

For example if the user has selected an image then I would like to serve one set of buttons. If the user has selected a paragraph I would like to serve another set of buttons.

After looking through the examples found here:

https://www.slatejs.org/examples/richtext

I have pieced together a rough example of my desired text editor without the context dependant hovering toolbar buttons:

https://codesandbox.io/s/suspicious-pine-lrxgw

But I am struggling to work out how to detect what type of element is selected within the editor? I don't know if there is a way to do this using slate-react? Or even in vanilla JS?

Ideally I would also be able to get other information about the element as well. For example the images height and width as this would help with styling.

Any help appreciated

Conor Heena
  • 86
  • 1
  • 2
  • 13

3 Answers3

7

You can get the currently focused node by using the selection property from the editor and which is a Range. You then use the anchor or focus to select the currently selected children.

anchor & focus are Points which are basically an array the first item is the current block and the second item indicates the position in the block.

Point objects refer to a specific location in a text node in a Slate document. Its path refers to the location of the node in the tree, and its offset refers to distance into the node's string of text. Points may only refer to Text nodes.

using the selection array we can get the current selected block like so

if (selection !== null && selection.anchor !== null) {
  selected = editor.children[selection.anchor.path[0]];
} else {
  selected = null;
}

Later in your render function you can have a check to render what you want.

fayeed
  • 2,375
  • 16
  • 22
1

Here, path comes from selection.anchor.path, which I noticed will walk into leaves down to a final text node (I want the cell of my table, not the text), same with paragraphs. I want the full { type: 'paragraph', children: [...text node] }, not just the text. So, I pop the end off and reduce.

    const pathClone = [...path];
    pathClone.pop(); // get rid of trailing text node postion in path.
    const focusedNode = pathClone.reduce((node, pathPosition) => {
      if (!node) return editor.children[pathPosition];
      return node.children[pathPosition];
    }, null);
    // console.log('last node type before text node: ', focusedNode.type, focusedNode);

cursor at focus here table cell

console log of input and output

Neil Gaetano Lindberg
  • 2,488
  • 26
  • 23
0

Looking at the slate source code, you could do:

const selectedNode = editor.selection && Editor.node(editor, editor.selection.focus);

Poelinca Dorin
  • 9,577
  • 2
  • 39
  • 43