fix: errors caused by events fired while editor is not initialized yet (#2532)

* fix(init): errors cause by events fired while editor is not initialized yet

* Update package.json
This commit is contained in:
Peter Savchenko 2023-11-08 22:35:52 +03:00 committed by GitHub
parent af6b64a3e6
commit 5e8fe06dd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 47 additions and 20 deletions

View file

@ -9,6 +9,7 @@
- `Fix` — When the focusing Block is out of the viewport, the page will be scrolled. - `Fix` — When the focusing Block is out of the viewport, the page will be scrolled.
- `Fix``blocks.render()` won't lead the `onChange` call in Safari - `Fix``blocks.render()` won't lead the `onChange` call in Safari
- `Fix` — Editor wrapper element growing on the Inline Toolbar close - `Fix` — Editor wrapper element growing on the Inline Toolbar close
- `Fix` — Fix errors thrown by clicks on a document when the editor is being initialized
### 2.28.2 ### 2.28.2

View file

@ -1,6 +1,6 @@
{ {
"name": "@editorjs/editorjs", "name": "@editorjs/editorjs",
"version": "2.29.0-rc.5", "version": "2.29.0-rc.6",
"description": "Editor.js — Native JS, based on API and Open Source", "description": "Editor.js — Native JS, based on API and Open Source",
"main": "dist/editorjs.umd.js", "main": "dist/editorjs.umd.js",
"module": "dist/editorjs.mjs", "module": "dist/editorjs.mjs",

View file

@ -744,7 +744,11 @@ export default class BlockManager extends Module {
* @param {Node} childNode - node to get Block by * @param {Node} childNode - node to get Block by
* @returns {Block} * @returns {Block}
*/ */
public getBlockByChildNode(childNode: Node): Block { public getBlockByChildNode(childNode: Node): Block | undefined {
if (!childNode || childNode instanceof Node === false) {
return undefined;
}
/** /**
* If node is Text TextNode * If node is Text TextNode
*/ */

View file

@ -187,6 +187,13 @@ export default class CrossBlockSelection extends Module {
private onMouseOver = (event: MouseEvent): void => { private onMouseOver = (event: MouseEvent): void => {
const { BlockManager, BlockSelection } = this.Editor; const { BlockManager, BlockSelection } = this.Editor;
/**
* Probably, editor is not initialized yet
*/
if (event.relatedTarget === null && event.target === null) {
return;
}
const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock; const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock;
const targetBlock = BlockManager.getBlockByChildNode(event.target as Node); const targetBlock = BlockManager.getBlockByChildNode(event.target as Node);

View file

@ -13,7 +13,10 @@ import Popover, { PopoverEvent } from '../../utils/popover';
* HTML Elements that used for BlockSettings * HTML Elements that used for BlockSettings
*/ */
interface BlockSettingsNodes { interface BlockSettingsNodes {
wrapper: HTMLElement; /**
* Block Settings wrapper. Undefined when before "make" method called
*/
wrapper: HTMLElement | undefined;
} }
/** /**

View file

@ -32,12 +32,12 @@ import { BlockHovered } from '../../events/BlockHovered';
* HTML Elements used for Toolbar UI * HTML Elements used for Toolbar UI
*/ */
interface ToolbarNodes { interface ToolbarNodes {
wrapper: HTMLElement; wrapper: HTMLElement | undefined;
content: HTMLElement; content: HTMLElement | undefined;
actions: HTMLElement; actions: HTMLElement | undefined;
plusButton: HTMLElement; plusButton: HTMLElement | undefined;
settingsToggler: HTMLElement; settingsToggler: HTMLElement | undefined;
} }
/** /**
* *
@ -316,7 +316,7 @@ export default class Toolbar extends Module<ToolbarNodes> {
return; return;
} }
this.nodes.wrapper.classList.remove(this.CSS.toolbarOpened); this.nodes.wrapper?.classList.remove(this.CSS.toolbarOpened);
/** Close components */ /** Close components */
this.blockActions.hide(); this.blockActions.hide();

View file

@ -17,16 +17,16 @@ import { IconChevronDown } from '@codexteam/icons';
* Inline Toolbar elements * Inline Toolbar elements
*/ */
interface InlineToolbarNodes { interface InlineToolbarNodes {
wrapper: HTMLElement; wrapper: HTMLElement | undefined;
togglerAndButtonsWrapper: HTMLElement; togglerAndButtonsWrapper: HTMLElement | undefined;
buttons: HTMLElement; buttons: HTMLElement | undefined;
conversionToggler: HTMLElement; conversionToggler: HTMLElement | undefined;
conversionTogglerContent: HTMLElement; conversionTogglerContent: HTMLElement | undefined;
/** /**
* Zone below the buttons where Tools can create additional actions by 'renderActions()' method * Zone below the buttons where Tools can create additional actions by 'renderActions()' method
* For example, input for the 'link' tool or textarea for the 'comment' tool * For example, input for the 'link' tool or textarea for the 'comment' tool
*/ */
actions: HTMLElement; actions: HTMLElement | undefined;
} }
/** /**
@ -238,6 +238,10 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
* @param {Node} node node to check * @param {Node} node node to check
*/ */
public containsNode(node: Node): boolean { public containsNode(node: Node): boolean {
if (this.nodes.wrapper === undefined) {
return false;
}
return this.nodes.wrapper.contains(node); return this.nodes.wrapper.contains(node);
} }

View file

@ -150,12 +150,20 @@ export default class UI extends Module<UINodes> {
*/ */
if (!readOnlyEnabled) { if (!readOnlyEnabled) {
/** /**
* Unbind all events * Postpone events binding to the next tick to make sure all ui elements are ready
*/ */
this.enableModuleBindings(); window.requestIdleCallback(() => {
/**
* Bind events for the UI elements
*/
this.enableModuleBindings();
}, {
timeout: 2000,
});
} else { } else {
/** /**
* Bind events for the UI elements * Unbind all events
*
*/ */
this.disableModuleBindings(); this.disableModuleBindings();
} }
@ -633,8 +641,8 @@ export default class UI extends Module<UINodes> {
* But allow clicking inside Block Settings. * But allow clicking inside Block Settings.
* Also, do not process clicks on the Block Settings Toggler, because it has own click listener * Also, do not process clicks on the Block Settings Toggler, because it has own click listener
*/ */
const isClickedInsideBlockSettings = this.Editor.BlockSettings.nodes.wrapper.contains(target); const isClickedInsideBlockSettings = this.Editor.BlockSettings.nodes.wrapper?.contains(target);
const isClickedInsideBlockSettingsToggler = this.Editor.Toolbar.nodes.settingsToggler.contains(target); const isClickedInsideBlockSettingsToggler = this.Editor.Toolbar.nodes.settingsToggler?.contains(target);
const doNotProcess = isClickedInsideBlockSettings || isClickedInsideBlockSettingsToggler; const doNotProcess = isClickedInsideBlockSettings || isClickedInsideBlockSettingsToggler;
if (this.Editor.BlockSettings.opened && !doNotProcess) { if (this.Editor.BlockSettings.opened && !doNotProcess) {