Skip to content

Commit

Permalink
refactor: goodbye legacy rich text operation (#7988)
Browse files Browse the repository at this point in the history
  • Loading branch information
Saul-Mirone authored Aug 15, 2024
1 parent 1b2b60c commit b8c2ef5
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
handleUnindent,
} from '../rich-text-operations.js';
import { bracketPairs } from './bracket-pairs.js';
import { hardEnter } from './legacy.js';

// FIXME: use selection manager to set selection
export const bindContainerHotkey = (block: BlockComponent) => {
Expand Down Expand Up @@ -123,15 +122,13 @@ export const bindContainerHotkey = (block: BlockComponent) => {

block.doc.captureSync();

_preventDefault(ctx);

if (tryConvertBlock(block, inlineEditor)) {
_preventDefault(ctx);
return true;
}

const state = ctx.get('keyboardState');
hardEnter(editorHost, model, inlineRange, state.raw);

return true;
return false;
},
'Mod-Enter': ctx => {
if (!block.selected?.is('text')) return;
Expand All @@ -142,8 +139,6 @@ export const bindContainerHotkey = (block: BlockComponent) => {
if (!inlineRange) return;

_preventDefault(ctx);
const state = ctx.get('keyboardState');
hardEnter(editorHost, model, inlineRange, state.raw, true);

return true;
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './container.js';
export * from './legacy.js';
28 changes: 0 additions & 28 deletions packages/blocks/src/_common/components/rich-text/keymap/legacy.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import type {
ListBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import type { ListBlockModel } from '@blocksuite/affine-model';
import type { EditorHost } from '@blocksuite/block-std';
import type { BlockModel } from '@blocksuite/store';

import { focusTextModel } from '@blocksuite/affine-components/rich-text';
import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { type BlockModel, Text } from '@blocksuite/store';

import type { ExtendedModel } from '../../types.js';

Expand All @@ -26,113 +23,6 @@ function supportsChildren(model: BlockModel): boolean {
return true;
}

export function handleBlockEndEnter(
editorHost: EditorHost,
model: ExtendedModel
) {
const doc = model.doc;
const parent = doc.getParent(model);
if (!parent) {
return;
}

const getProps = (): [
'affine:paragraph',
BlockSuite.ModelProps<ParagraphBlockModel>,
] => {
return ['affine:paragraph', { type: 'text' }];
};
const [flavour, blockProps] = getProps();

const index = parent.children.indexOf(model);
if (index === -1) {
return;
}
// make adding text block by enter a standalone operation
doc.captureSync();

let id: string;

if (model.children.length > 0) {
// before:
// aaa|
// bbb
//
// after:
// aaa
// |
// bbb
id = doc.addBlock(flavour, blockProps, model, 0);
} else {
// before:
// aaa|
//
// after:
// aaa
// |
id = doc.addBlock(flavour, blockProps, parent, index + 1);
}

if (model.text) {
focusTextModel(editorHost.std, id);
}
}

export function handleBlockSplit(
editorHost: EditorHost,
model: ExtendedModel,
splitIndex: number,
splitLength: number
) {
if (!(model.text instanceof Text)) return;

// On press enter, it may convert symbols from yjs ContentString
// to yjs ContentFormat. Once it happens, the converted symbol will
// be deleted and not counted as model.text.yText.length.
// Example: "`a`[enter]" -> yText[<ContentFormat: Code>, "a", <ContentFormat: Code>]
// In this case, we should not split the block.
if (model.text.yText.length < splitIndex + splitLength) return;

const doc = model.doc;

const parent = doc.getParent(model);
if (!parent) return;
const modelIndex = parent.children.indexOf(model);
if (modelIndex === -1) return;

doc.captureSync();
const right = model.text.split(splitIndex, splitLength);

if (model.children.length > 0 && splitIndex > 0) {
const id = doc.addBlock(
model.flavour as BlockSuite.Flavour,
{
text: right,
type: model.type,
},
model,
0
);
return focusTextModel(editorHost.std, id);
} else {
const id = doc.addBlock(
model.flavour as BlockSuite.Flavour,
{
text: right,
type: model.type,
},
parent,
modelIndex + 1
);
const newModel = doc.getBlock(id)?.model;
if (!newModel) {
return;
}
doc.moveBlocks(model.children, newModel);
return focusTextModel(editorHost.std, id);
}
}

/**
* @example
* before unindent:
Expand Down
64 changes: 64 additions & 0 deletions packages/blocks/src/paragraph-block/commands/add-paragraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Command } from '@blocksuite/block-std';

import { focusTextModel } from '@blocksuite/affine-components/rich-text';

/**
* Add a paragraph next to the current block.
*/
export const addParagraphCommand: Command<
never,
'paragraphConvertedId',
{
blockId?: string;
}
> = (ctx, next) => {
const { std } = ctx;
const { doc, selection } = std;
doc.captureSync();

let blockId = ctx.blockId;
if (!blockId) {
const text = selection.find('text');
blockId = text?.blockId;
}
if (!blockId) return;

const model = doc.getBlock(blockId)?.model;
if (!model) return;

let id: string;
if (model.children.length > 0) {
// before:
// aaa|
// bbb
//
// after:
// aaa
// |
// bbb
id = doc.addBlock('affine:paragraph', {}, model, 0);
} else {
const parent = doc.getParent(model);
if (!parent) return;
const index = parent.children.indexOf(model);
if (index < 0) return;
// before:
// aaa|
//
// after:
// aaa
// |
id = doc.addBlock('affine:paragraph', {}, parent, index + 1);
}

focusTextModel(std, id);
return next({ paragraphConvertedId: id });
};

declare global {
namespace BlockSuite {
interface Commands {
addParagraph: typeof addParagraphCommand;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { focusTextModel } from '@blocksuite/affine-components/rich-text';

import { getLastNoteBlock } from '../../_common/edgeless/note/get-last-note.js';

/**
* Append a paragraph block at the end of the whole page.
*/
export const appendParagraphCommand: Command<
never,
never,
Expand Down
12 changes: 12 additions & 0 deletions packages/blocks/src/paragraph-block/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import type { BlockCommands } from '@blocksuite/block-std';

import { addParagraphCommand } from './add-paragraph.js';
import { appendParagraphCommand } from './append-paragraph.js';
import { splitParagraphCommand } from './split-paragraph.js';

export const commands: BlockCommands = {
appendParagraph: appendParagraphCommand,
splitParagraph: splitParagraphCommand,
addParagraph: addParagraphCommand,
};

declare global {
namespace BlockSuite {
interface CommandContext {
paragraphConvertedId?: string;
}
}
}
88 changes: 88 additions & 0 deletions packages/blocks/src/paragraph-block/commands/split-paragraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { Command } from '@blocksuite/block-std';

import {
focusTextModel,
getInlineEditorByModel,
} from '@blocksuite/affine-components/rich-text';
import { matchFlavours } from '@blocksuite/affine-shared/utils';

export const splitParagraphCommand: Command<
never,
'paragraphConvertedId',
{
blockId?: string;
}
> = (ctx, next) => {
const { std } = ctx;
const { doc, host, selection } = std;
let blockId = ctx.blockId;
if (!blockId) {
const text = selection.find('text');
blockId = text?.blockId;
}
if (!blockId) return;

const model = doc.getBlock(blockId)?.model;
if (!model || !matchFlavours(model, ['affine:paragraph'])) return;

const inlineEditor = getInlineEditorByModel(host, model);
const range = inlineEditor?.getInlineRange();
if (!range) return;

const splitIndex = range.index;
const splitLength = range.length;
// On press enter, it may convert symbols from yjs ContentString
// to yjs ContentFormat. Once it happens, the converted symbol will
// be deleted and not counted as model.text.yText.length.
// Example: "`a`[enter]" -> yText[<ContentFormat: Code>, "a", <ContentFormat: Code>]
// In this case, we should not split the block.
if (model.text.yText.length < splitIndex + splitLength) return;

if (model.children.length > 0 && splitIndex > 0) {
doc.captureSync();
const right = model.text.split(splitIndex, splitLength);
const id = doc.addBlock(
model.flavour as BlockSuite.Flavour,
{
text: right,
type: model.type,
},
model,
0
);
focusTextModel(std, id);
return next({ paragraphConvertedId: id });
}

const parent = doc.getParent(model);
if (!parent) return;
const index = parent.children.indexOf(model);
if (index < 0) return;
doc.captureSync();
const right = model.text.split(splitIndex, splitLength);
const id = doc.addBlock(
model.flavour,
{
text: right,
type: model.type,
},
parent,
index + 1
);
const newModel = doc.getBlock(id)?.model;
if (newModel) {
doc.moveBlocks(model.children, newModel);
} else {
console.error('Failed to find the new model split from the paragraph');
}
focusTextModel(std, id);
return next({ paragraphConvertedId: id });
};

declare global {
namespace BlockSuite {
interface Commands {
splitParagraph: typeof splitParagraphCommand;
}
}
}
Loading

0 comments on commit b8c2ef5

Please sign in to comment.