This commit is contained in:
VolgaIgor 2026-03-11 21:17:07 +03:00 committed by GitHub
commit 722f4a037d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 50 additions and 32 deletions

View file

@ -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 {

View file

@ -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<BlockEvents> {
}
/**
* Exports Block data as string using conversion config
* Exports Block data using conversion config
*/
public async exportDataAsString(): Promise<string> {
public async exportData(): Promise<string|object> {
const blockData = await this.data;
return convertBlockDataToString(blockData, this.tool.conversionConfig);
return convertBlockDataForExport(blockData, this.tool.conversionConfig);
}
/**

View file

@ -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, replacingTool.settings);
let newBlockData = convertExportToBlockData(exportedData, replacingTool.conversionConfig, replacingTool.settings);
/**
* Optional data overrides.

View file

@ -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';
@ -60,6 +60,7 @@ 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,7 +68,7 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools
if (!isToolConvertable(tool, 'import')) {
return result;
}
/**
* Skip tools that does not specify toolbox
*/
@ -75,6 +76,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) => {
/**
@ -149,7 +158,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)) {
@ -162,7 +171,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 '';
@ -170,20 +179,25 @@ 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
* @param targetToolConfig - target tool config, used in conversionConfig.import method
*/
export function convertStringToBlockData(stringToImport: string, conversionConfig?: ConversionConfig, targetToolConfig?: ToolConfig): BlockToolData {
export function convertExportToBlockData(dataToImport: string | object, conversionConfig?: ConversionConfig, targetToolConfig?: ToolConfig): BlockToolData {
const importProp = conversionConfig?.import;
if (isFunction(importProp)) {
return importProp(stringToImport, targetToolConfig);
} else if (isString(importProp)) {
try {
return importProp(dataToImport, targetToolConfig);
} catch (err) {
log('Conversion «import» function returned an error');
return {};
}
} else if (isString(importProp) && isString(dataToImport)) {
return {
[importProp]: stringToImport,
[importProp]: dataToImport,
};
} else {
/**
@ -191,7 +205,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 {};

View file

@ -5,14 +5,14 @@ import type { BlockToolData, ToolConfig } 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, config: ToolConfig) => BlockToolData) | string;
import?: ((data: string | object, config: ToolConfig) => 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;
}