mirror of
https://github.com/codex-team/editor.js
synced 2024-05-15 21:06:52 +02:00
Add Toolbox tooltip (#604)
* Add tooltip * styles updated * updates * Update CHANGELOG.md * rm map * update * upd pkg
This commit is contained in:
parent
7aa35bb7dd
commit
758c5080df
|
@ -19,10 +19,11 @@ Welcome to testing stage. Please, join a [public Telegram-chat](//t.me/codex_edi
|
|||
|
||||
### 2.7-2.9 changelog
|
||||
|
||||
- `Improvements` Prevent navigating back on Firefox when Block is removing by backspace
|
||||
- `New` Blocks selected with RectangleSelection can be also removed, copied or cut
|
||||
- `New` Migrate from postcss-cssnext to postcss-preset-env and disable postcss-custom-properties which conflicts with postcss-preset-env
|
||||
- `New` *RectangeSelection* - Ability to select Block or several Blocks with mouse
|
||||
- `New` — Toolbox now have beautiful helpers with Tool names and shortcuts
|
||||
- `Improvements` — Prevent navigating back on Firefox when Block is removing by backspace
|
||||
- `New` — Blocks selected with Rectangle Selection can be also removed, copied or cut
|
||||
- `New` — Migrate from `postcss-cssnext` to `postcss-preset-env` and disable `postcss-custom-properties` which conflicts with `postcss-preset-env`
|
||||
- `New` *RectangeSelection* — Ability to select Block or several Blocks with mouse
|
||||
|
||||
### 2.2—2.7 changelog
|
||||
|
||||
|
|
10
dist/codex-editor.js
vendored
10
dist/codex-editor.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,52 +1,56 @@
|
|||
# Changelog
|
||||
|
||||
### 2.9.5
|
||||
|
||||
- `New` — Toolbox now have beautiful helpers with Tool names and shortcuts
|
||||
|
||||
### 2.9.4
|
||||
|
||||
- `Improvements` Prevent navigating back on Firefox when Block is removing by backspace
|
||||
- `Improvements` — Prevent navigating back on Firefox when Block is removing by backspace
|
||||
|
||||
### 2.9.3
|
||||
|
||||
- `Fix` Handle paste only on initial Block
|
||||
- `Fix` — Handle paste only on initial Block
|
||||
|
||||
### 2.9.2
|
||||
|
||||
- `New` Blocks selected with RectangleSelection can be also removed, copied or cut
|
||||
- `New` — Blocks selected with Rectangle Selection can be also removed, copied or cut
|
||||
|
||||
### 2.9.1
|
||||
|
||||
- `Improvements` Migrate from postcss-cssnext to postcss-preset-env and disable postcss-custom-properties which conflicts with postcss-preset-env
|
||||
- `Improvements` — Migrate from `postcss-cssnext` to `postcss-preset-env` and disable `postcss-custom-properties` which conflicts with `postcss-preset-env`
|
||||
|
||||
### 2.9.0
|
||||
|
||||
- `New` *RectangeSelection* - Ability to select Block or several Blocks with mouse
|
||||
- `New` *RectangeSelection* — Ability to select Block or several Blocks with mouse
|
||||
|
||||
### 2.8.1
|
||||
|
||||
- `Fix` *Caret* - Fix "History back" call on backspace in Firefox
|
||||
- `Fix` *Caret* — Fix "History back" call on backspace in Firefox
|
||||
|
||||
### 2.8.0
|
||||
|
||||
- `Imporvements` *API* - Added [API methods](api.md#caretapi) to manage caret position
|
||||
- `Imporvements` *API* — Added [API methods](api.md#caretapi) to manage caret position
|
||||
|
||||
### 2.7.32
|
||||
|
||||
- `Improvements` *Types* - TypeScript types sre updated
|
||||
- `Improvements` *Types* — TypeScript types sre updated
|
||||
|
||||
### 2.7.31
|
||||
|
||||
- `Fix` Caret now goes through <input> elements without `type` attribute
|
||||
- `Fix` — Caret now goes through <input> elements without `type` attribute
|
||||
|
||||
### 2.7.30
|
||||
|
||||
- `Fix` Fixed selection behavior when text has modifiers form Inline Toolbar
|
||||
- `Fix` — Fixed selection behavior when text has modifiers form Inline Toolbar
|
||||
|
||||
### 2.7.29
|
||||
|
||||
- `Fix` cmd+x works only for custom selection now
|
||||
- `Fix` — cmd+x works only for custom selection now
|
||||
|
||||
### 2.7.28
|
||||
|
||||
- `New` [Tools Validation](https://github.com/codex-team/codex.editor/blob/master/docs/tools.md#validate-optional) is added.
|
||||
- `New` — [Tools Validation](https://github.com/codex-team/codex.editor/blob/master/docs/tools.md#validate-optional) is added.
|
||||
|
||||
### 2.2.27
|
||||
|
||||
|
|
|
@ -87,7 +87,8 @@
|
|||
inlineToolbar: ['link'],
|
||||
config: {
|
||||
placeholder: 'Header'
|
||||
}
|
||||
},
|
||||
shortcut: 'CMD+SHIFT+H'
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -100,12 +101,13 @@
|
|||
|
||||
list: {
|
||||
class: List,
|
||||
inlineToolbar: true
|
||||
inlineToolbar: true,
|
||||
shortcut: 'CMD+SHIFT+L'
|
||||
},
|
||||
|
||||
checklist: {
|
||||
class: Checklist,
|
||||
inlineToolbar: true
|
||||
inlineToolbar: true,
|
||||
},
|
||||
|
||||
quote: {
|
||||
|
@ -115,6 +117,7 @@
|
|||
quotePlaceholder: 'Enter a quote',
|
||||
captionPlaceholder: 'Quote\'s author',
|
||||
},
|
||||
shortcut: 'CMD+SHIFT+O'
|
||||
},
|
||||
|
||||
marker: {
|
||||
|
@ -124,7 +127,7 @@
|
|||
|
||||
code: {
|
||||
class: CodeTool,
|
||||
shortcut: 'CMD+SHIFT+D'
|
||||
shortcut: 'CMD+SHIFT+C'
|
||||
},
|
||||
|
||||
delimiter: Delimiter,
|
||||
|
@ -138,7 +141,8 @@
|
|||
|
||||
table: {
|
||||
class: Table,
|
||||
inlineToolbar: true
|
||||
inlineToolbar: true,
|
||||
shortcut: 'CMD+ALT+T'
|
||||
},
|
||||
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "codex.editor",
|
||||
"version": "2.9.4",
|
||||
"version": "2.9.5",
|
||||
"description": "CodeX Editor. Native JS, based on API and Open Source",
|
||||
"main": "dist/codex-editor.js",
|
||||
"types": "./types/index.d.ts",
|
||||
|
|
|
@ -112,6 +112,30 @@ export default class Toolbar extends Module {
|
|||
* - Toolbox
|
||||
*/
|
||||
this.nodes.plusButton = $.make('div', Toolbar.CSS.plusButton);
|
||||
|
||||
/**
|
||||
* Add events to show/hide tooltip for plus button
|
||||
*/
|
||||
this.Editor.Listeners.on(this.nodes.plusButton, 'mouseenter', () => {
|
||||
const tooltip = this.Editor.Toolbox.nodes.tooltip;
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
fragment.appendChild(document.createTextNode('Add'));
|
||||
fragment.appendChild($.make('div', this.Editor.Toolbox.CSS.tooltipShortcut, {
|
||||
textContent: '⇥ Tab',
|
||||
}));
|
||||
|
||||
tooltip.style.left = '-17px';
|
||||
|
||||
tooltip.innerHTML = '';
|
||||
tooltip.appendChild(fragment);
|
||||
tooltip.classList.add(this.Editor.Toolbox.CSS.tooltipShown);
|
||||
});
|
||||
|
||||
this.Editor.Listeners.on(this.nodes.plusButton, 'mouseleave', () => {
|
||||
this.Editor.Toolbox.hideTooltip();
|
||||
});
|
||||
|
||||
$.append(this.nodes.plusButton, $.svg('plus', 14, 14));
|
||||
$.append(this.nodes.content, this.nodes.plusButton);
|
||||
this.Editor.Listeners.on(this.nodes.plusButton, 'click', () => this.plusButtonClicked(), false);
|
||||
|
|
|
@ -15,6 +15,47 @@ import {BlockToolConstructable, ToolboxConfig} from '../../../../types';
|
|||
*/
|
||||
export default class Toolbox extends Module {
|
||||
|
||||
/**
|
||||
* CSS styles
|
||||
* @return {{toolbox: string, toolboxButton string, toolboxButtonActive: string,
|
||||
* toolboxOpened: string, tooltip: string, tooltipShown: string, tooltipShortcut: string}}
|
||||
*/
|
||||
get CSS() {
|
||||
return {
|
||||
toolbox: 'ce-toolbox',
|
||||
toolboxButton: 'ce-toolbox__button',
|
||||
toolboxButtonActive : 'ce-toolbox__button--active',
|
||||
toolboxOpened: 'ce-toolbox--opened',
|
||||
tooltip: 'ce-toolbox__tooltip',
|
||||
tooltipShown: 'ce-toolbox__tooltip--shown',
|
||||
tooltipShortcut: 'ce-toolbox__tooltip-shortcut',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* get tool name when it is selected
|
||||
* In case when nothing selected returns null
|
||||
*
|
||||
* @return {String|null}
|
||||
*/
|
||||
public get getActiveTool(): string {
|
||||
const childNodes = this.nodes.toolbox.childNodes;
|
||||
|
||||
if (this.activeButtonIndex === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (childNodes[this.activeButtonIndex] as HTMLElement).dataset.tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns True if Toolbox is Empty and nothing to show
|
||||
* @return {boolean}
|
||||
*/
|
||||
public get isEmpty(): boolean {
|
||||
return this.displayedToolsCount === 0;
|
||||
}
|
||||
|
||||
private static LEAF_DIRECTIONS = {
|
||||
RIGHT: 'right',
|
||||
LEFT: 'left',
|
||||
|
@ -31,9 +72,11 @@ export default class Toolbox extends Module {
|
|||
*/
|
||||
public nodes: {
|
||||
toolbox: HTMLElement,
|
||||
tooltip: HTMLElement,
|
||||
buttons: HTMLElement[],
|
||||
} = {
|
||||
toolbox: null,
|
||||
tooltip: null,
|
||||
buttons: [],
|
||||
};
|
||||
|
||||
|
@ -50,27 +93,15 @@ export default class Toolbox extends Module {
|
|||
*/
|
||||
private displayedToolsCount: number = 0;
|
||||
|
||||
/**
|
||||
* CSS styles
|
||||
* @return {{toolbox: string, toolboxButton: string, toolboxOpened: string}}
|
||||
*/
|
||||
static get CSS() {
|
||||
return {
|
||||
toolbox: 'ce-toolbox',
|
||||
toolboxButton: 'ce-toolbox__button',
|
||||
toolboxButtonActive : 'ce-toolbox__button--active',
|
||||
toolboxOpened: 'ce-toolbox--opened',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the Toolbox
|
||||
*/
|
||||
public make(): void {
|
||||
this.nodes.toolbox = $.make('div', Toolbox.CSS.toolbox);
|
||||
this.nodes.toolbox = $.make('div', this.CSS.toolbox);
|
||||
$.append(this.Editor.Toolbar.nodes.content, this.nodes.toolbox);
|
||||
|
||||
this.addTools();
|
||||
this.addTooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,7 +124,7 @@ export default class Toolbox extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
this.nodes.toolbox.classList.add(Toolbox.CSS.toolboxOpened);
|
||||
this.nodes.toolbox.classList.add(this.CSS.toolboxOpened);
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
|
@ -101,15 +132,17 @@ export default class Toolbox extends Module {
|
|||
* Close Toolbox
|
||||
*/
|
||||
public close(): void {
|
||||
this.nodes.toolbox.classList.remove(Toolbox.CSS.toolboxOpened);
|
||||
this.hideTooltip();
|
||||
|
||||
this.nodes.toolbox.classList.remove(this.CSS.toolboxOpened);
|
||||
this.opened = false;
|
||||
|
||||
/** remove active item pointer */
|
||||
this.activeButtonIndex = -1;
|
||||
const activeButton = this.nodes.toolbox.querySelector(`.${Toolbox.CSS.toolboxButtonActive}`);
|
||||
const activeButton = this.nodes.toolbox.querySelector(`.${this.CSS.toolboxButtonActive}`);
|
||||
|
||||
if (activeButton) {
|
||||
activeButton.classList.remove(Toolbox.CSS.toolboxButtonActive);
|
||||
activeButton.classList.remove(this.CSS.toolboxButtonActive);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +189,7 @@ export default class Toolbox extends Module {
|
|||
/**
|
||||
* If we have chosen Tool then remove highlighting
|
||||
*/
|
||||
(childNodes[this.activeButtonIndex] as HTMLElement).classList.remove(Toolbox.CSS.toolboxButtonActive);
|
||||
(childNodes[this.activeButtonIndex] as HTMLElement).classList.remove(this.CSS.toolboxButtonActive);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,31 +213,14 @@ export default class Toolbox extends Module {
|
|||
/**
|
||||
* Highlight new chosen Tool
|
||||
*/
|
||||
(childNodes[this.activeButtonIndex] as HTMLElement).classList.add(Toolbox.CSS.toolboxButtonActive);
|
||||
(childNodes[this.activeButtonIndex] as HTMLElement).classList.add(this.CSS.toolboxButtonActive);
|
||||
}
|
||||
|
||||
/**
|
||||
* get tool name when it is selected
|
||||
* In case when nothing selection returns null
|
||||
*
|
||||
* @return {String|null}
|
||||
* Hide toolbox tooltip
|
||||
*/
|
||||
public get getActiveTool(): string {
|
||||
const childNodes = this.nodes.toolbox.childNodes;
|
||||
|
||||
if (this.activeButtonIndex === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (childNodes[this.activeButtonIndex] as HTMLElement).dataset.tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns True if Toolbox is Empty and nothing to show
|
||||
* @return {boolean}
|
||||
*/
|
||||
public get isEmpty(): boolean {
|
||||
return this.displayedToolsCount === 0;
|
||||
public hideTooltip(): void {
|
||||
this.nodes.tooltip.classList.remove(this.CSS.tooltipShown);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -253,9 +269,7 @@ export default class Toolbox extends Module {
|
|||
|
||||
const {toolbox: userToolboxSettings = {} as ToolboxConfig} = this.Editor.Tools.getToolSettings(toolName);
|
||||
|
||||
const button = $.make('li', [ Toolbox.CSS.toolboxButton ], {
|
||||
title: userToolboxSettings.title || toolToolboxSettings.title || toolName,
|
||||
});
|
||||
const button = $.make('li', [ this.CSS.toolboxButton ]);
|
||||
|
||||
button.dataset.tool = toolName;
|
||||
button.innerHTML = userToolboxSettings.icon || toolToolboxSettings.icon;
|
||||
|
@ -272,6 +286,17 @@ export default class Toolbox extends Module {
|
|||
this.toolButtonActivate(event, toolName);
|
||||
});
|
||||
|
||||
/**
|
||||
* Add listeners to show/hide toolbox tooltip
|
||||
*/
|
||||
this.Editor.Listeners.on(button, 'mouseenter', () => {
|
||||
this.showTooltip(button, toolName);
|
||||
});
|
||||
|
||||
this.Editor.Listeners.on(button, 'mouseleave', () => {
|
||||
this.hideTooltip();
|
||||
});
|
||||
|
||||
/**
|
||||
* Enable shortcut
|
||||
*/
|
||||
|
@ -285,6 +310,72 @@ export default class Toolbox extends Module {
|
|||
this.displayedToolsCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add toolbox tooltip to page
|
||||
*/
|
||||
private addTooltip(): void {
|
||||
this.nodes.tooltip = $.make('div', this.CSS.tooltip, {
|
||||
innerHTML: '',
|
||||
});
|
||||
|
||||
$.append(this.Editor.Toolbar.nodes.content, this.nodes.tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show tooltip for toolbox button
|
||||
* @param {HTMLElement} button
|
||||
* @param {string} toolName
|
||||
*/
|
||||
private showTooltip(button: HTMLElement, toolName: string): void {
|
||||
const toolSettings = this.Editor.Tools.getToolSettings(toolName);
|
||||
const toolboxSettings = this.Editor.Tools.available[toolName][this.Editor.Tools.apiSettings.TOOLBOX] || {};
|
||||
const userToolboxSettings = toolSettings.toolbox || {};
|
||||
const name = userToolboxSettings.title || toolboxSettings.title || toolName;
|
||||
|
||||
let shortcut = toolSettings[this.Editor.Tools.apiSettings.SHORTCUT];
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
const hint = document.createTextNode(_.capitalize(name));
|
||||
|
||||
fragment.appendChild(hint);
|
||||
|
||||
if (shortcut) {
|
||||
const OS = _.getUserOS();
|
||||
|
||||
shortcut = shortcut
|
||||
.replace(/shift/gi, '⇧')
|
||||
.replace(/backspace/gi, '⌫')
|
||||
.replace(/enter/gi, '⏎')
|
||||
.replace(/up/gi, '↑')
|
||||
.replace(/left/gi, '→')
|
||||
.replace(/down/gi, '↓')
|
||||
.replace(/right/gi, '←')
|
||||
.replace(/escape/gi, '⎋')
|
||||
.replace(/insert/gi, 'Ins')
|
||||
.replace(/delete/gi, '␡')
|
||||
.replace(/\+/gi, ' + ');
|
||||
|
||||
if (OS.mac) {
|
||||
shortcut = shortcut.replace(/ctrl|cmd/gi, '⌘').replace(/alt/gi, '⌥');
|
||||
} else {
|
||||
shortcut = shortcut.replace(/cmd/gi, 'Ctrl').replace(/windows/gi, 'WIN');
|
||||
}
|
||||
|
||||
fragment.appendChild($.make('div', this.CSS.tooltipShortcut, {
|
||||
textContent: shortcut,
|
||||
}));
|
||||
}
|
||||
|
||||
const offset = 16;
|
||||
const coordinate = button.offsetLeft;
|
||||
|
||||
this.nodes.tooltip.innerHTML = '';
|
||||
this.nodes.tooltip.appendChild(fragment);
|
||||
|
||||
this.nodes.tooltip.style.left = `${coordinate + offset}px`;
|
||||
this.nodes.tooltip.classList.add(this.CSS.tooltipShown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable shortcut Block Tool implemented shortcut
|
||||
* @param {BlockToolConstructable} tool - Tool class
|
||||
|
|
|
@ -276,4 +276,36 @@ export default class Util {
|
|||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns object with os name as key and boolean as value. Shows current user OS
|
||||
*
|
||||
* @return {[key: string]: boolean}
|
||||
*/
|
||||
public static getUserOS(): {[key: string]: boolean} {
|
||||
const OS = {
|
||||
win: false,
|
||||
mac: false,
|
||||
x11: false,
|
||||
linux: false,
|
||||
};
|
||||
|
||||
const userOS = Object.keys(OS).find((os: string) => navigator.appVersion.toLowerCase().indexOf(os) !== -1);
|
||||
|
||||
if (userOS) {
|
||||
OS[userOS] = true;
|
||||
return OS;
|
||||
}
|
||||
|
||||
return OS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes first letter of the string
|
||||
* @param {string} text
|
||||
* @return {string}
|
||||
*/
|
||||
public static capitalize(text: string): string {
|
||||
return text[0].toUpperCase() + text.slice(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,51 @@
|
|||
&__button {
|
||||
@apply --toolbox-button;
|
||||
}
|
||||
|
||||
&__tooltip {
|
||||
position: absolute;
|
||||
top: 43px;
|
||||
padding: 6px 10px;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 5px;
|
||||
line-height: 21px;
|
||||
opacity: 0;
|
||||
background: var(--bg-light);
|
||||
box-shadow: 0 10px 12px -9px rgba(26, 39, 54, 0.32), 0 3px 2px -2px rgba(33, 48, 73, 0.05);
|
||||
color: #5C6174;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
transition: opacity 150ms ease-in, left 0.1s linear;
|
||||
will-change: opacity, left;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1em;
|
||||
|
||||
&-shortcut {
|
||||
color: rgba(100, 105, 122, 0.6);
|
||||
word-spacing: -2px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&--shown {
|
||||
opacity: 1;
|
||||
transition-delay: 0.1s, 0s;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
transform: rotate(-45deg);
|
||||
background-color: var(--bg-light);
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue