mirror of
https://github.com/codex-team/editor.js
synced 2024-05-17 22:06:49 +02:00
Pass configuration to internal tools (#648)
* Pass configuration to internal tools * Bump version Add changelog * Move tools validation to Tools module Add class for wrapper when toolbox is opened * Add emptiness check * Update submodule
This commit is contained in:
parent
1178a2f9e2
commit
6bd857d4f6
10
dist/editor.js
vendored
10
dist/editor.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
### 2.11.11
|
||||
|
||||
- `New` — Add ability to pass configuration for internal Tools
|
||||
|
||||
### 2.11.10
|
||||
|
||||
- `Fix` - Fix editor view on mobile devices
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@editorjs/editorjs",
|
||||
"version": "2.11.10",
|
||||
"version": "2.11.11",
|
||||
"description": "Editor.js — Native JS, based on API and Open Source",
|
||||
"main": "dist/editor.js",
|
||||
"types": "./types/index.d.ts",
|
||||
|
|
|
@ -200,21 +200,6 @@ export default class Core {
|
|||
if (!$.get(this.config.holderId)) {
|
||||
throw Error(`element with ID «${this.config.holderId}» is missing. Pass correct holder's ID.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Tools for a class containing
|
||||
*/
|
||||
for (const toolName in this.config.tools) {
|
||||
if (this.config.tools.hasOwnProperty(toolName)) {
|
||||
const tool = this.config.tools[toolName];
|
||||
|
||||
if (!_.isFunction(tool) && !_.isFunction((tool as ToolSettings).class)) {
|
||||
throw Error(
|
||||
`Tool «${toolName}» must be a constructor function or an object with function in the «class» property`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -128,6 +128,15 @@ export default class BlockManager extends Module {
|
|||
return this._blocks.array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if each Block is empty
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public get isEditorEmpty(): boolean {
|
||||
return this.blocks.every((block) => block.isEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index of current working block
|
||||
*
|
||||
|
|
|
@ -32,6 +32,7 @@ export default class ModificationsObserver extends Module {
|
|||
* @type {Function}
|
||||
*/
|
||||
private mutationDebouncer = _.debounce( () => {
|
||||
this.checkEmptiness();
|
||||
this.config.onChange();
|
||||
}, ModificationsObserver.DebounceTimer);
|
||||
|
||||
|
@ -142,4 +143,13 @@ export default class ModificationsObserver extends Module {
|
|||
this.mutationDebouncer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Editor is empty and set CSS class to wrapper
|
||||
*/
|
||||
private checkEmptiness(): void {
|
||||
const {BlockManager, UI} = this.Editor;
|
||||
|
||||
UI.nodes.wrapper.classList.toggle(UI.CSS.editorEmpty, BlockManager.isEditorEmpty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import $ from '../dom';
|
|||
|
||||
import SelectionUtils from '../selection';
|
||||
import Block from '../block';
|
||||
import UI from './ui';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
|
||||
export default class RectangleSelection extends Module {
|
||||
|
@ -138,7 +137,7 @@ export default class RectangleSelection extends Module {
|
|||
this.stackOfSelected = [];
|
||||
|
||||
const elemWhereSelectionStart = document.elementFromPoint(pageX - window.pageXOffset, pageY - window.pageYOffset);
|
||||
if (!(elemWhereSelectionStart.closest('.' + UI.CSS.editorWrapper) &&
|
||||
if (!(elemWhereSelectionStart.closest('.' + this.Editor.UI.CSS.editorWrapper) &&
|
||||
!elemWhereSelectionStart.closest('.' + Block.CSS.content))) {
|
||||
return;
|
||||
}
|
||||
|
@ -197,7 +196,9 @@ export default class RectangleSelection extends Module {
|
|||
}
|
||||
|
||||
private genHTML() {
|
||||
const container = this.Editor.UI.nodes.holder.querySelector('.' + UI.CSS.editorWrapper);
|
||||
const {UI} = this.Editor;
|
||||
|
||||
const container = UI.nodes.holder.querySelector('.' + UI.CSS.editorWrapper);
|
||||
const overlay = $.make('div', RectangleSelection.CSS.overlay, {});
|
||||
const overlayContainer = $.make('div', RectangleSelection.CSS.overlayContainer, {});
|
||||
const overlayRectangle = $.make('div', RectangleSelection.CSS.rect, {});
|
||||
|
|
|
@ -29,6 +29,7 @@ export default class Toolbox extends Module {
|
|||
tooltip: 'ce-toolbox__tooltip',
|
||||
tooltipShown: 'ce-toolbox__tooltip--shown',
|
||||
tooltipShortcut: 'ce-toolbox__tooltip-shortcut',
|
||||
openedToolbarHolderModifier: 'codex-editor--toolbox-opened',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -124,7 +125,9 @@ export default class Toolbox extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
this.Editor.UI.nodes.wrapper.classList.add(this.CSS.openedToolbarHolderModifier);
|
||||
this.nodes.toolbox.classList.add(this.CSS.toolboxOpened);
|
||||
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
|
@ -135,6 +138,8 @@ export default class Toolbox extends Module {
|
|||
this.hideTooltip();
|
||||
|
||||
this.nodes.toolbox.classList.remove(this.CSS.toolboxOpened);
|
||||
this.Editor.UI.nodes.wrapper.classList.remove(this.CSS.openedToolbarHolderModifier);
|
||||
|
||||
this.opened = false;
|
||||
|
||||
/** remove active item pointer */
|
||||
|
|
|
@ -195,10 +195,12 @@ export default class Tools extends Module {
|
|||
* @return {Promise}
|
||||
*/
|
||||
public prepare() {
|
||||
this.validateTools();
|
||||
|
||||
/**
|
||||
* Assign internal tools
|
||||
*/
|
||||
Object.assign(this.config.tools, this.internalTools);
|
||||
_.deepMerge(this.config.tools, this.internalTools);
|
||||
|
||||
if (!this.config.hasOwnProperty('tools') || Object.keys(this.config.tools).length === 0) {
|
||||
throw Error('Can\'t start without tools');
|
||||
|
@ -379,6 +381,30 @@ export default class Tools extends Module {
|
|||
return toolPreparationList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Tools configuration objects and throw Error for user if it is invalid
|
||||
*/
|
||||
private validateTools() {
|
||||
/**
|
||||
* Check Tools for a class containing
|
||||
*/
|
||||
for (const toolName in this.config.tools) {
|
||||
if (this.config.tools.hasOwnProperty(toolName)) {
|
||||
if (toolName in this.internalTools) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tool = this.config.tools[toolName];
|
||||
|
||||
if (!_.isFunction(tool) && !_.isFunction((tool as ToolSettings).class)) {
|
||||
throw Error(
|
||||
`Tool «${toolName}» must be a constructor function or an object with function in the «class» property`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns internal tools
|
||||
* Includes Bold, Italic, Link and Paragraph
|
||||
|
|
|
@ -38,9 +38,9 @@ export default class UI extends Module {
|
|||
* Editor.js UI CSS class names
|
||||
* @return {{editorWrapper: string, editorZone: string}}
|
||||
*/
|
||||
public static get CSS(): {
|
||||
public get CSS(): {
|
||||
editorWrapper: string, editorWrapperNarrow: string, editorZone: string, editorZoneHidden: string,
|
||||
editorLoader: string,
|
||||
editorLoader: string, editorEmpty: string,
|
||||
} {
|
||||
return {
|
||||
editorWrapper : 'codex-editor',
|
||||
|
@ -48,6 +48,7 @@ export default class UI extends Module {
|
|||
editorZone : 'codex-editor__redactor',
|
||||
editorZoneHidden : 'codex-editor__redactor--hidden',
|
||||
editorLoader : 'codex-editor__loader',
|
||||
editorEmpty : 'codex-editor--empty',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -70,9 +71,9 @@ export default class UI extends Module {
|
|||
* Adds loader to editor while content is not ready
|
||||
*/
|
||||
public addLoader(): void {
|
||||
this.nodes.loader = $.make('div', UI.CSS.editorLoader);
|
||||
this.nodes.loader = $.make('div', this.CSS.editorLoader);
|
||||
this.nodes.wrapper.prepend(this.nodes.loader);
|
||||
this.nodes.redactor.classList.add(UI.CSS.editorZoneHidden);
|
||||
this.nodes.redactor.classList.add(this.CSS.editorZoneHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +81,7 @@ export default class UI extends Module {
|
|||
*/
|
||||
public removeLoader(): void {
|
||||
this.nodes.loader.remove();
|
||||
this.nodes.redactor.classList.remove(UI.CSS.editorZoneHidden);
|
||||
this.nodes.redactor.classList.remove(this.CSS.editorZoneHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,14 +143,14 @@ export default class UI extends Module {
|
|||
/**
|
||||
* Create and save main UI elements
|
||||
*/
|
||||
this.nodes.wrapper = $.make('div', UI.CSS.editorWrapper);
|
||||
this.nodes.redactor = $.make('div', UI.CSS.editorZone);
|
||||
this.nodes.wrapper = $.make('div', this.CSS.editorWrapper);
|
||||
this.nodes.redactor = $.make('div', this.CSS.editorZone);
|
||||
|
||||
/**
|
||||
* If Editor has injected into the narrow container, enable Narrow Mode
|
||||
*/
|
||||
if (this.nodes.holder.offsetWidth < this.contentWidth) {
|
||||
this.nodes.wrapper.classList.add(UI.CSS.editorWrapperNarrow);
|
||||
this.nodes.wrapper.classList.add(this.CSS.editorWrapperNarrow);
|
||||
}
|
||||
|
||||
this.nodes.wrapper.appendChild(this.nodes.redactor);
|
||||
|
@ -216,7 +217,7 @@ export default class UI extends Module {
|
|||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
private defaultBehaviour(event: KeyboardEvent): void {
|
||||
const keyDownOnEditor = (event.target as HTMLElement).closest(`.${UI.CSS.editorWrapper}`);
|
||||
const keyDownOnEditor = (event.target as HTMLElement).closest(`.${this.CSS.editorWrapper}`);
|
||||
const {currentBlock} = this.Editor.BlockManager;
|
||||
const isMetaKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit abd28780af5c4bdb4c4d47cdeb6940af2ba7830e
|
||||
Subproject commit 698fdbe5a739043b69349e8ed8ff49788722feae
|
|
@ -308,4 +308,34 @@ export default class Util {
|
|||
public static capitalize(text: string): string {
|
||||
return text[0].toUpperCase() + text.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge to objects recursively
|
||||
* @param {object} target
|
||||
* @param {object[]} sources
|
||||
* @return {object}
|
||||
*/
|
||||
public static deepMerge(target, ...sources) {
|
||||
const isObject = (item) => item && typeof item === 'object' && !Array.isArray(item);
|
||||
|
||||
if (!sources.length) { return target; }
|
||||
const source = sources.shift();
|
||||
|
||||
if (isObject(target) && isObject(source)) {
|
||||
for (const key in source) {
|
||||
if (isObject(source[key])) {
|
||||
if (!target[key]) {
|
||||
Object.assign(target, { [key]: {} });
|
||||
}
|
||||
|
||||
Util.deepMerge(target[key], source[key]);
|
||||
} else {
|
||||
Object.assign(target, { [key]: source[key] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Util.deepMerge(target, ...sources);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -115,6 +115,10 @@
|
|||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus::before {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes editor-loader-spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
|
|
Loading…
Reference in a new issue