import type { EventName, UIEventHandler } from '@blockeditor/block-std'; import { PathFinder } from '@blockeditor/block-std'; import type { Disposable } from '@blockeditor/global/utils'; import { assertExists, Slot } from '@blockeditor/global/utils'; import { ShadowlessElement, WithDisposable } from '@blockeditor/lit'; import type { PropertyValues } from 'lit'; import { css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { createModal } from '../../../../../_common/components/menu/index.js'; import { renderTemplate } from '../../../../../_common/components/uni-component/render-template.js'; import { CrossIcon, ExpandWideIcon, } from '../../../../../_common/icons/index.js'; import type { DataViewSelection } from '../../../../../_common/utils/index.js'; import type { DataViewNativeConfig } from '../../../../data-view.js'; import type { DatabaseBlockComponent } from '../../../../database-block.js'; import { DatabaseSelection } from '../../../selection.js'; export function showDatabasePreviewModal(database: DatabaseBlockComponent) { const viewComponent = new DatabaseBlockModalPreview(); viewComponent.database = database; const root = document.querySelector('editor-host'); assertExists(root); const modal = createModal(root); const close = () => { modal.remove(); }; const container = renderTemplate(() => { return html`
${CrossIcon}
`; }); container.onclick = e => { e.stopPropagation(); }; modal.onclick = close; modal.style.backgroundColor = 'var(--affine-black-60)'; modal.append(container); } @customElement('expand-database-block-modal') export class ExpandDatabaseBlockModal extends WithDisposable( ShadowlessElement ) { expandDatabase = () => { const database = this.closest('affine-database'); if (database) { showDatabasePreviewModal(database); } }; get database() { return this.closest('affine-database'); } protected override render(): unknown { if ( this.closest('database-block-modal-preview') || !this.database?.page.awarenessStore.getFlag( 'enable_expand_database_block' ) ) { return; } return html`
${ExpandWideIcon}
`; } } @customElement('database-block-modal-preview') export class DatabaseBlockModalPreview extends WithDisposable( ShadowlessElement ) { static override styles = css` database-block-modal-preview { display: flex; flex-direction: column; flex: 1; overflow: hidden; } `; path = ['modal', 'preview']; @property({ attribute: false }) database!: DatabaseBlockComponent; public override connectedCallback() { super.connectedCallback(); this.database.selection.slots.changed.on(selections => { const selection = selections.find(v => { return PathFinder.equals(v.path, this.path); }); if (selection && selection instanceof DatabaseSelection) { this.selectionUpdated.emit(selection.viewSelection); } else { this.selectionUpdated.emit(undefined); } }); } protected override firstUpdated(_changedProperties: PropertyValues) { super.firstUpdated(_changedProperties); requestAnimationFrame(() => { this.querySelector('affine-data-view-native')?.focusFirstCell(); }); } selectionUpdated: Slot = new Slot(); setSelection: (selection?: DataViewSelection) => void = selection => { this.database.host.selection.set( selection ? [new DatabaseSelection({ path: this.path, viewSelection: selection })] : [] ); }; bindHotkey: (hotkeys: Record) => Disposable = hotkeys => { return { dispose: this.database.host.event.bindHotkey(hotkeys, { path: [], }), }; }; handleEvent: (name: EventName, handler: UIEventHandler) => Disposable = ( name, handler ) => { return { dispose: this.database.host.event.add(name, handler, { path: [], }), }; }; protected override render(): unknown { const config: DataViewNativeConfig = { bindHotkey: this.bindHotkey, handleEvent: this.handleEvent, getFlag: this.database.getFlag, selectionUpdated: this.selectionUpdated, setSelection: this.setSelection, dataSource: this.database.dataSource, viewSource: this.database.viewSource, headerComponent: this.database.headerComponent, std: this.database.std, }; return html` `; } }