feat(editor): unify block props api (#10888)

Closes: [BS-2707](https://linear.app/affine-design/issue/BS-2707/统一使用props获取和更新block-prop)
This commit is contained in:
Saul-Mirone
2025-03-16 05:48:34 +00:00
parent 8f9e5bf0aa
commit 26285f7dcb
193 changed files with 1019 additions and 891 deletions
@@ -90,8 +90,8 @@ export const dedentParagraphCommand: Command<{
if (
matchModels(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') &&
model.collapsed
model.props.type.startsWith('h') &&
model.props.collapsed
) {
const collapsedSiblings = calculateCollapsedSiblings(model);
store.moveBlocks([model, ...collapsedSiblings], grandParent, parent, false);
@@ -96,7 +96,7 @@ export const indentParagraphCommand: Command<{
if (
nearestHeading &&
matchModels(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed
nearestHeading.props.collapsed
) {
store.updateBlock(nearestHeading, {
collapsed: false,
@@ -106,8 +106,8 @@ export const indentParagraphCommand: Command<{
if (
matchModels(model, [ParagraphBlockModel]) &&
model.type.startsWith('h') &&
model.collapsed
model.props.type.startsWith('h') &&
model.props.collapsed
) {
const collapsedSiblings = calculateCollapsedSiblings(model);
store.moveBlocks([model, ...collapsedSiblings], previousSibling);
@@ -125,7 +125,7 @@ export const indentParagraphCommand: Command<{
if (
nearestHeading &&
matchModels(nearestHeading, [ParagraphBlockModel]) &&
nearestHeading.collapsed
nearestHeading.props.collapsed
) {
store.updateBlock(nearestHeading, {
collapsed: false,
@@ -136,7 +136,7 @@ export const indentParagraphCommand: Command<{
// update collapsed state of affine list
if (
matchModels(previousSibling, [ListBlockModel]) &&
previousSibling.collapsed
previousSibling.props.collapsed
) {
store.updateBlock(previousSibling, {
collapsed: false,
@@ -37,16 +37,16 @@ export const splitParagraphCommand: Command<
// 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.props.text.yText.length < splitIndex + splitLength) return;
if (model.children.length > 0 && splitIndex > 0) {
store.captureSync();
const right = model.text.split(splitIndex, splitLength);
const right = model.props.text.split(splitIndex, splitLength);
const id = store.addBlock(
model.flavour,
{
text: right,
type: model.type,
type: model.props.type,
},
model,
0
@@ -60,12 +60,12 @@ export const splitParagraphCommand: Command<
const index = parent.children.indexOf(model);
if (index < 0) return;
store.captureSync();
const right = model.text.split(splitIndex, splitLength);
const right = model.props.text.split(splitIndex, splitLength);
const id = store.addBlock(
model.flavour,
{
text: right,
type: model.type,
type: model.props.type,
},
parent,
index + 1
@@ -67,7 +67,7 @@ export class ParagraphHeadingIcon extends SignalWatcher(
`;
override render() {
const type = this.model.type$.value;
const type = this.model.props.type$.value;
if (!type.startsWith('h')) return nothing;
const i = parseInt(type.slice(1));
@@ -148,20 +148,20 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
this.disposables.add(
effect(() => {
const type = this.model.type$.value;
if (!type.startsWith('h') && this.model.collapsed) {
this.model.collapsed = false;
const type = this.model.props.type$.value;
if (!type.startsWith('h') && this.model.props.collapsed) {
this.model.props.collapsed = false;
}
})
);
this.disposables.add(
effect(() => {
const collapsed = this.model.collapsed$.value;
const collapsed = this.model.props.collapsed$.value;
this._readonlyCollapsed = collapsed;
// reset text selection when selected block is collapsed
if (this.model.type$.value.startsWith('h') && collapsed) {
if (this.model.props.type$.value.startsWith('h') && collapsed) {
const collapsedSiblings = this.collapsedSiblings;
const textSelection = this.host.selection.find(TextSelection);
@@ -181,19 +181,19 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
// # 456
//
// we need to update collapsed state of 123 when 456 converted to text
let beforeType = this.model.type$.peek();
let beforeType = this.model.props.type$.peek();
this.disposables.add(
effect(() => {
const type = this.model.type$.value;
const type = this.model.props.type$.value;
if (beforeType !== type && !type.startsWith('h')) {
const nearestHeading = getNearestHeadingBefore(this.model);
if (
nearestHeading &&
nearestHeading.type.startsWith('h') &&
nearestHeading.collapsed &&
nearestHeading.props.type.startsWith('h') &&
nearestHeading.props.collapsed &&
!this.doc.readonly
) {
nearestHeading.collapsed = false;
nearestHeading.props.collapsed = false;
}
}
beforeType = type;
@@ -208,14 +208,14 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
}
override renderBlock(): TemplateResult<1> {
const { type$ } = this.model;
const { type$ } = this.model.props;
const collapsed = this.doc.readonly
? this._readonlyCollapsed
: this.model.collapsed;
: this.model.props.collapsed;
const collapsedSiblings = this.collapsedSiblings;
let style = html``;
if (this.model.type$.value.startsWith('h') && collapsed) {
if (this.model.props.type$.value.startsWith('h') && collapsed) {
style = html`
<style>
${collapsedSiblings.map(sibling =>
@@ -259,14 +259,14 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
[TOGGLE_BUTTON_PARENT_CLASS]: true,
})}
>
${this.model.type$.value.startsWith('h')
${this.model.props.type$.value.startsWith('h')
? html`
<affine-paragraph-heading-icon
.model=${this.model}
></affine-paragraph-heading-icon>
`
: nothing}
${this.model.type$.value.startsWith('h') &&
${this.model.props.type$.value.startsWith('h') &&
collapsedSiblings.length > 0
? html`
<blocksuite-toggle-button
@@ -285,7 +285,7 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
`
: nothing}
<rich-text
.yText=${this.model.text.yText}
.yText=${this.model.props.text.yText}
.inlineEventSource=${this.topContenteditableElement ?? nothing}
.undoManager=${this.doc.history}
.attributesSchema=${this.attributesSchema}
@@ -53,7 +53,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
// When deleting at line start of a paragraph block,
// firstly switch it to normal text, then delete this empty block.
if (model.type !== 'text') {
if (model.props.type !== 'text') {
// Try to switch to normal text
store.captureSync();
store.updateBlock(model, { type: 'text' });
@@ -91,7 +91,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
if (!inlineRange || !inlineEditor) return;
const raw = ctx.get('keyboardState').raw;
raw.preventDefault();
if (model.type === 'quote') {
if (model.props.type === 'quote') {
store.captureSync();
inlineEditor.insertText(inlineRange, '\n');
inlineEditor.setInlineRange({
@@ -123,10 +123,10 @@ export const ParagraphKeymapExtension = KeymapExtension(
if (!inlineRange || !inlineEditor) return;
const raw = ctx.get('keyboardState').raw;
const isEnd = model.text.length === inlineRange.index;
const isEnd = model.props.text.length === inlineRange.index;
if (model.type === 'quote') {
const textStr = model.text.toString();
if (model.props.type === 'quote') {
const textStr = model.props.text.toString();
/**
* If quote block ends with two blank lines, split the block
@@ -145,7 +145,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
if (isEnd && endWithTwoBlankLines) {
raw.preventDefault();
store.captureSync();
model.text.delete(inlineRange.index - 1, 1);
model.props.text.delete(inlineRange.index - 1, 1);
std.command.chain().pipe(addParagraphCommand).run();
return true;
}
@@ -158,17 +158,17 @@ export const ParagraphKeymapExtension = KeymapExtension(
return true;
}
if (model.type.startsWith('h') && model.collapsed) {
if (model.props.type.startsWith('h') && model.props.collapsed) {
const parent = store.getParent(model);
if (!parent) return true;
const index = parent.children.indexOf(model);
if (index === -1) return true;
const collapsedSiblings = calculateCollapsedSiblings(model);
const rightText = model.text.split(inlineRange.index);
const rightText = model.props.text.split(inlineRange.index);
const newId = store.addBlock(
model.flavour,
{ type: model.type, text: rightText },
{ type: model.props.type, text: rightText },
parent,
index + collapsedSiblings.length + 1
);
@@ -8,7 +8,7 @@ export class ParagraphBlockService extends BlockService {
static override readonly flavour = ParagraphBlockSchema.model.flavour;
placeholderGenerator: (model: ParagraphBlockModel) => string = model => {
if (model.type === 'text') {
if (model.props.type === 'text') {
return "Type '/' for commands";
}
@@ -21,6 +21,6 @@ export class ParagraphBlockService extends BlockService {
h6: 'Heading 6',
quote: '',
};
return placeholders[model.type];
return placeholders[model.props.type];
};
}
@@ -32,7 +32,7 @@ export function forwardDelete(std: BlockStdScope) {
matchModels(model.parent, [CalloutBlockModel])
)
return;
const isEnd = isCollapsed && text.from.index === model.text.length;
const isEnd = isCollapsed && text.from.index === model.props.text.length;
if (!isEnd) return;
const parent = store.getParent(model);
if (!parent) return;
@@ -57,7 +57,7 @@ export function forwardDelete(std: BlockStdScope) {
}
if (matchModels(nextSibling, [ParagraphBlockModel, ListBlockModel])) {
model.text.join(nextSibling.text);
model.props.text.join(nextSibling.props.text);
if (nextSibling.children) {
const parent = store.getParent(nextSibling);
if (!parent) return false;
@@ -70,7 +70,7 @@ export function forwardDelete(std: BlockStdScope) {
const nextBlock = getNextContentBlock(host, model);
if (nextBlock?.text) {
model.text.join(nextBlock.text);
model.props.text.join(nextBlock.text);
if (nextBlock.children) {
const parent = store.getParent(nextBlock);
if (!parent) return false;
@@ -72,8 +72,8 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
)
return false;
const lengthBeforeJoin = prevBlock.text?.length ?? 0;
prevBlock.text.join(model.text as Text);
const lengthBeforeJoin = prevBlock.props.text?.length ?? 0;
prevBlock.props.text.join(model.text as Text);
doc.deleteBlock(model, {
bringChildrenTo: parent,
});
@@ -138,7 +138,7 @@ function handleNoPreviousSibling(editorHost: EditorHost, model: ExtendedModel) {
}
const rootModel = model.doc.root as RootBlockModel;
const title = rootModel.title;
const title = rootModel.props.title;
doc.captureSync();
let textLength = 0;