import { WidgetElement } from '@blockeditor/lit'; import { offset, shift } from '@floating-ui/dom'; import { html } from 'lit'; import { customElement } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import { HoverController } from '../../../_common/components/hover/controller.js'; import { toast } from '../../../_common/components/toast.js'; import { PAGE_HEADER_HEIGHT } from '../../../_common/consts.js'; import { EdgelessModeIcon } from '../../../_common/icons/edgeless.js'; import { CaptionIcon, CopyIcon, DeleteIcon, DownloadIcon, } from '../../../_common/icons/text.js'; import type { EdgelessModel } from '../../../_common/types.js'; import { downloadBlob } from '../../../_common/utils/filesys.js'; import { type SurfaceRefBlockComponent, type SurfaceRefBlockModel, } from '../../../surface-ref-block/index.js'; import { edgelessToBlob, writeImageBlobToClipboard } from './utils.js'; export const AFFINE_SURFACE_REF_TOOLBAR = 'affine-surface-ref-toolbar'; @customElement(AFFINE_SURFACE_REF_TOOLBAR) export class AffineSurfaceRefToolbar extends WidgetElement { private _hoverController = new HoverController( this, ({ abortController }) => { const surfaceRefBlock = this.blockElement; const selection = this.host.selection; const textSelection = selection.find('text'); if ( !!textSelection && (!!textSelection.to || !!textSelection.from.length) ) { return null; } const blockSelections = selection.filter('block'); if ( blockSelections.length > 1 || (blockSelections.length === 1 && blockSelections[0].path !== surfaceRefBlock.path) ) { return null; } return { template: SurfaceRefToolbarOptions({ blockElement: this.blockElement, model: this.blockElement.model, abortController, }), computePosition: { referenceElement: this.blockElement, placement: 'top-start', middleware: [ offset({ mainAxis: 12, crossAxis: 10, }), shift({ crossAxis: true, padding: { top: PAGE_HEADER_HEIGHT + 12, bottom: 12, right: 12, }, }), ], autoUpdate: true, }, }; } ); override connectedCallback() { super.connectedCallback(); this._hoverController.setReference(this.blockElement); } } declare global { interface HTMLElementTagNameMap { [AFFINE_SURFACE_REF_TOOLBAR]: AffineSurfaceRefToolbar; } } function SurfaceRefToolbarOptions(options: { blockElement: SurfaceRefBlockComponent; model: SurfaceRefBlockModel; abortController: AbortController; }) { const { blockElement, model, abortController } = options; const readonly = model.page.readonly; const hasValidReference = !!blockElement.referenceModel; return html`
blockElement.viewInEdgeless()} >${EdgelessModeIcon}
{ blockElement.showCaption(); }} > ${CaptionIcon} Caption { const referencedModel = blockElement.referenceModel; if (!referencedModel) return; edgelessToBlob(blockElement.host, { surfaceRefBlock: blockElement, surfaceRenderer: blockElement.surfaceRenderer, edgelessElement: referencedModel, blockContainer: blockElement.portal, }) .then(blob => { const fileName = 'title' in referencedModel ? referencedModel.title?.toString() ?? 'Edgeless Content' : 'Edgeless Content'; downloadBlob(blob, fileName); }) .catch(err => { console.error(err); }); }} > ${DownloadIcon} Download { edgelessToBlob(blockElement.host, { surfaceRefBlock: blockElement, surfaceRenderer: blockElement.surfaceRenderer, edgelessElement: blockElement.referenceModel as EdgelessModel, blockContainer: blockElement.portal, }) .then(blob => { return writeImageBlobToClipboard(blob); }) .then(() => { toast(blockElement.host, 'Copied image to clipboard'); }) .catch(err => { console.error(err); }); }} > ${CopyIcon} Copy to clipboard ${DeleteIcon} Delete
`; }