mirror of
https://github.com/codex-team/editor.js
synced 2024-06-10 09:52:36 +02:00
Tunes improvements for inline actions (#1722)
* Add tunes improvements * Allow to show Inline Toolbar at Block Tune Wrapper element * Add fake cursor on block selection * Fix lint * Update types * Fix bugs with selection * Remove selection observer * Update due to comments * Fix tests * Update docs/block-tunes.md Co-authored-by: Peter Savchenko <specc.dev@gmail.com> * Move fake cursor to selection utils * Fix missing range for Safari * Fix data attribute value * Add comment * Update z-index for inline-toolbar * Add changelog * Remove fake cursor visibility for the core Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
parent
cf494a7a28
commit
6f36707f67
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
### 2.22.2
|
||||
|
||||
- `Improvement` — Inline Toolbar might be used for any contenteditable element inside Editor.js zone
|
||||
- `Improvement` *Tunes API* - Tunes now can provide sanitize configuration
|
||||
- `Fix` *Tunes API* - Tune config now passed to constructor under `config` property
|
||||
- `Fix` *Types* - Add common type for internal and external Tools configuration
|
||||
|
||||
### 2.22.1
|
||||
|
||||
- `Fix` — I18n for internal Block Tunes [#1661](https://github.com/codex-team/editor.js/issues/1661)
|
||||
|
|
|
@ -22,7 +22,7 @@ At the constructor of Tune's class exemplar you will receive an object with foll
|
|||
| Parameter | Description |
|
||||
| --------- | ----------- |
|
||||
| api | Editor's [API](api.md) obejct |
|
||||
| settings | Configuration of Block Tool Tune is connected to (might be useful in some cases) |
|
||||
| config | Configuration of Block Tool Tune is connected to (might be useful in some cases) |
|
||||
| block | [Block API](api.md#block-api) methods for block Tune is connected to |
|
||||
| data | Saved Tune data |
|
||||
|
||||
|
@ -145,7 +145,24 @@ No return value
|
|||
|
||||
---
|
||||
|
||||
#### Format
|
||||
### static get sanitize()
|
||||
|
||||
If your Tune inserts any HTML markup into Block's content you need to provide sanitize configuration, so your HTML is not trimmed on save.
|
||||
|
||||
Please see more information at [sanitizer page](sanitizer.md).
|
||||
|
||||
|
||||
```javascript
|
||||
class Tune {
|
||||
static get sanitize() {
|
||||
return {
|
||||
sup: true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Format
|
||||
|
||||
Tunes data is saved to `tunes` property of output object:
|
||||
|
||||
|
|
|
@ -114,7 +114,6 @@
|
|||
* Tools list
|
||||
*/
|
||||
tools: {
|
||||
|
||||
/**
|
||||
* Each Tool is a Plugin. Pass them via 'class' option with necessary settings {@link docs/tools.md}
|
||||
*/
|
||||
|
@ -205,10 +204,10 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
type : 'paragraph',
|
||||
id: "b6ji-DvaKb",
|
||||
data : {
|
||||
text : 'Hey. Meet the new Editor. On this page you can see it in action — try to edit this text. Source code of the page contains the example of connection and configuration.'
|
||||
"id": "b6ji-DvaKb",
|
||||
"type": "paragraph",
|
||||
"data": {
|
||||
"text": "Hey. Meet the new Editor. On this page you can see it in action — try to edit this text. Source code of the page contains the example of connection and configuration."
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -201,7 +201,19 @@ export default class Block extends EventsDispatcher<BlockEvents> {
|
|||
/**
|
||||
* Is fired when DOM mutation has been happened
|
||||
*/
|
||||
private didMutated = _.debounce((): void => {
|
||||
private didMutated = _.debounce((mutations: MutationRecord[]): void => {
|
||||
const shouldFireUpdate = !mutations.some(({ addedNodes = [], removedNodes }) => {
|
||||
return [...Array.from(addedNodes), ...Array.from(removedNodes)]
|
||||
.some(node => $.isElement(node) && (node as HTMLElement).dataset.mutationFree === 'true');
|
||||
});
|
||||
|
||||
/**
|
||||
* In case some mutation free elements are added or removed, do not trigger didMutated event
|
||||
*/
|
||||
if (!shouldFireUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop cache
|
||||
*/
|
||||
|
@ -448,8 +460,12 @@ export default class Block extends EventsDispatcher<BlockEvents> {
|
|||
public set selected(state: boolean) {
|
||||
if (state) {
|
||||
this.holder.classList.add(Block.CSS.selected);
|
||||
|
||||
SelectionUtils.addFakeCursor(this.holder);
|
||||
} else {
|
||||
this.holder.classList.remove(Block.CSS.selected);
|
||||
|
||||
SelectionUtils.removeFakeCursor(this.holder);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -202,7 +202,7 @@ export default class Dom {
|
|||
public static get allInputsSelector(): string {
|
||||
const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url'];
|
||||
|
||||
return '[contenteditable], textarea, input:not([type]), ' +
|
||||
return '[contenteditable=true], textarea, input:not([type]), ' +
|
||||
allowedInputTypes.map((type) => `input[type="${type}"]`).join(', ');
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ export default class ReadOnlyAPI extends Module {
|
|||
public get methods(): ReadOnly {
|
||||
return {
|
||||
toggle: (state): Promise<boolean> => this.toggle(state),
|
||||
isEnabled: this.isEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -25,4 +26,11 @@ export default class ReadOnlyAPI extends Module {
|
|||
public toggle(state?: boolean): Promise<boolean> {
|
||||
return this.Editor.ReadOnly.toggle(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current read-only state
|
||||
*/
|
||||
public get isEnabled(): boolean {
|
||||
return this.Editor.ReadOnly.isEnabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,7 +211,16 @@ export default class RectangleSelection extends Module {
|
|||
if (mouseEvent.button !== this.MAIN_MOUSE_BUTTON) {
|
||||
return;
|
||||
}
|
||||
this.startSelection(mouseEvent.pageX, mouseEvent.pageY);
|
||||
|
||||
/**
|
||||
* Do not enable the Rectangle Selection when mouse dragging started some editable input
|
||||
* Used to prevent Rectangle Selection on Block Tune wrappers' inputs that also can be inside the Block
|
||||
*/
|
||||
const startedFromContentEditable = (mouseEvent.target as Element).closest($.allInputsSelector) !== null;
|
||||
|
||||
if (!startedFromContentEditable) {
|
||||
this.startSelection(mouseEvent.pageX, mouseEvent.pageY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,8 +5,8 @@ import I18n from '../../i18n';
|
|||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
import Tooltip from '../../utils/tooltip';
|
||||
import { ModuleConfig } from '../../../types-internal/module-config';
|
||||
import EventsDispatcher from '../../utils/events';
|
||||
import { EditorConfig } from '../../../../types';
|
||||
import SelectionUtils from '../../selection';
|
||||
|
||||
/**
|
||||
* HTML Elements used for Toolbar UI
|
||||
|
@ -348,10 +348,19 @@ export default class Toolbar extends Module<ToolbarNodes> {
|
|||
private enableModuleBindings(): void {
|
||||
/**
|
||||
* Settings toggler
|
||||
*
|
||||
* mousedown is used because on click selection is lost in Safari and FF
|
||||
*/
|
||||
this.readOnlyMutableListeners.on(this.nodes.settingsToggler, 'click', () => {
|
||||
this.readOnlyMutableListeners.on(this.nodes.settingsToggler, 'mousedown', (e) => {
|
||||
/**
|
||||
* Stop propagation to prevent block selection clearance
|
||||
*
|
||||
* @see UI.documentClicked
|
||||
*/
|
||||
e.stopPropagation();
|
||||
|
||||
this.settingsTogglerClicked();
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -134,10 +134,11 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
/**
|
||||
* Shows Inline Toolbar if something is selected
|
||||
*
|
||||
* @param {boolean} [needToClose] - pass true to close toolbar if it is not allowed.
|
||||
* @param [needToClose] - pass true to close toolbar if it is not allowed.
|
||||
* Avoid to use it just for closing IT, better call .close() clearly.
|
||||
* @param [needToShowConversionToolbar] - pass false to not to show Conversion Toolbar
|
||||
*/
|
||||
public tryToShow(needToClose = false): void {
|
||||
public tryToShow(needToClose = false, needToShowConversionToolbar = true): void {
|
||||
if (!this.allowedToShow()) {
|
||||
if (needToClose) {
|
||||
this.close();
|
||||
|
@ -147,7 +148,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
}
|
||||
|
||||
this.move();
|
||||
this.open();
|
||||
this.open(needToShowConversionToolbar);
|
||||
this.Editor.Toolbar.close();
|
||||
}
|
||||
|
||||
|
@ -233,8 +234,10 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
|
||||
/**
|
||||
* Shows Inline Toolbar
|
||||
*
|
||||
* @param [needToShowConversionToolbar] - pass false to not to show Conversion Toolbar
|
||||
*/
|
||||
public open(): void {
|
||||
public open(needToShowConversionToolbar = true): void {
|
||||
if (this.opened) {
|
||||
return;
|
||||
}
|
||||
|
@ -251,7 +254,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
this.buttonsList = this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`);
|
||||
this.opened = true;
|
||||
|
||||
if (this.Editor.ConversionToolbar.hasTools()) {
|
||||
if (needToShowConversionToolbar && this.Editor.ConversionToolbar.hasTools()) {
|
||||
/**
|
||||
* Change Conversion Dropdown content for current tool
|
||||
*/
|
||||
|
|
|
@ -340,7 +340,7 @@ export default class UI extends Module<UINodes> {
|
|||
this.documentKeydown(event);
|
||||
}, true);
|
||||
|
||||
this.readOnlyMutableListeners.on(document, 'click', (event: MouseEvent) => {
|
||||
this.readOnlyMutableListeners.on(document, 'mousedown', (event: MouseEvent) => {
|
||||
this.documentClicked(event);
|
||||
}, true);
|
||||
|
||||
|
@ -591,9 +591,7 @@ export default class UI extends Module<UINodes> {
|
|||
/**
|
||||
* Clear Selection if user clicked somewhere
|
||||
*/
|
||||
if (!this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted) {
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
this.Editor.BlockSelection.clearSelection(event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -754,10 +752,28 @@ export default class UI extends Module<UINodes> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Event can be fired on clicks at the Editor elements, for example, at the Inline Toolbar
|
||||
* We need to skip such firings
|
||||
* Usual clicks on some controls, for example, Block Tunes Toggler
|
||||
*/
|
||||
if (!focusedElement || !focusedElement.closest(`.${Block.CSS.content}`)) {
|
||||
if (!focusedElement) {
|
||||
/**
|
||||
* If there is no selected range, close inline toolbar
|
||||
*
|
||||
* @todo Make this method more straightforward
|
||||
*/
|
||||
if (!Selection.range) {
|
||||
this.Editor.InlineToolbar.close();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event can be fired on clicks at non-block-content elements,
|
||||
* for example, at the Inline Toolbar or some Block Tune element
|
||||
*/
|
||||
const clickedOutsideBlockContent = focusedElement.closest(`.${Block.CSS.content}`) === null;
|
||||
|
||||
if (clickedOutsideBlockContent) {
|
||||
/**
|
||||
* If new selection is not on Inline Toolbar, we need to close it
|
||||
*/
|
||||
|
@ -765,7 +781,16 @@ export default class UI extends Module<UINodes> {
|
|||
this.Editor.InlineToolbar.close();
|
||||
}
|
||||
|
||||
return;
|
||||
/**
|
||||
* Case when we click on external tool elements,
|
||||
* for example some Block Tune element.
|
||||
* If this external content editable element has data-inline-toolbar="true"
|
||||
*/
|
||||
const inlineToolbarEnabledForExternalTool = (focusedElement as HTMLElement).dataset.inlineToolbar === 'true';
|
||||
|
||||
if (!inlineToolbarEnabledForExternalTool) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -775,10 +800,12 @@ export default class UI extends Module<UINodes> {
|
|||
this.Editor.BlockManager.setCurrentBlockByChildNode(focusedElement);
|
||||
}
|
||||
|
||||
const isNeedToShowConversionToolbar = clickedOutsideBlockContent !== true;
|
||||
|
||||
/**
|
||||
* @todo add debounce
|
||||
*/
|
||||
this.Editor.InlineToolbar.tryToShow(true);
|
||||
this.Editor.InlineToolbar.tryToShow(true, isNeedToShowConversionToolbar);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,34 @@ interface Document {
|
|||
* @typedef {SelectionUtils} SelectionUtils
|
||||
*/
|
||||
export default class SelectionUtils {
|
||||
/**
|
||||
* Selection instances
|
||||
*
|
||||
* @todo Check if this is still relevant
|
||||
*/
|
||||
public instance: Selection = null;
|
||||
public selection: Selection = null;
|
||||
|
||||
/**
|
||||
* This property can store SelectionUtils's range for restoring later
|
||||
*
|
||||
* @type {Range|null}
|
||||
*/
|
||||
public savedSelectionRange: Range = null;
|
||||
|
||||
/**
|
||||
* Fake background is active
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isFakeBackgroundEnabled = false;
|
||||
|
||||
/**
|
||||
* Native Document's commands for fake background
|
||||
*/
|
||||
private readonly commandBackground: string = 'backColor';
|
||||
private readonly commandRemoveFormat: string = 'removeFormat';
|
||||
|
||||
/**
|
||||
* Editor styles
|
||||
*
|
||||
|
@ -112,7 +140,18 @@ export default class SelectionUtils {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
public static get isAtEditor(): boolean {
|
||||
const selection = SelectionUtils.get();
|
||||
return this.isSelectionAtEditor(SelectionUtils.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if passed selection is at Editor's zone
|
||||
*
|
||||
* @param selection - Selectoin object to check
|
||||
*/
|
||||
public static isSelectionAtEditor(selection: Selection): boolean {
|
||||
if (!selection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Something selected on document
|
||||
|
@ -132,7 +171,35 @@ export default class SelectionUtils {
|
|||
/**
|
||||
* SelectionUtils is not out of Editor because Editor's wrapper was found
|
||||
*/
|
||||
return editorZone && editorZone.nodeType === Node.ELEMENT_NODE;
|
||||
return editorZone ? editorZone.nodeType === Node.ELEMENT_NODE : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if passed range at Editor zone
|
||||
*
|
||||
* @param range - range to check
|
||||
*/
|
||||
public static isRangeAtEditor(range: Range): boolean {
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedNode = range.startContainer as HTMLElement;
|
||||
|
||||
if (selectedNode && selectedNode.nodeType === Node.TEXT_NODE) {
|
||||
selectedNode = selectedNode.parentNode as HTMLElement;
|
||||
}
|
||||
|
||||
let editorZone = null;
|
||||
|
||||
if (selectedNode && selectedNode instanceof Element) {
|
||||
editorZone = selectedNode.closest(`.${SelectionUtils.CSS.editorZone}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* SelectionUtils is not out of Editor because Editor's wrapper was found
|
||||
*/
|
||||
return editorZone ? editorZone.nodeType === Node.ELEMENT_NODE : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,8 +217,15 @@ export default class SelectionUtils {
|
|||
* @returns {Range|null}
|
||||
*/
|
||||
public static get range(): Range | null {
|
||||
const selection = window.getSelection();
|
||||
return this.getRangeFromSelection(this.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns range from passed Selection object
|
||||
*
|
||||
* @param selection - Selection object to get Range from
|
||||
*/
|
||||
public static getRangeFromSelection(selection: Selection): Range {
|
||||
return selection && selection.rangeCount ? selection.getRangeAt(0) : null;
|
||||
}
|
||||
|
||||
|
@ -237,34 +311,6 @@ export default class SelectionUtils {
|
|||
return window.getSelection ? window.getSelection().toString() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Selection instances
|
||||
*
|
||||
* @todo Check if this is still relevant
|
||||
*/
|
||||
public instance: Selection = null;
|
||||
public selection: Selection = null;
|
||||
|
||||
/**
|
||||
* This property can store SelectionUtils's range for restoring later
|
||||
*
|
||||
* @type {Range|null}
|
||||
*/
|
||||
public savedSelectionRange: Range = null;
|
||||
|
||||
/**
|
||||
* Fake background is active
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public isFakeBackgroundEnabled = false;
|
||||
|
||||
/**
|
||||
* Native Document's commands for fake background
|
||||
*/
|
||||
private readonly commandBackground: string = 'backColor';
|
||||
private readonly commandRemoveFormat: string = 'removeFormat';
|
||||
|
||||
/**
|
||||
* Returns window SelectionUtils
|
||||
* {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection}
|
||||
|
@ -308,6 +354,36 @@ export default class SelectionUtils {
|
|||
return range.getBoundingClientRect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fake cursor to the current range
|
||||
*
|
||||
* @param [container] - if passed cursor will be added only if container contains current range
|
||||
*/
|
||||
public static addFakeCursor(container?: HTMLElement): void {
|
||||
const range = SelectionUtils.range;
|
||||
const fakeCursor = $.make('span', 'codex-editor__fake-cursor');
|
||||
|
||||
fakeCursor.dataset.mutationFree = 'true';
|
||||
|
||||
if (!range || (container && !container.contains(range.startContainer))) {
|
||||
return;
|
||||
}
|
||||
|
||||
range.collapse();
|
||||
range.insertNode(fakeCursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes fake cursor from a container
|
||||
*
|
||||
* @param container - container to look for
|
||||
*/
|
||||
public static removeFakeCursor(container: HTMLElement = document.body): void {
|
||||
const fakeCursor = $.find(container, `.codex-editor__fake-cursor`);
|
||||
|
||||
fakeCursor && fakeCursor.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes fake background
|
||||
*/
|
||||
|
|
|
@ -116,7 +116,7 @@ export default class BlockTool extends BaseTool<IBlockTool> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns sanitize configuration for Block Tool including conifgs from Inline Tools
|
||||
* Returns sanitize configuration for Block Tool including configs from related Inline Tools and Block Tunes
|
||||
*/
|
||||
@_.cacheable
|
||||
public get sanitizeConfig(): SanitizerConfig {
|
||||
|
@ -160,6 +160,10 @@ export default class BlockTool extends BaseTool<IBlockTool> {
|
|||
.from(this.inlineTools.values())
|
||||
.forEach(tool => Object.assign(baseConfig, tool.sanitizeConfig));
|
||||
|
||||
Array
|
||||
.from(this.tunes.values())
|
||||
.forEach(tune => Object.assign(baseConfig, tune.sanitizeConfig));
|
||||
|
||||
return baseConfig;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ export default class BlockTune extends BaseTool<IBlockTune> {
|
|||
// eslint-disable-next-line new-cap
|
||||
return new this.constructable({
|
||||
api: this.api.getMethodsForTool(this),
|
||||
settings: this.settings,
|
||||
config: this.settings,
|
||||
block,
|
||||
data,
|
||||
});
|
||||
|
|
|
@ -429,14 +429,12 @@ export function isValidMimeType(type: string): boolean {
|
|||
* @param {boolean} immediate - call now
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function debounce(func: () => void, wait?: number, immediate?: boolean): () => void {
|
||||
export function debounce(func: (...args: unknown[]) => void, wait?: number, immediate?: boolean): () => void {
|
||||
let timeout;
|
||||
|
||||
return (): void => {
|
||||
return (...args: unknown[]): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const context = this,
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
args = arguments;
|
||||
const context = this;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
const later = () => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
will-change: transform, opacity;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
|
||||
&--showed {
|
||||
opacity: 1;
|
||||
|
|
33
test/cypress/tests/selection.spec.ts
Normal file
33
test/cypress/tests/selection.spec.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import * as _ from '../../../src/components/utils';
|
||||
|
||||
describe('Blocks selection', () => {
|
||||
beforeEach(() => {
|
||||
if (this && this.editorInstance) {
|
||||
this.editorInstance.destroy();
|
||||
} else {
|
||||
cy.createEditor({}).as('editorInstance');
|
||||
}
|
||||
});
|
||||
|
||||
it('should remove block selection on click', () => {
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.find('div.ce-block')
|
||||
.click()
|
||||
.type('First block{enter}');
|
||||
|
||||
cy.get('[data-cy=editorjs')
|
||||
.find('div.ce-block')
|
||||
.next()
|
||||
.type('Second block')
|
||||
.type('{movetostart}')
|
||||
.trigger('keydown', {
|
||||
shiftKey: true,
|
||||
keyCode: _.keyCodes.UP,
|
||||
});
|
||||
|
||||
cy.get('[data-cy=editorjs')
|
||||
.click()
|
||||
.find('div.ce-block')
|
||||
.should('not.have.class', '.ce-block--selected');
|
||||
});
|
||||
});
|
|
@ -16,16 +16,16 @@ describe('BlockTune', () => {
|
|||
public static prepare;
|
||||
|
||||
public api: object;
|
||||
public settings: ToolSettings;
|
||||
public config: ToolSettings;
|
||||
public data: BlockTuneData;
|
||||
public block: object;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor({ api, settings, block, data }) {
|
||||
constructor({ api, config, block, data }) {
|
||||
this.api = api;
|
||||
this.settings = settings;
|
||||
this.config = config;
|
||||
this.block = block;
|
||||
this.data = data;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ describe('BlockTune', () => {
|
|||
it('should return Tool instance with passed settings', () => {
|
||||
const instance = tool.create(data, blockAPI as any) as any;
|
||||
|
||||
expect(instance.settings).to.be.deep.eq(options.config.config);
|
||||
expect(instance.config).to.be.deep.eq(options.config.config);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
5
types/api/readonly.d.ts
vendored
5
types/api/readonly.d.ts
vendored
|
@ -9,4 +9,9 @@ export interface ReadOnly {
|
|||
* @returns {Promise<boolean>} current value
|
||||
*/
|
||||
toggle: (state?: boolean) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Contains current read-only state
|
||||
*/
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
|
9
types/block-tunes/block-tune.d.ts
vendored
9
types/block-tunes/block-tune.d.ts
vendored
|
@ -1,4 +1,4 @@
|
|||
import {API, BlockAPI, ToolConfig} from '../index';
|
||||
import {API, BlockAPI, SanitizerConfig, ToolConfig} from '../index';
|
||||
import { BlockTuneData } from './block-tune-data';
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,11 @@ export interface BlockTuneConstructable {
|
|||
*/
|
||||
isTune: boolean;
|
||||
|
||||
/**
|
||||
* Tune's sanitize configuration
|
||||
*/
|
||||
sanitize?: SanitizerConfig;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
|
@ -48,7 +53,7 @@ export interface BlockTuneConstructable {
|
|||
*/
|
||||
new(config: {
|
||||
api: API,
|
||||
settings?: ToolConfig,
|
||||
config?: ToolConfig,
|
||||
block: BlockAPI,
|
||||
data: BlockTuneData,
|
||||
}): BlockTune;
|
||||
|
|
4
types/configs/editor-config.d.ts
vendored
4
types/configs/editor-config.d.ts
vendored
|
@ -53,7 +53,9 @@ export interface EditorConfig {
|
|||
/**
|
||||
* Map of Tools to use
|
||||
*/
|
||||
tools?: {[toolName: string]: ToolConstructable|ToolSettings};
|
||||
tools?: {
|
||||
[toolName: string]: ToolConstructable|ToolSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data to render on Editor start
|
||||
|
|
12
types/tools/tool-settings.d.ts
vendored
12
types/tools/tool-settings.d.ts
vendored
|
@ -21,7 +21,7 @@ export interface ToolboxConfig {
|
|||
*
|
||||
* @template Config - the structure describing a config object supported by the tool
|
||||
*/
|
||||
export interface ToolSettings <Config extends object = any> {
|
||||
export interface ExternalToolSettings<Config extends object = any> {
|
||||
|
||||
/**
|
||||
* Tool's class
|
||||
|
@ -56,3 +56,13 @@ export interface ToolSettings <Config extends object = any> {
|
|||
*/
|
||||
toolbox?: ToolboxConfig | false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal Tools 'class' property is optional
|
||||
*/
|
||||
export type InternalToolSettings<Config extends object = any> = Omit<ExternalToolSettings<Config>, 'class'> & Partial<Pick<ExternalToolSettings<Config>, 'class'>>;
|
||||
|
||||
/**
|
||||
* Union of external and internal Tools settings
|
||||
*/
|
||||
export type ToolSettings<Config extends object = any> = InternalToolSettings<Config> | ExternalToolSettings<Config>;
|
||||
|
|
Loading…
Reference in a new issue