mirror of
https://github.com/codex-team/editor.js
synced 2024-06-08 08:52:15 +02:00
refactoring(modules): shortcuts module is util now (#1578)
* (refactoring)shortcuts module is util now * Fix eslint Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
parent
2a2a6f11ad
commit
2f5da52d1a
|
@ -16,6 +16,7 @@
|
|||
- `Fix` - Fix SanitizerConfig type definition [#1513](https://github.com/codex-team/editor.js/issues/1513)
|
||||
- `Refactoring` - The Listeners module now is a util.
|
||||
- `Fix` - Editor Config now immutable [#1552](https://github.com/codex-team/editor.js/issues/1552).
|
||||
- `Refactoring` - Shortcuts module is util now
|
||||
|
||||
### 2.19.1
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
<!-- Load Editor.js's Core -->
|
||||
<script src="../dist/editor.js" onload="document.getElementById('hint-core').hidden = true"></script>
|
||||
<script src="./tools/header/dist/bundle.js"></script><!-- Header -->
|
||||
|
||||
<!-- Initialization -->
|
||||
<script>
|
||||
|
@ -50,6 +51,12 @@
|
|||
*/
|
||||
var editor1 = new EditorJS({
|
||||
holder: 'editorjs1',
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
shortcut: 'CMD+SHIFT+H'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -57,6 +64,12 @@
|
|||
*/
|
||||
var editor2 = new EditorJS({
|
||||
holder: 'editorjs2',
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
shortcut: 'CMD+SHIFT+H'
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -9,6 +9,7 @@ import Module from '../__module';
|
|||
import Block from '../block';
|
||||
import * as _ from '../utils';
|
||||
import $ from '../dom';
|
||||
import Shortcuts from '../utils/shortcuts';
|
||||
|
||||
import SelectionUtils from '../selection';
|
||||
import { SanitizerConfig } from '../../../types/configs';
|
||||
|
@ -145,8 +146,6 @@ export default class BlockSelection extends Module {
|
|||
* to select all and copy them
|
||||
*/
|
||||
public prepare(): void {
|
||||
const { Shortcuts } = this.Editor;
|
||||
|
||||
this.selection = new SelectionUtils();
|
||||
|
||||
/**
|
||||
|
@ -181,6 +180,7 @@ export default class BlockSelection extends Module {
|
|||
|
||||
this.handleCommandA(event);
|
||||
},
|
||||
on: this.Editor.UI.nodes.redactor,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -361,10 +361,8 @@ export default class BlockSelection extends Module {
|
|||
* De-registers Shortcut CMD+A
|
||||
*/
|
||||
public destroy(): void {
|
||||
const { Shortcuts } = this.Editor;
|
||||
|
||||
/** Selection shortcut */
|
||||
Shortcuts.remove('CMD+A');
|
||||
Shortcuts.remove(this.Editor.UI.nodes.redactor, 'CMD+A');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ import { InlineTool, InlineToolConstructable, ToolConstructable, ToolSettings }
|
|||
import Flipper from '../../flipper';
|
||||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
import Shortcuts from '../../utils/shortcuts';
|
||||
import { EditorModules } from '../../../types-internal/editor-modules';
|
||||
|
||||
/**
|
||||
* Inline Toolbar elements
|
||||
|
@ -87,6 +89,38 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -185,7 +219,13 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
}
|
||||
|
||||
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'
|
||||
*/
|
||||
|
@ -621,40 +661,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
event.preventDefault();
|
||||
});
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
const shortcut = this.getToolShortcut(toolName);
|
||||
|
||||
if (shortcut) {
|
||||
this.enableShortcuts(tool, shortcut);
|
||||
|
@ -683,6 +690,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
|
||||
*
|
||||
|
@ -690,7 +726,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
* @param {string} shortcut - shortcut according to the ShortcutData Module format
|
||||
*/
|
||||
private enableShortcuts(tool: InlineTool, shortcut: string): void {
|
||||
this.Editor.Shortcuts.add({
|
||||
Shortcuts.add({
|
||||
name: shortcut,
|
||||
handler: (event) => {
|
||||
const { currentBlock } = this.Editor.BlockManager;
|
||||
|
@ -718,6 +754,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
event.preventDefault();
|
||||
this.toolClicked(tool);
|
||||
},
|
||||
on: this.Editor.UI.nodes.redactor,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import Flipper from '../../flipper';
|
|||
import { BlockToolAPI } from '../../block';
|
||||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
import Shortcuts from '../../utils/shortcuts';
|
||||
|
||||
/**
|
||||
* HTMLElements used for Toolbox UI
|
||||
|
@ -306,12 +307,13 @@ export default class Toolbox extends Module<ToolboxNodes> {
|
|||
* @param {string} shortcut - shortcut according to the ShortcutData Module format
|
||||
*/
|
||||
private enableShortcut(tool: BlockToolConstructable, toolName: string, shortcut: string): void {
|
||||
this.Editor.Shortcuts.add({
|
||||
Shortcuts.add({
|
||||
name: shortcut,
|
||||
handler: (event: KeyboardEvent) => {
|
||||
event.preventDefault();
|
||||
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]);
|
||||
|
||||
if (shortcut) {
|
||||
this.Editor.Shortcuts.remove(shortcut);
|
||||
Shortcuts.remove(this.Editor.UI.nodes.redactor, shortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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();
|
2
src/types-internal/editor-modules.d.ts
vendored
2
src/types-internal/editor-modules.d.ts
vendored
|
@ -5,7 +5,7 @@ import InlineToolbar from '../components/modules/toolbar/inline';
|
|||
import Toolbox from '../components/modules/toolbar/toolbox';
|
||||
import BlockSettings from '../components/modules/toolbar/blockSettings';
|
||||
import Events from '../components/modules/events';
|
||||
import Shortcuts from '../components/modules/shortcuts';
|
||||
import Shortcuts from '../components/utils/shortcuts';
|
||||
import Paste from '../components/modules/paste';
|
||||
import Notifier from '../components/modules/notifier';
|
||||
import Tooltip from '../components/modules/tooltip';
|
||||
|
|
Loading…
Reference in a new issue