mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
162 lines
4.0 KiB
TypeScript
162 lines
4.0 KiB
TypeScript
import type { BlockCaptionEditor } from '@blocksuite/affine-components/caption';
|
|
import { Peekable } from '@blocksuite/affine-components/peek';
|
|
import type { ImageBlockModel } from '@blocksuite/affine-model';
|
|
import { GfxBlockComponent } from '@blocksuite/block-std';
|
|
import { css, html } from 'lit';
|
|
import { property, query, state } from 'lit/decorators.js';
|
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
import { when } from 'lit/directives/when.js';
|
|
|
|
import type { ImageBlockFallbackCard } from './components/image-block-fallback.js';
|
|
import type { ImageBlockService } from './image-service.js';
|
|
import {
|
|
copyImageBlob,
|
|
downloadImageBlob,
|
|
fetchImageBlob,
|
|
resetImageSize,
|
|
turnImageIntoCardView,
|
|
} from './utils.js';
|
|
|
|
@Peekable()
|
|
export class ImageEdgelessBlockComponent extends GfxBlockComponent<
|
|
ImageBlockModel,
|
|
ImageBlockService
|
|
> {
|
|
static override styles = css`
|
|
affine-edgeless-image .resizable-img,
|
|
affine-edgeless-image .resizable-img img {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
`;
|
|
|
|
convertToCardView = () => {
|
|
turnImageIntoCardView(this).catch(console.error);
|
|
};
|
|
|
|
copy = () => {
|
|
copyImageBlob(this).catch(console.error);
|
|
};
|
|
|
|
download = () => {
|
|
downloadImageBlob(this).catch(console.error);
|
|
};
|
|
|
|
refreshData = () => {
|
|
this.retryCount = 0;
|
|
fetchImageBlob(this)
|
|
.then(() => {
|
|
const { width, height } = this.model;
|
|
if (!width || !height) {
|
|
return resetImageSize(this);
|
|
}
|
|
|
|
return;
|
|
})
|
|
.catch(console.error);
|
|
};
|
|
|
|
private _handleError(error: Error) {
|
|
this.dispatchEvent(new CustomEvent('error', { detail: error }));
|
|
}
|
|
|
|
override connectedCallback() {
|
|
super.connectedCallback();
|
|
|
|
this.refreshData();
|
|
this.contentEditable = 'false';
|
|
this.disposables.add(
|
|
this.model.propsUpdated.on(({ key }) => {
|
|
if (key === 'sourceId') {
|
|
this.refreshData();
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
override disconnectedCallback() {
|
|
if (this.blobUrl) {
|
|
URL.revokeObjectURL(this.blobUrl);
|
|
}
|
|
super.disconnectedCallback();
|
|
}
|
|
|
|
override renderGfxBlock() {
|
|
const rotate = this.model.rotate ?? 0;
|
|
const containerStyleMap = styleMap({
|
|
position: 'relative',
|
|
width: '100%',
|
|
transform: `rotate(${rotate}deg)`,
|
|
transformOrigin: 'center',
|
|
});
|
|
|
|
return html`
|
|
<div class="affine-image-container" style=${containerStyleMap}>
|
|
${when(
|
|
this.loading || this.error || !this.blobUrl,
|
|
() =>
|
|
html`<affine-image-fallback-card
|
|
.error=${this.error}
|
|
.loading=${this.loading}
|
|
.mode=${'page'}
|
|
></affine-image-fallback-card>`,
|
|
() =>
|
|
html`<div class="resizable-img">
|
|
<img
|
|
class="drag-target"
|
|
src=${this.blobUrl ?? ''}
|
|
draggable="false"
|
|
@error=${this._handleError}
|
|
loading="lazy"
|
|
/>
|
|
</div>`
|
|
)}
|
|
<affine-block-selection .block=${this}></affine-block-selection>
|
|
</div>
|
|
<block-caption-editor></block-caption-editor>
|
|
|
|
${Object.values(this.widgets)}
|
|
`;
|
|
}
|
|
|
|
override updated() {
|
|
this.fallbackCard?.requestUpdate();
|
|
}
|
|
|
|
@property({ attribute: false })
|
|
accessor blob: Blob | undefined = undefined;
|
|
|
|
@property({ attribute: false })
|
|
accessor blobUrl: string | undefined = undefined;
|
|
|
|
@query('block-caption-editor')
|
|
accessor captionEditor!: BlockCaptionEditor | null;
|
|
|
|
@property({ attribute: false })
|
|
accessor downloading = false;
|
|
|
|
@property({ attribute: false })
|
|
accessor error = false;
|
|
|
|
@query('affine-image-fallback-card')
|
|
accessor fallbackCard: ImageBlockFallbackCard | null = null;
|
|
|
|
@state()
|
|
accessor lastSourceId!: string;
|
|
|
|
@property({ attribute: false })
|
|
accessor loading = false;
|
|
|
|
@query('.resizable-img')
|
|
accessor resizableImg!: HTMLDivElement;
|
|
|
|
@property({ attribute: false })
|
|
accessor retryCount = 0;
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
'affine-edgeless-image': ImageEdgelessBlockComponent;
|
|
}
|
|
}
|