mirror of
https://github.com/codex-team/editor.js
synced 2024-06-10 09:52:36 +02:00
Release 2.14 (#799)
This commit is contained in:
parent
9e61e3ddea
commit
23a4b2ef93
|
@ -85,7 +85,7 @@ Shortcut | Action | Restrictions
|
||||||
Also we support shortcuts on the all type of Tools. Specify a shortcut with the Tools configuration. For example:
|
Also we support shortcuts on the all type of Tools. Specify a shortcut with the Tools configuration. For example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var editor = EditorJS({
|
var editor = new EditorJS({
|
||||||
//...
|
//...
|
||||||
tools: {
|
tools: {
|
||||||
header: {
|
header: {
|
||||||
|
@ -113,7 +113,7 @@ There are few steps to run Editor.js on your site.
|
||||||
|
|
||||||
## Load Editor's core
|
## Load Editor's core
|
||||||
|
|
||||||
Firstly you need to get Editor.js itself. It is a [minified script](build/editor.js) with Editor's core and some default must-have tools.
|
Firstly you need to get Editor.js itself. It is a [minified script](dist/editor.js) with Editor's core and some default must-have tools.
|
||||||
|
|
||||||
Choose the most usable method of getting Editor for you.
|
Choose the most usable method of getting Editor for you.
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ var editor = new EditorJS({
|
||||||
/**
|
/**
|
||||||
* Create a holder for the Editor and pass its ID
|
* Create a holder for the Editor and pass its ID
|
||||||
*/
|
*/
|
||||||
holderId : 'editorjs',
|
holder : 'editorjs',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available Tools list.
|
* Available Tools list.
|
||||||
|
|
18
dist/editor.js
vendored
18
dist/editor.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,14 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
|
### 2.14
|
||||||
|
|
||||||
|
- `Fix` *Config* — User config now has higher priority than internal settings [#771](https://github.com/codex-team/editor.js/issues/771)
|
||||||
|
- `New` — Ability to work with Block Actions and Inline Toolbar from the keyboard by Tab. [#705](https://github.com/codex-team/editor.js/issues/705)
|
||||||
|
- `Fix` — Fix error thrown by click on the empty editor after `blocks.clear()` method calling [#761](https://github.com/codex-team/editor.js/issues/761)
|
||||||
|
- `Fix` — Fix placeholder property appearance. Now you can assign it via `placeholder` property of EditorConfig. [#714](https://github.com/codex-team/editor.js/issues/714)
|
||||||
|
- `Fix` — Add API shorthands to TS types [#788](https://github.com/codex-team/editor.js/issues/788)
|
||||||
|
|
||||||
### 2.13
|
### 2.13
|
||||||
|
|
||||||
- `Improvements` *BlockSelection* — Block Selection allows to select single editable element via CMD+A
|
- `Improvements` *BlockSelection* — Block Selection allows to select single editable element via CMD+A
|
||||||
|
|
|
@ -83,3 +83,23 @@ var editor2 = new EditorJS({
|
||||||
holder: 'codex-editor' // like document.getElementById('codex-editor')
|
holder: 'codex-editor' // like document.getElementById('codex-editor')
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Placeholder
|
||||||
|
|
||||||
|
By default Editor\`s placeholder is empty.
|
||||||
|
|
||||||
|
You can pass your own placeholder via `placeholder` field:
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
var editor = new EditorJS({
|
||||||
|
//...
|
||||||
|
placeholder: 'My awesome placeholder'
|
||||||
|
//...
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using your custom `Initial Block`, `placeholder` property is passed in `config` to your Tool constructor.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a99abc4f84c8dc44b86f037f74e971374d28e22f
|
Subproject commit 0083d3bcbce82fd2063a15e3914158cb4bf8da7e
|
|
@ -1 +1 @@
|
||||||
Subproject commit 54c70585457f110cf7d9567a4bb741d8e98d1189
|
Subproject commit 37ad8316c138f55b5c210a0c8bb6b83db483187b
|
|
@ -1 +1 @@
|
||||||
Subproject commit fc365f869c12824a42d5081a3751741180a99e85
|
Subproject commit 70876fde289e9f8baa81ee4b5c8c3dc036ac7035
|
|
@ -1 +1 @@
|
||||||
Subproject commit 470a64c8ba8a65f3e291d77bc9e2d53a48591f87
|
Subproject commit 33ffba6f9104d163c69c963ca06fed329a819238
|
|
@ -1 +1 @@
|
||||||
Subproject commit da8d692adb3d1e3af10a192a9500e44bd4cc158b
|
Subproject commit 019fe76e6065d9d59be6c47b8f4a4706f285eee1
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7f2e71470910291ba3ebd2fc013fbef33d054aa3
|
Subproject commit c1db3e8b40e98c36fedb1af0b574e5a161e2a471
|
|
@ -1 +1 @@
|
||||||
Subproject commit d72369b6d78e92610681f2e874f6f17cb3a6df27
|
Subproject commit da9f8a109706d4b4593e19afbfc05614466eb987
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3d432a0279321566a23c4d6f27c913128b4c367e
|
Subproject commit 293109d03d9ff3cdecc52ec959866662d80dd0ce
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@editorjs/editorjs",
|
"name": "@editorjs/editorjs",
|
||||||
"version": "2.13.0",
|
"version": "2.14.0",
|
||||||
"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",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import $ from './dom';
|
import $ from './dom';
|
||||||
import _ from './utils';
|
import _ from './utils';
|
||||||
import {EditorConfig, OutputData, SanitizerConfig, ToolSettings} from '../../types';
|
import {EditorConfig, OutputData, SanitizerConfig} from '../../types';
|
||||||
import {EditorModules} from '../types-internal/editor-modules';
|
import {EditorModules} from '../types-internal/editor-modules';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +163,7 @@ export default class Core {
|
||||||
data : {},
|
data : {},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.config.placeholder = this.config.placeholder || 'write your story...';
|
this.config.placeholder = this.config.placeholder || false;
|
||||||
this.config.sanitizer = this.config.sanitizer || {
|
this.config.sanitizer = this.config.sanitizer || {
|
||||||
p: true,
|
p: true,
|
||||||
b: true,
|
b: true,
|
||||||
|
|
|
@ -520,6 +520,85 @@ export default class Dom {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Leafs nodes inside the target list from active element
|
||||||
|
*
|
||||||
|
* @param {HTMLElement[]} nodeList - target list of nodes
|
||||||
|
* @param {number} activeIndex — index of active node. By default it must be -1
|
||||||
|
* @param {string} direction - leaf direction. Can be 'left' or 'right'
|
||||||
|
* @param {string} activeCSSClass - css class that will be added
|
||||||
|
*
|
||||||
|
* @return {Number} index of active node
|
||||||
|
*/
|
||||||
|
public static leafNodesAndReturnIndex(
|
||||||
|
nodeList: HTMLElement[],
|
||||||
|
activeIndex: number,
|
||||||
|
direction: string,
|
||||||
|
activeCSSClass: string,
|
||||||
|
): number {
|
||||||
|
/**
|
||||||
|
* If activeButtonIndex === -1 then we have no chosen Tool in Toolbox
|
||||||
|
*/
|
||||||
|
if (activeIndex === -1) {
|
||||||
|
/**
|
||||||
|
* Normalize "previous" Tool index depending on direction.
|
||||||
|
* We need to do this to highlight "first" Tool correctly
|
||||||
|
*
|
||||||
|
* Order of Tools: [0] [1] ... [n - 1]
|
||||||
|
* [0 = n] because of: n % n = 0 % n
|
||||||
|
*
|
||||||
|
* Direction 'right': for [0] the [n - 1] is a previous index
|
||||||
|
* [n - 1] -> [0]
|
||||||
|
*
|
||||||
|
* Direction 'left': for [n - 1] the [0] is a previous index
|
||||||
|
* [n - 1] <- [0]
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
activeIndex = direction === 'right' ? -1 : 0;
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* If we have chosen Tool then remove highlighting
|
||||||
|
*/
|
||||||
|
nodeList[activeIndex].classList.remove(activeCSSClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count index for next Tool
|
||||||
|
*/
|
||||||
|
if (direction === 'right') {
|
||||||
|
/**
|
||||||
|
* If we go right then choose next (+1) Tool
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
activeIndex = (activeIndex + 1) % nodeList.length;
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* If we go left then choose previous (-1) Tool
|
||||||
|
* Before counting module we need to add length before because of "The JavaScript Modulo Bug"
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
activeIndex = (nodeList.length + activeIndex - 1) % nodeList.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Dom.isNativeInput(nodeList[activeIndex])) {
|
||||||
|
/**
|
||||||
|
* Focus input
|
||||||
|
*/
|
||||||
|
nodeList[activeIndex].focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight new chosen Tool
|
||||||
|
*/
|
||||||
|
nodeList[activeIndex].classList.add(activeCSSClass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return Active index
|
||||||
|
*/
|
||||||
|
return activeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
* Helper for get holder from {string} or return HTMLElement
|
* Helper for get holder from {string} or return HTMLElement
|
||||||
* @param element
|
* @param element
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -103,6 +103,7 @@ export default class BlocksAPI extends Module {
|
||||||
*/
|
*/
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
this.Editor.BlockManager.clear(true);
|
this.Editor.BlockManager.clear(true);
|
||||||
|
this.Editor.InlineToolbar.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
*/
|
*/
|
||||||
import Module from '../__module';
|
import Module from '../__module';
|
||||||
import _ from '../utils';
|
import _ from '../utils';
|
||||||
|
import SelectionUtils from '../selection';
|
||||||
|
|
||||||
export default class BlockEvents extends Module {
|
export default class BlockEvents extends Module {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All keydowns on Block
|
* All keydowns on Block
|
||||||
* @param {KeyboardEvent} event - keydown
|
* @param {KeyboardEvent} event - keydown
|
||||||
|
@ -62,7 +64,12 @@ export default class BlockEvents extends Module {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close Toolbar on any keypress except TAB, because TAB leafs Tools
|
||||||
|
*/
|
||||||
|
if (event.keyCode !== _.keyCodes.TAB) {
|
||||||
this.Editor.Toolbar.close();
|
this.Editor.Toolbar.close();
|
||||||
|
}
|
||||||
|
|
||||||
const cmdKey = event.ctrlKey || event.metaKey;
|
const cmdKey = event.ctrlKey || event.metaKey;
|
||||||
const altKey = event.altKey;
|
const altKey = event.altKey;
|
||||||
|
@ -92,6 +99,11 @@ export default class BlockEvents extends Module {
|
||||||
*/
|
*/
|
||||||
public keyup(event): void {
|
public keyup(event): void {
|
||||||
this.Editor.InlineToolbar.handleShowingEvent(event);
|
this.Editor.InlineToolbar.handleShowingEvent(event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if editor is empty on each keyup and add special css class to wrapper
|
||||||
|
*/
|
||||||
|
this.Editor.UI.checkEmptiness();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,6 +122,10 @@ export default class BlockEvents extends Module {
|
||||||
|
|
||||||
const {currentBlock} = this.Editor.BlockManager;
|
const {currentBlock} = this.Editor.BlockManager;
|
||||||
|
|
||||||
|
if (!currentBlock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/** Prevent Default behaviour */
|
/** Prevent Default behaviour */
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -119,31 +135,77 @@ export default class BlockEvents extends Module {
|
||||||
direction = shiftKey ? 'left' : 'right';
|
direction = shiftKey ? 'left' : 'right';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't show Plus and Toolbox near not-inital Tools
|
* For empty Blocks we show Plus button via Toobox only for initial Blocks
|
||||||
|
*/
|
||||||
|
if (this.Editor.Tools.isInitial(currentBlock.tool) && currentBlock.isEmpty) {
|
||||||
|
/**
|
||||||
|
* Work with Toolbox
|
||||||
|
* ------------------
|
||||||
|
*
|
||||||
|
* If Toolbox is not open, then just open it and show plus button
|
||||||
|
* Next Tab press will leaf Toolbox Tools
|
||||||
*/
|
*/
|
||||||
if (!this.Editor.Tools.isInitial(currentBlock.tool)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentBlock.isEmpty) {
|
|
||||||
if (!this.Editor.Toolbar.opened) {
|
if (!this.Editor.Toolbar.opened) {
|
||||||
this.Editor.Toolbar.open(false , false);
|
this.Editor.Toolbar.open(false , false);
|
||||||
this.Editor.Toolbar.plusButton.show();
|
this.Editor.Toolbar.plusButton.show();
|
||||||
|
} else {
|
||||||
|
this.Editor.Toolbox.leaf(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Editor.Toolbox.open();
|
this.Editor.Toolbox.open();
|
||||||
|
} else if (!currentBlock.isEmpty && !SelectionUtils.isCollapsed) {
|
||||||
|
/**
|
||||||
|
* Work with Inline Tools
|
||||||
|
* -----------------------
|
||||||
|
*
|
||||||
|
* If InlineToolbar is not open, just open it and focus first button
|
||||||
|
* Next Tab press will leaf InlineToolbar Tools
|
||||||
|
*/
|
||||||
|
if (this.Editor.InlineToolbar.opened) {
|
||||||
|
this.Editor.InlineToolbar.leaf(direction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* Open Toolbar and show BlockSettings
|
||||||
|
*/
|
||||||
|
if (!this.Editor.Toolbar.opened) {
|
||||||
|
this.Editor.BlockManager.currentBlock.focused = true;
|
||||||
|
this.Editor.Toolbar.open(true, false);
|
||||||
|
this.Editor.Toolbar.plusButton.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.Editor.Toolbox.opened) {
|
/**
|
||||||
this.Editor.Toolbox.leaf(direction);
|
* Work with Block Tunes
|
||||||
|
* ----------------------
|
||||||
|
*
|
||||||
|
* If BlockSettings is not open, then open BlockSettings
|
||||||
|
* Next Tab press will leaf Settings Buttons
|
||||||
|
*/
|
||||||
|
if (!this.Editor.BlockSettings.opened) {
|
||||||
|
this.Editor.BlockSettings.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Editor.BlockSettings.leaf(direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escape pressed
|
* Escape pressed
|
||||||
* @param event
|
* If some of Toolbar components are opened, then close it otherwise close Toolbar
|
||||||
|
*
|
||||||
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
public escapePressed(event): void { }
|
public escapePressed(event): void {
|
||||||
|
if (this.Editor.Toolbox.opened) {
|
||||||
|
this.Editor.Toolbox.close();
|
||||||
|
} else if (this.Editor.BlockSettings.opened) {
|
||||||
|
this.Editor.BlockSettings.close();
|
||||||
|
} else if (this.Editor.InlineToolbar.opened) {
|
||||||
|
this.Editor.InlineToolbar.close();
|
||||||
|
} else {
|
||||||
|
this.Editor.Toolbar.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add drop target styles
|
* Add drop target styles
|
||||||
|
@ -231,7 +293,10 @@ export default class BlockEvents extends Module {
|
||||||
* Don't handle Enter keydowns when Tool sets enableLineBreaks to true.
|
* Don't handle Enter keydowns when Tool sets enableLineBreaks to true.
|
||||||
* Uses for Tools like <code> where line breaks should be handled by default behaviour.
|
* Uses for Tools like <code> where line breaks should be handled by default behaviour.
|
||||||
*/
|
*/
|
||||||
if (tool && tool[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) {
|
if (tool
|
||||||
|
&& tool[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]
|
||||||
|
&& !this.Editor.BlockSettings.opened
|
||||||
|
&& !this.Editor.InlineToolbar.opened) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,10 +304,20 @@ export default class BlockEvents extends Module {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
this.Editor.Toolbox.toolButtonActivate(event, this.Editor.Toolbox.getActiveTool);
|
this.Editor.Toolbox.toolButtonActivate(event, this.Editor.Toolbox.getActiveTool);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.Editor.InlineToolbar.opened && this.Editor.InlineToolbar.focusedButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
this.Editor.InlineToolbar.focusedButton.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow to create linebreaks by Shift+Enter
|
* Allow to create linebreaks by Shift+Enter
|
||||||
*/
|
*/
|
||||||
|
@ -441,8 +516,15 @@ export default class BlockEvents extends Module {
|
||||||
*/
|
*/
|
||||||
private needToolbarClosing(event) {
|
private needToolbarClosing(event) {
|
||||||
const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbox.opened),
|
const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbox.opened),
|
||||||
flippingToolboxItems = event.keyCode === _.keyCodes.TAB;
|
blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened),
|
||||||
|
flippingToolbarItems = event.keyCode === _.keyCodes.TAB;
|
||||||
|
|
||||||
return !(event.shiftKey || flippingToolboxItems || toolboxItemSelected);
|
/**
|
||||||
|
* Do not close Toolbar in cases:
|
||||||
|
* 1. ShiftKey pressed (or combination with shiftKey)
|
||||||
|
* 2. When Toolbar is opened and Tab leafs its Tools
|
||||||
|
* 3. When Toolbar's component is opened and some its item selected
|
||||||
|
*/
|
||||||
|
return !(event.shiftKey || flippingToolbarItems || toolboxItemSelected || blockSettingsItemSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -575,6 +575,11 @@ export default class BlockManager extends Module {
|
||||||
if (needAddInitialBlock) {
|
if (needAddInitialBlock) {
|
||||||
this.insert(this.config.initialBlock);
|
this.insert(this.config.initialBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add empty modifier
|
||||||
|
*/
|
||||||
|
this.Editor.UI.checkEmptiness();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,7 +32,6 @@ export default class ModificationsObserver extends Module {
|
||||||
* @type {Function}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
private mutationDebouncer = _.debounce( () => {
|
private mutationDebouncer = _.debounce( () => {
|
||||||
this.checkEmptiness();
|
|
||||||
this.config.onChange();
|
this.config.onChange();
|
||||||
}, ModificationsObserver.DebounceTimer);
|
}, ModificationsObserver.DebounceTimer);
|
||||||
|
|
||||||
|
@ -143,13 +142,4 @@ export default class ModificationsObserver extends Module {
|
||||||
this.mutationDebouncer();
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,10 +42,14 @@ export default class Renderer extends Module {
|
||||||
* Make plugin blocks from array of plugin`s data
|
* Make plugin blocks from array of plugin`s data
|
||||||
* @param {RendererBlocks[]} blocks
|
* @param {RendererBlocks[]} blocks
|
||||||
*/
|
*/
|
||||||
public render(blocks: BlockToolData[]): Promise<void> {
|
public async render(blocks: BlockToolData[]): Promise<void> {
|
||||||
const chainData = blocks.map((block) => ({function: () => this.insertBlock(block)}));
|
const chainData = blocks.map((block) => ({function: () => this.insertBlock(block)}));
|
||||||
|
|
||||||
return _.sequence(chainData as ChainData[]);
|
const sequence = await _.sequence(chainData as ChainData[]);
|
||||||
|
|
||||||
|
this.Editor.UI.checkEmptiness();
|
||||||
|
|
||||||
|
return sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default class BlockSettings extends Module {
|
||||||
* Block Settings CSS
|
* Block Settings CSS
|
||||||
* @return {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}}
|
* @return {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}}
|
||||||
*/
|
*/
|
||||||
private static get CSS() {
|
public get CSS() {
|
||||||
return {
|
return {
|
||||||
// Settings Panel
|
// Settings Panel
|
||||||
wrapper: 'ce-settings',
|
wrapper: 'ce-settings',
|
||||||
|
@ -38,6 +38,9 @@ export default class BlockSettings extends Module {
|
||||||
defaultSettings: 'ce-settings__default-zone',
|
defaultSettings: 'ce-settings__default-zone',
|
||||||
|
|
||||||
button: 'ce-settings__button',
|
button: 'ce-settings__button',
|
||||||
|
|
||||||
|
focusedButton : 'ce-settings__button--focused',
|
||||||
|
focusedButtonAnimated: 'ce-settings__button--focused-animated',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ export default class BlockSettings extends Module {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
public get opened(): boolean {
|
public get opened(): boolean {
|
||||||
return this.nodes.wrapper.classList.contains(BlockSettings.CSS.wrapperOpened);
|
return this.nodes.wrapper.classList.contains(this.CSS.wrapperOpened);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +61,16 @@ export default class BlockSettings extends Module {
|
||||||
defaultSettings: null,
|
defaultSettings: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of buttons
|
||||||
|
*/
|
||||||
|
private buttons: HTMLElement[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of active button
|
||||||
|
*/
|
||||||
|
private focusedButtonIndex: number = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Panel with block settings with 2 sections:
|
* Panel with block settings with 2 sections:
|
||||||
* - Tool's Settings
|
* - Tool's Settings
|
||||||
|
@ -66,10 +79,10 @@ export default class BlockSettings extends Module {
|
||||||
* @return {Element}
|
* @return {Element}
|
||||||
*/
|
*/
|
||||||
public make(): void {
|
public make(): void {
|
||||||
this.nodes.wrapper = $.make('div', BlockSettings.CSS.wrapper);
|
this.nodes.wrapper = $.make('div', this.CSS.wrapper);
|
||||||
|
|
||||||
this.nodes.toolSettings = $.make('div', BlockSettings.CSS.toolSettings);
|
this.nodes.toolSettings = $.make('div', this.CSS.toolSettings);
|
||||||
this.nodes.defaultSettings = $.make('div', BlockSettings.CSS.defaultSettings);
|
this.nodes.defaultSettings = $.make('div', this.CSS.defaultSettings);
|
||||||
|
|
||||||
$.append(this.nodes.wrapper, [this.nodes.toolSettings, this.nodes.defaultSettings]);
|
$.append(this.nodes.wrapper, [this.nodes.toolSettings, this.nodes.defaultSettings]);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +91,7 @@ export default class BlockSettings extends Module {
|
||||||
* Open Block Settings pane
|
* Open Block Settings pane
|
||||||
*/
|
*/
|
||||||
public open(): void {
|
public open(): void {
|
||||||
this.nodes.wrapper.classList.add(BlockSettings.CSS.wrapperOpened);
|
this.nodes.wrapper.classList.add(this.CSS.wrapperOpened);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill Tool's settings
|
* Fill Tool's settings
|
||||||
|
@ -98,7 +111,7 @@ export default class BlockSettings extends Module {
|
||||||
* Close Block Settings pane
|
* Close Block Settings pane
|
||||||
*/
|
*/
|
||||||
public close(): void {
|
public close(): void {
|
||||||
this.nodes.wrapper.classList.remove(BlockSettings.CSS.wrapperOpened);
|
this.nodes.wrapper.classList.remove(this.CSS.wrapperOpened);
|
||||||
|
|
||||||
/** Clear settings */
|
/** Clear settings */
|
||||||
this.nodes.toolSettings.innerHTML = '';
|
this.nodes.toolSettings.innerHTML = '';
|
||||||
|
@ -106,8 +119,66 @@ export default class BlockSettings extends Module {
|
||||||
|
|
||||||
/** Tell to subscribers that block settings is closed */
|
/** Tell to subscribers that block settings is closed */
|
||||||
this.Editor.Events.emit(this.events.closed);
|
this.Editor.Events.emit(this.events.closed);
|
||||||
|
|
||||||
|
/** Clear cached buttons */
|
||||||
|
this.buttons = [];
|
||||||
|
|
||||||
|
/** Clear focus on active button */
|
||||||
|
this.focusedButtonIndex = -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Tools Settings and Default Settings
|
||||||
|
* @return {HTMLElement[]}
|
||||||
|
*/
|
||||||
|
public get blockTunesButtons(): HTMLElement[] {
|
||||||
|
/**
|
||||||
|
* Return from cache
|
||||||
|
* if exists
|
||||||
|
*/
|
||||||
|
if (this.buttons.length !== 0) {
|
||||||
|
return this.buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolSettings = this.nodes.toolSettings.querySelectorAll(`.${this.Editor.StylesAPI.classes.settingsButton}`);
|
||||||
|
const defaultSettings = this.nodes.defaultSettings.querySelectorAll(`.${this.CSS.button}`);
|
||||||
|
|
||||||
|
toolSettings.forEach((item, index) => {
|
||||||
|
this.buttons.push((item as HTMLElement));
|
||||||
|
if (item.classList.contains(this.CSS.focusedButton)) {
|
||||||
|
this.focusedButtonIndex = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
defaultSettings.forEach((item) => {
|
||||||
|
this.buttons.push((item as HTMLElement));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leaf Block Tunes
|
||||||
|
* @param {string} direction
|
||||||
|
*/
|
||||||
|
public leaf(direction: string = 'right'): void {
|
||||||
|
this.focusedButtonIndex = $.leafNodesAndReturnIndex(
|
||||||
|
this.blockTunesButtons, this.focusedButtonIndex, direction, this.CSS.focusedButton,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns active button HTML element
|
||||||
|
* @return {HTMLElement}
|
||||||
|
*/
|
||||||
|
public get focusedButton(): HTMLElement {
|
||||||
|
if (this.focusedButtonIndex === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.buttons[this.focusedButtonIndex] as HTMLElement);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Add Tool's settings
|
* Add Tool's settings
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -28,8 +28,15 @@ export default class InlineToolbar extends Module {
|
||||||
inlineToolButton: 'ce-inline-tool',
|
inlineToolButton: 'ce-inline-tool',
|
||||||
inlineToolButtonLast: 'ce-inline-tool--last',
|
inlineToolButtonLast: 'ce-inline-tool--last',
|
||||||
inputField: 'cdx-input',
|
inputField: 'cdx-input',
|
||||||
|
focusedButton: 'ce-inline-tool--focused',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of inline toolbar
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
public opened: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline Toolbar elements
|
* Inline Toolbar elements
|
||||||
*/
|
*/
|
||||||
|
@ -53,6 +60,25 @@ export default class InlineToolbar extends Module {
|
||||||
*/
|
*/
|
||||||
private toolsInstances: Map<string, InlineTool>;
|
private toolsInstances: Map<string, InlineTool>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buttons List
|
||||||
|
* @type {NodeList}
|
||||||
|
*/
|
||||||
|
private buttonsList: NodeList = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible Buttons
|
||||||
|
* Some Blocks might disable inline tools
|
||||||
|
* @type {HTMLElement[]}
|
||||||
|
*/
|
||||||
|
private visibleButtonsList: HTMLElement[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focused button index
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
private focusedButtonIndex: number = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline Toolbar Tools
|
* Inline Toolbar Tools
|
||||||
*
|
*
|
||||||
|
@ -85,7 +111,8 @@ export default class InlineToolbar extends Module {
|
||||||
this.Editor.Listeners.on(this.nodes.wrapper, 'mousedown', (event) => {
|
this.Editor.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, do not prevent default behaviour because actions might include interactive elements
|
// If click is on actions wrapper,
|
||||||
|
// do not prevent default behaviour because actions might include interactive elements
|
||||||
if (!isClickedOnActionsWrapper) {
|
if (!isClickedOnActionsWrapper) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -155,6 +182,47 @@ export default class InlineToolbar extends Module {
|
||||||
this.nodes.wrapper.style.top = Math.floor(newCoords.y) + 'px';
|
this.nodes.wrapper.style.top = Math.floor(newCoords.y) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leaf Inline Tools
|
||||||
|
* @param {string} direction
|
||||||
|
*/
|
||||||
|
public leaf(direction: string = 'right'): void {
|
||||||
|
this.visibleButtonsList = (Array.from(this.buttonsList)
|
||||||
|
.filter((tool) => !(tool as HTMLElement).hidden) as HTMLElement[]);
|
||||||
|
|
||||||
|
if (this.visibleButtonsList.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.focusedButtonIndex = $.leafNodesAndReturnIndex(
|
||||||
|
this.visibleButtonsList, this.focusedButtonIndex, direction, this.CSS.focusedButton,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops focused button index
|
||||||
|
*/
|
||||||
|
public dropFocusedButtonIndex(): void {
|
||||||
|
if (this.focusedButtonIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.visibleButtonsList[this.focusedButtonIndex].classList.remove(this.CSS.focusedButton);
|
||||||
|
this.focusedButtonIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Focused button Node
|
||||||
|
* @return {HTMLElement}
|
||||||
|
*/
|
||||||
|
public get focusedButton(): HTMLElement {
|
||||||
|
if (this.focusedButtonIndex === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.visibleButtonsList[this.focusedButtonIndex];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides Inline Toolbar
|
* Hides Inline Toolbar
|
||||||
*/
|
*/
|
||||||
|
@ -165,6 +233,13 @@ export default class InlineToolbar extends Module {
|
||||||
toolInstance.clear();
|
toolInstance.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.opened = false;
|
||||||
|
|
||||||
|
if (this.focusedButtonIndex !== -1) {
|
||||||
|
this.visibleButtonsList[this.focusedButtonIndex].classList.remove(this.CSS.focusedButton);
|
||||||
|
this.focusedButtonIndex = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -196,6 +271,9 @@ export default class InlineToolbar extends Module {
|
||||||
toolInstance.clear();
|
toolInstance.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.buttonsList = this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`);
|
||||||
|
this.opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -220,7 +298,9 @@ export default class InlineToolbar extends Module {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = currentSelection.anchorNode.parentElement;
|
const target = !$.isElement(currentSelection.anchorNode )
|
||||||
|
? currentSelection.anchorNode.parentElement
|
||||||
|
: currentSelection.anchorNode;
|
||||||
|
|
||||||
if (currentSelection && tagsConflictsWithSelection.includes(target.tagName)) {
|
if (currentSelection && tagsConflictsWithSelection.includes(target.tagName)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -142,12 +142,14 @@ export default class Toolbox extends Module {
|
||||||
|
|
||||||
this.opened = false;
|
this.opened = false;
|
||||||
|
|
||||||
/** remove active item pointer */
|
/**
|
||||||
this.activeButtonIndex = -1;
|
* Remove active item pointer
|
||||||
const activeButton = this.nodes.toolbox.querySelector(`.${this.CSS.toolboxButtonActive}`);
|
*/
|
||||||
|
if (this.activeButtonIndex !== -1) {
|
||||||
|
(this.nodes.toolbox.childNodes[this.activeButtonIndex] as HTMLElement)
|
||||||
|
.classList.remove(this.CSS.toolboxButtonActive);
|
||||||
|
|
||||||
if (activeButton) {
|
this.activeButtonIndex = -1;
|
||||||
activeButton.classList.remove(this.CSS.toolboxButtonActive);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,57 +170,10 @@ export default class Toolbox extends Module {
|
||||||
* @param {String} direction - leaf direction, right is default
|
* @param {String} direction - leaf direction, right is default
|
||||||
*/
|
*/
|
||||||
public leaf(direction: string = Toolbox.LEAF_DIRECTIONS.RIGHT): void {
|
public leaf(direction: string = Toolbox.LEAF_DIRECTIONS.RIGHT): void {
|
||||||
const childNodes = this.nodes.toolbox.childNodes;
|
const childNodes = (Array.from(this.nodes.toolbox.childNodes) as HTMLElement[]);
|
||||||
|
this.activeButtonIndex = $.leafNodesAndReturnIndex(
|
||||||
/**
|
childNodes, this.activeButtonIndex, direction, this.CSS.toolboxButtonActive,
|
||||||
* If activeButtonIndex === -1 then we have no chosen Tool in Toolbox
|
);
|
||||||
*/
|
|
||||||
if (this.activeButtonIndex === -1) {
|
|
||||||
/**
|
|
||||||
* Normalize "previous" Tool index depending on direction.
|
|
||||||
* We need to do this to highlight "first" Tool correctly
|
|
||||||
*
|
|
||||||
* Order of Tools: [0] [1] ... [n - 1]
|
|
||||||
* [0 = n] because of: n % n = 0 % n
|
|
||||||
*
|
|
||||||
* Direction 'right': for [0] the [n - 1] is a previous index
|
|
||||||
* [n - 1] -> [0]
|
|
||||||
*
|
|
||||||
* Direction 'left': for [n - 1] the [0] is a previous index
|
|
||||||
* [n - 1] <- [0]
|
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.activeButtonIndex = direction === Toolbox.LEAF_DIRECTIONS.RIGHT ? -1 : 0;
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* If we have chosen Tool then remove highlighting
|
|
||||||
*/
|
|
||||||
(childNodes[this.activeButtonIndex] as HTMLElement).classList.remove(this.CSS.toolboxButtonActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count index for next Tool
|
|
||||||
*/
|
|
||||||
if (direction === Toolbox.LEAF_DIRECTIONS.RIGHT) {
|
|
||||||
/**
|
|
||||||
* If we go right then choose next (+1) Tool
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.activeButtonIndex = (this.activeButtonIndex + 1) % childNodes.length;
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* If we go left then choose previous (-1) Tool
|
|
||||||
* Before counting module we need to add length before because of "The JavaScript Modulo Bug"
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.activeButtonIndex = (childNodes.length + this.activeButtonIndex - 1) % childNodes.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highlight new chosen Tool
|
|
||||||
*/
|
|
||||||
(childNodes[this.activeButtonIndex] as HTMLElement).classList.add(this.CSS.toolboxButtonActive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -207,7 +207,7 @@ export default class Tools extends Module {
|
||||||
/**
|
/**
|
||||||
* Assign internal tools
|
* Assign internal tools
|
||||||
*/
|
*/
|
||||||
_.deepMerge(this.config.tools, this.internalTools);
|
this.config.tools = _.deepMerge({}, this.internalTools, this.config.tools);
|
||||||
|
|
||||||
if (!this.config.hasOwnProperty('tools') || Object.keys(this.config.tools).length === 0) {
|
if (!this.config.hasOwnProperty('tools') || Object.keys(this.config.tools).length === 0) {
|
||||||
throw Error('Can\'t start without tools');
|
throw Error('Can\'t start without tools');
|
||||||
|
@ -302,14 +302,19 @@ export default class Tools extends Module {
|
||||||
/**
|
/**
|
||||||
* Configuration to be passed to the Tool's constructor
|
* Configuration to be passed to the Tool's constructor
|
||||||
*/
|
*/
|
||||||
const config = this.toolsSettings[tool][this.apiSettings.CONFIG];
|
const config = this.toolsSettings[tool][this.apiSettings.CONFIG] || {};
|
||||||
|
|
||||||
|
// Pass placeholder to initial Block config
|
||||||
|
if (tool === this.config.initialBlock && !config.placeholder) {
|
||||||
|
config.placeholder = this.config.placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{api: API, config: ({}), data: BlockToolData}}
|
* @type {{api: API, config: ({}), data: BlockToolData}}
|
||||||
*/
|
*/
|
||||||
const constructorOptions = {
|
const constructorOptions = {
|
||||||
api: this.Editor.API.methods,
|
api: this.Editor.API.methods,
|
||||||
config: config || {},
|
config,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,15 @@ export default class UI extends Module {
|
||||||
await this.bindEvents();
|
await this.bindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Editor is empty and set CSS class to wrapper
|
||||||
|
*/
|
||||||
|
public checkEmptiness(): void {
|
||||||
|
const {BlockManager} = this.Editor;
|
||||||
|
|
||||||
|
this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, BlockManager.isEditorEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean editor`s UI
|
* Clean editor`s UI
|
||||||
*/
|
*/
|
||||||
|
@ -268,9 +277,51 @@ export default class UI extends Module {
|
||||||
* @param event
|
* @param event
|
||||||
*/
|
*/
|
||||||
private enterPressed(event: KeyboardEvent): void {
|
private enterPressed(event: KeyboardEvent): void {
|
||||||
const {BlockManager, BlockSelection, Caret} = this.Editor;
|
const {BlockManager, BlockSelection, Caret, BlockSettings} = this.Editor;
|
||||||
const hasPointerToBlock = BlockManager.currentBlockIndex >= 0;
|
const hasPointerToBlock = BlockManager.currentBlockIndex >= 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If Block Settings is opened and have some active button
|
||||||
|
* Enter press is fired as out of the Block and that's why
|
||||||
|
* we handle it here
|
||||||
|
*/
|
||||||
|
if (BlockSettings.opened && BlockSettings.focusedButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
/** Click on settings button */
|
||||||
|
BlockSettings.focusedButton.click();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add animation on click
|
||||||
|
*/
|
||||||
|
BlockSettings.focusedButton.classList.add(BlockSettings.CSS.focusedButtonAnimated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove animation class
|
||||||
|
*/
|
||||||
|
_.delay( () => {
|
||||||
|
BlockSettings.focusedButton.classList.remove(BlockSettings.CSS.focusedButtonAnimated);
|
||||||
|
}, 280)();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restoring focus on current Block
|
||||||
|
*
|
||||||
|
* After changing Block state (when settings clicked, for example)
|
||||||
|
* Block's content points to the Node that is not in DOM, that's why we can not
|
||||||
|
* set caret and leaf next (via Tab)
|
||||||
|
*
|
||||||
|
* For that set cursor via Caret module to the current Block's content
|
||||||
|
* after some timeout
|
||||||
|
*/
|
||||||
|
_.delay( () => {
|
||||||
|
Caret.setToBlock(BlockManager.currentBlock);
|
||||||
|
}, 10)();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (BlockSelection.anyBlockSelected) {
|
if (BlockSelection.anyBlockSelected) {
|
||||||
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
|
const selectionPositionIndex = BlockManager.removeSelectedBlocks();
|
||||||
Caret.setToBlock(BlockManager.insertAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
Caret.setToBlock(BlockManager.insertAtIndex(selectionPositionIndex, true), Caret.positions.START);
|
||||||
|
@ -354,11 +405,16 @@ export default class UI extends Module {
|
||||||
|
|
||||||
if (Selection.isAtEditor) {
|
if (Selection.isAtEditor) {
|
||||||
/**
|
/**
|
||||||
* Focus clicked Block
|
* Focus clicked Block.
|
||||||
|
* Workaround case when user clicks on the bottom of editor
|
||||||
*/
|
*/
|
||||||
|
if (Selection.anchorNode === this.nodes.redactor) {
|
||||||
|
this.Editor.Caret.setToTheLastBlock();
|
||||||
|
} else {
|
||||||
this.Editor.BlockManager.setCurrentBlockByChildNode(Selection.anchorNode);
|
this.Editor.BlockManager.setCurrentBlockByChildNode(Selection.anchorNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All clicks on the redactor zone
|
* All clicks on the redactor zone
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 698fdbe5a739043b69349e8ed8ff49788722feae
|
Subproject commit fa20d187729c72d41129d2e4e89c3a6e989eed12
|
|
@ -78,3 +78,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes buttonClicked {
|
||||||
|
from,
|
||||||
|
20%,
|
||||||
|
40%,
|
||||||
|
60%,
|
||||||
|
80%,
|
||||||
|
to {
|
||||||
|
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
0% {
|
||||||
|
transform: scale3d(0.95, 0.95, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
60% {
|
||||||
|
transform: scale3d(1.02, 1.02, 1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
80% {
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&--confirm {
|
&--confirm {
|
||||||
background-color: var(--color-confirm);
|
background-color: var(--color-confirm) !important;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
@ -99,22 +99,8 @@
|
||||||
background-color: var(--selectionColor);
|
background-color: var(--selectionColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add placeholder to content editable elements with data attribute
|
|
||||||
* data-placeholder="Hello world!"
|
|
||||||
*/
|
|
||||||
[contentEditable=true][data-placeholder]:empty::before{
|
|
||||||
content: attr(data-placeholder);
|
|
||||||
color: var(--grayText);
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
[contentEditable=true][data-placeholder]:empty:focus::before {
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus::before {
|
.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus::before {
|
||||||
opacity: 0;
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes editor-loader-spin {
|
@keyframes editor-loader-spin {
|
||||||
|
|
|
@ -127,5 +127,15 @@
|
||||||
&--active {
|
&--active {
|
||||||
color: var(--color-active-icon);
|
color: var(--color-active-icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--focused {
|
||||||
|
box-shadow: inset 0 0 0px 1px rgba(7, 161, 227, 0.08);
|
||||||
|
background: rgba(34, 186, 255, 0.08) !important;
|
||||||
|
|
||||||
|
&-animated {
|
||||||
|
animation-name: buttonClicked;
|
||||||
|
animation-duration: 250ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
2
types/configs/editor-config.d.ts
vendored
2
types/configs/editor-config.d.ts
vendored
|
@ -29,7 +29,7 @@ export interface EditorConfig {
|
||||||
/**
|
/**
|
||||||
* First Block placeholder
|
* First Block placeholder
|
||||||
*/
|
*/
|
||||||
placeholder?: string;
|
placeholder?: string|false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define default sanitizer configuration
|
* Define default sanitizer configuration
|
||||||
|
|
43
types/index.d.ts
vendored
43
types/index.d.ts
vendored
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import {EditorConfig} from './configs';
|
import {EditorConfig} from './configs';
|
||||||
import {Blocks, Caret, Events, Listeners, Notifier, Sanitizer, Saver, Selection, Styles, Toolbar, InlineToolbar} from './api';
|
import {Blocks, Caret, Events, Listeners, Notifier, Sanitizer, Saver, Selection, Styles, Toolbar, InlineToolbar} from './api';
|
||||||
|
import {OutputData} from "./data-formats/output-data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interfaces used for development
|
* Interfaces used for development
|
||||||
|
@ -74,6 +75,48 @@ declare class EditorJS {
|
||||||
public inlineToolbar: InlineToolbar;
|
public inlineToolbar: InlineToolbar;
|
||||||
constructor(configuration?: EditorConfig|string);
|
constructor(configuration?: EditorConfig|string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API shorthands
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Saver.save
|
||||||
|
*/
|
||||||
|
save(): Promise<OutputData>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Blocks.clear
|
||||||
|
*/
|
||||||
|
clear(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Blocks.render
|
||||||
|
*/
|
||||||
|
render(data: OutputData): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Caret.focus
|
||||||
|
*/
|
||||||
|
focus(atEnd?: boolean): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Events.on
|
||||||
|
*/
|
||||||
|
on(eventName: string, callback: (data?: any) => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Events.off
|
||||||
|
*/
|
||||||
|
off(eventName: string, callback: (data?: any) => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Events.emit
|
||||||
|
*/
|
||||||
|
emit(eventName: string, data: any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy Editor instance and related DOM elements
|
||||||
|
*/
|
||||||
public destroy(): void;
|
public destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue