editor-js / paragraph

Paragraph Tool for Editor.js 2.0
MIT License
46 stars 127 forks source link

chore: migrate to vite, make render sync #75

Closed neSpecc closed 1 year ago

neSpecc commented 1 year ago

This PR introduces two changes:

The problem

There is a bug in Safari. When we call blocks.render() it leads the onChange call.

Explanation

The blocks.render() contains a mutation observer mutex:

  /**
   * Fills Editor with Blocks data
   *
   * @param {OutputData} data — Saved Editor data
   */
  public async render(data: OutputData): Promise<void> {
    // ...
    this.Editor.ModificationsObserver.disable();

    await this.Editor.BlockManager.clear();
    await this.Editor.Renderer.render(data.blocks);

    this.Editor.ModificationsObserver.enable();
  }

But the Paragraph's render creates a DOM and inserts a text using requestAnimationFrame — so text inserted in some time after render. It leads the onChange call since Block's DOM is changed.

Bug happened only in Safari, because Renderer.render() promise resolves in the requestIdleCallback (which happened after requestAnimationFrame), but Safari does not support it, and it uses polypill (working on setTimeout) instead, so promise resolves before Paragraph Dom mutation.

image

Solution

The first and simplest solution was to wrap this.Editor.ModificationsObserver.enable(); into requestAnimationFrame as well.

But more right way is to remove requestAnimationFrame from Paragraph render since it has no sense — it leads 2 layouts instead of one. For performance reason, it's important to not to call drawView in a constructor, but render can put a text without RaF