fix(editor): can not undo and redo of color of edgeless blocks (#12414)

Close [BS-3507](https://linear.app/affine-design/issue/BS-3507/edgeless-text-颜色无法-undoredo)
Close [BS-3426](https://linear.app/affine-design/issue/BS-3426/frame-修改背景色后不能撤销)

This PR fixes the issue where the color change of edgeless blocks could not be undone/redone, including notes, edgeless-text, and frames. It also addresses the problem of a tiny shape being unexpectedly retained on the canvas. The key changes are:
- Removal of `transact` from the `pop` method of edgeless elements.
- Refactoring of `onPickColor` for all edgeless elements and blocks to better control the lifecycle of custom color property changes.
- Addition of the missing custom background color feature for notes.
- Addition of undo/redo color tests for notes, frames, and edgeless-text.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added undo and redo support for color changes in frames, notes, and text blocks, allowing users to revert or reapply background and text color modifications.

- **Bug Fixes**
  - Improved reliability of color picker interactions, ensuring consistent state management and transactional updates during color changes.

- **Tests**
  - Introduced new end-to-end tests to verify undo/redo functionality for color changes in frames, notes, and text blocks.

- **Refactor**
  - Streamlined color picker event handling for better maintainability and consistency across toolbars and style panels.
  - Updated style panel structure and event handling for improved interaction and state management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
L-Sun
2025-05-22 04:10:16 +00:00
parent 9ac1da9fc1
commit 573c2faf76
11 changed files with 372 additions and 113 deletions

View File

@@ -40,12 +40,12 @@ export class EdgelessNoteStylePanel extends SignalWatcher(
@property({ attribute: false })
accessor std!: BlockStdScope;
@query('.edgeless-note-style-panel')
private accessor _panel!: HTMLDivElement;
@state()
accessor tabType: 'style' | 'customColor' = 'style';
@query('div.edgeless-note-style-panel-container')
accessor container!: HTMLDivElement;
static override styles = css`
.edgeless-note-style-panel {
display: flex;
@@ -187,7 +187,32 @@ export class EdgelessNoteStylePanel extends SignalWatcher(
};
private readonly _pickColor = (e: PickColorEvent) => {
console.log(e);
switch (e.type) {
case 'pick':
{
const color = e.detail.value;
const crud = this.std.get(EdgelessCRUDIdentifier);
this.notes.forEach(note => {
crud.updateElement(note.id, {
background: color,
} satisfies Partial<NoteProps>);
});
}
break;
case 'start':
this._beforeChange();
this.notes.forEach(note => {
note.stash('background');
});
break;
case 'end':
this.std.store.transact(() => {
this.notes.forEach(note => {
note.pop('background');
});
});
break;
}
};
private readonly _selectShadow = (e: CustomEvent<NoteShadow>) => {
@@ -265,7 +290,7 @@ export class EdgelessNoteStylePanel extends SignalWatcher(
};
private _renderStylePanel() {
return html` <div class="edgeless-note-style-panel">
return html`<div class="edgeless-note-style-panel">
<div class="edgeless-note-style-section">
<div class="edgeless-note-style-section-title">Fill color</div>
<edgeless-color-panel
@@ -369,9 +394,11 @@ export class EdgelessNoteStylePanel extends SignalWatcher(
}
override firstUpdated() {
this.disposables.addFromEvent(this._panel, 'click', e => {
e.stopPropagation();
});
if (this.container) {
this.disposables.addFromEvent(this.container, 'click', e => {
e.stopPropagation();
});
}
}
override render() {
@@ -383,11 +410,18 @@ export class EdgelessNoteStylePanel extends SignalWatcher(
${PaletteIcon()}
</editor-icon-button>
`}
@toggle=${(e: CustomEvent<boolean>) => {
if (!e.detail) {
this.tabType = 'style';
}
}}
>
${choose(this.tabType, [
['style', () => this._renderStylePanel()],
['customColor', () => this._renderCustomColorPanel()],
])}
<div class="edgeless-note-style-panel-container">
${choose(this.tabType, [
['style', () => this._renderStylePanel()],
['customColor', () => this._renderCustomColorPanel()],
])}
</div>
</editor-menu-button>
`;
}