refactor(editor): improve element adapters (#11473)
This commit is contained in:
2
blocksuite/affine/gfx/brush/src/adapter/index.ts
Normal file
2
blocksuite/affine/gfx/brush/src/adapter/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './markdown';
|
||||
export * from './plain-text';
|
||||
18
blocksuite/affine/gfx/brush/src/adapter/markdown/index.ts
Normal file
18
blocksuite/affine/gfx/brush/src/adapter/markdown/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ElementToMarkdownAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
export const brushToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||
name: 'brush',
|
||||
match: elementModel => elementModel.type === 'brush',
|
||||
toAST: () => {
|
||||
const content = `Brush Stroke`;
|
||||
return {
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: content,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
11
blocksuite/affine/gfx/brush/src/adapter/plain-text/index.ts
Normal file
11
blocksuite/affine/gfx/brush/src/adapter/plain-text/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ElementToPlainTextAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
export const brushToPlainTextAdapterMatcher =
|
||||
ElementToPlainTextAdapterExtension({
|
||||
name: 'brush',
|
||||
match: elementModel => elementModel.type === 'brush',
|
||||
toAST: () => {
|
||||
const content = `Brush Stroke`;
|
||||
return { content };
|
||||
},
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './adapter';
|
||||
export * from './brush-tool';
|
||||
export * from './element-renderer';
|
||||
export * from './eraser-tool';
|
||||
|
||||
2
blocksuite/affine/gfx/connector/src/adapter/index.ts
Normal file
2
blocksuite/affine/gfx/connector/src/adapter/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './markdown';
|
||||
export * from './plain-text';
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ElementToMarkdownAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getConnectorText } from '../text';
|
||||
|
||||
export const connectorToMarkdownAdapterMatcher =
|
||||
ElementToMarkdownAdapterExtension({
|
||||
name: 'connector',
|
||||
match: elementModel => elementModel.type === 'connector',
|
||||
toAST: elementModel => {
|
||||
const text = getConnectorText(elementModel);
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = `Connector, with text label "${text}"`;
|
||||
return {
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: content,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ElementToPlainTextAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getConnectorText } from '../text';
|
||||
|
||||
export const connectorToPlainTextAdapterMatcher =
|
||||
ElementToPlainTextAdapterExtension({
|
||||
name: 'connector',
|
||||
match: elementModel => elementModel.type === 'connector',
|
||||
toAST: elementModel => {
|
||||
const text = getConnectorText(elementModel);
|
||||
const content = `Connector, with text label "${text}"`;
|
||||
return { content };
|
||||
},
|
||||
});
|
||||
23
blocksuite/affine/gfx/connector/src/adapter/text.ts
Normal file
23
blocksuite/affine/gfx/connector/src/adapter/text.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { DeltaInsert } from '@blocksuite/affine/store';
|
||||
|
||||
export function getConnectorText(
|
||||
elementModel: Record<string, unknown>
|
||||
): string {
|
||||
let text = '';
|
||||
if (elementModel.type !== 'connector') {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (
|
||||
'text' in elementModel &&
|
||||
typeof elementModel.text === 'object' &&
|
||||
elementModel.text
|
||||
) {
|
||||
let delta: DeltaInsert[] = [];
|
||||
if ('delta' in elementModel.text) {
|
||||
delta = elementModel.text.delta as DeltaInsert[];
|
||||
}
|
||||
text = delta.map(d => d.insert).join('');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './adapter';
|
||||
export * from './connector-tool';
|
||||
export * from './element-renderer';
|
||||
export * from './element-transform';
|
||||
|
||||
2
blocksuite/affine/gfx/group/src/adapter/index.ts
Normal file
2
blocksuite/affine/gfx/group/src/adapter/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './markdown';
|
||||
export * from './plain-text';
|
||||
25
blocksuite/affine/gfx/group/src/adapter/markdown/index.ts
Normal file
25
blocksuite/affine/gfx/group/src/adapter/markdown/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ElementToMarkdownAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getGroupTitle } from '../text';
|
||||
|
||||
export const groupToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||
name: 'group',
|
||||
match: elementModel => elementModel.type === 'group',
|
||||
toAST: elementModel => {
|
||||
const title = getGroupTitle(elementModel);
|
||||
if (!title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = `Group, with title "${title}"`;
|
||||
return {
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: content,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
14
blocksuite/affine/gfx/group/src/adapter/plain-text/index.ts
Normal file
14
blocksuite/affine/gfx/group/src/adapter/plain-text/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ElementToPlainTextAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getGroupTitle } from '../text';
|
||||
|
||||
export const groupToPlainTextAdapterMatcher =
|
||||
ElementToPlainTextAdapterExtension({
|
||||
name: 'group',
|
||||
match: elementModel => elementModel.type === 'group',
|
||||
toAST: elementModel => {
|
||||
const title = getGroupTitle(elementModel);
|
||||
const content = `Group, with title "${title}"`;
|
||||
return { content };
|
||||
},
|
||||
});
|
||||
21
blocksuite/affine/gfx/group/src/adapter/text.ts
Normal file
21
blocksuite/affine/gfx/group/src/adapter/text.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { DeltaInsert } from '@blocksuite/affine/store';
|
||||
|
||||
export function getGroupTitle(elementModel: Record<string, unknown>): string {
|
||||
let title = '';
|
||||
if (elementModel.type !== 'group') {
|
||||
return title;
|
||||
}
|
||||
|
||||
if (
|
||||
'title' in elementModel &&
|
||||
typeof elementModel.title === 'object' &&
|
||||
elementModel.title
|
||||
) {
|
||||
let delta: DeltaInsert[] = [];
|
||||
if ('delta' in elementModel.title) {
|
||||
delta = elementModel.title.delta as DeltaInsert[];
|
||||
}
|
||||
title = delta.map(d => d.insert).join('');
|
||||
}
|
||||
return title;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './adapter';
|
||||
export * from './command';
|
||||
export * from './element-renderer';
|
||||
export * from './text/text';
|
||||
|
||||
2
blocksuite/affine/gfx/mindmap/src/adapter/index.ts
Normal file
2
blocksuite/affine/gfx/mindmap/src/adapter/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './markdown';
|
||||
export * from './plain-text';
|
||||
69
blocksuite/affine/gfx/mindmap/src/adapter/markdown/index.ts
Normal file
69
blocksuite/affine/gfx/mindmap/src/adapter/markdown/index.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ElementToMarkdownAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
import { getShapeText } from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
buildMindMapTree,
|
||||
type MindMapTreeNode,
|
||||
} from '@blocksuite/affine-model';
|
||||
|
||||
export const mindmapToMarkdownAdapterMatcher =
|
||||
ElementToMarkdownAdapterExtension({
|
||||
name: 'mindmap',
|
||||
match: elementModel => elementModel.type === 'mindmap',
|
||||
toAST: (elementModel, context) => {
|
||||
if (elementModel.type !== 'mindmap') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const mindMapTree = buildMindMapTree(elementModel);
|
||||
if (!mindMapTree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { walkerContext, elements } = context;
|
||||
const traverseMindMapTree = (node: MindMapTreeNode) => {
|
||||
const shapeElement = elements[node.id as string];
|
||||
const shapeText = getShapeText(shapeElement);
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
type: 'listItem',
|
||||
spread: false,
|
||||
children: [],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.openNode(
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: shapeText,
|
||||
},
|
||||
],
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode();
|
||||
node.children.forEach(child => {
|
||||
traverseMindMapTree(child);
|
||||
walkerContext.closeNode();
|
||||
});
|
||||
};
|
||||
|
||||
// First create a list node for the mind map tree
|
||||
walkerContext.openNode(
|
||||
{
|
||||
type: 'list',
|
||||
ordered: false,
|
||||
spread: false,
|
||||
children: [],
|
||||
},
|
||||
'children'
|
||||
);
|
||||
traverseMindMapTree(mindMapTree);
|
||||
walkerContext.closeNode().closeNode();
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ElementToPlainTextAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getMindMapTreeText } from '../utils';
|
||||
|
||||
export const mindmapToPlainTextAdapterMatcher =
|
||||
ElementToPlainTextAdapterExtension({
|
||||
name: 'mindmap',
|
||||
match: elementModel => elementModel.type === 'mindmap',
|
||||
toAST: (elementModel, context) => {
|
||||
const mindMapContent = getMindMapTreeText(elementModel, context.elements);
|
||||
return { content: mindMapContent };
|
||||
},
|
||||
});
|
||||
65
blocksuite/affine/gfx/mindmap/src/adapter/utils.ts
Normal file
65
blocksuite/affine/gfx/mindmap/src/adapter/utils.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { getShapeText } from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
buildMindMapTree,
|
||||
type MindMapTreeNode,
|
||||
} from '@blocksuite/affine-model';
|
||||
|
||||
/**
|
||||
* traverse the mindMapTree and construct the content string
|
||||
* like:
|
||||
* - Root
|
||||
* - Child 1
|
||||
* - Child 1.1
|
||||
* - Child 1.2
|
||||
* - Child 2
|
||||
* - Child 2.1
|
||||
* - Child 2.2
|
||||
* - Child 3
|
||||
* - Child 3.1
|
||||
* - Child 3.2
|
||||
* @param elementModel - the mindmap element model
|
||||
* @param elements - the elements map
|
||||
* @returns the mindmap tree text
|
||||
*/
|
||||
export function getMindMapTreeText(
|
||||
elementModel: Record<string, unknown>,
|
||||
elements: Record<string, Record<string, unknown>>,
|
||||
options: {
|
||||
prefix: string;
|
||||
repeat: number;
|
||||
} = {
|
||||
prefix: ' ',
|
||||
repeat: 2,
|
||||
}
|
||||
): string {
|
||||
let mindMapContent = '';
|
||||
if (elementModel.type !== 'mindmap') {
|
||||
return mindMapContent;
|
||||
}
|
||||
|
||||
const mindMapTree = buildMindMapTree(elementModel);
|
||||
if (!mindMapTree) {
|
||||
return mindMapContent;
|
||||
}
|
||||
|
||||
let layer = 0;
|
||||
const traverseMindMapTree = (
|
||||
node: MindMapTreeNode,
|
||||
prefix: string,
|
||||
repeat: number
|
||||
) => {
|
||||
const shapeElement = elements[node.id as string];
|
||||
const shapeText = getShapeText(shapeElement);
|
||||
if (shapeElement) {
|
||||
mindMapContent += `${prefix.repeat(layer * repeat)}- ${shapeText}\n`;
|
||||
}
|
||||
node.children.forEach(child => {
|
||||
layer++;
|
||||
traverseMindMapTree(child, prefix, repeat);
|
||||
layer--;
|
||||
});
|
||||
};
|
||||
traverseMindMapTree(mindMapTree, options.prefix, options.repeat);
|
||||
|
||||
return mindMapContent;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './adapter';
|
||||
export * from './element-renderer';
|
||||
export * from './element-transform';
|
||||
export * from './indicator-overlay';
|
||||
|
||||
3
blocksuite/affine/gfx/shape/src/adapter/index.ts
Normal file
3
blocksuite/affine/gfx/shape/src/adapter/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './markdown';
|
||||
export * from './plain-text';
|
||||
export * from './utils';
|
||||
46
blocksuite/affine/gfx/shape/src/adapter/markdown/index.ts
Normal file
46
blocksuite/affine/gfx/shape/src/adapter/markdown/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ElementToMarkdownAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
import type { MindMapTreeNode } from '@blocksuite/affine-model';
|
||||
|
||||
import { getShapeText, getShapeType } from '../utils';
|
||||
|
||||
export const shapeToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||
name: 'shape',
|
||||
match: elementModel => elementModel.type === 'shape',
|
||||
toAST: (elementModel, context) => {
|
||||
let content = '';
|
||||
const { walkerContext } = context;
|
||||
const mindMapNodeMaps = walkerContext.getGlobalContext(
|
||||
'surface:mindMap:nodeMapArray'
|
||||
) as Array<Map<string, MindMapTreeNode>>;
|
||||
if (mindMapNodeMaps && mindMapNodeMaps.length > 0) {
|
||||
// Check if the elementModel is a mindMap node
|
||||
// If it is, we should return { content: '' } directly
|
||||
// And get the content when we handle the whole mindMap
|
||||
const isMindMapNode = mindMapNodeMaps.some(nodeMap =>
|
||||
nodeMap.has(elementModel.id as string)
|
||||
);
|
||||
if (isMindMapNode) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If it is not, we should return the text and shapeType
|
||||
const text = getShapeText(elementModel);
|
||||
const type = getShapeType(elementModel);
|
||||
if (!text && !type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const shapeType = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
content = `${shapeType}, with text label "${text}"`;
|
||||
return {
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: content,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
35
blocksuite/affine/gfx/shape/src/adapter/plain-text/index.ts
Normal file
35
blocksuite/affine/gfx/shape/src/adapter/plain-text/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ElementToPlainTextAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
import type { MindMapTreeNode } from '@blocksuite/affine-model';
|
||||
|
||||
import { getShapeText, getShapeType } from '../utils';
|
||||
|
||||
export const shapeToPlainTextAdapterMatcher =
|
||||
ElementToPlainTextAdapterExtension({
|
||||
name: 'shape',
|
||||
match: elementModel => elementModel.type === 'shape',
|
||||
toAST: (elementModel, context) => {
|
||||
let content = '';
|
||||
const { walkerContext } = context;
|
||||
const mindMapNodeMaps = walkerContext.getGlobalContext(
|
||||
'surface:mindMap:nodeMapArray'
|
||||
) as Array<Map<string, MindMapTreeNode>>;
|
||||
if (mindMapNodeMaps && mindMapNodeMaps.length > 0) {
|
||||
// Check if the elementModel is a mindMap node
|
||||
// If it is, we should return { content: '' } directly
|
||||
// And get the content when we handle the whole mindMap
|
||||
const isMindMapNode = mindMapNodeMaps.some(nodeMap =>
|
||||
nodeMap.has(elementModel.id as string)
|
||||
);
|
||||
if (isMindMapNode) {
|
||||
return { content };
|
||||
}
|
||||
}
|
||||
|
||||
// If it is not, we should return the text and shapeType
|
||||
const text = getShapeText(elementModel);
|
||||
const type = getShapeType(elementModel);
|
||||
const shapeType = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
content = `${shapeType}, with text label "${text}"`;
|
||||
return { content };
|
||||
},
|
||||
});
|
||||
36
blocksuite/affine/gfx/shape/src/adapter/utils.ts
Normal file
36
blocksuite/affine/gfx/shape/src/adapter/utils.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
|
||||
export function getShapeType(elementModel: Record<string, unknown>): string {
|
||||
let shapeType = '';
|
||||
if (elementModel.type !== 'shape') {
|
||||
return shapeType;
|
||||
}
|
||||
|
||||
if (
|
||||
'shapeType' in elementModel &&
|
||||
typeof elementModel.shapeType === 'string'
|
||||
) {
|
||||
shapeType = elementModel.shapeType;
|
||||
}
|
||||
return shapeType;
|
||||
}
|
||||
|
||||
export function getShapeText(elementModel: Record<string, unknown>): string {
|
||||
let text = '';
|
||||
if (elementModel.type !== 'shape') {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (
|
||||
'text' in elementModel &&
|
||||
typeof elementModel.text === 'object' &&
|
||||
elementModel.text
|
||||
) {
|
||||
let delta: DeltaInsert[] = [];
|
||||
if ('delta' in elementModel.text) {
|
||||
delta = elementModel.text.delta as DeltaInsert[];
|
||||
}
|
||||
text = delta.map(d => d.insert).join('');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './adapter';
|
||||
export * from './consts';
|
||||
export * from './draggable';
|
||||
export * from './element-renderer';
|
||||
|
||||
2
blocksuite/affine/gfx/text/src/adapter/index.ts
Normal file
2
blocksuite/affine/gfx/text/src/adapter/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './markdown';
|
||||
export * from './plain-text';
|
||||
24
blocksuite/affine/gfx/text/src/adapter/markdown/index.ts
Normal file
24
blocksuite/affine/gfx/text/src/adapter/markdown/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ElementToMarkdownAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getTextElementText } from '../text';
|
||||
|
||||
export const textToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||
name: 'text',
|
||||
match: elementModel => elementModel.type === 'text',
|
||||
toAST: elementModel => {
|
||||
const content = getTextElementText(elementModel);
|
||||
if (!content) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: content,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
14
blocksuite/affine/gfx/text/src/adapter/plain-text/index.ts
Normal file
14
blocksuite/affine/gfx/text/src/adapter/plain-text/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ElementToPlainTextAdapterExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { getTextElementText } from '../text';
|
||||
|
||||
export const textToPlainTextAdapterMatcher = ElementToPlainTextAdapterExtension(
|
||||
{
|
||||
name: 'text',
|
||||
match: elementModel => elementModel.type === 'text',
|
||||
toAST: elementModel => {
|
||||
const content = getTextElementText(elementModel);
|
||||
return { content };
|
||||
},
|
||||
}
|
||||
);
|
||||
22
blocksuite/affine/gfx/text/src/adapter/text.ts
Normal file
22
blocksuite/affine/gfx/text/src/adapter/text.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { DeltaInsert } from '@blocksuite/affine/store';
|
||||
|
||||
export function getTextElementText(
|
||||
elementModel: Record<string, unknown>
|
||||
): string {
|
||||
let text = '';
|
||||
if (elementModel.type !== 'text') {
|
||||
return text;
|
||||
}
|
||||
if (
|
||||
'text' in elementModel &&
|
||||
typeof elementModel.text === 'object' &&
|
||||
elementModel.text
|
||||
) {
|
||||
let delta: DeltaInsert[] = [];
|
||||
if ('delta' in elementModel.text) {
|
||||
delta = elementModel.text.delta as DeltaInsert[];
|
||||
}
|
||||
text = delta.map(d => d.insert).join('');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './adapter';
|
||||
export * from './commands';
|
||||
export * from './edgeless-text-editor';
|
||||
export * from './element-renderer';
|
||||
|
||||
Reference in New Issue
Block a user