import '../_common/components/embed-card/embed-card-caption.js';
import { PathFinder } from '@blockeditor/block-std';
import { assertExists } from '@blockeditor/global/utils';
import { flip, offset } from '@floating-ui/dom';
import { html, nothing } from 'lit';
import { customElement, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ref } from 'lit/directives/ref.js';
import { styleMap } from 'lit/directives/style-map.js';
import type { EmbedCardCaption } from '../_common/components/embed-card/embed-card-caption.js';
import { HoverController } from '../_common/components/hover/controller.js';
import { EMBED_CARD_HEIGHT, EMBED_CARD_WIDTH } from '../_common/consts.js';
import { EmbedBlockElement } from '../_common/embed-block-helper/embed-block-element.js';
import { OpenIcon } from '../_common/icons/text.js';
import type { EmbedFigmaStyles } from './embed-figma-model.js';
import { type EmbedFigmaModel } from './embed-figma-model.js';
import type { EmbedFigmaService } from './embed-figma-service.js';
import { FigmaIcon, styles } from './styles.js';
@customElement('affine-embed-figma-block')
export class EmbedFigmaBlockComponent extends EmbedBlockElement<
EmbedFigmaModel,
EmbedFigmaService
> {
static override styles = styles;
override _cardStyle: (typeof EmbedFigmaStyles)[number] = 'figma';
@state()
private _isSelected = false;
@state()
private _showOverlay = true;
@state()
showCaption = false;
@query('.affine-embed-figma-block')
private _figmaBlockEl!: HTMLDivElement;
@query('embed-card-caption')
captionElement!: EmbedCardCaption;
private _isDragging = false;
open = () => {
let link = this.model.url;
if (!link.match(/^[a-zA-Z]+:\/\//)) {
link = 'https://' + link;
}
window.open(link, '_blank');
};
refreshData = () => {};
private _selectBlock() {
const selectionManager = this.host.selection;
const blockSelection = selectionManager.create('block', {
path: this.path,
});
selectionManager.setGroup('note', [blockSelection]);
}
private _handleClick() {
if (!this.isInSurface) {
this._selectBlock();
}
}
private _handleDoubleClick(event: MouseEvent) {
event.stopPropagation();
this.open();
}
override connectedCallback() {
super.connectedCallback();
if (!!this.model.caption && !!this.model.caption.length) {
this.showCaption = true;
}
if (!this.model.description && !this.model.title) {
this.page.withoutTransact(() => {
this.page.updateBlock(this.model, {
title: 'Figma',
description: this.model.url,
});
});
}
this.disposables.add(
this.model.propsUpdated.on(({ key }) => {
this.requestUpdate();
if (key === 'url') this.refreshData();
})
);
// this is required to prevent iframe from capturing pointer events
this.disposables.add(
this.std.selection.slots.changed.on(sels => {
this._isSelected = sels.some(sel =>
PathFinder.equals(sel.path, this.path)
);
this._showOverlay = this._isDragging || !this._isSelected;
})
);
// this is required to prevent iframe from capturing pointer events
this.handleEvent('pointerMove', ctx => {
this._isDragging = ctx.get('pointerState').dragging;
if (this._isDragging) this._showOverlay = true;
});
if (this.isInSurface) {
const surface = this.surface;
assertExists(surface);
this.disposables.add(
this.model.propsUpdated.on(() => {
this.requestUpdate();
})
);
}
}
private _whenHover = new HoverController(this, ({ abortController }) => {
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 !== this.path)
) {
return null;
}
return {
template: html`