Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I set the maxLength of characters? #3897

Closed
YuriOlepir opened this issue Sep 30, 2020 · 9 comments
Closed

How can I set the maxLength of characters? #3897

YuriOlepir opened this issue Sep 30, 2020 · 9 comments

Comments

@YuriOlepir
Copy link

Hi! Do you have a working solution or example how to set a limit of the characters in the editor?

@gitcatrat
Copy link

Take a look at value object and you might get an idea. Probably the easiest way is to iterate over value object when editor loads and add up all text fields lengths to totalCount. Then listen insertions and if totalCount + insertion > allowedCount, prevent it. You might also want to have different length logic for emojis and void elements.

Sorry about lack of code, I haven't touched my Slate codebase for few months and I'd have to look things up from docs, e.g how to listen insertion, etc. While you're at it, do the lookup yourself. Hope it helps!

@BrentFarese
Copy link
Collaborator

A topic like this is best discussed in the Slate Slack. Closing for now. Thanks!

@jonybekov
Copy link

jonybekov commented Nov 30, 2021

I was able to get max length using text property.
You can get it like this.
value.document.text.length <-- Here value:Value

No need to iterate recursively

@ProjectBarks
Copy link

ProjectBarks commented Feb 9, 2022

Posting for slack answers for vis.

Example of counting length: https://codesandbox.io/s/pedantic-http-0ojcl?file=/src/App.js

const editor = useMemo(() => withTextLimit()(withReact(createEditor())), []);

const withTextLimit = ({ limit = maxLength } = {}) => function Plugin(editor) {
    const { insertText } = editor;
    editor.insertText = (text) => {
      if (Editor.string(editor, []).length < limit) {
        insertText(text);
      } else {
        console.log('max limit reached!');
      }
    };
    return editor;
  }; 

(Only seems to work up to 0.72 due to slate js changes, have not checked pasted 0.72)

@nephix
Copy link

nephix commented Nov 9, 2022

@ProjectBarks this seems to limit typing new characters after a certain limit, but still allows users to paste text

@cjjeon
Copy link

cjjeon commented Jan 16, 2023

I've found the workaround. However, this method only works when you can have 1 child and text only.

const withTextLimit = (editor: Editor, characterLimit: number) => {
  const { apply } = editor;

  editor.apply = operation => {
    apply(operation);

    // running method such that it only has 1 child with text
    if (editor.children.length > 1) {
      const mergeNodeOperation: MergeNodeOperation = {
        type: 'merge_node',
        path: [1],
        position: 1,
        properties: editor.children[1],
      };
      apply(mergeNodeOperation);
    }

    // Make sure to remove overflow of text beyond character limit
    if (editor.children.length > 0) {
      const child = editor.children[0];
      if (child.children.length > 0) {
        const { text } = child.children[0];
        if (text.length > characterLimit) {
          const removeTextOperation: RemoveTextOperation = {
            type: 'remove_text',
            path: [0, 0],
            offset: characterLimit,
            text: text.substring(characterLimit),
          };
          apply(removeTextOperation);
        }
      }
    }
  };

  return editor;
};

@arslaanzafar
Copy link

Another way to limit characters is to use onDOMBeforeInput of Editable component

const handleDOMBeforeInput = (event) => {
        const inputType = event.inputType;
        if (inputType === 'insertText') {
            const textLength = Editor.string(editor, []).length;
            if (textLength >= 20) {
              event.preventDefault();
              return;
            }
        }
      };

@JanPeter
Copy link

JanPeter commented Mar 14, 2024

I got it working by inversing the operation after I apply it and the length is greater then my defined max length for my control.

function withMaxLength<T extends BaseEditor>(
  editor: T,
  maxLength: number | undefined
) {
  const { apply } = editor;

  editor.apply = (operation) => {
    const isAstChange = operation.type !== "set_selection";
    apply(operation);

    if (
      isAstChange &&
      maxLength !== undefined &&
      maxLength > 0 &&
      getTextLength(editor) > maxLength
    ) {
      const undo = Operation.inverse(operation);
      apply(undo);
    }
  };

  return editor;
}

function getTextLength(editor: BaseEditor) {
  let length = 0;

  for (const [node] of Node.nodes(editor)) {
    if (Text.isText(node)) {
      length += node.text.length;
    }
  }

  return length;
}

@Dokome
Copy link

Dokome commented Dec 24, 2024

My solution

  • support insert text slice
  • support composition event
const withMaxLength = (editor: ReactEditor & HistoryEditor) => {
	const { apply } = editor;

	editor.apply = (operation) => {
		const isInsertText = operation.type === 'insert_text';
		const currentLength = editor.string([]).length;
		const substr = MAX_LENGTH - currentLength;

		if (isInsertText) {
			if (substr > 0) {
				operation.text = operation.text.slice(0, substr);
				apply(operation);
			} else {
				apply(operation);
				apply(Operation.inverse(operation));
			}
		} else {
			apply(operation);
		}
	};

	return editor;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants