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:
@@ -13,7 +13,7 @@ import { findAncestorModel } from '@blocksuite/affine-shared/utils';
|
|||||||
import type { BlockService } from '@blocksuite/block-std';
|
import type { BlockService } from '@blocksuite/block-std';
|
||||||
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
|
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
|
||||||
import type { BlockModel } from '@blocksuite/store';
|
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 type { TemplateResult } from 'lit';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { query } from 'lit/decorators.js';
|
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();
|
private _fetchAbortController = new AbortController();
|
||||||
|
|
||||||
_cardStyle: EmbedCardStyle = 'horizontal';
|
_cardStyle: EmbedCardStyle = 'horizontal';
|
||||||
@@ -114,6 +125,24 @@ export class EmbedBlockComponent<
|
|||||||
this._fetchAbortController = new AbortController();
|
this._fetchAbortController = new AbortController();
|
||||||
|
|
||||||
this.contentEditable = 'false';
|
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 {
|
override disconnectedCallback(): void {
|
||||||
|
|||||||
@@ -24,12 +24,6 @@ export function toEdgelessEmbedBlock<
|
|||||||
return class extends toGfxBlockComponent(block) {
|
return class extends toGfxBlockComponent(block) {
|
||||||
override selectedStyle$ = null;
|
override selectedStyle$ = null;
|
||||||
|
|
||||||
_isDragging = false;
|
|
||||||
|
|
||||||
_isResizing = false;
|
|
||||||
|
|
||||||
_showOverlay = false;
|
|
||||||
|
|
||||||
override [blockComponentSymbol] = true;
|
override [blockComponentSymbol] = true;
|
||||||
|
|
||||||
override blockDraggable = false;
|
override blockDraggable = false;
|
||||||
@@ -55,16 +49,13 @@ export function toEdgelessEmbedBlock<
|
|||||||
|
|
||||||
this._disposables.add(
|
this._disposables.add(
|
||||||
this.edgelessSlots.elementResizeStart.subscribe(() => {
|
this.edgelessSlots.elementResizeStart.subscribe(() => {
|
||||||
this._isResizing = true;
|
this.isResizing$.value = true;
|
||||||
this._showOverlay = true;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this._disposables.add(
|
this._disposables.add(
|
||||||
this.edgelessSlots.elementResizeEnd.subscribe(() => {
|
this.edgelessSlots.elementResizeEnd.subscribe(() => {
|
||||||
this._isResizing = false;
|
this.isResizing$.value = false;
|
||||||
this._showOverlay =
|
|
||||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import type {
|
|||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { BlockSelection } from '@blocksuite/block-std';
|
import { BlockSelection } from '@blocksuite/block-std';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing } from 'lit';
|
||||||
import { state } from 'lit/decorators.js';
|
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { styleMap } from 'lit/directives/style-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';
|
override _cardStyle: (typeof EmbedFigmaStyles)[number] = 'figma';
|
||||||
|
|
||||||
protected _isDragging = false;
|
|
||||||
|
|
||||||
protected _isResizing = false;
|
|
||||||
|
|
||||||
open = () => {
|
open = () => {
|
||||||
let link = this.model.props.url;
|
let link = this.model.props.url;
|
||||||
if (!link.match(/^[a-zA-Z]+:\/\//)) {
|
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() {
|
override renderBlock() {
|
||||||
@@ -115,10 +91,11 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
></iframe>
|
></iframe>
|
||||||
|
|
||||||
|
<!-- overlay to prevent the iframe from capturing pointer events -->
|
||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-embed-figma-iframe-overlay': true,
|
'affine-embed-figma-iframe-overlay': true,
|
||||||
hide: !this._showOverlay,
|
hide: !this.showOverlay$.value,
|
||||||
})}
|
})}
|
||||||
></div>
|
></div>
|
||||||
</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 type { EmbedHtmlModel, EmbedHtmlStyles } from '@blocksuite/affine-model';
|
||||||
import { BlockSelection } from '@blocksuite/block-std';
|
import { BlockSelection } from '@blocksuite/block-std';
|
||||||
import { html } from 'lit';
|
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 { classMap } from 'lit/directives/class-map.js';
|
||||||
import { type StyleInfo, styleMap } from 'lit/directives/style-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';
|
override _cardStyle: (typeof EmbedHtmlStyles)[number] = 'html';
|
||||||
|
|
||||||
protected _isDragging = false;
|
|
||||||
|
|
||||||
protected _isResizing = false;
|
|
||||||
|
|
||||||
close = () => {
|
close = () => {
|
||||||
document.exitFullscreen().catch(console.error);
|
document.exitFullscreen().catch(console.error);
|
||||||
};
|
};
|
||||||
@@ -50,25 +46,6 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
|
|||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this._cardStyle = this.model.props.style;
|
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 {
|
override renderBlock(): unknown {
|
||||||
@@ -112,10 +89,11 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
|
|||||||
></embed-html-fullscreen-toolbar>
|
></embed-html-fullscreen-toolbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- overlay to prevent the iframe from capturing pointer events -->
|
||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-embed-html-iframe-overlay': true,
|
'affine-embed-html-iframe-overlay': true,
|
||||||
hide: !this._showOverlay,
|
hide: !this.showOverlay$.value,
|
||||||
})}
|
})}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,9 +109,6 @@ export class EmbedHtmlBlockComponent extends EmbedBlockComponent<EmbedHtmlModel>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@state()
|
|
||||||
protected accessor _showOverlay = true;
|
|
||||||
|
|
||||||
@query('.embed-html-block-iframe-wrapper')
|
@query('.embed-html-block-iframe-wrapper')
|
||||||
accessor iframeWrapper!: HTMLDivElement;
|
accessor iframeWrapper!: HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { EmbedLoomModel, EmbedLoomStyles } from '@blocksuite/affine-model';
|
|||||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||||
import { BlockSelection } from '@blocksuite/block-std';
|
import { BlockSelection } from '@blocksuite/block-std';
|
||||||
import { html } from 'lit';
|
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 { classMap } from 'lit/directives/class-map.js';
|
||||||
import { styleMap } from 'lit/directives/style-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';
|
override _cardStyle: (typeof EmbedLoomStyles)[number] = 'video';
|
||||||
|
|
||||||
protected _isDragging = false;
|
|
||||||
|
|
||||||
protected _isResizing = false;
|
|
||||||
|
|
||||||
open = () => {
|
open = () => {
|
||||||
let link = this.model.props.url;
|
let link = this.model.props.url;
|
||||||
if (!link.match(/^[a-zA-Z]+:\/\//)) {
|
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() {
|
override renderBlock() {
|
||||||
@@ -152,10 +129,11 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
></iframe>
|
></iframe>
|
||||||
|
|
||||||
|
<!-- overlay to prevent the iframe from capturing pointer events -->
|
||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-embed-loom-video-iframe-overlay': true,
|
'affine-embed-loom-video-iframe-overlay': true,
|
||||||
hide: !this._showOverlay,
|
hide: !this.showOverlay$.value,
|
||||||
})}
|
})}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -188,9 +166,6 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@state()
|
|
||||||
protected accessor _showOverlay = true;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor loading = false;
|
accessor loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
|
|||||||
|
|
||||||
override _cardStyle: (typeof EmbedYoutubeStyles)[number] = 'video';
|
override _cardStyle: (typeof EmbedYoutubeStyles)[number] = 'video';
|
||||||
|
|
||||||
protected _isDragging = false;
|
|
||||||
|
|
||||||
protected _isResizing = false;
|
|
||||||
|
|
||||||
open = () => {
|
open = () => {
|
||||||
let link = this.model.props.url;
|
let link = this.model.props.url;
|
||||||
if (!link.match(/^[a-zA-Z]+:\/\//)) {
|
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', () => {
|
matchMedia('print').addEventListener('change', () => {
|
||||||
this._showImage = matchMedia('print').matches;
|
this._showImage = matchMedia('print').matches;
|
||||||
});
|
});
|
||||||
@@ -177,10 +154,12 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
|
|||||||
allowfullscreen
|
allowfullscreen
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
></iframe>
|
></iframe>
|
||||||
|
|
||||||
|
<!-- overlay to prevent the iframe from capturing pointer events -->
|
||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-embed-youtube-video-iframe-overlay': true,
|
'affine-embed-youtube-video-iframe-overlay': true,
|
||||||
hide: !this._showOverlay,
|
hide: !this.showOverlay$.value,
|
||||||
})}
|
})}
|
||||||
></div>
|
></div>
|
||||||
<img
|
<img
|
||||||
@@ -242,9 +221,6 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
|
|||||||
@state()
|
@state()
|
||||||
private accessor _showImage = false;
|
private accessor _showImage = false;
|
||||||
|
|
||||||
@state()
|
|
||||||
protected accessor _showOverlay = true;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor loading = false;
|
accessor loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user