import { menu, type MenuButtonData, type MenuConfig, popMenu, type PopupTarget, popupTargetFromElement, } from '@blocksuite/affine-components/context-menu'; import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; import { ArrowRightSmallIcon, DeleteIcon, DuplicateIcon, FilterIcon, GroupingIcon, InfoIcon, LayoutIcon, MoreHorizontalIcon, SortIcon, } from '@blocksuite/icons/lit'; import { css, html } from 'lit'; import { styleMap } from 'lit/directives/style-map.js'; import { popPropertiesSetting } from '../../../../core/common/properties.js'; import { filterTraitKey } from '../../../../core/filter/trait.js'; import { popGroupSetting, popSelectGroupByProperty, } from '../../../../core/group-by/setting.js'; import { groupTraitKey } from '../../../../core/group-by/trait.js'; import { type DataViewUILogicBase, emptyFilterGroup, popCreateFilter, renderUniLit, } from '../../../../core/index.js'; import { popCreateSort } from '../../../../core/sort/add-sort.js'; import { sortTraitKey } from '../../../../core/sort/manager.js'; import { createSortUtils } from '../../../../core/sort/utils.js'; import { WidgetBase } from '../../../../core/widget/widget-base.js'; import { popFilterRoot } from '../../../quick-setting-bar/filter/root-panel-view.js'; import { popSortRoot } from '../../../quick-setting-bar/sort/root-panel.js'; const styles = css` .affine-database-toolbar-item.more-action { padding: 2px; border-radius: 4px; display: flex; align-items: center; cursor: pointer; } .affine-database-toolbar-item.more-action:hover { background: var(--affine-hover-color); } .affine-database-toolbar-item.more-action { font-size: 20px; color: ${unsafeCSSVarV2('icon/primary')}; } .more-action.active { background: var(--affine-hover-color); } `; export class DataViewHeaderToolsViewOptions extends WidgetBase { static override styles = styles; clickMoreAction = (e: MouseEvent) => { e.stopPropagation(); this.openMoreAction(popupTargetFromElement(e.currentTarget as HTMLElement)); }; openMoreAction = (target: PopupTarget) => { popViewOptions(target, this.dataViewLogic); }; override render() { if (this.view.readonly$.value) { return; } return html`
${MoreHorizontalIcon()}
`; } } declare global { interface HTMLElementTagNameMap { 'data-view-header-tools-view-options': DataViewHeaderToolsViewOptions; } } const createSettingMenus = ( target: PopupTarget, dataViewLogic: DataViewUILogicBase, reopen: () => void ) => { const view = dataViewLogic.view; const settingItems: MenuConfig[] = []; settingItems.push( menu.action({ name: 'Properties', prefix: InfoIcon(), postfix: html`
${view.properties$.value.length} shown
${ArrowRightSmallIcon()}`, select: () => { popPropertiesSetting(target, { view: view, onBack: reopen, }); }, }) ); const filterTrait = view.traitGet(filterTraitKey); if (filterTrait) { const filterCount = filterTrait.filter$.value.conditions.length; settingItems.push( menu.action({ name: 'Filter', prefix: FilterIcon(), postfix: html`
${filterCount === 0 ? '' : filterCount === 1 ? '1 filter' : `${filterCount} filters`}
${ArrowRightSmallIcon()}`, select: () => { if (!filterTrait.filter$.value.conditions.length) { popCreateFilter(target, { vars: view.vars$, onBack: reopen, onSelect: filter => { filterTrait.filterSet({ ...(filterTrait.filter$.value ?? emptyFilterGroup), conditions: [...filterTrait.filter$.value.conditions, filter], }); popFilterRoot(target, { filterTrait: filterTrait, onBack: reopen, dataViewLogic: dataViewLogic, }); dataViewLogic.eventTrace('CreateDatabaseFilter', {}); }, }); } else { popFilterRoot(target, { filterTrait: filterTrait, onBack: reopen, dataViewLogic: dataViewLogic, }); } }, }) ); } const sortTrait = view.traitGet(sortTraitKey); if (sortTrait) { const sortCount = sortTrait.sortList$.value.length; settingItems.push( menu.action({ name: 'Sort', prefix: SortIcon(), postfix: html`
${sortCount === 0 ? '' : sortCount === 1 ? '1 sort' : `${sortCount} sorts`}
${ArrowRightSmallIcon()}`, select: () => { const sortList = sortTrait.sortList$.value; const sortUtils = createSortUtils( sortTrait, dataViewLogic.eventTrace ); if (!sortList.length) { popCreateSort(target, { sortUtils: sortUtils, onBack: reopen, }); } else { popSortRoot(target, { sortUtils: sortUtils, title: { text: 'Sort', onBack: reopen, }, }); } }, }) ); } const groupTrait = view.traitGet(groupTraitKey); if (groupTrait) { settingItems.push( menu.action({ name: 'Group', prefix: GroupingIcon(), postfix: html`
${groupTrait.property$.value?.name$.value ?? ''}
${ArrowRightSmallIcon()}`, select: () => { const groupBy = groupTrait.property$.value; if (!groupBy) { popSelectGroupByProperty(target, groupTrait, { onSelect: () => popGroupSetting(target, groupTrait, reopen), onBack: reopen, }); } else { popGroupSetting(target, groupTrait, reopen); } }, }) ); } return settingItems; }; export const popViewOptions = ( target: PopupTarget, dataViewLogic: DataViewUILogicBase, onClose?: () => void ) => { const view = dataViewLogic.view; const reopen = () => { popViewOptions(target, dataViewLogic); }; const items: MenuConfig[] = []; items.push( menu.input({ initialValue: view.name$.value, placeholder: 'View name', onChange: text => { view.nameSet(text); }, }) ); items.push( menu.group({ items: [ menu.action({ name: 'Layout', postfix: html`
${view.type}
${ArrowRightSmallIcon()}`, select: () => { const viewTypes = view.manager.viewMetas.map(meta => { return menu => { if (!menu.search(meta.model.defaultName)) { return; } const isSelected = meta.type === view.manager.currentView$.value?.type; const iconStyle = styleMap({ fontSize: '24px', color: isSelected ? 'var(--affine-text-emphasis-color)' : 'var(--affine-icon-secondary)', }); const textStyle = styleMap({ fontSize: '14px', lineHeight: '22px', color: isSelected ? 'var(--affine-text-emphasis-color)' : 'var(--affine-text-secondary-color)', }); const data: MenuButtonData = { content: () => html`
${renderUniLit(meta.renderer.icon)}
${meta.model.defaultName}
`, select: () => { const id = view.manager.currentViewId$.value; if (!id) { return; } view.manager.viewChangeType(id, meta.type); dataViewLogic.clearSelection(); }, class: {}, }; const containerStyle = styleMap({ flex: '1', }); return html` `; }; }); popMenu(target, { options: { title: { onBack: reopen, text: 'Layout', }, items: [ menu => { const result = menu.renderItems(viewTypes); if (result.length) { return html`
${result}
`; } return html``; }, // menu.toggleSwitch({ // name: 'Show block icon', // on: true, // onChange: value => { // console.log(value); // }, // }), // menu.toggleSwitch({ // name: 'Show Vertical lines', // on: true, // onChange: value => { // console.log(value); // }, // }), ], }, }); }, prefix: LayoutIcon(), }), ], }) ); items.push( menu.group({ items: createSettingMenus(target, dataViewLogic, reopen), }) ); items.push( menu.group({ items: [ menu.action({ name: 'Duplicate', prefix: DuplicateIcon(), select: () => { view.duplicate(); }, }), menu.action({ name: 'Delete', prefix: DeleteIcon(), select: () => { view.delete(); }, class: { 'delete-item': true }, }), ], }) ); popMenu(target, { options: { title: { text: 'View settings', }, items, onClose: onClose, }, }); };