refactor(editor): add a layer of ui-logic to enhance type safety (#12511)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced modular UI logic layers for Kanban and Table views, enhancing maintainability and scalability.
  - Added new CSS-in-JS style modules for database blocks and table views, improving visual consistency.
  - Expanded telemetry event tracking for database views, properties, filters, and groups.
  - Added utility functions for lazy initialization and cached computed values.

- **Refactor**
  - Unified logic and state management across Kanban and Table views by replacing direct component dependencies with logic-centric architecture.
  - Updated components and widgets to use the new logic-based approach for state, selection, and event handling.
  - Replaced inline styles with CSS classes; updated class names to align with new component structure.
  - Centralized state access through UI logic instances, eliminating direct DOM queries and simplifying dependencies.
  - Consolidated Kanban and Table view presets effects for streamlined initialization.
  - Replaced Lit reactive state with Preact signals in multiple components for improved reactivity.
  - Split monolithic components into separate logic and UI classes for clearer separation of concerns.
  - Removed obsolete components and consolidated exports for cleaner API surface.

- **Bug Fixes**
  - Enhanced selection and interaction reliability in database cells and views.
  - Fixed scrolling issues on mobile table views for improved compatibility.

- **Chores**
  - Updated end-to-end test selectors to reflect new component names and structure.
  - Removed deprecated utilities and cleaned up unused imports.

- **Documentation**
  - Improved type definitions and public API exports for better developer experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
zzj3720
2025-05-27 09:36:44 +00:00
parent 9bf86e3f61
commit 0f1a3c212d
109 changed files with 2625 additions and 2436 deletions
@@ -5,7 +5,7 @@ import { filterTraitKey } from '../../../core/filter/trait.js';
import type { DataViewWidgetProps } from '../../../core/widget/types.js';
export const renderFilterBar = (props: DataViewWidgetProps) => {
const filterTrait = props.dataViewInstance.view.traitGet(filterTraitKey);
const filterTrait = props.dataViewLogic.view.traitGet(filterTraitKey);
if (!filterTrait) {
return;
}
@@ -15,7 +15,7 @@ import { renderSortBar } from './sort/index.js';
export const widgetQuickSettingBar = defineUniComponent(
(props: DataViewWidgetProps) => {
const view = props.dataViewInstance.view;
const view = props.dataViewLogic.view;
const barList = [renderSortBar(props), renderFilterBar(props)].filter(
Boolean
);
@@ -8,7 +8,7 @@ import type { DataViewWidgetProps } from '../../../core/widget/types.js';
import { popSortRoot } from './root-panel.js';
export const renderSortBar = (props: DataViewWidgetProps) => {
const sortTrait = props.dataViewInstance.view.traitGet(sortTraitKey);
const sortTrait = props.dataViewLogic.view.traitGet(sortTraitKey);
if (!sortTrait) {
return;
}
@@ -19,7 +19,7 @@ export const renderSortBar = (props: DataViewWidgetProps) => {
const text = count === 1 ? html`1 Sort` : html`${count} Sorts`;
const click = (event: MouseEvent) => {
popSortRoot(popupTargetFromElement(event.currentTarget as HTMLElement), {
sortUtils: createSortUtils(sortTrait, props.dataViewInstance.eventTrace),
sortUtils: createSortUtils(sortTrait, props.dataViewLogic.eventTrace),
});
};
return html` <data-view-component-button
@@ -9,10 +9,8 @@ import { styleMap } from 'lit/directives/style-map.js';
import { stopPropagation } from '../../../../core/utils/event.js';
import { WidgetBase } from '../../../../core/widget/widget-base.js';
import type {
KanbanSingleView,
TableSingleView,
} from '../../../../view-presets/index.js';
import type { KanbanViewUILogic } from '../../../../view-presets/kanban/pc/kanban-view-ui-logic.js';
import type { VirtualTableViewUILogic } from '../../../../view-presets/table/pc-virtual/table-view-ui-logic.js';
const styles = css`
.affine-database-search-container {
@@ -89,13 +87,13 @@ const styles = css`
`;
export class DataViewHeaderToolsSearch extends WidgetBase<
TableSingleView | KanbanSingleView
VirtualTableViewUILogic | KanbanViewUILogic
> {
static override styles = styles;
private readonly _clearSearch = () => {
this._searchInput.value = '';
this.view.setSearch('');
this.dataViewLogic.view.setSearch('');
this.preventBlur = true;
setTimeout(() => {
this.preventBlur = false;
@@ -110,7 +108,7 @@ export class DataViewHeaderToolsSearch extends WidgetBase<
private readonly _onSearch = (event: InputEvent) => {
const el = event.target as HTMLInputElement;
const inputValue = el.value.trim();
this.view.setSearch(inputValue);
this.dataViewLogic.view.setSearch(inputValue);
};
private readonly _onSearchBlur = () => {
@@ -124,7 +122,7 @@ export class DataViewHeaderToolsSearch extends WidgetBase<
if (event.key === 'Escape') {
if (this._searchInput.value) {
this._searchInput.value = '';
this.view.setSearch('');
this.dataViewLogic.view.setSearch('');
} else {
this.showSearch = false;
}
@@ -43,7 +43,7 @@ export class DataViewHeaderToolsSort extends WidgetBase {
sortUtils$ = computed(() => {
const sortTrait = this.view.traitGet(sortTraitKey);
if (sortTrait) {
return createSortUtils(sortTrait, this.dataViewInstance.eventTrace);
return createSortUtils(sortTrait, this.dataViewLogic.eventTrace);
}
return;
});
@@ -19,22 +19,22 @@ const styles = css`
export class DataViewHeaderToolsAddRow extends WidgetBase {
static override styles = styles;
private readonly _onAddNewRecord = () => {
if (this.readonly) return;
this.viewMethods.addRow?.('start');
private readonly onAddNewRecord = () => {
if (this.readonly$.value) return;
this.dataViewLogic.addRow?.('start');
};
private get readonly() {
return this.view.readonly$.value;
private get readonly$() {
return this.view.readonly$;
}
override render() {
if (this.readonly) {
if (this.readonly$.value) {
return;
}
return html` <data-view-component-button
class="affine-database-toolbar-item new-record"
.onClick="${this._onAddNewRecord}"
.onClick="${this.onAddNewRecord}"
.icon="${PlusIcon()}"
.text="${IS_MOBILE
? html`<span style="font-weight: 500">New</span>`
@@ -29,7 +29,7 @@ import {
} from '../../../../core/group-by/setting.js';
import { groupTraitKey } from '../../../../core/group-by/trait.js';
import {
type DataViewInstance,
type DataViewUILogicBase,
emptyFilterGroup,
popCreateFilter,
renderUniLit,
@@ -73,7 +73,7 @@ export class DataViewHeaderToolsViewOptions extends WidgetBase {
};
openMoreAction = (target: PopupTarget) => {
popViewOptions(target, this.dataViewInstance);
popViewOptions(target, this.dataViewLogic);
};
override render() {
@@ -96,10 +96,10 @@ declare global {
}
const createSettingMenus = (
target: PopupTarget,
dataViewInstance: DataViewInstance,
dataViewLogic: DataViewUILogicBase,
reopen: () => void
) => {
const view = dataViewInstance.view;
const view = dataViewLogic.view;
const settingItems: MenuConfig[] = [];
settingItems.push(
menu.action({
@@ -177,7 +177,7 @@ const createSettingMenus = (
const sortList = sortTrait.sortList$.value;
const sortUtils = createSortUtils(
sortTrait,
dataViewInstance.eventTrace
dataViewLogic.eventTrace
);
if (!sortList.length) {
popCreateSort(target, {
@@ -225,12 +225,12 @@ const createSettingMenus = (
};
export const popViewOptions = (
target: PopupTarget,
dataViewInstance: DataViewInstance,
dataViewLogic: DataViewUILogicBase,
onClose?: () => void
) => {
const view = dataViewInstance.view;
const view = dataViewLogic.view;
const reopen = () => {
popViewOptions(target, dataViewInstance);
popViewOptions(target, dataViewLogic);
};
const items: MenuConfig[] = [];
items.push(
@@ -291,7 +291,7 @@ export const popViewOptions = (
return;
}
view.manager.viewChangeType(id, meta.type);
dataViewInstance.clearSelection();
dataViewLogic.clearSelection();
},
class: {},
};
@@ -345,7 +345,7 @@ export const popViewOptions = (
items.push(
menu.group({
items: createSettingMenus(target, dataViewInstance, reopen),
items: createSettingMenus(target, dataViewLogic, reopen),
})
);
items.push(
@@ -52,7 +52,7 @@ export class DataViewHeaderTools extends WidgetBase {
return html` <div class="${classList}">
${repeat(tools ?? [], uni => {
return renderUniLit(uni, {
dataViewInstance: this.dataViewInstance,
dataViewLogic: this.dataViewLogic,
});
})}
</div>`;
@@ -264,7 +264,7 @@ export class DataViewHeaderViews extends WidgetBase {
<div
class="${classList}"
style="margin-right: 4px;"
@click="${(event: MouseEvent) => this._clickView(event, id)}"
@click="${(event: MouseEvent) => this.clickView(event, id)}"
>
<uni-lit class="icon" .uni="${this.getRenderer(id)?.icon}"></uni-lit>
<div class="name">${view?.name}</div>
@@ -281,7 +281,7 @@ export class DataViewHeaderViews extends WidgetBase {
return this.dataSource.viewMetaGetById(viewId)?.renderer;
}
_clickView(event: MouseEvent, id: string) {
private clickView(event: MouseEvent, id: string) {
if (this.viewManager.currentViewId$.value !== id) {
this.viewManager.setCurrentView(id);
this.onChangeView?.(id);