import { menu, popFilterableSimpleMenu, popMenu, type PopupTarget, popupTargetFromElement, } from '@blocksuite/affine-components/context-menu'; import { DeleteIcon, DuplicateIcon, InfoIcon, MoreHorizontalIcon, MoveLeftIcon, MoveRightIcon, PlusIcon, } from '@blocksuite/icons/lit'; import { css, html } from 'lit'; import { property } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { WidgetBase } from '../../core/widget/widget-base.js'; export class DataViewHeaderViews extends WidgetBase { static override styles = css` data-view-header-views { height: 28px; display: flex; user-select: none; gap: 4px; } data-view-header-views::-webkit-scrollbar-thumb { width: 1px; } .database-view-button { height: 100%; cursor: pointer; padding: 2px 4px; border-radius: 4px; font-size: 14px; display: flex; align-items: center; justify-content: center; color: var(--affine-text-secondary-color); white-space: nowrap; max-width: 200px; min-width: 28px; } .database-view-button .name { align-items: center; font-size: 15px; line-height: 24px; overflow: hidden; text-overflow: ellipsis; font-weight: 500; padding-right: 2px; } .database-view-button .icon { margin-right: 6px; display: block; flex-shrink: 0; } .database-view-button .icon svg { width: 16px; height: 16px; } .database-view-button.selected { color: var(--affine-text-primary-color); background-color: var(--affine-hover-color-filled); } `; private addView(type: string) { const id = this.viewManager.viewAdd(type); this.viewManager.setCurrentView(id); this.dataViewLogic.root.config.eventTrace('AddDatabaseView', { type: type, }); } _addViewMenu = (event: MouseEvent) => { popFilterableSimpleMenu( popupTargetFromElement(event.currentTarget as HTMLElement), this.dataSource.viewMetas.map(v => { return menu.action({ name: v.model.defaultName, prefix: html``, select: () => { this.addView(v.type); }, }); }) ); }; _showMore = (event: MouseEvent) => { const views = this.viewManager.views$.value; popFilterableSimpleMenu( popupTargetFromElement(event.currentTarget as HTMLElement), [ menu.group({ items: views.flatMap(id => { const openViewOption = (event: MouseEvent) => { event.stopPropagation(); this.openViewOption( popupTargetFromElement(event.currentTarget as HTMLElement), id ); }; const view = this.viewManager.viewGet(id); if (!view) { return []; } return menu.action({ prefix: html``, name: view.name$.value ?? '', label: () => html`${view.name$.value}`, isSelected: this.viewManager.currentViewId$.value === id, select: () => { this.viewManager.setCurrentView(id); }, postfix: html`
${MoreHorizontalIcon()}
`, }); }), }), menu.group({ items: this.dataSource.viewMetas.map(v => { return menu.action({ name: `Create ${v.model.defaultName}`, hide: () => this.readonly, prefix: PlusIcon(), select: () => { this.addView(v.type); }, }); }), }), ] ); }; openViewOption = (target: PopupTarget, id: string) => { if (this.readonly) { return; } const views = this.viewManager.views$.value; const index = views.findIndex(v => v === id); const viewId = views[index]; if (!viewId) { return; } const view = this.viewManager.viewGet(viewId); if (!view) { return; } popMenu(target, { options: { items: [ menu.input({ initialValue: view.name$.value, placeholder: 'View name', onChange: text => { view.nameSet(text); }, }), menu.group({ items: [ menu.action({ name: 'Edit View', prefix: InfoIcon(), select: () => { this.closest('affine-data-view-renderer') ?.querySelector('data-view-header-tools-view-options') ?.openMoreAction(target); }, }), menu.action({ name: 'Move Left', hide: () => index === 0, prefix: MoveLeftIcon(), select: () => { const targetId = views[index - 1]; this.viewManager.moveTo( id, targetId ? { before: true, id: targetId } : 'start' ); }, }), menu.action({ name: 'Move Right', prefix: MoveRightIcon(), hide: () => index === views.length - 1, select: () => { const targetId = views[index + 1]; this.viewManager.moveTo( id, targetId ? { before: false, id: targetId } : 'end' ); }, }), ], }), menu.group({ items: [ menu.action({ name: 'Duplicate', prefix: DuplicateIcon(), select: () => { this.viewManager.viewDuplicate(id); }, }), menu.action({ name: 'Delete', prefix: DeleteIcon(), select: () => { view.delete(); }, class: { 'delete-item': true }, }), ], }), ], }, }); }; renderMore = (count: number) => { const views = this.viewManager.views$.value; if (count === views.length) { if (this.readonly) { return; } return html`
${PlusIcon()}
`; } return html`
${views.length - count} More
`; }; renderViews = () => { const views = this.viewManager.views$.value; return views.map(id => () => { const classList = classMap({ 'database-view-button': true, 'dv-hover': true, selected: this.viewManager.currentViewId$.value === id, }); const view = this.viewManager.viewDataGet(id); return html`
${view?.name}
`; }); }; get readonly() { return this.viewManager.readonly$.value; } private getRenderer(viewId: string) { return this.dataSource.viewMetaGetById(viewId)?.renderer; } private clickView(event: MouseEvent, id: string) { if (this.viewManager.currentViewId$.value !== id) { this.viewManager.setCurrentView(id); this.onChangeView?.(id); return; } this.openViewOption( popupTargetFromElement(event.currentTarget as HTMLElement), id ); } override render() { return html` `; } @property({ attribute: false }) accessor onChangeView: ((id: string) => void) | undefined; } declare global { interface HTMLElementTagNameMap { 'data-view-header-views': DataViewHeaderViews; } }