import { ShadowlessElement, WithDisposable } from '@blockeditor/lit'; import { html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { styleMap } from 'lit/directives/style-map.js'; import type { FrameBlockModel } from '../../../../../frame-block/index.js'; import { Bound } from '../../../../../surface-block/index.js'; import type { SurfaceBlockComponent } from '../../../../../surface-block/surface-block.js'; import type { EdgelessPageBlockComponent } from '../../../edgeless-page-block.js'; import { EdgelessPortalBase } from '../edgeless-portal-base.js'; const FRAME_OFFSET = 8; @customElement('edgeless-frame-title') export class EdgeelssFrameTitle extends WithDisposable(ShadowlessElement) { isInner = false; @state() private _editing = false; @state() private _isNavigator = false; @property({ attribute: false }) frame!: FrameBlockModel; @property({ attribute: false }) edgeless!: EdgelessPageBlockComponent; private _updateElement = () => { this.requestUpdate(); }; override firstUpdated() { const { _disposables, edgeless } = this; const { service, surface } = edgeless; _disposables.add( service.viewport.viewportUpdated.on(() => { this.requestUpdate(); }) ); _disposables.add( this.frame.propsUpdated.on(() => { this.requestUpdate(); }) ); _disposables.add( this.frame.page.slots.blockUpdated.on(({ type, id }) => { if (type === 'update' && id === this.frame.id) { this.requestUpdate(); } if (type === 'delete' && id === this.frame.id) { this.remove(); } }) ); _disposables.add( edgeless.service.selection.slots.updated.on(() => { if ( edgeless.service.selection.selectedIds[0] === this.frame.id && edgeless.service.selection.editing ) { this._editing = true; } else { this._editing = false; } }) ); surface.edgeless.slots.edgelessToolUpdated.on(tool => { this._isNavigator = tool.type === 'frameNavigator' ? true : false; }); this.setAttribute('data-frame-title-id', this.frame.id); this.frame.title.yText.observe(this._updateElement); _disposables.add(() => { this.frame.title.yText.unobserve(this._updateElement); }); } override render() { const { edgeless, frame: model, _isNavigator } = this; const { service } = edgeless; const { zoom } = service.viewport; const bound = Bound.deserialize(model.xywh); this.isInner = service.frames.some(frame => { if (frame.id === model.id) return false; if (Bound.deserialize(frame.xywh).contains(bound)) { return true; } return false; }); const [x, y] = service.viewport.toViewCoord(bound.x, bound.y); const { isInner } = this; const text = model.title.toString(); const top = (isInner ? FRAME_OFFSET : -(29 + FRAME_OFFSET)) + y; const left = (isInner ? FRAME_OFFSET : 0) + x; const maxWidth = isInner ? bound.w * zoom - FRAME_OFFSET / zoom : bound.w * zoom; const hidden = 32 / zoom > bound.h && isInner; return html` ${text && !_isNavigator && !this._editing ? html`