mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 11:36:25 +08:00
fix: drag embed block preview (#9791)
Fixes [BS-1518](https://linear.app/affine-design/issue/BS-1518/拖拽一个-embed-view-的-linked-doc,其-indicator-是错误的)
This commit is contained in:
@@ -14,6 +14,9 @@ import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
||||
import {
|
||||
DocDisplayMetaProvider,
|
||||
DocModeProvider,
|
||||
EditorSettingExtension,
|
||||
EditorSettingProvider,
|
||||
GeneralSettingSchema,
|
||||
ThemeExtensionIdentifier,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
@@ -30,7 +33,7 @@ import {
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { assertExists, Bound, getCommonBound } from '@blocksuite/global/utils';
|
||||
import { type GetBlocksOptions, type Query, Text } from '@blocksuite/store';
|
||||
import { computed } from '@preact/signals-core';
|
||||
import { computed, signal } from '@preact/signals-core';
|
||||
import { html, nothing, type PropertyValues } from 'lit';
|
||||
import { query, state } from 'lit/decorators.js';
|
||||
import { choose } from 'lit/directives/choose.js';
|
||||
@@ -114,6 +117,9 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
const nextDepth = this.depth + 1;
|
||||
const previewSpecBuilder = SpecProvider.getInstance().getSpec(name);
|
||||
const currentDisposables = this.disposables;
|
||||
const editorSetting =
|
||||
this.std.getOptional(EditorSettingProvider) ??
|
||||
signal(GeneralSettingSchema.parse({}));
|
||||
|
||||
class EmbedSyncedDocWatcher extends BlockServiceWatcher {
|
||||
static override readonly flavour = 'affine:embed-synced-doc';
|
||||
@@ -139,7 +145,10 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
}
|
||||
}
|
||||
|
||||
previewSpecBuilder.extend([EmbedSyncedDocWatcher]);
|
||||
previewSpecBuilder.extend([
|
||||
EmbedSyncedDocWatcher,
|
||||
EditorSettingExtension(editorSetting),
|
||||
]);
|
||||
|
||||
return previewSpecBuilder.value;
|
||||
};
|
||||
|
||||
@@ -223,6 +223,7 @@ export class SurfaceBlockComponent extends BlockComponent<
|
||||
|
||||
override firstUpdated() {
|
||||
this._renderer.attach(this._surfaceContainer);
|
||||
this._renderer['_render']();
|
||||
this._surfaceContainer.append(...this._renderer.stackingCanvas);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { NodePropsSchema } from '../utils/index.js';
|
||||
export const GeneralSettingSchema = z
|
||||
.object({
|
||||
edgelessScrollZoom: z.boolean().default(false),
|
||||
edgelessDisableScheduleUpdate: z.boolean().default(false),
|
||||
})
|
||||
.merge(NodePropsSchema);
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { Point } from '@blocksuite/global/utils';
|
||||
import { baseTheme } from '@toeverything/theme';
|
||||
import type { TemplateResult } from 'lit';
|
||||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
export class DragPreview extends ShadowlessElement {
|
||||
offset: Point;
|
||||
|
||||
constructor(offset?: Point) {
|
||||
super();
|
||||
this.offset = offset ?? new Point(0, 0);
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
if (this.onRemove) {
|
||||
this.onRemove();
|
||||
}
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`<style>
|
||||
affine-drag-preview {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: auto;
|
||||
font-family: ${baseTheme.fontSansFamily};
|
||||
font-size: var(--affine-font-base);
|
||||
line-height: var(--affine-line-height);
|
||||
color: var(--affine-text-primary-color);
|
||||
font-weight: 400;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform-origin: 0 0;
|
||||
opacity: 0.5;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
caret-color: transparent;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.affine-drag-preview-grabbing * {
|
||||
cursor: grabbing !important;
|
||||
}</style
|
||||
>${this.template}`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onRemove: (() => void) | null = null;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor template: TemplateResult | EditorHost | null = null;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'affine-drag-preview': DragPreview;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import { query, state } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { AFFINE_DRAG_HANDLE_WIDGET } from './consts.js';
|
||||
import { PreviewHelper } from './helpers/preview-helper.js';
|
||||
import { RectHelper } from './helpers/rect-helper.js';
|
||||
import { SelectionHelper } from './helpers/selection-helper.js';
|
||||
import { styles } from './styles.js';
|
||||
@@ -143,8 +142,6 @@ export class AffineDragHandleWidget extends WidgetComponent<RootBlockModel> {
|
||||
|
||||
pointerEventWatcher = new PointerEventWatcher(this);
|
||||
|
||||
previewHelper = new PreviewHelper(this);
|
||||
|
||||
scale = signal(1);
|
||||
|
||||
scaleInNote = computed(() => this.scale.value * this.noteScale.value);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { DragPreview } from './components/drag-preview';
|
||||
import { DropIndicator } from './components/drop-indicator';
|
||||
import { AFFINE_DRAG_HANDLE_WIDGET } from './consts';
|
||||
import { AffineDragHandleWidget } from './drag-handle';
|
||||
|
||||
export function effects() {
|
||||
customElements.define('affine-drag-preview', DragPreview);
|
||||
customElements.define('affine-drop-indicator', DropIndicator);
|
||||
customElements.define(AFFINE_DRAG_HANDLE_WIDGET, AffineDragHandleWidget);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
type BlockComponent,
|
||||
BlockStdScope,
|
||||
type DndEventState,
|
||||
} from '@blocksuite/block-std';
|
||||
import { Point } from '@blocksuite/global/utils';
|
||||
DocModeExtension,
|
||||
DocModeProvider,
|
||||
EditorSettingExtension,
|
||||
EditorSettingProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import { BlockStdScope } from '@blocksuite/block-std';
|
||||
import { type BlockViewType, type Query } from '@blocksuite/store';
|
||||
import { signal } from '@preact/signals-core';
|
||||
|
||||
import { DragPreview } from '../components/drag-preview.js';
|
||||
import type { AffineDragHandleWidget } from '../drag-handle.js';
|
||||
|
||||
export class PreviewHelper {
|
||||
private readonly _calculatePreviewOffset = (
|
||||
blocks: BlockComponent[],
|
||||
state: DndEventState
|
||||
) => {
|
||||
const { top, left } = blocks[0].getBoundingClientRect();
|
||||
const previewOffset = new Point(state.raw.x - left, state.raw.y - top);
|
||||
return previewOffset;
|
||||
};
|
||||
|
||||
private readonly _calculateQuery = (selectedIds: string[]): Query => {
|
||||
const ids: Array<{ id: string; viewType: BlockViewType }> = selectedIds.map(
|
||||
id => ({
|
||||
@@ -55,52 +47,34 @@ export class PreviewHelper {
|
||||
};
|
||||
};
|
||||
|
||||
createDragPreview = (
|
||||
blocks: BlockComponent[],
|
||||
state: DndEventState,
|
||||
dragPreviewEl?: HTMLElement,
|
||||
dragPreviewOffset?: Point
|
||||
): DragPreview => {
|
||||
let dragPreview: DragPreview;
|
||||
if (dragPreviewEl) {
|
||||
dragPreview = new DragPreview(dragPreviewOffset);
|
||||
dragPreview.append(dragPreviewEl);
|
||||
} else {
|
||||
let width = 0;
|
||||
blocks.forEach(element => {
|
||||
width = Math.max(width, element.getBoundingClientRect().width);
|
||||
});
|
||||
renderDragPreview = (blockIds: string[], container: HTMLElement): void => {
|
||||
const widget = this.widget;
|
||||
const std = widget.std;
|
||||
const docModeService = std.get(DocModeProvider);
|
||||
const editorSetting = std.get(EditorSettingProvider).peek();
|
||||
const query = this._calculateQuery(blockIds as string[]);
|
||||
const store = widget.doc.doc.getStore({ query });
|
||||
const previewSpec = SpecProvider.getInstance().getSpec('page:preview');
|
||||
const settingSignal = signal({ ...editorSetting });
|
||||
previewSpec.extend([
|
||||
DocModeExtension(docModeService),
|
||||
EditorSettingExtension(settingSignal),
|
||||
]);
|
||||
|
||||
const selectedIds = blocks.map(block => block.model.id);
|
||||
settingSignal.value = {
|
||||
...settingSignal.value,
|
||||
edgelessDisableScheduleUpdate: true,
|
||||
};
|
||||
|
||||
const query = this._calculateQuery(selectedIds);
|
||||
const previewStd = new BlockStdScope({
|
||||
store,
|
||||
extensions: previewSpec.value,
|
||||
});
|
||||
const previewTemplate = previewStd.render();
|
||||
const noteBlock = this.widget.host.querySelector('affine-note');
|
||||
|
||||
const store = this.widget.doc.doc.getStore({ query });
|
||||
|
||||
const previewSpec = SpecProvider.getInstance().getSpec('page:preview');
|
||||
const previewStd = new BlockStdScope({
|
||||
store,
|
||||
extensions: previewSpec.value,
|
||||
});
|
||||
const previewTemplate = previewStd.render();
|
||||
|
||||
const offset = this._calculatePreviewOffset(blocks, state);
|
||||
const posX = state.raw.x - offset.x;
|
||||
const posY = state.raw.y - offset.y;
|
||||
const altKey = state.raw.altKey;
|
||||
|
||||
dragPreview = new DragPreview(offset);
|
||||
dragPreview.template = previewTemplate;
|
||||
dragPreview.onRemove = () => {
|
||||
this.widget.doc.doc.clearQuery(query);
|
||||
};
|
||||
dragPreview.style.width = `${width / this.widget.scaleInNote.peek()}px`;
|
||||
dragPreview.style.transform = `translate(${posX}px, ${posY}px) scale(${this.widget.scaleInNote.peek()})`;
|
||||
|
||||
dragPreview.style.opacity = altKey ? '1' : '0.5';
|
||||
}
|
||||
this.widget.rootComponent.append(dragPreview);
|
||||
return dragPreview;
|
||||
container.style.width = `${noteBlock?.offsetWidth ?? noteBlock?.clientWidth ?? 500}px`;
|
||||
container.append(previewTemplate);
|
||||
};
|
||||
|
||||
constructor(readonly widget: AffineDragHandleWidget) {}
|
||||
|
||||
@@ -20,12 +20,11 @@ import {
|
||||
getRectByBlockComponent,
|
||||
getScrollContainer,
|
||||
matchFlavours,
|
||||
SpecProvider,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
type BlockComponent,
|
||||
BlockSelection,
|
||||
BlockStdScope,
|
||||
type BlockStdScope,
|
||||
type DragFromBlockSuite,
|
||||
type DragPayload,
|
||||
type DropPayload,
|
||||
@@ -37,6 +36,7 @@ import { Slice, type SliceSnapshot } from '@blocksuite/store';
|
||||
|
||||
import { DropIndicator } from '../components/drop-indicator.js';
|
||||
import type { AffineDragHandleWidget } from '../drag-handle.js';
|
||||
import { PreviewHelper } from '../helpers/preview-helper.js';
|
||||
import { newIdCrossDoc } from '../middleware/new-id-cross-doc.js';
|
||||
import { reorderList } from '../middleware/reorder-list';
|
||||
import { surfaceRefToEmbed } from '../middleware/surface-ref-to-embed.js';
|
||||
@@ -64,6 +64,8 @@ declare module '@blocksuite/block-std' {
|
||||
export class DragEventWatcher {
|
||||
dropIndicator: null | DropIndicator = null;
|
||||
|
||||
previewHelper = new PreviewHelper(this.widget);
|
||||
|
||||
get host() {
|
||||
return this.widget.host;
|
||||
}
|
||||
@@ -668,21 +670,10 @@ export class DragEventWatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
const query = widget.previewHelper['_calculateQuery'](
|
||||
source.data?.bsEntity?.modelIds as string[]
|
||||
this.previewHelper.renderDragPreview(
|
||||
source.data?.bsEntity?.modelIds,
|
||||
container
|
||||
);
|
||||
const store = widget.doc.doc.getStore({ query });
|
||||
const previewSpec =
|
||||
SpecProvider.getInstance().getSpec('page:preview');
|
||||
const previewStd = new BlockStdScope({
|
||||
store,
|
||||
extensions: previewSpec.value,
|
||||
});
|
||||
const previewTemplate = previewStd.render();
|
||||
const noteBlock = this.widget.host.querySelector('affine-note');
|
||||
|
||||
container.style.width = `${noteBlock?.offsetWidth ?? noteBlock?.clientWidth ?? 500}px`;
|
||||
container.append(previewTemplate);
|
||||
},
|
||||
setDragData: () => {
|
||||
const { snapshot } = this._getSnapshotFromHoveredBlocks();
|
||||
@@ -833,20 +824,10 @@ export class DragEventWatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
const query = widget.previewHelper['_calculateQuery'](
|
||||
source.data?.bsEntity?.modelIds as string[]
|
||||
this.previewHelper.renderDragPreview(
|
||||
source.data?.bsEntity?.modelIds,
|
||||
container
|
||||
);
|
||||
const store = widget.doc.doc.getStore({ query });
|
||||
const previewSpec =
|
||||
SpecProvider.getInstance().getSpec('page:preview');
|
||||
const previewStd = new BlockStdScope({
|
||||
store,
|
||||
extensions: previewSpec.value,
|
||||
});
|
||||
const previewTemplate = previewStd.render();
|
||||
|
||||
container.style.width = `${std.host.clientWidth || std.host.offsetWidth || 500}px`;
|
||||
container.append(previewTemplate);
|
||||
},
|
||||
setDragData: () => {
|
||||
const { snapshot } = this._getDraggedBlock(view);
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
import type { EdgelessPreviewer } from '@blocksuite/affine-block-surface-ref';
|
||||
import type { RootBlockModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
EditorSettingProvider,
|
||||
FontLoaderService,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
@@ -22,6 +23,7 @@ import { query, state } from 'lit/decorators.js';
|
||||
import type { EdgelessRootBlockWidgetName } from '../types.js';
|
||||
import type { EdgelessRootService } from './edgeless-root-service.js';
|
||||
import { getBackgroundGrid, isCanvasElement } from './utils/query.js';
|
||||
import { fitToScreen } from './utils/viewport.js';
|
||||
|
||||
export class EdgelessRootPreviewBlockComponent
|
||||
extends BlockComponent<
|
||||
@@ -176,9 +178,24 @@ export class EdgelessRootPreviewBlockComponent
|
||||
);
|
||||
}
|
||||
|
||||
private _initViewport() {
|
||||
const gfx = this.service.gfx;
|
||||
|
||||
fitToScreen(gfx.gfxElements, gfx.viewport, {
|
||||
smooth: false,
|
||||
});
|
||||
}
|
||||
|
||||
private get _disableScheduleUpdate() {
|
||||
const editorSetting = this.std.getOptional(EditorSettingProvider);
|
||||
|
||||
return editorSetting?.peek().edgelessDisableScheduleUpdate ?? false;
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this._initViewport();
|
||||
this.handleEvent('selectionChange', () => {
|
||||
const surface = this.host.selection.value.find(
|
||||
(sel): sel is SurfaceSelection => sel.is(SurfaceSelection)
|
||||
@@ -223,6 +240,7 @@ export class EdgelessRootPreviewBlockComponent
|
||||
return html`
|
||||
<div class="edgeless-background edgeless-container">
|
||||
<gfx-viewport
|
||||
.enableChildrenSchedule=${!this._disableScheduleUpdate}
|
||||
.viewport=${this.service.viewport}
|
||||
.getModelsInViewport=${() => {
|
||||
const blocks = this.service.gfx.grid.search(
|
||||
|
||||
@@ -98,6 +98,10 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
||||
this._hideOutsideBlock();
|
||||
};
|
||||
|
||||
if (!this.enableChildrenSchedule) {
|
||||
delete this.scheduleUpdateChildren;
|
||||
}
|
||||
|
||||
viewportUpdateCallback();
|
||||
this.disposables.add(
|
||||
this.viewport.viewportUpdated.on(() => viewportUpdateCallback())
|
||||
@@ -111,7 +115,7 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
scheduleUpdateChildren(id: string) {
|
||||
scheduleUpdateChildren? = (id: string) => {
|
||||
const { promise, resolve } = Promise.withResolvers<void>();
|
||||
|
||||
this._pendingChildrenUpdates.push({ id, resolve });
|
||||
@@ -143,7 +147,7 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
};
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor getModelsInViewport: undefined | (() => Set<GfxBlockElementModel>);
|
||||
@@ -154,6 +158,9 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
||||
@property({ type: Number })
|
||||
accessor maxConcurrentRenders: number = 2;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor enableChildrenSchedule: boolean = true;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor viewport!: Viewport;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user