refactor(editor): support virtual scroll for table view of database block (#11642)

close: BS-3378

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

- **New Features**
  - Introduced a modular virtualized table view with grouping, selection, drag-and-drop, clipboard support, and batch task management for optimized rendering.
  - Added comprehensive keyboard shortcuts, drag-to-fill functionality, and clipboard operations for efficient table editing.
  - Enabled dynamic column statistics, number formatting controls, and flexible switching between virtual and standard table views via a feature flag.
  - Provided detailed row and group header/footer components with context menus, row management actions, and column reordering/resizing.
  - Added a table view selector component to toggle between virtual and standard table views based on feature flags.
- **Style**
  - Added extensive styling modules for virtual table elements including headers, footers, rows, cells, and interactive controls.
- **Chores**
  - Registered numerous custom elements via modular effect functions to streamline component initialization.
  - Updated feature flag system to include virtual table scrolling toggle.
  - Added new dependencies to support styling and component functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
zzj3720
2025-04-29 09:52:07 +00:00
parent bce9f8cdf0
commit 1ea73456ca
55 changed files with 6941 additions and 227 deletions

View File

@@ -14,7 +14,19 @@ import {
} from '@blocksuite/icons/lit';
import type { BlockModel } from '@blocksuite/store';
import type { TemplateResult } from 'lit';
const icons: Record<string, TemplateResult> = {
text: TextIcon(),
quote: QuoteIcon(),
h1: Heading1Icon(),
h2: Heading2Icon(),
h3: Heading3Icon(),
h4: Heading4Icon(),
h5: Heading5Icon(),
h6: Heading6Icon(),
bulleted: BulletedListIcon(),
numbered: NumberedListIcon(),
todo: CheckBoxCheckLinearIcon(),
};
export const getIcon = (
model: BlockModel & {
props: {
@@ -24,27 +36,10 @@ export const getIcon = (
): TemplateResult => {
if (model.flavour === 'affine:paragraph') {
const type = model.props.type as ParagraphType;
return (
{
text: TextIcon(),
quote: QuoteIcon(),
h1: Heading1Icon(),
h2: Heading2Icon(),
h3: Heading3Icon(),
h4: Heading4Icon(),
h5: Heading5Icon(),
h6: Heading6Icon(),
} as Record<ParagraphType, TemplateResult>
)[type];
return icons[type] ?? TextIcon();
}
if (model.flavour === 'affine:list') {
return (
{
bulleted: BulletedListIcon(),
numbered: NumberedListIcon(),
todo: CheckBoxCheckLinearIcon(),
}[model.props.type ?? 'bulleted'] ?? BulletedListIcon()
);
return icons[model.props.type ?? 'bulleted'] ?? BulletedListIcon();
}
return TextIcon();
};

View File

@@ -71,11 +71,15 @@ export class DatabaseBlockDataSource extends DataSourceBase {
override featureFlags$: ReadonlySignal<DatabaseFlags> = computed(() => {
const featureFlagService = this.doc.get(FeatureFlagService);
const flag = featureFlagService.getFlag(
const enableNumberFormat = featureFlagService.getFlag(
'enable_database_number_formatting'
);
const enableTableVirtualScroll = featureFlagService.getFlag(
'enable_table_virtual_scroll'
);
return {
enable_number_formatting: flag ?? false,
enable_number_formatting: enableNumberFormat ?? false,
enable_table_virtual_scroll: enableTableVirtualScroll ?? false,
};
});

View File

@@ -12,7 +12,7 @@ import { BaseCellRenderer } from '@blocksuite/data-view';
import { IS_MAC } from '@blocksuite/global/env';
import { LinkedPageIcon } from '@blocksuite/icons/lit';
import type { BlockSnapshot, DeltaInsert, Text } from '@blocksuite/store';
import { signal } from '@preact/signals-core';
import { computed, signal } from '@preact/signals-core';
import { property } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { html } from 'lit/static-html.js';
@@ -217,17 +217,23 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
super.firstUpdated(props);
this.richText.value?.updateComplete
.then(() => {
this.disposables.addFromEvent(
this.richText.value,
'copy',
this._onCopy
);
this.disposables.addFromEvent(this.richText.value, 'cut', this._onCut);
this.disposables.addFromEvent(
this.richText.value,
'paste',
this._onPaste
);
if (this.richText.value) {
this.disposables.addFromEvent(
this.richText.value,
'copy',
this._onCopy
);
this.disposables.addFromEvent(
this.richText.value,
'cut',
this._onCut
);
this.disposables.addFromEvent(
this.richText.value,
'paste',
this._onPaste
);
}
})
.catch(console.error);
}
@@ -261,7 +267,14 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
class="${titleRichTextStyle}"
></rich-text>`;
}
icon$ = computed(() => {
const iconColumn = this.view.mainProperties$.value.iconColumn;
if (!iconColumn) return;
const icon = this.view.cellValueGet(this.cell.rowId, iconColumn) as string;
if (!icon) return;
return icon;
});
renderIcon() {
if (!this.showIcon) {
return;
@@ -271,10 +284,7 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
${LinkedPageIcon({})}
</div>`;
}
const iconColumn = this.view.mainProperties$.value.iconColumn;
if (!iconColumn) return;
const icon = this.view.cellValueGet(this.cell.rowId, iconColumn) as string;
const icon = this.icon$.value;
if (!icon) return;
return html` <div class="${headerAreaIconStyle}">${icon}</div>`;