tui.editor icon indicating copy to clipboard operation
tui.editor copied to clipboard

Menus don't work inside Shadow DOM

Open tpluscode opened this issue 2 years ago • 4 comments

Describe the bug

The menus, such as when selecting a heading, disappear when clicked. Likely caused by using the editor inside a shadow root?

To Reproduce

Steps to reproduce the behavior:

  1. Wrap the editor inside a LitElement (gist)
  2. Use it on a page as <tui-editor></tui-editor>
  3. Click the header menu icon
  4. Select a heading size
  5. Nothing happens

Expected behavior

Heading should be applied to the editor text

Screenshots

If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: macOS
  • Browser: Chrome (Brave)
  • Version 1.38.109 Chromium: 101.0.4951.41 (Official Build) (x86_64)

Additional context

I debugged the moment as menu disappears to this switch in code

function createDefaultToolbarItemInfo(type) {
    var info;
    switch (type) {
        case 'heading':
    }
}

Apparently, any time any menu icon or any popup is clicked, the value of type equals more and what would be expected...

tpluscode avatar Jun 24 '22 11:06 tpluscode

Unfortunately, we also ran into this problem. Within the Shadow DOM, several features do not work correctly, e.g. it is not possible to insert a URL via the link menu dialog. Open dialogues are closed immediately when clicking, in this case in the URL input field.

LeDNI avatar Sep 14 '22 07:09 LeDNI

Hello, I have also experienced this problem. As soon as I use the editor in a custom element with its own Shadow DOM, the pop-up menus do not work. As soon as I click on them, they close immediately. An input in input fields in the menus is not possible.

disoOoner avatar Sep 21 '22 07:09 disoOoner

I also ran into this. It is a showstopper for our potential use case. The cause is a mousedown handler on the document, installed by the base Popup class. It looks for the popup element as an ancestor of the event target, but when the event crosses the shadow DOM boundary, the target is changed to the host element. I wonder if we could solve this by putting an additional mousedown handler on the Popup element itself which stops propagation on the event:

private handleMousedownDocument = (ev: MouseEvent) => {
    this.props.hidePopup();
};

private handleMousedownPopup = (ev: MouseEvent) => {
    if (
        closest(ev.target as HTMLElement, `.${cls('popup')}`) ||
        closest(ev.target as HTMLElement, this.props.info.fromEl)
    ) {
        ev.stopPropagation();
    }
};

mounted() {
    document.addEventListener('mousedown', this.handleMousedownDocument );
    this.addEventListener('mousedown', this.handleMousedownPopup );
    this.props.eventEmitter.listen('closePopup', this.props.hidePopup);
}

m-akinc avatar Apr 28 '23 15:04 m-akinc

For anyone else getting tired of waiting for a fix for this - a workaround along these lines appear to work:

export class MarkdownEditorCustomElement
{
    // ...
    
    // The element, inside a Shadow DOM, for which the editor is initialized.
    private editorElement: HTMLElement;

    public attached(): void
    {
        // ...

        this.editorElement.addEventListener("mousedown", this.handleMousedown);
    }

    public deatached(): void
    {
        // ...

        this.editorElement.removeEventListener("mousedown", this.handleMousedown);
    }

    private handleMousedown = (event: MouseEvent) =>
    {
        if ((event.target as HTMLElement).closest(".toastui-editor-popup") != null)
        {
            event.stopPropagation();
        }
    };
}

thomas-darling avatar Nov 08 '23 16:11 thomas-darling