Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Block Tunes API #1596

Merged
merged 29 commits into from
Apr 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
24dc069
Add internal wrappers for tools classes
gohabereg Mar 14, 2021
224dd3b
FIx lint
gohabereg Mar 14, 2021
83e17bb
Change tools collections to map
gohabereg Mar 15, 2021
1ffcbed
Apply some more refactoring
gohabereg Mar 15, 2021
3d743f3
Make tool instance private field
gohabereg Mar 15, 2021
e56579c
Add some docs
gohabereg Mar 15, 2021
0069a2a
Fix eslint
gohabereg Mar 15, 2021
d407e32
Basic implementation for Block Tunes
gohabereg Mar 15, 2021
a0160ef
Small fix for demo
gohabereg Mar 15, 2021
dde3bd4
Review changes
gohabereg Mar 16, 2021
28bfacf
Merge branch 'next' of github.com:codex-team/editor.js into reafctori…
gohabereg Mar 16, 2021
ca1aef3
Fix
gohabereg Mar 16, 2021
a459ab5
Merge branch 'reafctoring/tools' of github.com:codex-team/editor.js i…
gohabereg Mar 16, 2021
6ecc3ac
Add common tunes and ToolsCollection class
gohabereg Mar 17, 2021
1f9c5f1
Fixes after review
gohabereg Mar 18, 2021
c81b9a0
Merge branch 'reafctoring/tools' of github.com:codex-team/editor.js i…
gohabereg Mar 18, 2021
b8ba38c
Rename tools collections
gohabereg Mar 18, 2021
aea3158
Readonly fix
gohabereg Mar 18, 2021
82f3bfa
Merge branch 'reafctoring/tools' of github.com:codex-team/editor.js i…
gohabereg Mar 18, 2021
a3bb039
Some fixes after review
gohabereg Mar 27, 2021
41274fb
Merge branch 'next' of github.com:codex-team/editor.js into feature/b…
gohabereg Mar 31, 2021
13b64eb
Apply suggestions from code review
gohabereg Apr 2, 2021
4e9a098
Fixes after review
gohabereg Apr 2, 2021
ebc8b3f
Add docs and changelog
gohabereg Apr 2, 2021
cad9691
Update docs/block-tunes.md
gohabereg Apr 2, 2021
66feab7
Apply suggestions from code review
gohabereg Apr 2, 2021
8ce77a8
Update src/components/block/index.ts
gohabereg Apr 4, 2021
94bfd10
[Dev] Tools utils tests (#1602)
gohabereg Apr 4, 2021
5fe699c
Fix test & bump version
gohabereg Apr 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Apply some more refactoring
  • Loading branch information
gohabereg committed Mar 15, 2021
commit 1ffcbed33b070043df4ed7002ad60c3dc32e4e30
5 changes: 2 additions & 3 deletions src/components/modules/blockEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,14 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private enter(event: KeyboardEvent): void {
const { BlockManager, Tools, UI } = this.Editor;
const { BlockManager, UI } = this.Editor;
const currentBlock = BlockManager.currentBlock;
const tool = Tools.block.get(currentBlock.name);

/**
* 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.
*/
if (tool?.isLineBreaksEnabled) {
if (currentBlock.tool.isLineBreaksEnabled) {
return;
}

Expand Down
69 changes: 34 additions & 35 deletions src/components/modules/paste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface TagSubstitute {
*
* @type {string}
*/
tool: string;
tool: BlockTool;
}

/**
Expand All @@ -46,7 +46,7 @@ interface PatternSubstitute {
*
* @type {string}
*/
tool: string;
tool: BlockTool;
}

/**
Expand Down Expand Up @@ -279,18 +279,20 @@ export default class Paste extends Module {
private processTools(): void {
const tools = this.Editor.Tools.block;

Object.entries(tools).forEach(this.processTool);
Array
.from(tools.values())
.forEach(this.processTool);
}

/**
* Process paste config for each tool
*/
private processTool = ([name, tool]: [string, BlockTool]): void => {
private processTool = (tool: BlockTool): void => {
try {
const toolInstance = this.Editor.Tools.block.get(name).instance({}, {} as BlockAPI, false);
const toolInstance = tool.instance({}, {} as BlockAPI, false);

if (tool.pasteConfig === false) {
this.exceptionList.push(name);
this.exceptionList.push(tool.name);

return;
}
Expand All @@ -299,11 +301,9 @@ export default class Paste extends Module {
return;
}

const toolPasteConfig = tool.pasteConfig || {};

this.getTagsConfig(name, toolPasteConfig);
this.getFilesConfig(name, toolPasteConfig);
this.getPatternsConfig(name, toolPasteConfig);
this.getTagsConfig(tool);
this.getFilesConfig(tool);
this.getPatternsConfig(tool);
} catch (e) {
_.log(
`Paste handling for «${name}» Tool hasn't been set up because of the error`,
Expand All @@ -319,26 +319,26 @@ export default class Paste extends Module {
* @param {string} name - Tool name
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/
private getTagsConfig(name: string, toolPasteConfig: PasteConfig): void {
const tags = toolPasteConfig.tags || [];
private getTagsConfig(tool: BlockTool): void {
const tags = tool.pasteConfig.tags || [];

tags.forEach((tag) => {
if (Object.prototype.hasOwnProperty.call(this.toolsTags, tag)) {
_.log(
`Paste handler for «${name}» Tool on «${tag}» tag is skipped ` +
`because it is already used by «${this.toolsTags[tag].tool}» Tool.`,
`Paste handler for «${tool.name}» Tool on «${tag}» tag is skipped ` +
`because it is already used by «${this.toolsTags[tag].tool.name}» Tool.`,
'warn'
);

return;
}

this.toolsTags[tag.toUpperCase()] = {
tool: name,
tool,
};
});

this.tagsByTool[name] = tags.map((t) => t.toUpperCase());
this.tagsByTool[tool.name] = tags.map((t) => t.toUpperCase());
}

/**
Expand All @@ -347,28 +347,28 @@ export default class Paste extends Module {
* @param {string} name - Tool name
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/
private getFilesConfig(name: string, toolPasteConfig: PasteConfig): void {
const { files = {} } = toolPasteConfig;
private getFilesConfig(tool: BlockTool): void {
const { files = {} } = tool.pasteConfig;
let { extensions, mimeTypes } = files;

if (!extensions && !mimeTypes) {
return;
}

if (extensions && !Array.isArray(extensions)) {
_.log(`«extensions» property of the onDrop config for «${name}» Tool should be an array`);
_.log(`«extensions» property of the onDrop config for «${tool.name}» Tool should be an array`);
extensions = [];
}

if (mimeTypes && !Array.isArray(mimeTypes)) {
_.log(`«mimeTypes» property of the onDrop config for «${name}» Tool should be an array`);
_.log(`«mimeTypes» property of the onDrop config for «${tool.name}» Tool should be an array`);
mimeTypes = [];
}

if (mimeTypes) {
mimeTypes = mimeTypes.filter((type) => {
if (!_.isValidMimeType(type)) {
_.log(`MIME type value «${type}» for the «${name}» Tool is not a valid MIME type`, 'warn');
_.log(`MIME type value «${type}» for the «${tool.name}» Tool is not a valid MIME type`, 'warn');

return false;
}
Expand All @@ -377,7 +377,7 @@ export default class Paste extends Module {
});
}

this.toolsFiles[name] = {
this.toolsFiles[tool.name] = {
extensions: extensions || [],
mimeTypes: mimeTypes || [],
};
Expand All @@ -389,12 +389,12 @@ export default class Paste extends Module {
* @param {string} name - Tool name
* @param {PasteConfig} toolPasteConfig - Tool onPaste configuration
*/
private getPatternsConfig(name: string, toolPasteConfig: PasteConfig): void {
if (!toolPasteConfig.patterns || _.isEmpty(toolPasteConfig.patterns)) {
private getPatternsConfig(tool: BlockTool): void {
if (!tool.pasteConfig.patterns || _.isEmpty(tool.pasteConfig.patterns)) {
return;
}

Object.entries(toolPasteConfig.patterns).forEach(([key, pattern]: [string, RegExp]) => {
Object.entries(tool.pasteConfig.patterns).forEach(([key, pattern]: [string, RegExp]) => {
/** Still need to validate pattern as it provided by user */
if (!(pattern instanceof RegExp)) {
_.log(
Expand All @@ -406,7 +406,7 @@ export default class Paste extends Module {
this.toolsPatterns.push({
key,
pattern,
tool: name,
tool,
});
});
}
Expand Down Expand Up @@ -525,7 +525,6 @@ export default class Paste extends Module {
*/
private processHTML(innerHTML: string): PasteData[] {
const { Tools, Sanitizer } = this.Editor;
const initialTool = this.config.defaultBlock;
const wrapper = $.make('DIV');

wrapper.innerHTML = innerHTML;
Expand All @@ -534,7 +533,7 @@ export default class Paste extends Module {

return nodes
.map((node) => {
let content, tool = initialTool, isBlock = false;
let content, tool = Tools.defaultTool, isBlock = false;

switch (node.nodeType) {
/** If node is a document fragment, use temp wrapper to get innerHTML */
Expand All @@ -554,7 +553,7 @@ export default class Paste extends Module {
break;
}

const { tags } = Tools.block.get(tool).pasteConfig as PasteConfig;
const { tags } = tool.pasteConfig;

const toolTags = tags.reduce((result, tag) => {
result[tag.toLowerCase()] = {};
Expand All @@ -572,7 +571,7 @@ export default class Paste extends Module {
return {
content,
isBlock,
tool,
tool: tool.name,
event,
};
})
Expand Down Expand Up @@ -673,7 +672,7 @@ export default class Paste extends Module {

/** If there is no pattern substitute - insert string as it is */
if (BlockManager.currentBlock && BlockManager.currentBlock.currentInput) {
const currentToolSanitizeConfig = Sanitizer.getInlineToolsConfig(BlockManager.currentBlock.name);
const currentToolSanitizeConfig = Sanitizer.getInlineToolsConfig(BlockManager.currentBlock.tool);

document.execCommand(
'insertHTML',
Expand Down Expand Up @@ -714,7 +713,7 @@ export default class Paste extends Module {

return {
event,
tool: pattern.tool,
tool: pattern.tool.name,
};
}

Expand Down Expand Up @@ -787,8 +786,8 @@ export default class Paste extends Module {

const element = node as HTMLElement;

const { tool = '' } = this.toolsTags[element.tagName] || {};
const toolTags = this.tagsByTool[tool] || [];
const { tool } = this.toolsTags[element.tagName] || {};
const toolTags = this.tagsByTool[tool?.name] || [];

const isSubstitutable = tags.includes(element.tagName);
const isBlockElement = $.blockElements.includes(element.tagName.toLowerCase());
Expand Down
5 changes: 2 additions & 3 deletions src/components/modules/sanitizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export default class Sanitizer extends Module {
}

const tool = this.Editor.Tools.available.get(toolName);
const baseConfig = this.getInlineToolsConfig(toolName);
const baseConfig = this.getInlineToolsConfig(tool as BlockTool);

/**
* If Tools doesn't provide sanitizer config or it is empty
Expand Down Expand Up @@ -189,9 +189,8 @@ export default class Sanitizer extends Module {
*
* @param {string} name - Inline Tool name
*/
public getInlineToolsConfig(name: string): SanitizerConfig {
public getInlineToolsConfig(tool: BlockTool): SanitizerConfig {
const { Tools } = this.Editor;
const tool = Tools.block.get(name);
const enableInlineTools = tool.enabledInlineTools || [];

let config = {} as SanitizerConfig;
Expand Down
47 changes: 24 additions & 23 deletions src/components/modules/toolbar/inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Shortcuts from '../../utils/shortcuts';
import {ToolType} from '../tools';
import InlineTool from '../../tools/inline';
import {InternalSettings} from '../../tools/base';
import BlockTool from '../../tools/block';

/**
* Inline Toolbar elements
Expand Down Expand Up @@ -283,9 +284,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
* @param {string} toolName - user specified name of tool
* @returns {string[] | boolean} array of ordered tool names or false
*/
private getInlineToolbarSettings(toolName): string[] | boolean {
const tool = this.Editor.Tools.block.get(toolName);

private getInlineToolbarSettings(tool: BlockTool): string[] | boolean {
/**
* InlineToolbar property of a particular tool
*/
Expand Down Expand Up @@ -454,7 +453,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
/**
* getInlineToolbarSettings could return an string[] (order of tools) or false (Inline Toolbar disabled).
*/
const inlineToolbarSettings = this.getInlineToolbarSettings(currentBlock.name);
const inlineToolbarSettings = this.getInlineToolbarSettings(currentBlock.tool);

return inlineToolbarSettings !== false;
}
Expand Down Expand Up @@ -511,12 +510,13 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
*/
private setConversionTogglerContent(): void {
const { BlockManager, Tools } = this.Editor;
const toolName = BlockManager.currentBlock.name;
const { currentBlock } = BlockManager;
const toolName = currentBlock.name;

/**
* If tool does not provide 'export' rule, hide conversion dropdown
*/
const conversionConfig = Tools.block.get(toolName).conversionConfig;
const conversionConfig = currentBlock.tool.conversionConfig;
const exportRuleDefined = conversionConfig && conversionConfig.export;

this.nodes.conversionToggler.hidden = !exportRuleDefined;
Expand All @@ -525,7 +525,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
/**
* Get icon or title for dropdown
*/
const toolboxSettings = Tools.block.get(toolName).toolbox || {};
const toolboxSettings = currentBlock.tool.toolbox || {};

this.nodes.conversionTogglerContent.innerHTML =
toolboxSettings.icon ||
Expand Down Expand Up @@ -568,13 +568,12 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
* For this moment, inlineToolbarOrder could not be 'false'
* because this method will be called only if the Inline Toolbar is enabled
*/
const inlineToolbarOrder = this.getInlineToolbarSettings(currentBlock.name) as string[];
const inlineToolbarOrder = this.getInlineToolbarSettings(currentBlock.tool) as string[];

inlineToolbarOrder.forEach((toolName) => {
const tool = this.Editor.Tools.inline.get(toolName).instance();
const tool = this.Editor.Tools.inline.get(toolName);

this.addTool(toolName, tool);
tool.checkState(SelectionUtils.get());
this.addTool(tool);
});

/**
Expand All @@ -589,39 +588,39 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
* @param {string} toolName - name of Tool to add
* @param {InlineTool} tool - Tool class instance
*/
private addTool(toolName: string, tool: IInlineTool): void {
private addTool(tool: InlineTool): void {
const {
Tools,
Tooltip,
} = this.Editor;

const button = tool.render();
const instance = tool.instance();
const button = instance.render();

if (!button) {
_.log('Render method must return an instance of Node', 'warn', toolName);
_.log('Render method must return an instance of Node', 'warn', tool.name);

return;
}

button.dataset.tool = toolName;
button.dataset.tool = tool.name;
this.nodes.buttons.appendChild(button);
this.toolsInstances.set(toolName, tool);
this.toolsInstances.set(tool.name, instance);

if (_.isFunction(tool.renderActions)) {
const actions = tool.renderActions();
if (_.isFunction(instance.renderActions)) {
const actions = instance.renderActions();

this.nodes.actions.appendChild(actions);
}

this.listeners.on(button, 'click', (event) => {
this.toolClicked(tool);
this.toolClicked(instance);
event.preventDefault();
});

const shortcut = this.getToolShortcut(toolName);
const shortcut = this.getToolShortcut(tool.name);

if (shortcut) {
this.enableShortcuts(tool, shortcut);
this.enableShortcuts(instance, shortcut);
}

/**
Expand All @@ -630,7 +629,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
const tooltipContent = $.make('div');
const toolTitle = I18n.t(
I18nInternalNS.toolNames,
Tools.inline.get(toolName).title || _.capitalize(toolName)
tool.title || _.capitalize(tool.name)
);

tooltipContent.appendChild($.text(toolTitle));
Expand All @@ -645,6 +644,8 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
placement: 'top',
hidingDelay: 100,
});

instance.checkState(SelectionUtils.get());
}

/**
Expand Down
Loading