Add tools` titles for Toolbox (#540)

* Add tools` titles for Toolbox

* Bump version

* Use isEmpty method to check object emptiness

* Improve isEmpty method

* Override toolbox settings from editor config
This commit is contained in:
George Berezhnoy 2018-12-04 20:24:41 +03:00 committed by GitHub
parent 8a53ba5aef
commit c93ed2501b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 78 additions and 45 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -53,8 +53,7 @@ Options that Tool can specify. All settings should be passed as static propertie
| Name | Type | Default Value | Description | | Name | Type | Default Value | Description |
| -- | -- | -- | -- | | -- | -- | -- | -- |
| `displayInToolbox` | _Boolean_ | `false` | Pass `true` to display this `Tool` in the Editor's `Toolbox` | | `toolbox` | _Object_ | `undefined` | Pass here `icon` and `title` to display this `Tool` in the Editor's `Toolbox` <br /> `icon` - HTML string with icon for Toolbox <br /> `title` - optional title to display in Toolbox |
| `toolboxIcon` | _String_ | — | Icon for Toolbox HTML string |
| `enableLineBreaks` | _Boolean_ | `false` | With this option, CodeX Editor won't handle Enter keydowns. Can be helpful for Tools like `<code>` where line breaks should be handled by default behaviour. | | `enableLineBreaks` | _Boolean_ | `false` | With this option, CodeX Editor won't handle Enter keydowns. Can be helpful for Tools like `<code>` where line breaks should be handled by default behaviour. |
| `isInline` | _Boolean_ | `false` | Describes Tool as a [Tool for the Inline Toolbar](tools-inline.md) | | `isInline` | _Boolean_ | `false` | Describes Tool as a [Tool for the Inline Toolbar](tools-inline.md) |
@ -96,15 +95,15 @@ When user pastes content into Editor, pasted content will be splitted into block
Also Editor API allows you to define your own pasting scenario. You can either: Also Editor API allows you to define your own pasting scenario. You can either:
1. Specify **HTML tags**, that can be represented by your Tool. For example, Image Tool can handle `<img>` tags. 1. Specify **HTML tags**, that can be represented by your Tool. For example, Image Tool can handle `<img>` tags.
If tags you specified will be found on content pasting, your Tool will be rendered. If tags you specified will be found on content pasting, your Tool will be rendered.
2. Specify **RegExp** for pasted strings. If pattern has been matched, your Tool will be rendered. 2. Specify **RegExp** for pasted strings. If pattern has been matched, your Tool will be rendered.
3. Specify **MIME type** or **extensions** of files that can be handled by your Tool on pasting by drag-n-drop or from clipboard. 3. Specify **MIME type** or **extensions** of files that can be handled by your Tool on pasting by drag-n-drop or from clipboard.
For each scenario, you should do 2 next things: For each scenario, you should do 2 next things:
1. Define static getter `pasteConfig` in Tool class. Specify handled patterns there. 1. Define static getter `pasteConfig` in Tool class. Specify handled patterns there.
2. Define public method `onPaste` that will handle PasteEvent to process pasted data. 2. Define public method `onPaste` that will handle PasteEvent to process pasted data.
### HTML tags handling ### HTML tags handling
@ -187,10 +186,10 @@ static get pasteConfig() {
If you registered some paste substitutions in `pasteConfig` property, you **should** provide `onPaste` callback in your Tool class. If you registered some paste substitutions in `pasteConfig` property, you **should** provide `onPaste` callback in your Tool class.
`onPaste` should be public non-static method. It accepts custom _PasteEvent_ object as argument. `onPaste` should be public non-static method. It accepts custom _PasteEvent_ object as argument.
PasteEvent is an alias for three types of events - `tag`, `pattern` and `file`. You can get the type from _PasteEvent_ object's `type` property. PasteEvent is an alias for three types of events - `tag`, `pattern` and `file`. You can get the type from _PasteEvent_ object's `type` property.
Each of these events provide `detail` property with info about pasted content. Each of these events provide `detail` property with info about pasted content.
| Type | Detail | | Type | Detail |
| ----- | ------ | | ----- | ------ |
| `tag` | `data` - pasted HTML element | | `tag` | `data` - pasted HTML element |
| `pattern` | `key` - matched pattern key you specified in `pasteConfig` object <br /> `data` - pasted string | | `pattern` | `key` - matched pattern key you specified in `pasteConfig` object <br /> `data` - pasted string |
@ -203,14 +202,14 @@ onPaste (event) {
switch (event.type) { switch (event.type) {
case 'tag': case 'tag':
const element = event.detail.data; const element = event.detail.data;
this.handleHTMLPaste(element); this.handleHTMLPaste(element);
break; break;
case 'pattern': case 'pattern':
const text = event.detail.data; const text = event.detail.data;
const key = event.detail.key; const key = event.detail.key;
this.handlePatternPaste(key, text); this.handlePatternPaste(key, text);
break; break;
@ -225,7 +224,7 @@ onPaste (event) {
## Sanitize ## Sanitize
CodeX Editor provides [API](sanitizer.md) to clean taint strings. CodeX Editor provides [API](sanitizer.md) to clean taint strings.
Use it manually at the `save()` method or or pass `sanitizer` config to do it automatically. Use it manually at the `save()` method or or pass `sanitizer` config to do it automatically.
### Sanitizer Configuration ### Sanitizer Configuration
@ -239,7 +238,7 @@ let sanitizerConfig = {
} }
``` ```
Keys of config object is tags and the values is a rules. Keys of config object is tags and the values is a rules.
#### Rule #### Rule
@ -251,7 +250,7 @@ but leave tag.
Also you can pass special attributes that you want to leave. Also you can pass special attributes that you want to leave.
```javascript ```javascript
a: { a: {
href: true href: true
} }
``` ```
@ -290,11 +289,11 @@ Call API method `sanitizer.clean()` at the save method for each field in returne
```javascript ```javascript
save() { save() {
return { return {
text: this.api.sanitizer.clean(taintString, sanitizerConfig) text: this.api.sanitizer.clean(taintString, sanitizerConfig)
} }
} }
``` ```
### Automatic sanitize ### Automatic sanitize
If you pass the sanitizer config as static getter, CodeX Editor will automatically sanitize your saved data. If you pass the sanitizer config as static getter, CodeX Editor will automatically sanitize your saved data.
@ -318,7 +317,7 @@ static get sanitize() {
Don't forget to set the rule for each embedded subitems otherwise they will Don't forget to set the rule for each embedded subitems otherwise they will
not be sanitized. not be sanitized.
if you want to sanitize everything and get data without any tags, use `{}` or just if you want to sanitize everything and get data without any tags, use `{}` or just
ignore field in case if you want to get pure HTML ignore field in case if you want to get pure HTML
@ -332,7 +331,7 @@ static get sanitize() {
// other objects here won't be sanitized // other objects here won't be sanitized
subitems: { subitems: {
// leave <a> and <b> in subitems // leave <a> and <b> in subitems
a: true, a: true,
b: true, b: true,
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "codex.editor", "name": "codex.editor",
"version": "2.6.2", "version": "2.7.0",
"description": "Codex Editor. Native JS, based on API and Open Source", "description": "Codex Editor. Native JS, based on API and Open Source",
"main": "build/codex-editor.js", "main": "build/codex-editor.js",
"types": "./types/index.d.ts", "types": "./types/index.d.ts",

View file

@ -1,7 +1,7 @@
import Module from '../../__module'; import Module from '../../__module';
import $ from '../../dom'; import $ from '../../dom';
import _ from '../../utils'; import _ from '../../utils';
import {BlockToolConstructable} from '../../../../types'; import {BlockToolConstructable, ToolboxConfig} from '../../../../types';
/** /**
* @class Toolbox * @class Toolbox
@ -196,7 +196,7 @@ export default class Toolbox extends Module {
return null; return null;
} }
return (childNodes[this.activeButtonIndex] as HTMLElement).title; return (childNodes[this.activeButtonIndex] as HTMLElement).dataset.tool;
} }
/** /**
@ -229,7 +229,16 @@ export default class Toolbox extends Module {
private addTool(toolName: string, tool: BlockToolConstructable): void { private addTool(toolName: string, tool: BlockToolConstructable): void {
const api = this.Editor.Tools.apiSettings; const api = this.Editor.Tools.apiSettings;
if (tool[api.IS_DISPLAYED_IN_TOOLBOX] && !tool[api.TOOLBAR_ICON]) { const toolToolboxSettings = tool[api.TOOLBOX];
/**
* Skip tools that don't pass 'toolbox' property
*/
if (_.isEmpty(toolToolboxSettings)) {
return;
}
if (toolToolboxSettings && !toolToolboxSettings.icon) {
_.log('Toolbar icon is missed. Tool %o skipped', 'warn', toolName); _.log('Toolbar icon is missed. Tool %o skipped', 'warn', toolName);
return; return;
} }
@ -242,18 +251,14 @@ export default class Toolbox extends Module {
// return; // return;
// } // }
/** const {toolbox: userToolboxSettings = {} as ToolboxConfig} = this.Editor.Tools.getToolSettings(toolName);
* Skip tools that pass 'displayInToolbox=false'
*/
if (!tool[api.IS_DISPLAYED_IN_TOOLBOX]) {
return;
}
const button = $.make('li', [ Toolbox.CSS.toolboxButton ], { const button = $.make('li', [ Toolbox.CSS.toolboxButton ], {
title: toolName, title: userToolboxSettings.title || toolToolboxSettings.title || toolName,
}); });
button.innerHTML = tool.toolboxIcon; button.dataset.tool = toolName;
button.innerHTML = userToolboxSettings.icon || toolToolboxSettings.icon;
$.append(this.nodes.toolbox, button); $.append(this.nodes.toolbox, button);

View file

@ -113,13 +113,12 @@ export default class Tools extends Module {
public get apiSettings() { public get apiSettings() {
return { return {
CONFIG: 'config', CONFIG: 'config',
IS_DISPLAYED_IN_TOOLBOX: 'displayInToolbox',
IS_ENABLED_INLINE_TOOLBAR: 'inlineToolbar', IS_ENABLED_INLINE_TOOLBAR: 'inlineToolbar',
IS_ENABLED_LINE_BREAKS: 'enableLineBreaks', IS_ENABLED_LINE_BREAKS: 'enableLineBreaks',
IS_INLINE: 'isInline', IS_INLINE: 'isInline',
IS_PASTE_DISALLOWED: 'disallowPaste', IS_PASTE_DISALLOWED: 'disallowPaste',
SHORTCUT: 'shortcut', SHORTCUT: 'shortcut',
TOOLBAR_ICON: 'toolboxIcon', TOOLBOX: 'toolbox',
SANITIZE_CONFIG: 'sanitize', SANITIZE_CONFIG: 'sanitize',
}; };
} }
@ -331,7 +330,7 @@ export default class Tools extends Module {
* @param {string} toolName * @param {string} toolName
* @return {ToolSettings} * @return {ToolSettings}
*/ */
public getToolSettings(toolName) { public getToolSettings(toolName): ToolSettings {
return this.toolsSettings[toolName]; return this.toolsSettings[toolName];
} }

View file

@ -169,6 +169,10 @@ export default class Util {
* @return {boolean} * @return {boolean}
*/ */
public static isEmpty(object: object): boolean { public static isEmpty(object: object): boolean {
if (!object) {
return true;
}
return Object.keys(object).length === 0 && object.constructor === Object; return Object.keys(object).length === 0 && object.constructor === Object;
} }

1
types/index.d.ts vendored
View file

@ -18,6 +18,7 @@ export {
BlockToolConstructable, BlockToolConstructable,
BlockTool, BlockTool,
BlockToolData, BlockToolData,
ToolboxConfig,
ToolSettings, ToolSettings,
ToolConfig, ToolConfig,
PasteEvent, PasteEvent,

View file

@ -57,14 +57,19 @@ export interface BlockTool extends Tool {
export interface BlockToolConstructable extends ToolConstructable { export interface BlockToolConstructable extends ToolConstructable {
/** /**
* Should this Tool be displayed in the Editor's Toolbox * Tool's Toolbox settings
*/ */
displayInToolbox?: boolean; toolbox?: {
/**
* HTML string with an icon for Toolbox
*/
icon: string;
/** /**
* String with an icon for Toolbox * Tool title for Toolbox
*/ */
toolboxIcon?: string; title?: string;
};
/** /**
* Paste substitutions configuration * Paste substitutions configuration

View file

@ -1,6 +1,21 @@
import {ToolConfig} from './tool-config'; import {ToolConfig} from './tool-config';
import {ToolConstructable} from './tool'; import {ToolConstructable} from './tool';
/**
* Tool's Toolbox settings
*/
export interface ToolboxConfig {
/**
* Tool title for Toolbox
*/
title?: string;
/**
* HTML string with an icon for Toolbox
*/
icon?: string;
}
/** /**
* Object passed to the Tool's constructor by {@link EditorConfig#tools} * Object passed to the Tool's constructor by {@link EditorConfig#tools}
*/ */
@ -31,4 +46,9 @@ export interface ToolSettings {
* Define shortcut that will render Tool * Define shortcut that will render Tool
*/ */
shortcut?: string; shortcut?: string;
/**
* Tool's Toolbox settings
*/
toolbox?: ToolboxConfig;
} }