mirror of
https://github.com/codex-team/editor.js
synced 2024-05-28 03:23:27 +02:00
Use data instead of config
This commit is contained in:
parent
0ddc09de0c
commit
30c4b39f3a
|
@ -54,11 +54,6 @@ interface BlockConstructorOptions {
|
|||
* Tunes data for current Block
|
||||
*/
|
||||
tunesData: { [name: string]: BlockTuneData };
|
||||
|
||||
/**
|
||||
* May contain overrides for tool default config
|
||||
*/
|
||||
configOverrides: ToolConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,15 +254,9 @@ export default class Block extends EventsDispatcher<BlockEvents> {
|
|||
api,
|
||||
readOnly,
|
||||
tunesData,
|
||||
configOverrides,
|
||||
}: BlockConstructorOptions) {
|
||||
super();
|
||||
|
||||
// Merge tool default settings with overrides
|
||||
Object.entries(configOverrides).forEach(([prop, value]) => {
|
||||
tool.settings[prop] = value;
|
||||
});
|
||||
|
||||
this.name = tool.name;
|
||||
this.id = id;
|
||||
this.settings = tool.settings;
|
||||
|
@ -747,15 +736,45 @@ export default class Block extends EventsDispatcher<BlockEvents> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns current active toolbox entry
|
||||
* Tool could specify several entries to be displayed at the Toolbox (for example, "Heading 1", "Heading 2", "Heading 3")
|
||||
* This method returns the entry that is related to the Block (depended on the Block data)
|
||||
*/
|
||||
public get activeToolboxEntry(): ToolboxConfig {
|
||||
const entry = Array.isArray(this.tool.toolbox) ? this.toolInstance.activeToolboxEntry : this.tool.toolbox;
|
||||
public async getActiveToolboxEntry(): Promise<ToolboxConfig> {
|
||||
const toolboxSettings = this.tool.toolbox;
|
||||
|
||||
return {
|
||||
...entry,
|
||||
id: _.getHash(entry.icon + entry.title),
|
||||
};
|
||||
/**
|
||||
* If Tool specifies just the single entry, treat it like an active
|
||||
*/
|
||||
if (Array.isArray(toolboxSettings) === false) {
|
||||
return Promise.resolve(this.tool.toolbox as ToolboxConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have several entries with their own data overrides,
|
||||
* find those who matches some current data property
|
||||
*
|
||||
* Example:
|
||||
* Tools' toolbox: [
|
||||
* {title: "Heading 1", data: {level: 1} },
|
||||
* {title: "Heading 2", data: {level: 2} }
|
||||
* ]
|
||||
*
|
||||
* the Block data: {
|
||||
* text: "Heading text",
|
||||
* level: 2
|
||||
* }
|
||||
*
|
||||
* that means that for the current block, the second toolbox item (matched by "{level: 2}") is active
|
||||
*/
|
||||
const blockData = await this.data;
|
||||
const toolboxItems = toolboxSettings as ToolboxConfig[];
|
||||
|
||||
return toolboxItems.find((item) => {
|
||||
return Object.entries(item.data)
|
||||
.some(([propName, propValue]) => {
|
||||
return blockData[propName] && blockData[propName] === propValue;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -242,7 +242,6 @@ export default class BlocksAPI extends Module {
|
|||
index,
|
||||
needToFocus,
|
||||
replace,
|
||||
config,
|
||||
});
|
||||
|
||||
return new BlockAPI(insertedBlock);
|
||||
|
|
|
@ -229,7 +229,6 @@ export default class BlockManager extends Module {
|
|||
* @param {string} options.tool - tools passed in editor config {@link EditorConfig#tools}
|
||||
* @param {string} [options.id] - unique id for this block
|
||||
* @param {BlockToolData} [options.data] - constructor params
|
||||
* @param {ToolConfig} [options.config] - may contain tool default settings overrides
|
||||
*
|
||||
* @returns {Block}
|
||||
*/
|
||||
|
@ -238,7 +237,6 @@ export default class BlockManager extends Module {
|
|||
data = {},
|
||||
id = undefined,
|
||||
tunes: tunesData = {},
|
||||
config = {},
|
||||
}: {tool: string; id?: string; data?: BlockToolData; tunes?: {[name: string]: BlockTuneData}; config?: ToolConfig}): Block {
|
||||
const readOnly = this.Editor.ReadOnly.isEnabled;
|
||||
const tool = this.Editor.Tools.blockTools.get(name);
|
||||
|
@ -250,7 +248,6 @@ export default class BlockManager extends Module {
|
|||
api: this.Editor.API,
|
||||
readOnly,
|
||||
tunesData,
|
||||
configOverrides: config,
|
||||
});
|
||||
|
||||
if (!readOnly) {
|
||||
|
@ -270,7 +267,6 @@ export default class BlockManager extends Module {
|
|||
* @param {number} [options.index] - index where to insert new Block
|
||||
* @param {boolean} [options.needToFocus] - flag shows if needed to update current Block index
|
||||
* @param {boolean} [options.replace] - flag shows if block by passed index should be replaced with inserted one
|
||||
* @param {ToolConfig} [options.config] - may contain tool default settings overrides
|
||||
*
|
||||
* @returns {Block}
|
||||
*/
|
||||
|
@ -282,7 +278,6 @@ export default class BlockManager extends Module {
|
|||
needToFocus = true,
|
||||
replace = false,
|
||||
tunes = {},
|
||||
config,
|
||||
}: {
|
||||
id?: string;
|
||||
tool?: string;
|
||||
|
@ -291,7 +286,6 @@ export default class BlockManager extends Module {
|
|||
needToFocus?: boolean;
|
||||
replace?: boolean;
|
||||
tunes?: {[name: string]: BlockTuneData};
|
||||
config?: ToolConfig;
|
||||
} = {}): Block {
|
||||
let newIndex = index;
|
||||
|
||||
|
@ -303,7 +297,6 @@ export default class BlockManager extends Module {
|
|||
tool,
|
||||
data,
|
||||
tunes,
|
||||
config,
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -340,21 +333,18 @@ export default class BlockManager extends Module {
|
|||
* @param {object} options - replace options
|
||||
* @param {string} options.tool — plugin name
|
||||
* @param {BlockToolData} options.data — plugin data
|
||||
* @param {ToolConfig} options.config - may contain tool default settings overrides-
|
||||
*
|
||||
* @returns {Block}
|
||||
*/
|
||||
public replace({
|
||||
tool = this.config.defaultBlock,
|
||||
data = {},
|
||||
config = {},
|
||||
}): Block {
|
||||
return this.insert({
|
||||
tool,
|
||||
data,
|
||||
index: this.currentBlockIndex,
|
||||
replace: true,
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import Flipper from '../../flipper';
|
|||
import I18n from '../../i18n';
|
||||
import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||
import { clean } from '../../utils/sanitizer';
|
||||
import { ToolboxConfig, ToolConfig } from '../../../../types';
|
||||
import { ToolboxConfig, ToolConfig, BlockToolData } from '../../../../types';
|
||||
|
||||
/**
|
||||
* HTML Elements used for ConversionToolbar
|
||||
|
@ -136,10 +136,10 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
this.nodes.wrapper.classList.add(ConversionToolbar.CSS.conversionToolbarShowed);
|
||||
|
||||
/**
|
||||
* We use timeout to prevent bubbling Enter keydown on first dropdown item
|
||||
* We use RAF to prevent bubbling Enter keydown on first dropdown item
|
||||
* Conversion flipper will be activated after dropdown will open
|
||||
*/
|
||||
setTimeout(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.flipper.activate(this.tools.map(tool => tool.button).filter((button) => {
|
||||
return !button.classList.contains(ConversionToolbar.CSS.conversionToolHidden);
|
||||
}));
|
||||
|
@ -147,7 +147,7 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
if (_.isFunction(this.togglingCallback)) {
|
||||
this.togglingCallback(true);
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,27 +175,17 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
* For that Tools must provide import/export methods
|
||||
*
|
||||
* @param {string} replacingToolName - name of Tool which replaces current
|
||||
* @param {ToolConfig} [config] -
|
||||
* @param blockDataOverrides - Block data overrides. Could be passed in case if Multiple Toolbox items specified
|
||||
*/
|
||||
public async replaceWithBlock(replacingToolName: string, config?: ToolConfig): Promise<void> {
|
||||
public async replaceWithBlock(replacingToolName: string, blockDataOverrides?: BlockToolData): Promise<void> {
|
||||
/**
|
||||
* At first, we get current Block data
|
||||
*
|
||||
* @type {BlockToolConstructable}
|
||||
*/
|
||||
const currentBlockTool = this.Editor.BlockManager.currentBlock.tool;
|
||||
const currentBlockName = this.Editor.BlockManager.currentBlock.name;
|
||||
const savedBlock = await this.Editor.BlockManager.currentBlock.save() as SavedData;
|
||||
const blockData = savedBlock.data;
|
||||
const isToolboxItemActive = this.Editor.BlockManager.currentBlock.activeToolboxEntry.id === config?.id;
|
||||
|
||||
/**
|
||||
* When current Block name is equals to the replacing tool Name,
|
||||
* than convert this Block back to the default Block
|
||||
*/
|
||||
if (currentBlockName === replacingToolName && isToolboxItemActive) {
|
||||
replacingToolName = this.config.defaultBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getting a class of replacing Tool
|
||||
|
@ -252,10 +242,17 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this conversion fired by the one of multiple Toolbox items,
|
||||
* extend converted data with this item's "data" overrides
|
||||
*/
|
||||
if (blockDataOverrides) {
|
||||
newBlockData = Object.assign(newBlockData, blockDataOverrides);
|
||||
}
|
||||
|
||||
this.Editor.BlockManager.replace({
|
||||
tool: replacingToolName,
|
||||
data: newBlockData,
|
||||
config,
|
||||
});
|
||||
this.Editor.BlockSelection.clearSelection();
|
||||
|
||||
|
@ -287,12 +284,8 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
}
|
||||
|
||||
if (Array.isArray(tool.toolbox)) {
|
||||
tool.toolbox.forEach((configItem, i) =>
|
||||
this.addToolIfValid(
|
||||
name,
|
||||
configItem,
|
||||
(tool.toolbox as ToolboxConfig[]).slice(0, i)
|
||||
)
|
||||
tool.toolbox.forEach((toolboxItem) =>
|
||||
this.addToolIfValid(name, toolboxItem)
|
||||
);
|
||||
} else {
|
||||
this.addToolIfValid(name, tool.toolbox);
|
||||
|
@ -305,9 +298,8 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
*
|
||||
* @param name - tool's name
|
||||
* @param toolboxSettings - tool's single toolbox setting
|
||||
* @param otherToolboxEntries - other entries in tool's toolbox config (if any)
|
||||
*/
|
||||
private addToolIfValid(name: string, toolboxSettings: ToolboxConfig, otherToolboxEntries: ToolboxConfig[] = []): void {
|
||||
private addToolIfValid(name: string, toolboxSettings: ToolboxConfig): void {
|
||||
/**
|
||||
* Skip tools that don't pass 'toolbox' property
|
||||
*/
|
||||
|
@ -315,13 +307,6 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip tools that has not unique hash
|
||||
*/
|
||||
if (otherToolboxEntries.find(otherItem => otherItem.id === toolboxSettings.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addTool(name, toolboxSettings);
|
||||
}
|
||||
|
||||
|
@ -349,18 +334,29 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
|
|||
});
|
||||
|
||||
this.listeners.on(tool, 'click', async () => {
|
||||
await this.replaceWithBlock(toolName, toolboxItem.config);
|
||||
await this.replaceWithBlock(toolName, toolboxItem.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide current Tool and show others
|
||||
*/
|
||||
private filterTools(): void {
|
||||
private async filterTools(): Promise<void> {
|
||||
const { currentBlock } = this.Editor.BlockManager;
|
||||
const currentBlockActiveToolboxEntry = await currentBlock.getActiveToolboxEntry();
|
||||
|
||||
/**
|
||||
* Compares two Toolbox entries
|
||||
*
|
||||
* @param entry1 - entry to compare
|
||||
* @param entry2 - entry to compare with
|
||||
*/
|
||||
function isTheSameToolboxEntry(entry1, entry2): boolean {
|
||||
return entry1.icon + entry1.title === entry2.icon + entry2.title;
|
||||
}
|
||||
|
||||
this.tools.forEach(tool => {
|
||||
const isToolboxItemActive = currentBlock.activeToolboxEntry.id === tool.toolboxItem.id;
|
||||
const isToolboxItemActive = isTheSameToolboxEntry(currentBlockActiveToolboxEntry, tool.toolboxItem);
|
||||
const hidden = (tool.button.dataset.tool === currentBlock.name && isToolboxItemActive);
|
||||
|
||||
tool.button.hidden = hidden;
|
||||
|
|
|
@ -463,7 +463,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
/**
|
||||
* Changes Conversion Dropdown content for current block's Tool
|
||||
*/
|
||||
private setConversionTogglerContent(): void {
|
||||
private async setConversionTogglerContent(): Promise<void> {
|
||||
const { BlockManager } = this.Editor;
|
||||
const { currentBlock } = BlockManager;
|
||||
const toolName = currentBlock.name;
|
||||
|
@ -480,7 +480,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
|
|||
/**
|
||||
* Get icon or title for dropdown
|
||||
*/
|
||||
const toolboxSettings = currentBlock.activeToolboxEntry || {};
|
||||
const toolboxSettings = await currentBlock.getActiveToolboxEntry() || {};
|
||||
|
||||
this.nodes.conversionTogglerContent.innerHTML =
|
||||
toolboxSettings.icon ||
|
||||
|
|
|
@ -76,9 +76,7 @@ export default class BlockTool extends BaseTool<IBlockTool> {
|
|||
const toolToolboxSettings = this.constructable[InternalBlockToolSettings.Toolbox] as ToolboxConfig;
|
||||
|
||||
if (Array.isArray(toolToolboxSettings)) {
|
||||
return toolToolboxSettings
|
||||
.map(item => this.getActualToolboxSettings(item))
|
||||
.map(item => this.addIdToToolboxConfig(item));
|
||||
return toolToolboxSettings.map(item => this.getActualToolboxSettings(item));
|
||||
} else {
|
||||
return this.getActualToolboxSettings(toolToolboxSettings);
|
||||
}
|
||||
|
@ -165,7 +163,7 @@ export default class BlockTool extends BaseTool<IBlockTool> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns toolbox items settings merged with user defined settings
|
||||
* Returns toolbox item's settings merged with user defined settings
|
||||
*
|
||||
* @param toolboxItemSettings - toolbox item settings to merge
|
||||
*/
|
||||
|
@ -182,17 +180,4 @@ export default class BlockTool extends BaseTool<IBlockTool> {
|
|||
|
||||
return Object.assign({}, toolboxItemSettings, userToolboxSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns toolbox config entry with apended id field which is used later for
|
||||
* identifying an entry in case the tool has multiple toolbox entries configured.
|
||||
*
|
||||
* @param config - toolbox config entry
|
||||
*/
|
||||
private addIdToToolboxConfig(config: ToolboxConfig): ToolboxConfig {
|
||||
return {
|
||||
...config,
|
||||
id: _.getHash(config.icon + config.title),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { BlockToolAPI } from '../block';
|
|||
import Shortcuts from '../utils/shortcuts';
|
||||
import BlockTool from '../tools/block';
|
||||
import ToolsCollection from '../tools/collection';
|
||||
import { API, ToolboxConfig, ToolConfig } from '../../../types';
|
||||
import {API, BlockToolData, ToolboxConfig, ToolConfig} from '../../../types';
|
||||
import EventsDispatcher from '../utils/events';
|
||||
import Popover, { PopoverEvent, PopoverItem } from '../utils/popover';
|
||||
import I18n from '../i18n';
|
||||
|
@ -175,10 +175,10 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
|
|||
* Toolbox Tool's button click handler
|
||||
*
|
||||
* @param toolName - tool type to be activated
|
||||
* @param config -
|
||||
* @param blockDataOverrides - Block data predefined by the activated Toolbox item
|
||||
*/
|
||||
public toolButtonActivated(toolName: string, config: ToolConfig): void {
|
||||
this.insertNewBlock(toolName, config);
|
||||
public toolButtonActivated(toolName: string, blockDataOverrides: BlockToolData): void {
|
||||
this.insertNewBlock(toolName, blockDataOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -257,26 +257,16 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
|
|||
const toolToolboxSettings = tool.toolbox;
|
||||
|
||||
if (Array.isArray(toolToolboxSettings)) {
|
||||
const validToolboxSettings = toolToolboxSettings
|
||||
.filter(item => this.areToolboxSetttingsValid(item, tool.name))
|
||||
.filter((item, i) => {
|
||||
const notUnique = toolToolboxSettings.slice(0, i).find(otherItem => otherItem.id === item.id);
|
||||
|
||||
if (notUnique) {
|
||||
_.log('Toolbox entry has not unique combination of icon and title. Toolbox entry %o is skipped', 'warn', item.title);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
const validToolboxSettings = toolToolboxSettings.filter(item => {
|
||||
return this.areToolboxSettingsValid(item, tool.name);
|
||||
});
|
||||
|
||||
result.push({
|
||||
...tool,
|
||||
toolbox: validToolboxSettings,
|
||||
});
|
||||
} else {
|
||||
if (this.areToolboxSetttingsValid(toolToolboxSettings, tool.name)) {
|
||||
if (this.areToolboxSettingsValid(toolToolboxSettings, tool.name)) {
|
||||
result.push(tool);
|
||||
}
|
||||
}
|
||||
|
@ -293,13 +283,13 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
|
|||
/**
|
||||
* Maps tool data to popover item structure
|
||||
*/
|
||||
const toPopoverItem = (config: ToolboxConfig, tool: BlockTool): PopoverItem => {
|
||||
const toPopoverItem = (toolboxItem: ToolboxConfig, tool: BlockTool): PopoverItem => {
|
||||
return {
|
||||
icon: config.icon,
|
||||
label: I18n.t(I18nInternalNS.toolNames, config.title || _.capitalize(tool.name)),
|
||||
icon: toolboxItem.icon,
|
||||
label: I18n.t(I18nInternalNS.toolNames, toolboxItem.title || _.capitalize(tool.name)),
|
||||
name: tool.name,
|
||||
onClick: (e): void => {
|
||||
this.toolButtonActivated(tool.name, config);
|
||||
this.toolButtonActivated(tool.name, toolboxItem.data);
|
||||
},
|
||||
secondaryLabel: tool.shortcut ? _.beautifyShortcut(tool.shortcut) : '',
|
||||
};
|
||||
|
@ -323,9 +313,9 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
|
|||
* Validates tool's toolbox settings
|
||||
*
|
||||
* @param toolToolboxSettings - item to validate
|
||||
* @param toolName - name of the tool used in consone warning if item is not valid
|
||||
* @param toolName - name of the tool used in console warning if item is not valid
|
||||
*/
|
||||
private areToolboxSetttingsValid(toolToolboxSettings: ToolboxConfig, toolName: string): boolean {
|
||||
private areToolboxSettingsValid(toolToolboxSettings: ToolboxConfig, toolName: string): boolean {
|
||||
/**
|
||||
* Skip tools that don't pass 'toolbox' property
|
||||
*/
|
||||
|
@ -391,8 +381,9 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
|
|||
* Can be called when button clicked on Toolbox or by ShortcutData
|
||||
*
|
||||
* @param {string} toolName - Tool name
|
||||
* @param blockDataOverrides - predefined Block data
|
||||
*/
|
||||
private insertNewBlock(toolName: string, toolboxConfig?): void {
|
||||
private insertNewBlock(toolName: string, blockDataOverrides?: BlockToolData): void {
|
||||
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
|
||||
const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
|
||||
|
||||
|
@ -408,8 +399,8 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
|
|||
|
||||
const newBlock = this.api.blocks.insert(
|
||||
toolName,
|
||||
blockDataOverrides,
|
||||
undefined,
|
||||
toolboxConfig.config,
|
||||
index,
|
||||
undefined,
|
||||
currentBlock.isEmpty
|
||||
|
|
9
types/tools/tool-settings.d.ts
vendored
9
types/tools/tool-settings.d.ts
vendored
|
@ -1,5 +1,5 @@
|
|||
import {ToolConfig} from './tool-config';
|
||||
import {ToolConstructable} from './index';
|
||||
import {ToolConstructable, BlockToolData} from './index';
|
||||
|
||||
/**
|
||||
* Tool's Toolbox settings
|
||||
|
@ -15,15 +15,10 @@ export interface ToolboxConfig {
|
|||
*/
|
||||
icon?: string;
|
||||
|
||||
/**
|
||||
* Toolbox item id for distinguishing one toolbox item from another
|
||||
*/
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* May contain overrides for tool default config
|
||||
*/
|
||||
config?: ToolConfig
|
||||
data?: BlockToolData
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue