mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
feat(editor): improve status display in attachment embed view (#12180)
Closes: [BS-3438](https://linear.app/affine-design/issue/BS-3438/attachment-embed-view-中的-status-组件) Closes: [BS-3447](https://linear.app/affine-design/issue/BS-3447/触发-litportal-re-render) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a visual status indicator for embedded attachments with reload capability. - Added a new resource status component to display error messages and reload actions. - **Improvements** - Enhanced attachment rendering flow with reactive state and unified embed handling. - Simplified resource state and blob URL lifecycle management. - Added status visibility flags for PDF and video embeds. - **Bug Fixes** - Improved error handling and refresh support for embedded content including PDFs, videos, and audio. - **Style** - Added styles for the attachment embed status indicator positioning. - **Refactor** - Streamlined attachment and resource controller implementations for better maintainability. - **Tests** - Added end-to-end test verifying PDF viewer reload and re-rendering in embed mode. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import {
|
||||
fontBaseStyle,
|
||||
panelBaseColorsStyle,
|
||||
} from '@blocksuite/affine-shared/styles';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import {
|
||||
createButtonPopper,
|
||||
stopPropagation,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { WithDisposable } from '@blocksuite/global/lit';
|
||||
import { InformationIcon } from '@blocksuite/icons/lit';
|
||||
import { PropTypes, requiredProperties } from '@blocksuite/std';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
|
||||
@requiredProperties({
|
||||
message: PropTypes.string,
|
||||
reload: PropTypes.instanceOf(Function),
|
||||
})
|
||||
export class ResourceStatus extends WithDisposable(LitElement) {
|
||||
static override styles = css`
|
||||
button.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
font-size: 18px;
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: ${unsafeCSSVarV2('button/pureWhiteText')};
|
||||
background: ${unsafeCSSVarV2('status/error')};
|
||||
box-shadow: var(--affine-overlay-shadow);
|
||||
}
|
||||
|
||||
${panelBaseColorsStyle('.popper')}
|
||||
${fontBaseStyle('.popper')}
|
||||
.popper {
|
||||
display: none;
|
||||
outline: none;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
width: 260px;
|
||||
font-size: var(--affine-font-sm);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
|
||||
&[data-show] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
color: ${unsafeCSSVarV2('text/primary')};
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
button.reload {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 12px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
color: ${unsafeCSSVarV2('button/primary')};
|
||||
}
|
||||
`;
|
||||
|
||||
private _popper: ReturnType<typeof createButtonPopper> | null = null;
|
||||
|
||||
private _updatePopper() {
|
||||
this._popper?.dispose();
|
||||
this._popper = createButtonPopper({
|
||||
reference: this._trigger,
|
||||
popperElement: this._content,
|
||||
mainAxis: 8,
|
||||
allowedPlacements: ['top-start', 'bottom-start'],
|
||||
});
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
this._updatePopper();
|
||||
this.disposables.addFromEvent(this, 'click', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'keydown', (e: KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape') {
|
||||
this._popper?.hide();
|
||||
}
|
||||
});
|
||||
this.disposables.addFromEvent(this._trigger, 'click', (_: MouseEvent) => {
|
||||
this._popper?.toggle();
|
||||
});
|
||||
this.disposables.addFromEvent(
|
||||
this._reloadButton,
|
||||
'click',
|
||||
(_: MouseEvent) => {
|
||||
this._popper?.hide();
|
||||
this.reload();
|
||||
}
|
||||
);
|
||||
this.disposables.add(() => this._popper?.dispose());
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<button class="status">${InformationIcon()}</button>
|
||||
<div class="popper">
|
||||
<div class="content">${this.message}</div>
|
||||
<div class="footer">
|
||||
<button class="reload">Reload</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@query('div.popper')
|
||||
private accessor _content!: HTMLDivElement;
|
||||
|
||||
@query('button.status')
|
||||
private accessor _trigger!: HTMLButtonElement;
|
||||
|
||||
@query('button.reload')
|
||||
private accessor _reloadButton!: HTMLButtonElement;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor message!: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor reload!: () => void;
|
||||
}
|
||||
Reference in New Issue
Block a user