feat(editor): improve visibility of hidden content of edgeless note (#12068)

Close [BS-3066](https://linear.app/affine-design/issue/BS-3066/优化长note的展示和折叠)

- Enhanced the visibility behavior of hidden content in edgeless notes by:
  - Showing hidden content when a note is being edited, even when it's outside the viewport
  - Improving hover behavior with a delay when leaving from the bottom of the note
  - Adding proper cleanup of hover timeouts when the component is disconnected
  - Optimizing the viewport element to keep editing blocks or elements visible

## Testing
- Added new E2E test cases covering:
  - Hover behavior on selected notes
  - Content visibility during editing
  - Viewport scrolling behavior
  - Edge cases for content visibility

## Impact
This change improves the user experience when working with collapsed notes in edgeless mode by making the content more accessible and preventing accidental content hiding during editing.

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

- **Bug Fixes**
	- Improved visibility of hidden content in edgeless notes when hovering near the bottom edge or editing the note, especially after resizing or clipping.
- **New Features**
	- Enhanced hover behavior with delayed clearing based on mouse position to improve user experience.
- **Tests**
	- Added new tests verifying hidden content visibility in edgeless notes during hover and editing, simulating diverse user interactions.
- **Chores**
	- Added utilities to get and set viewport center for improved test control.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
L-Sun
2025-04-29 13:24:56 +00:00
parent 8b2a01d4cf
commit 365a0a605b
4 changed files with 187 additions and 12 deletions
@@ -31,10 +31,10 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
) {
private get _isShowCollapsedContent() {
return (
this.model.props.edgeless.collapse &&
this.gfx.selection.has(this.model.id) &&
!!this.model.props.edgeless.collapse &&
this.selected$.value &&
!this._dragging &&
(this._isResizing || this._isHover)
(this._isResizing || this._isHover || this._editing)
);
}
@@ -93,12 +93,33 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
sel => sel.type === 'surface' && sel.blockId === this.model.id
)
) {
if (this._hoverTimeout) {
clearTimeout(this._hoverTimeout);
this._hoverTimeout = null;
}
this._isHover = true;
}
}
private _leaved() {
if (this._isHover) {
private _hoverTimeout: ReturnType<typeof setTimeout> | null = null;
private _leaved(e: MouseEvent) {
if (this._hoverTimeout) {
clearTimeout(this._hoverTimeout);
this._hoverTimeout = null;
}
const rect = this.getBoundingClientRect();
const threshold = -10;
const leavedFromBottom =
e.clientY - rect.bottom > threshold &&
rect.left < e.clientX &&
e.clientX < rect.right;
if (leavedFromBottom) {
this._hoverTimeout = setTimeout(() => {
this._isHover = false;
}, 300);
} else {
this._isHover = false;
}
}
@@ -144,6 +165,14 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
this.disposables.addFromEvent(this, 'keydown', this._handleKeyDown);
}
override disconnectedCallback() {
super.disconnectedCallback();
if (this._hoverTimeout) {
clearTimeout(this._hoverTimeout);
this._hoverTimeout = null;
}
}
get edgelessSlots() {
return this.std.get(EdgelessLegacySlotIdentifier);
}