slate icon indicating copy to clipboard operation
slate copied to clipboard

Rich Text example: 'select all' and 'delete' does not delete everything when there is a list

Open aleksblendwerk opened this issue 6 years ago • 9 comments

Do you want to request a feature or report a bug?

A bug.

What's the current behavior?

On the current "Rich Text" example on https://www.slatejs.org/#/rich-text the following steps are causing an unexpected behavior:

  • Delete the default contents
  • Create a new unordered list
  • Press 'Enter'
  • Click on the 'unordered list' button again
  • Select all (with CTRL-A)
  • Press 'Delete'

The editor's contents are not entirely removed, instead the unordered list is kept and the cursor is still indented, without any list items.

0i2imaxy4c

What's the expected behavior?

The entire content should be deleted.

aleksblendwerk avatar Dec 14 '18 21:12 aleksblendwerk

This bug still exists, and there doesn't seem to be an easy way to get around it either.

dons20 avatar Feb 21 '19 21:02 dons20

Possible duct tape fix (haven't tested it): enforce via schema that first element is paragraph (or other).

adjourn avatar Feb 21 '19 23:02 adjourn

I'm working on an editor built similar to the rich text example, so I might test if the same issue occurs there and attempt a fix.

dons20 avatar Feb 21 '19 23:02 dons20

The same issue still occurs in mine. This is after replicating the steps twice. You'll notice there are still two blocks of type ordered list. image

I've tried using editor.moveToRangeOfDocument().delete() which did work to an extent...I attached it to a button. After clicking once, it didn't seem to activate, but after the 2nd and 3rd clicks they worked. However, upon examining the document, it also seemed to delete the required child block node, essentially leaving the editor in an unusable state.

The most effective quick-fix to this problem is to add a button or command to reset the editor to a default value. That way if it bugs out, you can return the editor to a usable state.

dons20 avatar Feb 23 '19 21:02 dons20

One possible fix is to keep track of both Ctrl+A and Delete usages. If Delete is used after select all Ctrl+A then you can use setBlocks(DEFAULT_NODE) to have it prevent the stacking in the first place. It's not the most efficient fix as it might have some unintended behaviours in rare cases, but I can confirm that it does work with this particular rich text example (and perhaps other similar list implementations).

const DEFAULT_NODE = "paragraph";
const isDeleteHotKey = isKeyHotkey("delete");
const isSelectAllHotKey = isKeyHotkey("mod+a");

//using React hooks
function Editor() {
  const [isAllSelected, setAllSelected] = useState(false);
  ...

  const onKeyDown = (event, editor, next) => {
    ...
    } else if (isSelectAllHotKey(event)) {
      setAllSelected(!isAllSelected); //Toggle selected flag for Ctrl+A
      return next();
    } else if (isDeleteHotKey(event)) { 
      if (isAllSelected) {
        editor.setBlocks(DEFAULT_NODE);
        setAllSelected(false);
        return next();
      } else {
        return next();
      }
    } else {
       if (isAllSelected) setAllSelected(false); //Ensure isAllSelected is false on other key presses.
       return next();
    }
  }
}

dons20 avatar Feb 25 '19 23:02 dons20

I had a similar issue and I fixed it with user-select: none.

const Uneditable: FunctionComponent = props => {
  return (
    <>
      <div contentEditable={false}>{props.children}</div>
      {/* 
        To fix errror: "IndexSizeError: Failed to execute 'getRangeAt'
        on 'Selection': 0 is not a valid index." and some others.
      */}
      <style jsx>{`
        div {
          user-select: none;
        }
      `}</style>
    </>
  );
};

steida avatar Mar 09 '19 01:03 steida

Went down the same path as @dons20 - here is the updated code to work with Slate 0.50+

import { Transforms, .. } from 'slate';

..

const isSelectAllHotkey = isHotkey('mod+a');
const isDeleteHotkey = isHotkey('backspace');

..

const [isAllSelected, setAllSelected] = React.useState(false);

..


onKeyDown={event => {
  if (isDeleteHotkey(event)) {
    if (isAllSelected) {
      Transforms.delete(editor);
      Transforms.setNodes(editor, { type: 'paragraph' });
      setAllSelected(false);
      return;
    }
  }

  if (isSelectAllHotkey(event)) {
    setAllSelected(!isAllSelected);
  } else {
    setAllSelected(false);
  }

While not ideal, it fixed the bug for me 🤷‍♂️

hanford avatar Apr 01 '20 05:04 hanford

This reproduced for use when using tables. Can reproduce here: https://www.slatejs.org/examples/tables

  1. delete everything after the table
  2. select all
  3. delete

Now we're always in a single td (without table, tr etc.) and trying to break out of it just adds more tds.

So to solve it we added this to the editor.insertBreak():

if (selection) {
    const [cell] = Editor.nodes(editor, {
        match: (n) => !Editor.isEditor(n) && SlateElement.isElement(n) && n.type === 'table-cell',
    });

    if (cell) {
        const [, cellPath] = cell;
        const start = Editor.start(editor, cellPath);
        const end = Editor.end(editor, cellPath);

        if (Point.equals(selection.anchor, end) || Point.equals(selection.anchor, start)) {
            Transforms.setNodes(editor, { type: 'container' });
        }
    }
}

insertBreak();

Hope this might help others

idanen avatar Feb 08 '23 06:02 idanen

I used Transforms.select(editor, []) and it work fine for me

https://docs.slatejs.org/concepts/03-locations

kumarajay0412 avatar Feb 14 '24 12:02 kumarajay0412