From 4743f53cb5da985bda98b7bef11d16d611343cb1 Mon Sep 17 00:00:00 2001 From: VolgaIgor <43250768+VolgaIgor@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:54:13 +0300 Subject: [PATCH 1/3] Fixed display of convert menu for blocks without export rule According to the workflow script from the documentation: https://editorjs.io/tools-api/#conversionconfig --- src/components/utils/blocks.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/utils/blocks.ts b/src/components/utils/blocks.ts index 471bb8647..b10cd0d93 100644 --- a/src/components/utils/blocks.ts +++ b/src/components/utils/blocks.ts @@ -51,6 +51,14 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools const savedData = await block.save() as SavedData; const blockData = savedData.data; + /** + * Checking that the block has an «export» rule + */ + const blockTool = allBlockTools.find((tool) => tool.name === block.name); + if (!isToolConvertable(blockTool, 'export')) { + return []; + } + return allBlockTools.reduce((result, tool) => { /** * Skip tools without «import» rule specified From ca96c932c662a2d1448ea35a7f7cea3bf1ebffd8 Mon Sep 17 00:00:00 2001 From: VolgaIgor <43250768+VolgaIgor@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:52:21 +0300 Subject: [PATCH 2/3] Block conversion update: ability to convert to complex objects --- docs/tools.md | 4 +-- src/components/block/index.ts | 8 +++--- src/components/modules/blockManager.ts | 28 ++++++++++++--------- src/components/utils/blocks.ts | 35 ++++++++++++++++++-------- types/configs/conversion-config.ts | 6 ++--- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/docs/tools.md b/docs/tools.md index 7cc4fd210..8570fad2f 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -449,7 +449,7 @@ It can be a `String` or a `Function`. `String` means a key of your Tool data object that should be used as string to export. -`Function` is a method that accepts your Tool data and compose a string to export from it. See example below: +`Function` is a method that accepts your Tool data and compose a string or object to export from it. See example below: ```js class ListTool { @@ -484,7 +484,7 @@ It can be a `String` or a `Function`. `String` means the key in tool data that will be filled by an exported string. For example, `import: 'text'` means that `constructor` of your block will accept a `data` object with `text` property filled with string composed by original block. -`Function` allows you to specify own logic, how a string should be converted to your tool data. For example: +`Function` allows you to specify own logic, how a string or object should be converted to your tool data. For example: ```js class ListTool { diff --git a/src/components/block/index.ts b/src/components/block/index.ts index 36c4aa2ae..d4e4cd2b2 100644 --- a/src/components/block/index.ts +++ b/src/components/block/index.ts @@ -26,7 +26,7 @@ import { isMutationBelongsToElement } from '../utils/mutations'; import type { EditorEventMap } from '../events'; import { FakeCursorAboutToBeToggled, FakeCursorHaveBeenSet, RedactorDomChanged } from '../events'; import type { RedactorDomChangedPayload } from '../events/RedactorDomChanged'; -import { convertBlockDataToString, isSameBlockData } from '../utils/blocks'; +import { convertBlockDataForExport, isSameBlockData } from '../utils/blocks'; import { PopoverItemType } from '@/types/utils/popover/popover-item-type'; /** @@ -729,12 +729,12 @@ export default class Block extends EventsDispatcher { } /** - * Exports Block data as string using conversion config + * Exports Block data using conversion config */ - public async exportDataAsString(): Promise { + public async exportData(): Promise { const blockData = await this.data; - return convertBlockDataToString(blockData, this.tool.conversionConfig); + return convertBlockDataForExport(blockData, this.tool.conversionConfig); } /** diff --git a/src/components/modules/blockManager.ts b/src/components/modules/blockManager.ts index fd06dd71b..66b6c1d38 100644 --- a/src/components/modules/blockManager.ts +++ b/src/components/modules/blockManager.ts @@ -19,7 +19,7 @@ import { BlockMovedMutationType } from '../../../types/events/block/BlockMoved'; import { BlockChangedMutationType } from '../../../types/events/block/BlockChanged'; import { BlockChanged } from '../events'; import { clean, sanitizeBlocks } from '../utils/sanitizer'; -import { convertStringToBlockData, isBlockConvertable } from '../utils/blocks'; +import { convertExportToBlockData, isBlockConvertable } from '../utils/blocks'; import PromiseQueue from '../utils/promise-queue'; /** @@ -501,10 +501,12 @@ export default class BlockManager extends Module { * 2) Blocks with different Tools if they provides conversionConfig */ } else if (targetBlock.mergeable && isBlockConvertable(blockToMerge, 'export') && isBlockConvertable(targetBlock, 'import')) { - const blockToMergeDataStringified = await blockToMerge.exportDataAsString(); - const cleanData = clean(blockToMergeDataStringified, targetBlock.tool.sanitizeConfig); + let blockToMergeExportData = await blockToMerge.exportData(); + if (_.isString(blockToMergeExportData)) { + blockToMergeExportData = clean(blockToMergeExportData, targetBlock.tool.sanitizeConfig); + } - blockToMergeData = convertStringToBlockData(cleanData, targetBlock.tool.conversionConfig); + blockToMergeData = convertExportToBlockData(blockToMergeExportData, targetBlock.tool.conversionConfig); } if (blockToMergeData === undefined) { @@ -848,22 +850,24 @@ export default class BlockManager extends Module { } /** - * Using Conversion Config "export" we get a stringified version of the Block data + * Using Conversion Config "export" we get a exported version of the Block data */ - const exportedData = await blockToConvert.exportDataAsString(); + let exportedData = await blockToConvert.exportData(); /** - * Clean exported data with replacing sanitizer config + * Clean exported data, if it is a string, with replacing sanitizer config */ - const cleanData: string = clean( - exportedData, - replacingTool.sanitizeConfig - ); + if (_.isString(exportedData)) { + exportedData = clean( + exportedData, + replacingTool.sanitizeConfig + ); + } /** * Now using Conversion Config "import" we compose a new Block data */ - let newBlockData = convertStringToBlockData(cleanData, replacingTool.conversionConfig); + let newBlockData = convertExportToBlockData(exportedData, replacingTool.conversionConfig); /** * Optional data overrides. diff --git a/src/components/utils/blocks.ts b/src/components/utils/blocks.ts index b10cd0d93..b5a89396a 100644 --- a/src/components/utils/blocks.ts +++ b/src/components/utils/blocks.ts @@ -4,7 +4,7 @@ import type { SavedData } from '../../../types/data-formats'; import type { BlockToolData } from '../../../types/tools/block-tool-data'; import type Block from '../block'; import type BlockToolAdapter from '../tools/block'; -import { isFunction, isString, log, equals, isEmpty } from '../utils'; +import { isFunction, isString, log, equals, isEmpty, isUndefined } from '../utils'; import { isToolConvertable } from './tools'; @@ -59,6 +59,8 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools return []; } + const exportData = convertBlockDataForExport(blockData, blockTool.conversionConfig); + return allBlockTools.reduce((result, tool) => { /** * Skip tools without «import» rule specified @@ -67,6 +69,14 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools return result; } + /** + * Checking that the block is not empty after conversion + */ + const importData = convertExportToBlockData(exportData, tool.conversionConfig); + if (isUndefined(importData) || isEmpty(importData)) { + return result; + } + /** Filter out invalid toolbox entries */ const actualToolboxItems = tool.toolbox.filter((toolboxItem) => { /** @@ -141,7 +151,7 @@ export function areBlocksMergeable(targetBlock: Block, blockToMerge: Block): boo * @param blockData - block data to convert * @param conversionConfig - tool's conversion config */ -export function convertBlockDataToString(blockData: BlockToolData, conversionConfig?: ConversionConfig ): string { +export function convertBlockDataForExport(blockData: BlockToolData, conversionConfig?: ConversionConfig ): string | object { const exportProp = conversionConfig?.export; if (isFunction(exportProp)) { @@ -154,7 +164,7 @@ export function convertBlockDataToString(blockData: BlockToolData, conversionCon */ if (exportProp !== undefined) { log('Conversion «export» property must be a string or function. ' + - 'String means key of saved data object to export. Function should export processed string to export.'); + 'String means key of saved data object to export. Function should export processed string or object to export.'); } return ''; @@ -162,19 +172,24 @@ export function convertBlockDataToString(blockData: BlockToolData, conversionCon } /** - * Using conversionConfig, convert string to block data. + * Using conversionConfig, convert export string|object to block data. * - * @param stringToImport - string to convert + * @param dataToImport - string|object to convert * @param conversionConfig - tool's conversion config */ -export function convertStringToBlockData(stringToImport: string, conversionConfig?: ConversionConfig): BlockToolData { +export function convertExportToBlockData(dataToImport: string | object, conversionConfig?: ConversionConfig): BlockToolData { const importProp = conversionConfig?.import; if (isFunction(importProp)) { - return importProp(stringToImport); - } else if (isString(importProp)) { + try { + return importProp(dataToImport); + } catch (err) { + log('Conversion «import» function returned an error'); + return {}; + } + } else if (isString(importProp) && isString(dataToImport)) { return { - [importProp]: stringToImport, + [importProp]: dataToImport, }; } else { /** @@ -182,7 +197,7 @@ export function convertStringToBlockData(stringToImport: string, conversionConfi */ if (importProp !== undefined) { log('Conversion «import» property must be a string or function. ' + - 'String means key of tool data to import. Function accepts a imported string and return composed tool data.'); + 'String means key of tool data to import. Function accepts a imported string or object and return composed tool data.'); } return {}; diff --git a/types/configs/conversion-config.ts b/types/configs/conversion-config.ts index b61aa478d..d8f621bd6 100644 --- a/types/configs/conversion-config.ts +++ b/types/configs/conversion-config.ts @@ -5,14 +5,14 @@ import type { BlockToolData } from '../tools'; */ export interface ConversionConfig { /** - * How to import string to this Tool. + * How to import data to this Tool. * * Can be a String or Function: * * 1. String — the key of Tool data object to fill it with imported string on render. * 2. Function — method that accepts importing string and composes Tool data to render. */ - import?: ((data: string) => string) | string; + import?: ((data: string | object) => BlockToolData) | string; /** * How to export this Tool to make other Block. @@ -22,5 +22,5 @@ export interface ConversionConfig { * 1. String — which property of saved Tool data should be used as exported string. * 2. Function — accepts saved Tool data and create a string to export */ - export?: ((data: BlockToolData) => string) | string; + export?: ((data: BlockToolData) => string | object) | string; } From 9b7fd5f5b38a6ee0acfa7de6fe63c1e2c9aef815 Mon Sep 17 00:00:00 2001 From: VolgaIgor <43250768+VolgaIgor@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:37:04 +0300 Subject: [PATCH 3/3] Merge fix --- src/components/modules/blockManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/modules/blockManager.ts b/src/components/modules/blockManager.ts index 5db72161b..5b5c931dd 100644 --- a/src/components/modules/blockManager.ts +++ b/src/components/modules/blockManager.ts @@ -867,7 +867,7 @@ export default class BlockManager extends Module { /** * Now using Conversion Config "import" we compose a new Block data */ - let newBlockData = convertStringToBlockData(exportedData, replacingTool.conversionConfig, replacingTool.settings); + let newBlockData = convertExportToBlockData(exportedData, replacingTool.conversionConfig, replacingTool.settings); /** * Optional data overrides.