fix(editor): dragging area gets stuck when hovering some embed blocks (#11223)

Close [BS-2778](https://linear.app/affine-design/issue/BS-2778/[bug]-选区框选-html-block-卡顿)
This commit is contained in:
donteatfriedrice
2025-03-27 03:59:26 +00:00
parent b00584c4cc
commit e763061bd6
6 changed files with 43 additions and 123 deletions
@@ -13,7 +13,7 @@ import { findAncestorModel } from '@blocksuite/affine-shared/utils';
import type { BlockService } from '@blocksuite/block-std';
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
import type { BlockModel } from '@blocksuite/store';
import { computed, type ReadonlySignal } from '@preact/signals-core';
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
import type { TemplateResult } from 'lit';
import { html } from 'lit';
import { query } from 'lit/decorators.js';
@@ -31,6 +31,17 @@ export class EmbedBlockComponent<
})
);
readonly isDraggingOnHost$ = signal(false);
readonly isResizing$ = signal(false);
// show overlay to prevent the iframe from capturing pointer events
// when the block is dragging, resizing, or not selected
readonly showOverlay$ = computed(
() =>
this.isDraggingOnHost$.value ||
this.isResizing$.value ||
!this.selected$.value
);
private _fetchAbortController = new AbortController();
_cardStyle: EmbedCardStyle = 'horizontal';
@@ -114,6 +125,24 @@ export class EmbedBlockComponent<
this._fetchAbortController = new AbortController();
this.contentEditable = 'false';
// subscribe the editor host global dragging event
// to show the overlay for the dragging area or other pointer events
this.handleEvent(
'dragStart',
() => {
this.isDraggingOnHost$.value = true;
},
{ global: true }
);
this.handleEvent(
'dragEnd',
() => {
this.isDraggingOnHost$.value = false;
},
{ global: true }
);
}
override disconnectedCallback(): void {
@@ -24,12 +24,6 @@ export function toEdgelessEmbedBlock<
return class extends toGfxBlockComponent(block) {
override selectedStyle$ = null;
_isDragging = false;
_isResizing = false;
_showOverlay = false;
override [blockComponentSymbol] = true;
override blockDraggable = false;
@@ -55,16 +49,13 @@ export function toEdgelessEmbedBlock<
this._disposables.add(
this.edgelessSlots.elementResizeStart.subscribe(() => {
this._isResizing = true;
this._showOverlay = true;
this.isResizing$.value = true;
})
);
this._disposables.add(
this.edgelessSlots.elementResizeEnd.subscribe(() => {
this._isResizing = false;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
this.isResizing$.value = false;
})
);
}
@@ -5,7 +5,6 @@ import type {
} from '@blocksuite/affine-model';
import { BlockSelection } from '@blocksuite/block-std';
import { html, nothing } from 'lit';
import { state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
@@ -17,10 +16,6 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
override _cardStyle: (typeof EmbedFigmaStyles)[number] = 'figma';
protected _isDragging = false;
protected _isResizing = false;
open = () => {
let link = this.model.props.url;
if (!link.match(/^[a-zA-Z]+:\/\//)) {
@@ -68,25 +63,6 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
}
})
);
// this is required to prevent iframe from capturing pointer events
this.disposables.add(
this.selected$.subscribe(selected => {
this._showOverlay = this._isResizing || this._isDragging || !selected;
})
);
// this is required to prevent iframe from capturing pointer events
this.handleEvent('dragStart', () => {
this._isDragging = true;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
this.handleEvent('dragEnd', () => {
this._isDragging = false;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
}
override renderBlock() {
@@ -115,10 +91,11 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
loading="lazy"
></iframe>
<!-- overlay to prevent the iframe from capturing pointer events -->
<div
class=${classMap({
'affine-embed-figma-iframe-overlay': true,
hide: !this._showOverlay,
hide: !this.showOverlay$.value,
})}
></div>
</div>
@@ -150,7 +127,4 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
`
);
}
@state()
protected accessor _showOverlay = true;
}
@@ -1,7 +1,7 @@
import type { EmbedHtmlModel, EmbedHtmlStyles } from '@blocksuite/affine-model';
import { BlockSelection } from '@blocksuite/block-std';
import { html } from 'lit';
import { query, state } from 'lit/decorators.js';
import { query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
@@ -13,10 +13,6 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
override _cardStyle: (typeof EmbedHtmlStyles)[number] = 'html';
protected _isDragging = false;
protected _isResizing = false;
close = () => {
document.exitFullscreen().catch(console.error);
};
@@ -50,25 +46,6 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
override connectedCallback() {
super.connectedCallback();
this._cardStyle = this.model.props.style;
// this is required to prevent iframe from capturing pointer events
this.disposables.add(
this.selected$.subscribe(selected => {
this._showOverlay = this._isResizing || this._isDragging || !selected;
})
);
// this is required to prevent iframe from capturing pointer events
this.handleEvent('dragStart', () => {
this._isDragging = true;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
this.handleEvent('dragEnd', () => {
this._isDragging = false;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
}
override renderBlock(): unknown {
@@ -112,10 +89,11 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
></embed-html-fullscreen-toolbar>
</div>
<!-- overlay to prevent the iframe from capturing pointer events -->
<div
class=${classMap({
'affine-embed-html-iframe-overlay': true,
hide: !this._showOverlay,
hide: !this.showOverlay$.value,
})}
></div>
</div>
@@ -131,9 +109,6 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
});
}
@state()
protected accessor _showOverlay = true;
@query('.embed-html-block-iframe-wrapper')
accessor iframeWrapper!: HTMLDivElement;
}
@@ -3,7 +3,7 @@ import type { EmbedLoomModel, EmbedLoomStyles } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { BlockSelection } from '@blocksuite/block-std';
import { html } from 'lit';
import { property, state } from 'lit/decorators.js';
import { property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
@@ -22,10 +22,6 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
override _cardStyle: (typeof EmbedLoomStyles)[number] = 'video';
protected _isDragging = false;
protected _isResizing = false;
open = () => {
let link = this.model.props.url;
if (!link.match(/^[a-zA-Z]+:\/\//)) {
@@ -89,25 +85,6 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
}
})
);
// this is required to prevent iframe from capturing pointer events
this.disposables.add(
this.selected$.subscribe(selected => {
this._showOverlay = this._isResizing || this._isDragging || !selected;
})
);
// this is required to prevent iframe from capturing pointer events
this.handleEvent('dragStart', () => {
this._isDragging = true;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
this.handleEvent('dragEnd', () => {
this._isDragging = false;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
}
override renderBlock() {
@@ -152,10 +129,11 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
loading="lazy"
></iframe>
<!-- overlay to prevent the iframe from capturing pointer events -->
<div
class=${classMap({
'affine-embed-loom-video-iframe-overlay': true,
hide: !this._showOverlay,
hide: !this.showOverlay$.value,
})}
></div>
</div>
@@ -188,9 +166,6 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
);
}
@state()
protected accessor _showOverlay = true;
@property({ attribute: false })
accessor loading = false;
}
@@ -25,10 +25,6 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
override _cardStyle: (typeof EmbedYoutubeStyles)[number] = 'video';
protected _isDragging = false;
protected _isResizing = false;
open = () => {
let link = this.model.props.url;
if (!link.match(/^[a-zA-Z]+:\/\//)) {
@@ -93,25 +89,6 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
})
);
// this is required to prevent iframe from capturing pointer events
this.disposables.add(
this.selected$.subscribe(selected => {
this._showOverlay = this._isResizing || this._isDragging || !selected;
})
);
// this is required to prevent iframe from capturing pointer events
this.handleEvent('dragStart', () => {
this._isDragging = true;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
this.handleEvent('dragEnd', () => {
this._isDragging = false;
this._showOverlay =
this._isResizing || this._isDragging || !this.selected$.peek();
});
matchMedia('print').addEventListener('change', () => {
this._showImage = matchMedia('print').matches;
});
@@ -177,10 +154,12 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
allowfullscreen
loading="lazy"
></iframe>
<!-- overlay to prevent the iframe from capturing pointer events -->
<div
class=${classMap({
'affine-embed-youtube-video-iframe-overlay': true,
hide: !this._showOverlay,
hide: !this.showOverlay$.value,
})}
></div>
<img
@@ -242,9 +221,6 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
@state()
private accessor _showImage = false;
@state()
protected accessor _showOverlay = true;
@property({ attribute: false })
accessor loading = false;
}