doouding
2025-01-20 09:25:05 +00:00
parent cb53baca89
commit 1560880abd
10 changed files with 83 additions and 160 deletions
@@ -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;
}