fix(editor): wrong position of remote selection and at menu in edgeless (#10137)

Close [BS-2552](https://linear.app/affine-design/issue/BS-2552/menu-loading-时滚动,定位错误), [BS-2490](https://linear.app/affine-design/issue/BS-2490/note-block-的menu的输入阴影错位), [BS-2300](https://linear.app/affine-design/issue/BS-2300/at-menu的输入阴影在暗黑模式看不见)

### What  Changes
- fix the position of remote selection mask and @ menu input mask in edgeless
- fix the position of @ menu is no updated during edgeless viewport change
- update @ menu mask color in dark mode

### Before
https://github.com/user-attachments/assets/f44f618e-a791-497a-9f53-74824fe48dea

### After
https://github.com/user-attachments/assets/5d87b999-deae-4435-9b8b-4cdf55393395
This commit is contained in:
L-Sun
2025-02-13 01:56:00 +00:00
parent f0a99851aa
commit 9a17422d36
7 changed files with 160 additions and 114 deletions

View File

@@ -1,6 +1,4 @@
import type { BlockComponent, EditorHost } from '@blocksuite/block-std';
import { isInsidePageEditor } from './checker.js';
import type { EditorHost } from '@blocksuite/block-std';
/**
* Get editor viewport element.
@@ -13,22 +11,9 @@ import { isInsidePageEditor } from './checker.js';
* });
* ```
*/
export function getViewportElement(editorHost: EditorHost): HTMLElement | null {
if (!isInsidePageEditor(editorHost)) return null;
const doc = editorHost.doc;
if (!doc.root) {
console.error('Failed to get root doc');
return null;
}
const rootComponent = editorHost.view.getBlock(doc.root.id);
if (
!rootComponent ||
rootComponent.closest('affine-page-root') !== rootComponent
) {
console.error('Failed to get viewport element!');
return null;
}
return (rootComponent as BlockComponent & { viewportElement: HTMLElement })
.viewportElement;
export function getViewportElement(editorHost: EditorHost) {
return (
editorHost.closest<HTMLElement>('.affine-page-viewport') ??
editorHost.closest<HTMLElement>('.affine-edgeless-viewport')
);
}

View File

@@ -14,6 +14,7 @@ import {
TextSelection,
WidgetComponent,
} from '@blocksuite/block-std';
import { GfxController } from '@blocksuite/block-std/gfx';
import { throttle } from '@blocksuite/global/utils';
import type { BaseSelection, UserInfo } from '@blocksuite/store';
import { computed, effect } from '@preact/signals-core';
@@ -268,24 +269,28 @@ export class AffineDocRemoteSelectionWidget extends WidgetComponent {
this._abortController.abort();
}
private readonly _updateSelections = throttle(
(selections: typeof this._remoteSelections.value) => {
const remoteUsers = new Set<number>();
this._selections = selections.flatMap(({ selections, id, user }) => {
if (remoteUsers.has(id)) {
return [];
} else {
remoteUsers.add(id);
}
private readonly _updateSelections = (
selections: typeof this._remoteSelections.value
) => {
const remoteUsers = new Set<number>();
this._selections = selections.flatMap(({ selections, id, user }) => {
if (remoteUsers.has(id)) {
return [];
} else {
remoteUsers.add(id);
}
return {
id,
selections,
rects: this._getSelectionRect(selections),
user,
};
});
},
return {
id,
selections,
rects: this._getSelectionRect(selections),
user,
};
});
};
private readonly _updateSelectionsThrottled = throttle(
this._updateSelections,
60
);
@@ -293,13 +298,22 @@ export class AffineDocRemoteSelectionWidget extends WidgetComponent {
this.disposables.add(
effect(() => {
const selections = this._remoteSelections.value;
this._updateSelections(selections);
this._updateSelectionsThrottled(selections);
})
);
this.disposables.add(
this.std.store.slots.blockUpdated.on(() => {
this._updateSelections(this._remoteSelections.peek());
this._updateSelectionsThrottled(this._remoteSelections.peek());
})
);
const gfx = this.std.getOptional(GfxController);
if (!gfx) return;
this.disposables.add(
gfx.viewport.viewportUpdated.on(() => {
const selections = this._remoteSelections.peek();
this._updateSelections(selections);
})
);
}