fix(editor): improve link popup positioning with autoUpdate (#11510)
Closes: [BS-3038](https://linear.app/affine-design/issue/BS-3038/遇到一个新bug-抽象的一p) Maybe the `mock selection widget` is needed.
This commit is contained in:
@@ -13,7 +13,13 @@ import {
|
|||||||
TextSelection,
|
TextSelection,
|
||||||
} from '@blocksuite/std';
|
} from '@blocksuite/std';
|
||||||
import type { InlineRange } from '@blocksuite/std/inline';
|
import type { InlineRange } from '@blocksuite/std/inline';
|
||||||
import { computePosition, inline, offset, shift } from '@floating-ui/dom';
|
import {
|
||||||
|
autoUpdate,
|
||||||
|
computePosition,
|
||||||
|
inline,
|
||||||
|
offset,
|
||||||
|
shift,
|
||||||
|
} from '@floating-ui/dom';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { property, query } from 'lit/decorators.js';
|
import { property, query } from 'lit/decorators.js';
|
||||||
import { choose } from 'lit/directives/choose.js';
|
import { choose } from 'lit/directives/choose.js';
|
||||||
@@ -184,6 +190,36 @@ export class LinkPopup extends WithDisposable(ShadowlessElement) {
|
|||||||
this.confirmButton.requestUpdate();
|
this.confirmButton.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateMockSelection(rects: DOMRect[]) {
|
||||||
|
if (!this.mockSelectionContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mockSelectionContainer
|
||||||
|
.querySelectorAll('div')
|
||||||
|
.forEach(e => e.remove());
|
||||||
|
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
rects.forEach(domRect => {
|
||||||
|
const mockSelection = document.createElement('div');
|
||||||
|
mockSelection.classList.add('mock-selection');
|
||||||
|
|
||||||
|
// Get the container's bounding rect to account for its position
|
||||||
|
const containerRect = this.mockSelectionContainer.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Adjust the position by subtracting the container's offset
|
||||||
|
mockSelection.style.left = `${domRect.left - containerRect.left}px`;
|
||||||
|
mockSelection.style.top = `${domRect.top - containerRect.top}px`;
|
||||||
|
mockSelection.style.width = `${domRect.width}px`;
|
||||||
|
mockSelection.style.height = `${domRect.height}px`;
|
||||||
|
|
||||||
|
fragment.append(mockSelection);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mockSelectionContainer.append(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
@@ -213,6 +249,40 @@ export class LinkPopup extends WithDisposable(ShadowlessElement) {
|
|||||||
this.std.host.selection.setGroup('note', []);
|
this.std.host.selection.setGroup('note', []);
|
||||||
this.abortController.abort();
|
this.abortController.abort();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const range = this.inlineEditor.toDomRange(this.targetInlineRange);
|
||||||
|
if (!range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const visualElement = {
|
||||||
|
getBoundingClientRect: () => range.getBoundingClientRect(),
|
||||||
|
getClientRects: () => range.getClientRects(),
|
||||||
|
};
|
||||||
|
const popover = this.popoverContainer;
|
||||||
|
|
||||||
|
this.disposables.add(
|
||||||
|
autoUpdate(visualElement, popover, () => {
|
||||||
|
computePosition(visualElement, popover, {
|
||||||
|
middleware: [
|
||||||
|
offset(10),
|
||||||
|
inline(),
|
||||||
|
shift({
|
||||||
|
padding: 6,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.then(({ x, y }) => {
|
||||||
|
popover.style.left = `${x}px`;
|
||||||
|
popover.style.top = `${y}px`;
|
||||||
|
|
||||||
|
this.updateMockSelection(
|
||||||
|
Array.from(visualElement.getClientRects())
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
@@ -230,55 +300,6 @@ export class LinkPopup extends WithDisposable(ShadowlessElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
override updated() {
|
|
||||||
const range = this.inlineEditor.toDomRange(this.targetInlineRange);
|
|
||||||
if (!range) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const domRects = range.getClientRects();
|
|
||||||
|
|
||||||
Object.values(domRects).forEach(domRect => {
|
|
||||||
if (!this.mockSelectionContainer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const mockSelection = document.createElement('div');
|
|
||||||
mockSelection.classList.add('mock-selection');
|
|
||||||
|
|
||||||
// Get the container's bounding rect to account for its position
|
|
||||||
const containerRect = this.mockSelectionContainer.getBoundingClientRect();
|
|
||||||
|
|
||||||
// Adjust the position by subtracting the container's offset
|
|
||||||
mockSelection.style.left = `${domRect.left - containerRect.left}px`;
|
|
||||||
mockSelection.style.top = `${domRect.top - containerRect.top}px`;
|
|
||||||
mockSelection.style.width = `${domRect.width}px`;
|
|
||||||
mockSelection.style.height = `${domRect.height}px`;
|
|
||||||
|
|
||||||
this.mockSelectionContainer.append(mockSelection);
|
|
||||||
});
|
|
||||||
|
|
||||||
const visualElement = {
|
|
||||||
getBoundingClientRect: () => range.getBoundingClientRect(),
|
|
||||||
getClientRects: () => range.getClientRects(),
|
|
||||||
};
|
|
||||||
const popover = this.popoverContainer;
|
|
||||||
|
|
||||||
computePosition(visualElement, popover, {
|
|
||||||
middleware: [
|
|
||||||
offset(10),
|
|
||||||
inline(),
|
|
||||||
shift({
|
|
||||||
padding: 6,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.then(({ x, y }) => {
|
|
||||||
popover.style.left = `${x}px`;
|
|
||||||
popover.style.top = `${y}px`;
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor abortController!: AbortController;
|
accessor abortController!: AbortController;
|
||||||
|
|
||||||
|
|||||||
@@ -116,8 +116,7 @@ export const linkPopupStyle = css`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-mask,
|
.overlay-root {
|
||||||
.mock-selection-container {
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user