From d7d512084eba5b56f3fef134f3de36699b01885d Mon Sep 17 00:00:00 2001 From: donteatfriedrice Date: Sun, 16 Mar 2025 09:05:04 +0000 Subject: [PATCH] feat(editor): embed iframe error status card in surface (#10869) To close [BS-2806](https://linear.app/affine-design/issue/BS-2806/iframe-embed-block-edgeless-loading-and-error-status) --- .../components/embed-iframe-error-card.ts | 80 ++++++++++++++----- .../components/embed-iframe-loading-card.ts | 22 +---- .../embed-iframe-block/embed-iframe-block.ts | 7 +- .../src/embed-iframe-block/types.ts | 16 ++++ 4 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 blocksuite/affine/blocks/block-embed/src/embed-iframe-block/types.ts diff --git a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-error-card.ts b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-error-card.ts index 83f69e772..9cb48f6f1 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-error-card.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-error-card.ts @@ -1,7 +1,6 @@ import { createLitPortal } from '@blocksuite/affine-components/portal'; import type { EmbedIframeBlockModel } from '@blocksuite/affine-model'; import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; -import { stopPropagation } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import { WithDisposable } from '@blocksuite/global/lit'; import { EditIcon, InformationIcon, ResetIcon } from '@blocksuite/icons/lit'; @@ -9,24 +8,27 @@ import { flip, offset } from '@floating-ui/dom'; import { baseTheme } from '@toeverything/theme'; import { css, html, LitElement, unsafeCSS } from 'lit'; import { property, query } from 'lit/decorators.js'; +import { classMap } from 'lit/directives/class-map.js'; +import { styleMap } from 'lit/directives/style-map.js'; + +import type { EmbedIframeStatusCardOptions } from '../types'; const LINK_EDIT_POPUP_OFFSET = 12; +const ERROR_CARD_DEFAULT_HEIGHT = 114; export class EmbedIframeErrorCard extends WithDisposable(LitElement) { static override styles = css` :host { width: 100%; + height: 100%; } .affine-embed-iframe-error-card { container: affine-embed-iframe-error-card / inline-size; display: flex; box-sizing: border-box; - width: 100%; user-select: none; - height: 114px; padding: 12px; - align-items: flex-start; gap: 12px; overflow: hidden; border-radius: 8px; @@ -38,7 +40,6 @@ export class EmbedIframeErrorCard extends WithDisposable(LitElement) { .error-content { display: flex; flex-direction: column; - align-items: flex-start; gap: 4px; flex: 1 0 0; @@ -68,8 +69,6 @@ export class EmbedIframeErrorCard extends WithDisposable(LitElement) { .error-message { display: flex; - height: 40px; - align-items: flex-start; align-self: stretch; color: ${unsafeCSSVarV2('text/secondary')}; overflow: hidden; @@ -121,17 +120,42 @@ export class EmbedIframeErrorCard extends WithDisposable(LitElement) { } } - .error-banner { - width: 204px; - height: 102px; - } - @container affine-embed-iframe-error-card (width < 480px) { .error-banner { display: none; } } } + + .affine-embed-iframe-error-card.horizontal { + flex-direction: row; + align-items: flex-start; + + .error-content { + align-items: flex-start; + + .error-message { + height: 40px; + align-items: flex-start; + } + } + } + + .affine-embed-iframe-error-card.vertical { + flex-direction: column-reverse; + align-items: center; + justify-content: center; + + .error-content { + justify-content: center; + align-items: center; + + .error-message { + justify-content: center; + align-items: center; + } + } + } `; private _editAbortController: AbortController | null = null; @@ -168,14 +192,28 @@ export class EmbedIframeErrorCard extends WithDisposable(LitElement) { }); }; - override connectedCallback() { - super.connectedCallback(); - this.disposables.addFromEvent(this, 'click', stopPropagation); - } + private readonly _handleRetry = (e: MouseEvent) => { + e.stopPropagation(); + this.onRetry(); + }; override render() { + const { layout, width, height } = this.options; + const cardClasses = classMap({ + 'affine-embed-iframe-error-card': true, + horizontal: layout === 'horizontal', + vertical: layout === 'vertical', + }); + + const cardWidth = width ? `${width}px` : '100%'; + const cardHeight = height ? `${height}px` : '100%'; + const cardStyle = styleMap({ + width: cardWidth, + height: cardHeight, + }); + return html` -
+
@@ -193,7 +231,7 @@ export class EmbedIframeErrorCard extends WithDisposable(LitElement) { > Edit
-
+
${ResetIcon({ width: '16px', height: '16px' })} @@ -227,4 +265,10 @@ export class EmbedIframeErrorCard extends WithDisposable(LitElement) { @property({ attribute: false }) accessor std!: BlockStdScope; + + @property({ attribute: false }) + accessor options: EmbedIframeStatusCardOptions = { + layout: 'horizontal', + height: ERROR_CARD_DEFAULT_HEIGHT, + }; } diff --git a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-loading-card.ts b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-loading-card.ts index 7e01687be..4f29110d3 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-loading-card.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/components/embed-iframe-loading-card.ts @@ -8,23 +8,9 @@ import { classMap } from 'lit/directives/class-map.js'; import { styleMap } from 'lit/directives/style-map.js'; import { getEmbedCardIcons } from '../../common/utils'; +import type { EmbedIframeStatusCardOptions } from '../types'; -/** - * The options for the embed iframe loading card - * layout: the layout of the card, horizontal or vertical - * width: the width of the card, if not set, the card width will be 100% - * height: the height of the card, if not set, the card height will be 100% - * @example - * { - * layout: 'horizontal', - * height: 114, - * } - */ -export type EmbedIframeLoadingCardOptions = { - layout: 'horizontal' | 'vertical'; - width?: number; - height?: number; -}; +const LOADING_CARD_DEFAULT_HEIGHT = 114; export class EmbedIframeLoadingCard extends LitElement { static override styles = css` @@ -199,8 +185,8 @@ export class EmbedIframeLoadingCard extends LitElement { accessor std!: BlockStdScope; @property({ attribute: false }) - accessor options: EmbedIframeLoadingCardOptions = { + accessor options: EmbedIframeStatusCardOptions = { layout: 'horizontal', - height: 114, + height: LOADING_CARD_DEFAULT_HEIGHT, }; } diff --git a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/embed-iframe-block.ts b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/embed-iframe-block.ts index 2294c920a..f3ad8ec7e 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/embed-iframe-block.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/embed-iframe-block.ts @@ -16,10 +16,10 @@ import { html, nothing } from 'lit'; import { type ClassInfo, classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; -import type { EmbedIframeLoadingCardOptions } from './components/embed-iframe-loading-card.js'; import type { IframeOptions } from './extension/embed-iframe-config.js'; import { EmbedIframeService } from './extension/embed-iframe-service.js'; import { embedIframeBlockStyles } from './style.js'; +import type { EmbedIframeStatusCardOptions } from './types.js'; export type EmbedIframeStatus = 'idle' | 'loading' | 'success' | 'error'; const DEFAULT_IFRAME_HEIGHT = 152; @@ -75,7 +75,7 @@ export class EmbedIframeBlockComponent extends CaptionedBlockComponent`; } @@ -223,6 +223,7 @@ export class EmbedIframeBlockComponent extends CaptionedBlockComponent`; } diff --git a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/types.ts b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/types.ts new file mode 100644 index 000000000..119b01c94 --- /dev/null +++ b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/types.ts @@ -0,0 +1,16 @@ +/** + * The options for the embed iframe status card + * layout: the layout of the card, horizontal or vertical + * width: the width of the card, if not set, the card width will be 100% + * height: the height of the card, if not set, the card height will be 100% + * @example + * { + * layout: 'horizontal', + * height: 114, + * } + */ +export type EmbedIframeStatusCardOptions = { + layout: 'horizontal' | 'vertical'; + width?: number; + height?: number; +};