mirror of
https://github.com/codex-team/editor.js
synced 2024-06-29 02:40:23 +02:00
Merge branch 'next' into feature/improve-caret-up-down-position
This commit is contained in:
commit
8361d8e41a
|
@ -1,5 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 2.19.4
|
||||||
|
|
||||||
|
- `Improvements` - Vertical caret moving with UP or DOWN key [#857](https://github.com/codex-team/editor.js/issues/857).
|
||||||
|
|
||||||
|
### 2.19.3
|
||||||
|
|
||||||
|
- `Fix` — Ignore error raised by Shortcut module
|
||||||
|
|
||||||
### 2.19.2
|
### 2.19.2
|
||||||
|
|
||||||
- `New` - `toolbar.toggleBlockSettings()` API method added [#1442](https://github.com/codex-team/editor.js/issues/1421).
|
- `New` - `toolbar.toggleBlockSettings()` API method added [#1442](https://github.com/codex-team/editor.js/issues/1421).
|
||||||
|
@ -8,13 +16,17 @@
|
||||||
- `Improvements` - Remove bundles from the repo [#1541](https://github.com/codex-team/editor.js/pull/1541).
|
- `Improvements` - Remove bundles from the repo [#1541](https://github.com/codex-team/editor.js/pull/1541).
|
||||||
- `Improvements` - Document will be scrolled when blocks are selected with `SHIFT+UP` or `SHIFT+DOWN` [#1447](https://github.com/codex-team/editor.js/issues/1447)
|
- `Improvements` - Document will be scrolled when blocks are selected with `SHIFT+UP` or `SHIFT+DOWN` [#1447](https://github.com/codex-team/editor.js/issues/1447)
|
||||||
- `Improvements` - The caret will be set on editor copy/paste [#1470](https://github.com/codex-team/editor.js/pull/1470)
|
- `Improvements` - The caret will be set on editor copy/paste [#1470](https://github.com/codex-team/editor.js/pull/1470)
|
||||||
- `Improvements` - Vertical caret moving with UP or DOWN key [#857](https://github.com/codex-team/editor.js/issues/857).
|
|
||||||
- `Improvements` - Added generic types to OutputBlockData [#1551](https://github.com/codex-team/editor.js/issues/1551).
|
- `Improvements` - Added generic types to OutputBlockData [#1551](https://github.com/codex-team/editor.js/issues/1551).
|
||||||
- `Fix` - Fix BlockManager.setCurrentBlockByChildNode() with multiple Editor.js instances [#1503](https://github.com/codex-team/editor.js/issues/1503).
|
- `Fix` - Fix BlockManager.setCurrentBlockByChildNode() with multiple Editor.js instances [#1503](https://github.com/codex-team/editor.js/issues/1503).
|
||||||
- `Fix` - Fix an unstable block cut process [#1489](https://github.com/codex-team/editor.js/issues/1489).
|
- `Fix` - Fix an unstable block cut process [#1489](https://github.com/codex-team/editor.js/issues/1489).
|
||||||
- `Fix` - Type definition of the Sanitizer config: the sanitize function now contains param definition [#1491](https://github.com/codex-team/editor.js/pull/1491).
|
- `Fix` - Type definition of the Sanitizer config: the sanitize function now contains param definition [#1491](https://github.com/codex-team/editor.js/pull/1491).
|
||||||
- `Fix` - Fix unexpected behavior on an empty link pasting [#1348](https://github.com/codex-team/editor.js/issues/1348).
|
- `Fix` - Fix unexpected behavior on an empty link pasting [#1348](https://github.com/codex-team/editor.js/issues/1348).
|
||||||
|
- `Fix` - Fix SanitizerConfig type definition [#1513](https://github.com/codex-team/editor.js/issues/1513)
|
||||||
|
- `Refactoring` - The Listeners module now is a util.
|
||||||
|
- `Refactoring` - The Events module now is a util.
|
||||||
- `Fix` - Editor Config now immutable [#1552](https://github.com/codex-team/editor.js/issues/1552).
|
- `Fix` - Editor Config now immutable [#1552](https://github.com/codex-team/editor.js/issues/1552).
|
||||||
|
- `Refactoring` - Shortcuts module is util now.
|
||||||
|
- `Fix` - Fix bubbling on BlockManagers' listener [#1433](https://github.com/codex-team/editor.js/issues/1433).
|
||||||
|
|
||||||
### 2.19.1
|
### 2.19.1
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
|
|
||||||
<!-- Load Editor.js's Core -->
|
<!-- Load Editor.js's Core -->
|
||||||
<script src="../dist/editor.js" onload="document.getElementById('hint-core').hidden = true"></script>
|
<script src="../dist/editor.js" onload="document.getElementById('hint-core').hidden = true"></script>
|
||||||
|
<script src="./tools/header/dist/bundle.js"></script><!-- Header -->
|
||||||
|
|
||||||
<!-- Initialization -->
|
<!-- Initialization -->
|
||||||
<script>
|
<script>
|
||||||
|
@ -50,6 +51,12 @@
|
||||||
*/
|
*/
|
||||||
var editor1 = new EditorJS({
|
var editor1 = new EditorJS({
|
||||||
holder: 'editorjs1',
|
holder: 'editorjs1',
|
||||||
|
tools: {
|
||||||
|
header: {
|
||||||
|
class: Header,
|
||||||
|
shortcut: 'CMD+SHIFT+H'
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,6 +64,12 @@
|
||||||
*/
|
*/
|
||||||
var editor2 = new EditorJS({
|
var editor2 = new EditorJS({
|
||||||
holder: 'editorjs2',
|
holder: 'editorjs2',
|
||||||
|
tools: {
|
||||||
|
header: {
|
||||||
|
class: Header,
|
||||||
|
shortcut: 'CMD+SHIFT+H'
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@editorjs/editorjs",
|
"name": "@editorjs/editorjs",
|
||||||
"version": "2.19.2-rc.1",
|
"version": "2.19.3",
|
||||||
"description": "Editor.js — Native JS, based on API and Open Source",
|
"description": "Editor.js — Native JS, based on API and Open Source",
|
||||||
"main": "dist/editor.js",
|
"main": "dist/editor.js",
|
||||||
"types": "./types/index.d.ts",
|
"types": "./types/index.d.ts",
|
||||||
|
|
|
@ -87,6 +87,7 @@ export default class EditorJS {
|
||||||
if (_.isFunction(moduleInstance.destroy)) {
|
if (_.isFunction(moduleInstance.destroy)) {
|
||||||
moduleInstance.destroy();
|
moduleInstance.destroy();
|
||||||
}
|
}
|
||||||
|
moduleInstance.listeners.removeAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
editor = null;
|
editor = null;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { EditorModules } from '../types-internal/editor-modules';
|
import { EditorModules } from '../types-internal/editor-modules';
|
||||||
import { EditorConfig } from '../../types';
|
import { EditorConfig } from '../../types';
|
||||||
import { ModuleConfig } from '../types-internal/module-config';
|
import { ModuleConfig } from '../types-internal/module-config';
|
||||||
|
import Listeners from './utils/listeners';
|
||||||
|
import EventsDispatcher from './utils/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type <T> of the Module generic.
|
* The type <T> of the Module generic.
|
||||||
|
@ -38,6 +40,16 @@ export default class Module<T extends ModuleNodes = {}> {
|
||||||
*/
|
*/
|
||||||
protected config: EditorConfig;
|
protected config: EditorConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editor event dispatcher class
|
||||||
|
*/
|
||||||
|
protected eventsDispatcher: EventsDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util for bind/unbind DOM event listeners
|
||||||
|
*/
|
||||||
|
protected listeners: Listeners = new Listeners();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This object provides methods to push into set of listeners that being dropped when read-only mode is enabled
|
* This object provides methods to push into set of listeners that being dropped when read-only mode is enabled
|
||||||
*/
|
*/
|
||||||
|
@ -56,10 +68,8 @@ export default class Module<T extends ModuleNodes = {}> {
|
||||||
handler: (event: Event) => void,
|
handler: (event: Event) => void,
|
||||||
options: boolean | AddEventListenerOptions = false
|
options: boolean | AddEventListenerOptions = false
|
||||||
): void => {
|
): void => {
|
||||||
const { Listeners } = this.Editor;
|
|
||||||
|
|
||||||
this.mutableListenerIds.push(
|
this.mutableListenerIds.push(
|
||||||
Listeners.on(element, eventType, handler, options)
|
this.listeners.on(element, eventType, handler, options)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -67,10 +77,8 @@ export default class Module<T extends ModuleNodes = {}> {
|
||||||
* Clears all mutable listeners
|
* Clears all mutable listeners
|
||||||
*/
|
*/
|
||||||
clearAll: (): void => {
|
clearAll: (): void => {
|
||||||
const { Listeners } = this.Editor;
|
|
||||||
|
|
||||||
for (const id of this.mutableListenerIds) {
|
for (const id of this.mutableListenerIds) {
|
||||||
Listeners.offById(id);
|
this.listeners.offById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mutableListenerIds = [];
|
this.mutableListenerIds = [];
|
||||||
|
@ -84,14 +92,17 @@ export default class Module<T extends ModuleNodes = {}> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
|
*
|
||||||
* @param {EditorConfig} config - Editor's config
|
* @param {EditorConfig} config - Editor's config
|
||||||
|
* @param {EventsDispatcher} eventsDispatcher - Editor's event dispatcher
|
||||||
*/
|
*/
|
||||||
constructor({ config }: ModuleConfig) {
|
constructor({ config, eventsDispatcher }: ModuleConfig) {
|
||||||
if (new.target === Module) {
|
if (new.target === Module) {
|
||||||
throw new TypeError('Constructors for abstract class Module are not allowed.');
|
throw new TypeError('Constructors for abstract class Module are not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.eventsDispatcher = eventsDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { EditorConfig, SanitizerConfig } from '../../types';
|
||||||
import { EditorModules } from '../types-internal/editor-modules';
|
import { EditorModules } from '../types-internal/editor-modules';
|
||||||
import I18n from './i18n';
|
import I18n from './i18n';
|
||||||
import { CriticalError } from './errors/critical';
|
import { CriticalError } from './errors/critical';
|
||||||
|
import EventsDispatcher from './utils/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Core} Core - editor core class
|
* @typedef {Core} Core - editor core class
|
||||||
|
@ -53,6 +54,11 @@ export default class Core {
|
||||||
*/
|
*/
|
||||||
public isReady: Promise<void>;
|
public isReady: Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event Dispatcher util
|
||||||
|
*/
|
||||||
|
private eventsDispatcher: EventsDispatcher = new EventsDispatcher();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {EditorConfig} config - user configuration
|
* @param {EditorConfig} config - user configuration
|
||||||
*
|
*
|
||||||
|
@ -338,6 +344,7 @@ export default class Core {
|
||||||
*/
|
*/
|
||||||
this.moduleInstances[Module.displayName] = new Module({
|
this.moduleInstances[Module.displayName] = new Module({
|
||||||
config: this.configuration,
|
config: this.configuration,
|
||||||
|
eventsDispatcher: this.eventsDispatcher,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_.log(`Module ${Module.displayName} skipped because`, 'warn', e);
|
_.log(`Module ${Module.displayName} skipped because`, 'warn', e);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Events } from '../../../../types/api';
|
|
||||||
import Module from '../../__module';
|
import Module from '../../__module';
|
||||||
|
import { Events } from '../../../../types/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class EventsAPI
|
* @class EventsAPI
|
||||||
|
@ -26,7 +26,7 @@ export default class EventsAPI extends Module {
|
||||||
* @param {Function} callback - event handler
|
* @param {Function} callback - event handler
|
||||||
*/
|
*/
|
||||||
public on(eventName, callback): void {
|
public on(eventName, callback): void {
|
||||||
this.Editor.Events.on(eventName, callback);
|
this.eventsDispatcher.on(eventName, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +36,7 @@ export default class EventsAPI extends Module {
|
||||||
* @param {object} data - event's data
|
* @param {object} data - event's data
|
||||||
*/
|
*/
|
||||||
public emit(eventName, data): void {
|
public emit(eventName, data): void {
|
||||||
this.Editor.Events.emit(eventName, data);
|
this.eventsDispatcher.emit(eventName, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,6 +46,6 @@ export default class EventsAPI extends Module {
|
||||||
* @param {Function} callback - event handler
|
* @param {Function} callback - event handler
|
||||||
*/
|
*/
|
||||||
public off(eventName, callback): void {
|
public off(eventName, callback): void {
|
||||||
this.Editor.Events.off(eventName, callback);
|
this.eventsDispatcher.off(eventName, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default class ListenersAPI extends Module {
|
||||||
* @param {boolean} useCapture - capture event or not
|
* @param {boolean} useCapture - capture event or not
|
||||||
*/
|
*/
|
||||||
public on(element: HTMLElement, eventType: string, handler: () => void, useCapture?: boolean): void {
|
public on(element: HTMLElement, eventType: string, handler: () => void, useCapture?: boolean): void {
|
||||||
this.Editor.Listeners.on(element, eventType, handler, useCapture);
|
this.listeners.on(element, eventType, handler, useCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,6 +39,6 @@ export default class ListenersAPI extends Module {
|
||||||
* @param {boolean} useCapture - capture event or not
|
* @param {boolean} useCapture - capture event or not
|
||||||
*/
|
*/
|
||||||
public off(element: Element, eventType: string, handler: () => void, useCapture?: boolean): void {
|
public off(element: Element, eventType: string, handler: () => void, useCapture?: boolean): void {
|
||||||
this.Editor.Listeners.off(element, eventType, handler, useCapture);
|
this.listeners.off(element, eventType, handler, useCapture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ export default class BlockManager extends Module {
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Copy event */
|
/** Copy event */
|
||||||
this.Editor.Listeners.on(
|
this.listeners.on(
|
||||||
document,
|
document,
|
||||||
'copy',
|
'copy',
|
||||||
(e: ClipboardEvent) => this.Editor.BlockEvents.handleCommandC(e)
|
(e: ClipboardEvent) => this.Editor.BlockEvents.handleCommandC(e)
|
||||||
|
@ -719,7 +719,7 @@ export default class BlockManager extends Module {
|
||||||
|
|
||||||
this.readOnlyMutableListeners.on(block.holder, 'keydown', (event: KeyboardEvent) => {
|
this.readOnlyMutableListeners.on(block.holder, 'keydown', (event: KeyboardEvent) => {
|
||||||
BlockEvents.keydown(event);
|
BlockEvents.keydown(event);
|
||||||
}, true);
|
});
|
||||||
|
|
||||||
this.readOnlyMutableListeners.on(block.holder, 'keyup', (event: KeyboardEvent) => {
|
this.readOnlyMutableListeners.on(block.holder, 'keyup', (event: KeyboardEvent) => {
|
||||||
BlockEvents.keyup(event);
|
BlockEvents.keyup(event);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import Module from '../__module';
|
||||||
import Block from '../block';
|
import Block from '../block';
|
||||||
import * as _ from '../utils';
|
import * as _ from '../utils';
|
||||||
import $ from '../dom';
|
import $ from '../dom';
|
||||||
|
import Shortcuts from '../utils/shortcuts';
|
||||||
|
|
||||||
import SelectionUtils from '../selection';
|
import SelectionUtils from '../selection';
|
||||||
import { SanitizerConfig } from '../../../types/configs';
|
import { SanitizerConfig } from '../../../types/configs';
|
||||||
|
@ -145,8 +146,6 @@ export default class BlockSelection extends Module {
|
||||||
* to select all and copy them
|
* to select all and copy them
|
||||||
*/
|
*/
|
||||||
public prepare(): void {
|
public prepare(): void {
|
||||||
const { Shortcuts } = this.Editor;
|
|
||||||
|
|
||||||
this.selection = new SelectionUtils();
|
this.selection = new SelectionUtils();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,6 +180,7 @@ export default class BlockSelection extends Module {
|
||||||
|
|
||||||
this.handleCommandA(event);
|
this.handleCommandA(event);
|
||||||
},
|
},
|
||||||
|
on: this.Editor.UI.nodes.redactor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,10 +361,8 @@ export default class BlockSelection extends Module {
|
||||||
* De-registers Shortcut CMD+A
|
* De-registers Shortcut CMD+A
|
||||||
*/
|
*/
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
const { Shortcuts } = this.Editor;
|
|
||||||
|
|
||||||
/** Selection shortcut */
|
/** Selection shortcut */
|
||||||
Shortcuts.remove('CMD+A');
|
Shortcuts.remove(this.Editor.UI.nodes.redactor, 'CMD+A');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,9 +23,7 @@ export default class CrossBlockSelection extends Module {
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
public async prepare(): Promise<void> {
|
public async prepare(): Promise<void> {
|
||||||
const { Listeners } = this.Editor;
|
this.listeners.on(document, 'mousedown', (event: MouseEvent) => {
|
||||||
|
|
||||||
Listeners.on(document, 'mousedown', (event: MouseEvent) => {
|
|
||||||
this.enableCrossBlockSelection(event);
|
this.enableCrossBlockSelection(event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -40,13 +38,13 @@ export default class CrossBlockSelection extends Module {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { BlockManager, Listeners } = this.Editor;
|
const { BlockManager } = this.Editor;
|
||||||
|
|
||||||
this.firstSelectedBlock = BlockManager.getBlock(event.target as HTMLElement);
|
this.firstSelectedBlock = BlockManager.getBlock(event.target as HTMLElement);
|
||||||
this.lastSelectedBlock = this.firstSelectedBlock;
|
this.lastSelectedBlock = this.firstSelectedBlock;
|
||||||
|
|
||||||
Listeners.on(document, 'mouseover', this.onMouseOver);
|
this.listeners.on(document, 'mouseover', this.onMouseOver);
|
||||||
Listeners.on(document, 'mouseup', this.onMouseUp);
|
this.listeners.on(document, 'mouseup', this.onMouseUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,10 +174,8 @@ export default class CrossBlockSelection extends Module {
|
||||||
* Removes the listeners
|
* Removes the listeners
|
||||||
*/
|
*/
|
||||||
private onMouseUp = (): void => {
|
private onMouseUp = (): void => {
|
||||||
const { Listeners } = this.Editor;
|
this.listeners.off(document, 'mouseover', this.onMouseOver);
|
||||||
|
this.listeners.off(document, 'mouseup', this.onMouseUp);
|
||||||
Listeners.off(document, 'mouseover', this.onMouseOver);
|
|
||||||
Listeners.off(document, 'mouseup', this.onMouseUp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,7 +58,7 @@ export default class ModificationsObserver extends Module {
|
||||||
this.observer.disconnect();
|
this.observer.disconnect();
|
||||||
}
|
}
|
||||||
this.observer = null;
|
this.observer = null;
|
||||||
this.nativeInputs.forEach((input) => this.Editor.Listeners.off(input, 'input', this.mutationDebouncer));
|
this.nativeInputs.forEach((input) => this.listeners.off(input, 'input', this.mutationDebouncer));
|
||||||
this.mutationDebouncer = null;
|
this.mutationDebouncer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,13 +163,13 @@ export default class ModificationsObserver extends Module {
|
||||||
private updateNativeInputs(): void {
|
private updateNativeInputs(): void {
|
||||||
if (this.nativeInputs) {
|
if (this.nativeInputs) {
|
||||||
this.nativeInputs.forEach((input) => {
|
this.nativeInputs.forEach((input) => {
|
||||||
this.Editor.Listeners.off(input, 'input');
|
this.listeners.off(input, 'input');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nativeInputs = Array.from(this.Editor.UI.nodes.redactor.querySelectorAll('textarea, input, select'));
|
this.nativeInputs = Array.from(this.Editor.UI.nodes.redactor.querySelectorAll('textarea, input, select'));
|
||||||
|
|
||||||
this.nativeInputs.forEach((input) => this.Editor.Listeners.on(input, 'input', this.mutationDebouncer));
|
this.nativeInputs.forEach((input) => this.listeners.on(input, 'input', this.mutationDebouncer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -263,18 +263,14 @@ export default class Paste extends Module {
|
||||||
* Set onPaste callback handler
|
* Set onPaste callback handler
|
||||||
*/
|
*/
|
||||||
private setCallback(): void {
|
private setCallback(): void {
|
||||||
const { Listeners } = this.Editor;
|
this.listeners.on(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
|
||||||
|
|
||||||
Listeners.on(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unset onPaste callback handler
|
* Unset onPaste callback handler
|
||||||
*/
|
*/
|
||||||
private unsetCallback(): void {
|
private unsetCallback(): void {
|
||||||
const { Listeners } = this.Editor;
|
this.listeners.off(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
|
||||||
|
|
||||||
Listeners.off(this.Editor.UI.nodes.holder, 'paste', this.handlePasteEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -179,26 +179,25 @@ export default class RectangleSelection extends Module {
|
||||||
* Sets Module necessary event handlers
|
* Sets Module necessary event handlers
|
||||||
*/
|
*/
|
||||||
private enableModuleBindings(): void {
|
private enableModuleBindings(): void {
|
||||||
const { Listeners } = this.Editor;
|
|
||||||
const { container } = this.genHTML();
|
const { container } = this.genHTML();
|
||||||
|
|
||||||
Listeners.on(container, 'mousedown', (mouseEvent: MouseEvent) => {
|
this.listeners.on(container, 'mousedown', (mouseEvent: MouseEvent) => {
|
||||||
this.processMouseDown(mouseEvent);
|
this.processMouseDown(mouseEvent);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
Listeners.on(document.body, 'mousemove', (mouseEvent: MouseEvent) => {
|
this.listeners.on(document.body, 'mousemove', (mouseEvent: MouseEvent) => {
|
||||||
this.processMouseMove(mouseEvent);
|
this.processMouseMove(mouseEvent);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
Listeners.on(document.body, 'mouseleave', () => {
|
this.listeners.on(document.body, 'mouseleave', () => {
|
||||||
this.processMouseLeave();
|
this.processMouseLeave();
|
||||||
});
|
});
|
||||||
|
|
||||||
Listeners.on(window, 'scroll', (mouseEvent: MouseEvent) => {
|
this.listeners.on(window, 'scroll', (mouseEvent: MouseEvent) => {
|
||||||
this.processScroll(mouseEvent);
|
this.processScroll(mouseEvent);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
Listeners.on(document.body, 'mouseup', () => {
|
this.listeners.on(document.body, 'mouseup', () => {
|
||||||
this.processMouseUp();
|
this.processMouseUp();
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import Shortcut from '@codexteam/shortcuts';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains keyboard and mouse events binded on each Block by Block Manager
|
|
||||||
*/
|
|
||||||
import Module from '../__module';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShortcutData interface
|
|
||||||
* Each shortcut must have name and handler
|
|
||||||
* `name` is a shortcut, like 'CMD+K', 'CMD+B' etc
|
|
||||||
* `handler` is a callback
|
|
||||||
*
|
|
||||||
* @interface ShortcutData
|
|
||||||
*/
|
|
||||||
export interface ShortcutData {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shortcut name
|
|
||||||
* Ex. CMD+I, CMD+B ....
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shortcut handler
|
|
||||||
*/
|
|
||||||
handler(event): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Shortcut
|
|
||||||
* @classdesc Allows to register new shortcut
|
|
||||||
*
|
|
||||||
* Internal Shortcuts Module
|
|
||||||
*/
|
|
||||||
export default class Shortcuts extends Module {
|
|
||||||
/**
|
|
||||||
* All registered shortcuts
|
|
||||||
*
|
|
||||||
* @type {Shortcut[]}
|
|
||||||
*/
|
|
||||||
private registeredShortcuts: Shortcut[] = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register shortcut
|
|
||||||
*
|
|
||||||
* @param {ShortcutData} shortcut - shortcut options
|
|
||||||
*/
|
|
||||||
public add(shortcut: ShortcutData): void {
|
|
||||||
const newShortcut = new Shortcut({
|
|
||||||
name: shortcut.name,
|
|
||||||
on: this.Editor.UI.nodes.redactor,
|
|
||||||
callback: shortcut.handler,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.registeredShortcuts.push(newShortcut);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove shortcut
|
|
||||||
*
|
|
||||||
* @param {string} shortcut - shortcut name
|
|
||||||
*/
|
|
||||||
public remove(shortcut: string): void {
|
|
||||||
const index = this.registeredShortcuts.findIndex((shc) => shc.name === shortcut);
|
|
||||||
|
|
||||||
if (index === -1 || !this.registeredShortcuts[index]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.registeredShortcuts[index].remove();
|
|
||||||
this.registeredShortcuts.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -147,7 +147,7 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
||||||
this.addDefaultSettings();
|
this.addDefaultSettings();
|
||||||
|
|
||||||
/** Tell to subscribers that block settings is opened */
|
/** Tell to subscribers that block settings is opened */
|
||||||
this.Editor.Events.emit(this.events.opened);
|
this.eventsDispatcher.emit(this.events.opened);
|
||||||
|
|
||||||
this.flipper.activate(this.blockTunesButtons);
|
this.flipper.activate(this.blockTunesButtons);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ export default class BlockSettings extends Module<BlockSettingsNodes> {
|
||||||
this.nodes.defaultSettings.innerHTML = '';
|
this.nodes.defaultSettings.innerHTML = '';
|
||||||
|
|
||||||
/** Tell to subscribers that block settings is closed */
|
/** Tell to subscribers that block settings is closed */
|
||||||
this.Editor.Events.emit(this.events.closed);
|
this.eventsDispatcher.emit(this.events.closed);
|
||||||
|
|
||||||
/** Clear cached buttons */
|
/** Clear cached buttons */
|
||||||
this.buttons = [];
|
this.buttons = [];
|
||||||
|
|
|
@ -325,7 +325,7 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
||||||
$.append(this.nodes.tools, tool);
|
$.append(this.nodes.tools, tool);
|
||||||
this.tools[toolName] = tool;
|
this.tools[toolName] = tool;
|
||||||
|
|
||||||
this.Editor.Listeners.on(tool, 'click', async () => {
|
this.listeners.on(tool, 'click', async () => {
|
||||||
await this.replaceWithBlock(toolName);
|
await this.replaceWithBlock(toolName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { InlineTool, InlineToolConstructable, ToolConstructable, ToolSettings }
|
||||||
import Flipper from '../../flipper';
|
import Flipper from '../../flipper';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||||
|
import Shortcuts from '../../utils/shortcuts';
|
||||||
|
import { EditorModules } from '../../../types-internal/editor-modules';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline Toolbar elements
|
* Inline Toolbar elements
|
||||||
|
@ -87,6 +89,38 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
*/
|
*/
|
||||||
private flipper: Flipper = null;
|
private flipper: Flipper = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal inline tools: Link, Bold, Italic
|
||||||
|
*/
|
||||||
|
private internalTools: {[name: string]: InlineToolConstructable} = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editor modules setter
|
||||||
|
*
|
||||||
|
* @param {EditorModules} Editor - Editor's Modules
|
||||||
|
*/
|
||||||
|
public set state(Editor: EditorModules) {
|
||||||
|
this.Editor = Editor;
|
||||||
|
|
||||||
|
const { Tools } = Editor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set internal inline tools
|
||||||
|
*/
|
||||||
|
Object
|
||||||
|
.entries(Tools.internalTools)
|
||||||
|
.filter(([, toolClass]: [string, ToolConstructable | ToolSettings]) => {
|
||||||
|
if (_.isFunction(toolClass)) {
|
||||||
|
return toolClass[Tools.INTERNAL_SETTINGS.IS_INLINE];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (toolClass as ToolSettings).class[Tools.INTERNAL_SETTINGS.IS_INLINE];
|
||||||
|
})
|
||||||
|
.map(([name, toolClass]: [string, InlineToolConstructable | ToolSettings]) => {
|
||||||
|
this.internalTools[name] = _.isFunction(toolClass) ? toolClass : (toolClass as ToolSettings).class;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles read-only mode
|
* Toggles read-only mode
|
||||||
*
|
*
|
||||||
|
@ -185,7 +219,13 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nodes.wrapper.classList.remove(this.CSS.inlineToolbarShowed);
|
this.nodes.wrapper.classList.remove(this.CSS.inlineToolbarShowed);
|
||||||
this.toolsInstances.forEach((toolInstance) => {
|
Array.from(this.toolsInstances.entries()).forEach(([name, toolInstance]) => {
|
||||||
|
const shortcut = this.getToolShortcut(name);
|
||||||
|
|
||||||
|
if (shortcut) {
|
||||||
|
Shortcuts.remove(this.Editor.UI.nodes.redactor, shortcut);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo replace 'clear' with 'destroy'
|
* @todo replace 'clear' with 'destroy'
|
||||||
*/
|
*/
|
||||||
|
@ -357,7 +397,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
this.nodes.actions = $.make('div', this.CSS.actionsWrapper);
|
this.nodes.actions = $.make('div', this.CSS.actionsWrapper);
|
||||||
|
|
||||||
// To prevent reset of a selection when click on the wrapper
|
// To prevent reset of a selection when click on the wrapper
|
||||||
this.Editor.Listeners.on(this.nodes.wrapper, 'mousedown', (event) => {
|
this.listeners.on(this.nodes.wrapper, 'mousedown', (event) => {
|
||||||
const isClickedOnActionsWrapper = (event.target as Element).closest(`.${this.CSS.actionsWrapper}`);
|
const isClickedOnActionsWrapper = (event.target as Element).closest(`.${this.CSS.actionsWrapper}`);
|
||||||
|
|
||||||
// If click is on actions wrapper,
|
// If click is on actions wrapper,
|
||||||
|
@ -479,7 +519,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
|
|
||||||
this.nodes.togglerAndButtonsWrapper.appendChild(this.nodes.conversionToggler);
|
this.nodes.togglerAndButtonsWrapper.appendChild(this.nodes.conversionToggler);
|
||||||
|
|
||||||
this.Editor.Listeners.on(this.nodes.conversionToggler, 'click', () => {
|
this.listeners.on(this.nodes.conversionToggler, 'click', () => {
|
||||||
this.Editor.ConversionToolbar.toggle((conversionToolbarOpened) => {
|
this.Editor.ConversionToolbar.toggle((conversionToolbarOpened) => {
|
||||||
/**
|
/**
|
||||||
* When ConversionToolbar is opening on activated InlineToolbar flipper
|
* When ConversionToolbar is opening on activated InlineToolbar flipper
|
||||||
|
@ -594,7 +634,6 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
*/
|
*/
|
||||||
private addTool(toolName: string, tool: InlineTool): void {
|
private addTool(toolName: string, tool: InlineTool): void {
|
||||||
const {
|
const {
|
||||||
Listeners,
|
|
||||||
Tools,
|
Tools,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} = this.Editor;
|
} = this.Editor;
|
||||||
|
@ -617,48 +656,17 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
this.nodes.actions.appendChild(actions);
|
this.nodes.actions.appendChild(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Listeners.on(button, 'click', (event) => {
|
this.listeners.on(button, 'click', (event) => {
|
||||||
this.toolClicked(tool);
|
this.toolClicked(tool);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
const shortcut = this.getToolShortcut(toolName);
|
||||||
* Enable shortcuts
|
|
||||||
* Ignore tool that doesn't have shortcut or empty string
|
|
||||||
*/
|
|
||||||
const toolSettings = Tools.getToolSettings(toolName);
|
|
||||||
|
|
||||||
let shortcut = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get internal inline tools
|
|
||||||
*/
|
|
||||||
const internalTools: string[] = Object
|
|
||||||
.entries(Tools.internalTools)
|
|
||||||
.filter(([, toolClass]: [string, ToolConstructable | ToolSettings]) => {
|
|
||||||
if (_.isFunction(toolClass)) {
|
|
||||||
return toolClass[Tools.INTERNAL_SETTINGS.IS_INLINE];
|
|
||||||
}
|
|
||||||
|
|
||||||
return (toolClass as ToolSettings).class[Tools.INTERNAL_SETTINGS.IS_INLINE];
|
|
||||||
})
|
|
||||||
.map(([ name ]: [string, InlineToolConstructable | ToolSettings]) => name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1) For internal tools, check public getter 'shortcut'
|
|
||||||
* 2) For external tools, check tool's settings
|
|
||||||
* 3) If shortcut is not set in settings, check Tool's public property
|
|
||||||
*/
|
|
||||||
if (internalTools.includes(toolName)) {
|
|
||||||
shortcut = this.inlineTools[toolName][Tools.INTERNAL_SETTINGS.SHORTCUT];
|
|
||||||
} else if (toolSettings && toolSettings[Tools.USER_SETTINGS.SHORTCUT]) {
|
|
||||||
shortcut = toolSettings[Tools.USER_SETTINGS.SHORTCUT];
|
|
||||||
} else if (tool.shortcut) {
|
|
||||||
shortcut = tool.shortcut;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortcut) {
|
if (shortcut) {
|
||||||
|
try {
|
||||||
this.enableShortcuts(tool, shortcut);
|
this.enableShortcuts(tool, shortcut);
|
||||||
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -684,6 +692,35 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get shortcut name for tool
|
||||||
|
*
|
||||||
|
* @param toolName — Tool name
|
||||||
|
*/
|
||||||
|
private getToolShortcut(toolName): string | void {
|
||||||
|
const { Tools } = this.Editor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable shortcuts
|
||||||
|
* Ignore tool that doesn't have shortcut or empty string
|
||||||
|
*/
|
||||||
|
const toolSettings = Tools.getToolSettings(toolName);
|
||||||
|
const tool = this.toolsInstances.get(toolName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1) For internal tools, check public getter 'shortcut'
|
||||||
|
* 2) For external tools, check tool's settings
|
||||||
|
* 3) If shortcut is not set in settings, check Tool's public property
|
||||||
|
*/
|
||||||
|
if (Object.keys(this.internalTools).includes(toolName)) {
|
||||||
|
return this.inlineTools[toolName][Tools.INTERNAL_SETTINGS.SHORTCUT];
|
||||||
|
} else if (toolSettings && toolSettings[Tools.USER_SETTINGS.SHORTCUT]) {
|
||||||
|
return toolSettings[Tools.USER_SETTINGS.SHORTCUT];
|
||||||
|
} else if (tool.shortcut) {
|
||||||
|
return tool.shortcut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable Tool shortcut with Editor Shortcuts Module
|
* Enable Tool shortcut with Editor Shortcuts Module
|
||||||
*
|
*
|
||||||
|
@ -691,7 +728,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
* @param {string} shortcut - shortcut according to the ShortcutData Module format
|
* @param {string} shortcut - shortcut according to the ShortcutData Module format
|
||||||
*/
|
*/
|
||||||
private enableShortcuts(tool: InlineTool, shortcut: string): void {
|
private enableShortcuts(tool: InlineTool, shortcut: string): void {
|
||||||
this.Editor.Shortcuts.add({
|
Shortcuts.add({
|
||||||
name: shortcut,
|
name: shortcut,
|
||||||
handler: (event) => {
|
handler: (event) => {
|
||||||
const { currentBlock } = this.Editor.BlockManager;
|
const { currentBlock } = this.Editor.BlockManager;
|
||||||
|
@ -719,6 +756,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.toolClicked(tool);
|
this.toolClicked(tool);
|
||||||
},
|
},
|
||||||
|
on: this.Editor.UI.nodes.redactor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Flipper from '../../flipper';
|
||||||
import { BlockToolAPI } from '../../block';
|
import { BlockToolAPI } from '../../block';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||||
|
import Shortcuts from '../../utils/shortcuts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTMLElements used for Toolbox UI
|
* HTMLElements used for Toolbox UI
|
||||||
|
@ -225,7 +226,7 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
||||||
/**
|
/**
|
||||||
* Add click listener
|
* Add click listener
|
||||||
*/
|
*/
|
||||||
this.Editor.Listeners.on(button, 'click', (event: KeyboardEvent|MouseEvent) => {
|
this.listeners.on(button, 'click', (event: KeyboardEvent|MouseEvent) => {
|
||||||
this.toolButtonActivate(event, toolName);
|
this.toolButtonActivate(event, toolName);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -306,12 +307,13 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
||||||
* @param {string} shortcut - shortcut according to the ShortcutData Module format
|
* @param {string} shortcut - shortcut according to the ShortcutData Module format
|
||||||
*/
|
*/
|
||||||
private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string): void {
|
private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string): void {
|
||||||
this.Editor.Shortcuts.add({
|
Shortcuts.add({
|
||||||
name: shortcut,
|
name: shortcut,
|
||||||
handler: (event: KeyboardEvent) => {
|
handler: (event: KeyboardEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.insertNewBlock(tool, toolName);
|
this.insertNewBlock(tool, toolName);
|
||||||
},
|
},
|
||||||
|
on: this.Editor.UI.nodes.redactor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +329,7 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
||||||
const shortcut = this.getToolShortcut(toolName, tools[toolName]);
|
const shortcut = this.getToolShortcut(toolName, tools[toolName]);
|
||||||
|
|
||||||
if (shortcut) {
|
if (shortcut) {
|
||||||
this.Editor.Shortcuts.remove(shortcut);
|
Shortcuts.remove(this.Editor.UI.nodes.redactor, shortcut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ import BoldInlineTool from '../inline-tools/inline-tool-bold';
|
||||||
import ItalicInlineTool from '../inline-tools/inline-tool-italic';
|
import ItalicInlineTool from '../inline-tools/inline-tool-italic';
|
||||||
import LinkInlineTool from '../inline-tools/inline-tool-link';
|
import LinkInlineTool from '../inline-tools/inline-tool-link';
|
||||||
import Stub from '../tools/stub';
|
import Stub from '../tools/stub';
|
||||||
|
import { ModuleConfig } from '../../types-internal/module-config';
|
||||||
|
import EventsDispatcher from '../utils/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module Editor.js Tools Submodule
|
* @module Editor.js Tools Submodule
|
||||||
|
@ -198,9 +200,13 @@ export default class Tools extends Module {
|
||||||
* @class
|
* @class
|
||||||
*
|
*
|
||||||
* @param {EditorConfig} config - Editor's configuration
|
* @param {EditorConfig} config - Editor's configuration
|
||||||
|
* @param {EventsDispatcher} eventsDispatcher - Editor's event dispatcher
|
||||||
*/
|
*/
|
||||||
constructor({ config }) {
|
constructor({ config, eventsDispatcher }: ModuleConfig) {
|
||||||
super({ config });
|
super({
|
||||||
|
config,
|
||||||
|
eventsDispatcher,
|
||||||
|
});
|
||||||
|
|
||||||
this.toolsClasses = {};
|
this.toolsClasses = {};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Module from '../__module';
|
||||||
* Use external module CodeX Tooltip
|
* Use external module CodeX Tooltip
|
||||||
*/
|
*/
|
||||||
import CodeXTooltips, { TooltipContent, TooltipOptions } from 'codex-tooltip';
|
import CodeXTooltips, { TooltipContent, TooltipOptions } from 'codex-tooltip';
|
||||||
import { ModuleConfig } from '../../types-internal/module-config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tooltip
|
* Tooltip
|
||||||
|
@ -20,14 +19,6 @@ export default class Tooltip extends Module {
|
||||||
*/
|
*/
|
||||||
private lib: CodeXTooltips = new CodeXTooltips();
|
private lib: CodeXTooltips = new CodeXTooltips();
|
||||||
|
|
||||||
/**
|
|
||||||
* @class
|
|
||||||
* @param {EditorConfig} - Editor's config
|
|
||||||
*/
|
|
||||||
constructor({ config }: ModuleConfig) {
|
|
||||||
super({ config });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows tooltip on element with passed HTML content
|
* Shows tooltip on element with passed HTML content
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* TextRange interface fot IE9-
|
* TextRange interface for IE9-
|
||||||
*/
|
*/
|
||||||
import * as _ from './utils';
|
import * as _ from './utils';
|
||||||
import $ from './dom';
|
import $ from './dom';
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
import Module from '../__module';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module eventDispatcher
|
* @class EventDispatcher
|
||||||
*
|
*
|
||||||
* Has two important methods:
|
* Has two important methods:
|
||||||
* - {Function} on - appends subscriber to the event. If event doesn't exist - creates new one
|
* - {Function} on - appends subscriber to the event. If event doesn't exist - creates new one
|
||||||
* - {Function} emit - fires all subscribers with data
|
* - {Function} emit - fires all subscribers with data
|
||||||
* - {Function off - unsubsribes callback
|
* - {Function off - unsubscribes callback
|
||||||
*
|
*
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*
|
*
|
||||||
* @typedef {Events} Events
|
* @typedef {Events} Events
|
||||||
* @property {object} subscribers - all subscribers grouped by event name
|
* @property {object} subscribers - all subscribers grouped by event name
|
||||||
*/
|
*/
|
||||||
export default class Events extends Module {
|
export default class EventsDispatcher {
|
||||||
/**
|
/**
|
||||||
* Object with events` names as key and array of callback functions as value
|
* Object with events` names as key and array of callback functions as value
|
||||||
*
|
*
|
|
@ -1,4 +1,3 @@
|
||||||
import Module from '../__module';
|
|
||||||
import * as _ from '../utils';
|
import * as _ from '../utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,11 +35,9 @@ export interface ListenerData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Editor.js Listeners module
|
* Editor.js Listeners helper
|
||||||
*
|
*
|
||||||
* @module Listeners
|
* Decorator for event listeners assignment
|
||||||
*
|
|
||||||
* Module-decorator for event listeners assignment
|
|
||||||
*
|
*
|
||||||
* @author Codex Team
|
* @author Codex Team
|
||||||
* @version 2.0.0
|
* @version 2.0.0
|
||||||
|
@ -50,7 +47,7 @@ export interface ListenerData {
|
||||||
* @typedef {Listeners} Listeners
|
* @typedef {Listeners} Listeners
|
||||||
* @property {ListenerData[]} allListeners - listeners store
|
* @property {ListenerData[]} allListeners - listeners store
|
||||||
*/
|
*/
|
||||||
export default class Listeners extends Module {
|
export default class Listeners {
|
||||||
/**
|
/**
|
||||||
* Stores all listeners data to find/remove/process it
|
* Stores all listeners data to find/remove/process it
|
||||||
*
|
*
|
||||||
|
@ -114,7 +111,7 @@ export default class Listeners extends Module {
|
||||||
existingListeners.forEach((listener, i) => {
|
existingListeners.forEach((listener, i) => {
|
||||||
const index = this.allListeners.indexOf(existingListeners[i]);
|
const index = this.allListeners.indexOf(existingListeners[i]);
|
||||||
|
|
||||||
if (index > 0) {
|
if (index > -1) {
|
||||||
this.allListeners.splice(index, 1);
|
this.allListeners.splice(index, 1);
|
||||||
|
|
||||||
listener.element.removeEventListener(listener.eventType, listener.handler, listener.options);
|
listener.element.removeEventListener(listener.eventType, listener.handler, listener.options);
|
107
src/components/utils/shortcuts.ts
Normal file
107
src/components/utils/shortcuts.ts
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import Shortcut from '@codexteam/shortcuts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains keyboard and mouse events binded on each Block by Block Manager
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ShortcutData interface
|
||||||
|
* Each shortcut must have name and handler
|
||||||
|
* `name` is a shortcut, like 'CMD+K', 'CMD+B' etc
|
||||||
|
* `handler` is a callback
|
||||||
|
*
|
||||||
|
* @interface ShortcutData
|
||||||
|
*/
|
||||||
|
export interface ShortcutData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut name
|
||||||
|
* Ex. CMD+I, CMD+B ....
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut handler
|
||||||
|
*/
|
||||||
|
handler(event): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element handler should be added for
|
||||||
|
*/
|
||||||
|
on: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Shortcut
|
||||||
|
* @classdesc Allows to register new shortcut
|
||||||
|
*
|
||||||
|
* Internal Shortcuts Module
|
||||||
|
*/
|
||||||
|
class Shortcuts {
|
||||||
|
/**
|
||||||
|
* All registered shortcuts
|
||||||
|
*
|
||||||
|
* @type {Map<Element, Shortcut[]>}
|
||||||
|
*/
|
||||||
|
private registeredShortcuts: Map<Element, Shortcut[]> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register shortcut
|
||||||
|
*
|
||||||
|
* @param shortcut - shortcut options
|
||||||
|
*/
|
||||||
|
public add(shortcut: ShortcutData): void {
|
||||||
|
const foundShortcut = this.findShortcut(shortcut.on, shortcut.name);
|
||||||
|
|
||||||
|
if (foundShortcut) {
|
||||||
|
throw Error(
|
||||||
|
`Shortcut ${shortcut.name} is already registered for ${shortcut.on}. Please remove it before add a new handler.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newShortcut = new Shortcut({
|
||||||
|
name: shortcut.name,
|
||||||
|
on: shortcut.on,
|
||||||
|
callback: shortcut.handler,
|
||||||
|
});
|
||||||
|
const shortcuts = this.registeredShortcuts.get(shortcut.on) || [];
|
||||||
|
|
||||||
|
this.registeredShortcuts.set(shortcut.on, [...shortcuts, newShortcut]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove shortcut
|
||||||
|
*
|
||||||
|
* @param element - Element shortcut is set for
|
||||||
|
* @param name - shortcut name
|
||||||
|
*/
|
||||||
|
public remove(element: Element, name: string): void {
|
||||||
|
const shortcut = this.findShortcut(element, name);
|
||||||
|
|
||||||
|
if (!shortcut) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcut.remove();
|
||||||
|
|
||||||
|
const shortcuts = this.registeredShortcuts.get(element);
|
||||||
|
|
||||||
|
this.registeredShortcuts.set(element, shortcuts.filter(el => el !== shortcut));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Shortcut instance if exist
|
||||||
|
*
|
||||||
|
* @param element - Element shorcut is set for
|
||||||
|
* @param shortcut - shortcut name
|
||||||
|
*
|
||||||
|
* @returns {number} index - shortcut index if exist
|
||||||
|
*/
|
||||||
|
private findShortcut(element: Element, shortcut: string): Shortcut | void {
|
||||||
|
const shortcuts = this.registeredShortcuts.get(element) || [];
|
||||||
|
|
||||||
|
return shortcuts.find(({ name }) => name === shortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Shortcuts();
|
6
src/types-internal/editor-modules.d.ts
vendored
6
src/types-internal/editor-modules.d.ts
vendored
|
@ -1,12 +1,9 @@
|
||||||
import UI from '../components/modules/ui';
|
import UI from '../components/modules/ui';
|
||||||
import BlockEvents from '../components/modules/blockEvents';
|
import BlockEvents from '../components/modules/blockEvents';
|
||||||
import Listeners from '../components/modules/listeners';
|
|
||||||
import Toolbar from '../components/modules/toolbar/index';
|
import Toolbar from '../components/modules/toolbar/index';
|
||||||
import InlineToolbar from '../components/modules/toolbar/inline';
|
import InlineToolbar from '../components/modules/toolbar/inline';
|
||||||
import Toolbox from '../components/modules/toolbar/toolbox';
|
import Toolbox from '../components/modules/toolbar/toolbox';
|
||||||
import BlockSettings from '../components/modules/toolbar/blockSettings';
|
import BlockSettings from '../components/modules/toolbar/blockSettings';
|
||||||
import Events from '../components/modules/events';
|
|
||||||
import Shortcuts from '../components/modules/shortcuts';
|
|
||||||
import Paste from '../components/modules/paste';
|
import Paste from '../components/modules/paste';
|
||||||
import Notifier from '../components/modules/notifier';
|
import Notifier from '../components/modules/notifier';
|
||||||
import Tooltip from '../components/modules/tooltip';
|
import Tooltip from '../components/modules/tooltip';
|
||||||
|
@ -44,14 +41,11 @@ export interface EditorModules {
|
||||||
BlockEvents: BlockEvents;
|
BlockEvents: BlockEvents;
|
||||||
BlockSelection: BlockSelection;
|
BlockSelection: BlockSelection;
|
||||||
RectangleSelection: RectangleSelection;
|
RectangleSelection: RectangleSelection;
|
||||||
Listeners: Listeners;
|
|
||||||
Toolbar: Toolbar;
|
Toolbar: Toolbar;
|
||||||
InlineToolbar: InlineToolbar;
|
InlineToolbar: InlineToolbar;
|
||||||
Toolbox: Toolbox;
|
Toolbox: Toolbox;
|
||||||
BlockSettings: BlockSettings;
|
BlockSettings: BlockSettings;
|
||||||
ConversionToolbar: ConversionToolbar;
|
ConversionToolbar: ConversionToolbar;
|
||||||
Events: Events;
|
|
||||||
Shortcuts: Shortcuts;
|
|
||||||
Paste: Paste;
|
Paste: Paste;
|
||||||
DragNDrop: DragNDrop;
|
DragNDrop: DragNDrop;
|
||||||
ModificationsObserver: ModificationsObserver;
|
ModificationsObserver: ModificationsObserver;
|
||||||
|
|
8
src/types-internal/html-janitor.d.ts
vendored
8
src/types-internal/html-janitor.d.ts
vendored
|
@ -3,9 +3,15 @@
|
||||||
* After that we can use it at the TS modules
|
* After that we can use it at the TS modules
|
||||||
*/
|
*/
|
||||||
declare module 'html-janitor' {
|
declare module 'html-janitor' {
|
||||||
|
/**
|
||||||
|
* Sanitizer config of each HTML element
|
||||||
|
* @see {@link https://github.com/guardian/html-janitor#options}
|
||||||
|
*/
|
||||||
|
type TagConfig = boolean | { [attr: string]: boolean | string };
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
tags: {
|
tags: {
|
||||||
[key: string]: boolean|{[attr: string]: boolean|string}|(() => any)
|
[key: string]: TagConfig | ((el: Element) => TagConfig)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
src/types-internal/module-config.d.ts
vendored
2
src/types-internal/module-config.d.ts
vendored
|
@ -1,8 +1,10 @@
|
||||||
import { EditorConfig } from '../../types/index';
|
import { EditorConfig } from '../../types/index';
|
||||||
|
import EventsDispatcher from '../components/utils/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes object passed to Editor modules constructor
|
* Describes object passed to Editor modules constructor
|
||||||
*/
|
*/
|
||||||
export interface ModuleConfig {
|
export interface ModuleConfig {
|
||||||
config: EditorConfig;
|
config: EditorConfig;
|
||||||
|
eventsDispatcher: EventsDispatcher;
|
||||||
}
|
}
|
||||||
|
|
8
types/configs/sanitizer-config.d.ts
vendored
8
types/configs/sanitizer-config.d.ts
vendored
|
@ -1,3 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Sanitizer config of each HTML element
|
||||||
|
* @see {@link https://github.com/guardian/html-janitor#options}
|
||||||
|
*/
|
||||||
|
type TagConfig = boolean | { [attr: string]: boolean | string };
|
||||||
|
|
||||||
export interface SanitizerConfig {
|
export interface SanitizerConfig {
|
||||||
/**
|
/**
|
||||||
* Tag name and params not to be stripped off
|
* Tag name and params not to be stripped off
|
||||||
|
@ -31,5 +37,5 @@ export interface SanitizerConfig {
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
[key: string]: boolean|{[attr: string]: boolean|string}|((el?: Element) => any);
|
[key: string]: TagConfig | ((el: Element) => TagConfig);
|
||||||
}
|
}
|
||||||
|
|
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
|
@ -104,8 +104,6 @@ declare class EditorJS {
|
||||||
|
|
||||||
public blocks: Blocks;
|
public blocks: Blocks;
|
||||||
public caret: Caret;
|
public caret: Caret;
|
||||||
public events: Events;
|
|
||||||
public listeners: Listeners;
|
|
||||||
public sanitizer: Sanitizer;
|
public sanitizer: Sanitizer;
|
||||||
public saver: Saver;
|
public saver: Saver;
|
||||||
public selection: Selection;
|
public selection: Selection;
|
||||||
|
|
Loading…
Reference in a new issue