import { assertExists } from '@blocksuite/global/utils'; import { html, LitElement, type TemplateResult } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import { VIRGO_ROOT_ATTR, ZERO_WIDTH_SPACE } from '../consts.js'; import type { DeltaInsert } from '../types.js'; import type { VirgoRootElement } from '../virgo.js'; import { EmbedGap } from './embed-gap.js'; @customElement('v-line') export class VirgoLine extends LitElement { @property({ attribute: false }) elements: [TemplateResult<1>, DeltaInsert][] = []; get vElements() { return Array.from(this.querySelectorAll('v-element')); } get vTexts() { return Array.from(this.querySelectorAll('v-text')); } get textLength() { return this.vElements.reduce((acc, el) => acc + el.delta.insert.length, 0); } override get textContent() { return this.vElements.reduce((acc, el) => acc + el.delta.insert, ''); } override async getUpdateComplete() { const result = await super.getUpdateComplete(); await Promise.all(this.vElements.map(el => el.updateComplete)); await Promise.all(this.vTexts.map(el => el.updateComplete)); return result; } protected override firstUpdated(): void { this.style.display = 'block'; } override render() { if (this.elements.length === 0) { return html`
`; } if (!this.isConnected) return; const rootElement = this.closest( `[${VIRGO_ROOT_ATTR}]` ) as VirgoRootElement; assertExists(rootElement, 'v-line must be inside a v-root'); const virgoEditor = rootElement.virgoEditor; assertExists( virgoEditor, 'v-line must be inside a v-root with virgo-editor' ); const renderElements = this.elements.flatMap(([template, delta], index) => { if (virgoEditor.isEmbed(delta)) { if (delta.insert.length !== 1) { throw new Error(`The length of embed node should only be 1. This seems to be an internal issue with Virgo. Please go to https://github.com/toeverything/blocksuite/issues to report it.`); } // we add `EmbedGap` to make cursor can be placed between embed elements if (index === 0) { const nextDelta = this.elements[index + 1]?.[1]; if (!nextDelta || virgoEditor.isEmbed(nextDelta)) { return [EmbedGap, template, EmbedGap]; } else { return [EmbedGap, template]; } } else { const nextDelta = this.elements[index + 1]?.[1]; if (!nextDelta || virgoEditor.isEmbed(nextDelta)) { return [template, EmbedGap]; } else { return [template]; } } } return template; }); // prettier will generate \n and cause unexpected space and line break // prettier-ignore return html`
${renderElements}
`; } override createRenderRoot() { return this; } } declare global { interface HTMLElementTagNameMap { 'v-line': VirgoLine; } }