slate icon indicating copy to clipboard operation
slate copied to clipboard

Nesting Block Elements

Open jayaike opened this issue 1 year ago • 6 comments

Problem

I am creating a FeedbackElement that can wrap any element. So it could appear at the root of the editor (ie. one of the editors children) or as a child of some block node: like a paragraph. FeedbackElements can contain other FeedbackElements, or really any element, like images, lists, etc.

The only issue with this is that the default normalizeNode does not permit this (especially as a child of a paragraph). I could get this to work by completely disabling normalisation using a plugin that modifies normalizeNode to a noop function. But, of course, you don't want to do this because it creates its own sets of bugs.

The type definition for feedback is as follows:

export type FeedbackElement = {
  type: "feedback";
  feedback: string;
  children: Element[];
};

Solution There should be a way to nest block elements.

[
    {
        "type": "paragraph",
        "children": [
            {
                "text": "example "
            },
            {
                "type": "feedback",
                "feedback": "great job with this!"
                "children": [
                     {
                          "text": "some text "
                    },
                ]
            },
        ]
    },
    {
        "type": "paragraph",
        "children": [
            {
                "text": ""
            }
        ]
    }
]

jayaike avatar Mar 13 '23 14:03 jayaike

I also hit this problem, but with lists.

If I have a list like https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul

the slate editor will remove the sublist element in the <li> image

This is due to https://github.com/ianstormtaylor/slate/blob/1d8010be8500ef5c98dbd4179354db1986d5c8f4/packages/slate/src/create-editor.ts#L253-L256

More context: https://github.com/plone/volto/issues/4538

tiberiuichim avatar Mar 21 '23 14:03 tiberiuichim

@jayaike I've been exploring this similar problem, here's possible fixes:

  • override normalizeNode, wrap the inline children with a container <span> or something similar. This way Slate sees element as first child
  • my chosen fix was to write code in my normalizeNode that lifts the child <ul> to be a sibling of its original parent <li>

tiberiuichim avatar Mar 22 '23 15:03 tiberiuichim

@tiberiuichim yea im also thinking of modifying the default normalizeNode fn to support my needs.

  • Allow merging of adjacent text nodes that have the same properties
  • Allow nesting of block nodes

I see no reason why block nodes should not be nested. Nested block nodes would be similar to inline-block in regular HTML which is of course a thing.

jayaike avatar Mar 22 '23 16:03 jayaike

@jayaike We usually implement isInline => true for all our custom elements.

tiberiuichim avatar Mar 22 '23 18:03 tiberiuichim

@tiberiuichim yes i considered this but that wont cover the case (for me) of where i wrap maybe 4 block elements. since inline elements dont typically contain block elements.

whereas a block element can contain block elements and inline elements.

my use case would be something like this.

<feedback>
   <p>Here is some random <feedback>data</feedback></p>
   <feedback><img src="..." /></feedback>
</feedback>

so really it is an inline-block type of element you could say

jayaike avatar Mar 22 '23 18:03 jayaike

Does anyone have an example of how to overwrite the normalizeNode method to support this?

allison-luminos avatar Jan 24 '24 22:01 allison-luminos