Slate: Single line?

Created on 13 Nov 2016  路  16Comments  路  Source: ianstormtaylor/slate

Is there a way to restrict the editor to a single line? I'm looking for an extension of input rather than textarea, essentially.

question

Most helpful comment

This issue is old and closed, but I had the same problem and came up with a nice solution. I'm posting it here in case anybody else needs it. 馃槈

function withSingleLine(editor) {
  const { normalizeNode } = editor;

  editor.normalizeNode = ([node, path]) => {
    if (path.length === 0) {
      if (editor.children.length > 1) {
        Transforms.mergeNodes(editor);
      }
    }

    return normalizeNode([node, path]);
  };

  return editor;
}

Then I just use the withSingleLine plugin while creating the editor:

withSingleLine(withReact(createEditor()))

All 16 comments

My ideas are to either cause the enter key to do nothing, and perhaps strip newlines from pasted text; or better yet, do what the Draft.js single-line plugin does, and collapse blocks down to one block inside an onChange handler.

Any thoughts / recommendations? :)

You can use a schema to enforce only one block in the document:

const plugins = [
{
    schema: {
         rules: [
              {
                    match(node) {
                        return node.kind === 'document';
                    },
                    validate(node) {
                        return node.nodes.size > 1 ? true : null
                    },
                    normalize(transform, node, value) {
                        const toRemove = node.nodes.slice(1);
                        toRemove.forEach(child => transform.removeNodeByKey(child.key)
                    }
                }
         ]
    }
}

]

(Not tested).

馃憤 to what @SamyPesse said about schema.

As a baseline you want to introduce a schema validation/transform so that you can be confident that the document won't ever get in a state with multiple blocks.

But then in addition to that, you'll probably want to layer in other logic for better UX. With only the schema restriction, if someone hits enter the block will be split, and the second block immediately removed. So you'd probably want to prevent enter from doing anything instead. Similar weirdness's would crop up onPaste, onDrop potentially that you could design better user experiences for.

Edit: Your onChange handler idea also could work! It might be an easier way to achieve the right UX too.

Hope that helps! Good use case!

Thanks for the tips. onChange turned out to be a good approach - I'll try to clean up my code into a reusable plugin. (The schema rule raised errors which I didn't pursue fixing, so maybe that approach can also work out.)

Hey @aehlke. Did ya have that code lying around somewhere for perusal? Would be cool to see. Thanks

@frederickfogerty https://github.com/aehlke/manabi/blob/b47e1dcfec31bacc189ab4def48237287c7e7bc5/manabi/static/js/index.jsx#L582-L614 this repo is AGPLv3, let me know if you need this snippet relicensed.

Hey @aehlke. Thanks so much! It seems like the repo is MIT (just had a brief look at the root LICENSE, am I missing something?). I'll check with our team if we need it licensed, but MIT should be fine.

Ah that's a mistake, sorry, will fix - the JS at least is AGPL: https://github.com/aehlke/manabi/blob/eb10101c34db02bef7d88f3a6ff9046577d2489d/package.json

Hey there, I realise this is closed and quite old. I've searched a few other issues but can't seem to find what I'm looking for there or in the examples (maybe I've just missed it).

Wondering if there was a way to restrict input to a single line or n' number of lines or n' characters, similar to what this original question was about.

I've tried variations of the above schema example by @SamyPesse but I've been unable to get that working. I've also attempted the collapseToSingleLine from @aehlke but it appears transform is no longer supported on the Value object?

This may be my implementation or perhaps the version of Slate has since changed - I'm trying out Slate v 0.44.10.

Again, I realise this is really old but if anyone wouldn't mind pointing me in the right direction I'd be really grateful. Cheers.

This is timely; this is the next step in my project! This is a HUGE use case and if there was a well-documented, standard way to address, I would be most grateful!

I had a very simple rich text editor use case:

  • User cannot author blocks, other than a single default paragraph, i.e., <div>
  • User has limited ability to mark, i.e., underline, code, subscript, and superscript

Here is what I did

  schema = {
    document: {
      nodes: [
        {
          match: { type: 'paragraph' },
          min: 1,
          max: 1
        },
      ],
    },
    blocks: {
      paragraph: {
        marks: [
          { type: 'underline' },
          { type: 'code' },
          { type: 'superscript' },
          { type: 'subscript' }
        ],
        nodes: [
          { match: { object: 'text' } }
        ]
      },
    }
  };

In the beginning, I was writing my own normalize functions (this one was listed right after nodes in document)

 normalize: (editor, { code, child }) => {
    switch (code) {
      case 'child_max_invalid':
        return editor.removeNodeByKey(child.key);
      default:
        break;
      }
    }
  }  

But, upon testing, I realized that the out-the-box functionality worked for everything I needed:

  • The first acceptable block was taken; the rest were removed
  • Non-matching blocks were filtered out
  • Non-whitelisted marking was prohibited

I realize that this open-ended schema functionality is some of the most daunting and tedious to pull of, but it looks like it works for general use cases like mine. I must admit, however, that this portion of the documentation was kind of confusing: it took me a while to realize that I could nest the marks rule within the blocks object. I kept trying to add a rules array on to the end of my schema object and I couldn't get it working. This is an area where more examples would really be helpful; I am sharing the above in this spirit! Comments and edits gratuitously accepted.

This issue is old and closed, but I had the same problem and came up with a nice solution. I'm posting it here in case anybody else needs it. 馃槈

function withSingleLine(editor) {
  const { normalizeNode } = editor;

  editor.normalizeNode = ([node, path]) => {
    if (path.length === 0) {
      if (editor.children.length > 1) {
        Transforms.mergeNodes(editor);
      }
    }

    return normalizeNode([node, path]);
  };

  return editor;
}

Then I just use the withSingleLine plugin while creating the editor:

withSingleLine(withReact(createEditor()))

@PullJosh's solution worked well for us. In addition, we wanted to enable automatic scrolling to the caret (as in regular HTML inputs). I'm posting our hacky solution here, in case anybody else needs it :)

const [prevSelection, setPrevSelection] = useState({ anchor: { path: [0, 0], offset: 0 }, focus: { path: [0, 0], offset: 0 }});
useEffect(() => {
  if (!prevSelection || !ReactEditor.isFocused(editor)) return;
  const editorDOM = ReactEditor.toDOMNode(editor, editor);
  if (!editorDOM) return;
  const padding = 5;
  const editorRects = editorDOM.getClientRects();
  const caretTextDom = ReactEditor.toDOMRange(editor, prevSelection);
  const caretTextRects = caretTextDom.getClientRects();
  if (caretTextRects.length > 0 && editorRects.length > 0) {
    const caretX = caretTextRects[0].left - editorRects[0].left + editorDOM.scrollLeft;
    if (caretX - editorDOM.clientWidth + padding > editorDOM.scrollLeft) {
      editorDOM.scrollTo(caretX - editorDOM.clientWidth + padding, 0);
    } else if (caretX - padding < editorDOM.scrollLeft) {
      editorDOM.scrollTo(caretX - padding, 0);
    }
  }
}, [editor, prevSelection]);
return (
  <Slate
    onChange={v => {
      const { selection } = editor;
      if (selection) setPrevSelection(selection);
    }}
  >
    ...

For anyone interested in this, here's a working example with @PullJosh and @lzear snippets:

https://codesandbox.io/s/slate-single-line-2190g?file=/src/PlaceholderInput.tsx

For anyone interested in this, here's a working example with @PullJosh and @lzear snippets:

https://codesandbox.io/s/slate-single-line-2190g?file=/src/PlaceholderInput.tsx

404?

For anyone interested in this, here's a working example with @PullJosh and @lzear snippets:
https://codesandbox.io/s/slate-single-line-2190g?file=/src/PlaceholderInput.tsx

404?
@beqaweb it's back, no idea why it got deleted

Was this page helpful?
0 / 5 - 0 ratings

Related issues

varoot picture varoot  路  3Comments

YurkaninRyan picture YurkaninRyan  路  3Comments

JSH3R0 picture JSH3R0  路  3Comments

bunterWolf picture bunterWolf  路  3Comments

ianstormtaylor picture ianstormtaylor  路  3Comments