feat(editor): add grouping support for member property of the database block (#12243)

close: BS-3433

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

- **New Features**
  - Introduced advanced group-by configurations for database blocks with user membership support.
  - Added a React hook for fetching and displaying user information in member-related components.
  - Enabled dynamic user and membership data types in database properties.

- **Improvements**
  - Replaced context-based service access with a dependency injection system for shared services and state.
  - Enhanced type safety and consistency across group-by UI components and data handling.
  - Centralized group data management with a new Group class and refined group trait logic.

- **Bug Fixes**
  - Improved reliability and consistency in retrieving and rendering user and group information.

- **Style**
  - Removed obsolete member selection styles for cleaner UI code.

- **Chores**
  - Registered external group-by configurations via dependency injection.
  - Refactored internal APIs for data sources, views, and group-by matchers to use service-based patterns.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
zzj3720
2025-05-13 13:53:37 +00:00
parent fe2fc892df
commit a2a90df276
59 changed files with 702 additions and 374 deletions

View File

@@ -1,7 +1,4 @@
import { createContextKey } from '@blocksuite/data-view';
import { createIdentifier } from '@blocksuite/global/di';
import type { EditorHost } from '@blocksuite/std';
export const HostContextKey = createContextKey<EditorHost | undefined>(
'editor-host',
undefined
);
export const EditorHostKey = createIdentifier<EditorHost>('editor-host');

View File

@@ -57,6 +57,9 @@ type SpacialProperty = {
valueGet: (rowId: string, propertyId: string) => unknown;
};
export class DatabaseBlockDataSource extends DataSourceBase {
override get parentProvider() {
return this._model.store.provider;
}
spacialProperties: Record<string, SpacialProperty> = {
'created-time': {
valueSet: () => {},
@@ -186,9 +189,13 @@ export class DatabaseBlockDataSource extends DataSourceBase {
);
});
constructor(model: DatabaseBlockModel) {
constructor(
model: DatabaseBlockModel,
init?: (dataSource: DatabaseBlockDataSource) => void
) {
super();
this._model = model;
this._model = model; // ensure invariants first
init?.(this); // then allow external initialisation
}
private _runCapture() {

View File

@@ -27,6 +27,7 @@ import {
type DataViewWidget,
type DataViewWidgetProps,
defineUniComponent,
ExternalGroupByConfigProvider,
renderUniLit,
type SingleView,
uniMap,
@@ -47,7 +48,7 @@ import { css, html, nothing, unsafeCSS } from 'lit';
import { popSideDetail } from './components/layout.js';
import { DatabaseConfigExtension } from './config.js';
import { HostContextKey } from './context/host-context.js';
import { EditorHostKey } from './context/host-context.js';
import { DatabaseBlockDataSource } from './data-source.js';
import { BlockRenderer } from './detail-panel/block-renderer.js';
import { NoteRenderer } from './detail-panel/note-renderer.js';
@@ -333,8 +334,17 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
get dataSource(): DatabaseBlockDataSource {
if (!this._dataSource) {
this._dataSource = new DatabaseBlockDataSource(this.model);
this._dataSource.contextSet(HostContextKey, this.host);
this._dataSource = new DatabaseBlockDataSource(this.model, dataSource => {
dataSource.serviceSet(EditorHostKey, this.host);
this.std.provider
.getAll(ExternalGroupByConfigProvider)
.forEach(config => {
dataSource.serviceSet(
ExternalGroupByConfigProvider(config.name),
config
);
});
});
const id = currentViewStorage.getCurrentView(this.model.id);
if (id && this.dataSource.viewManager.viewGet(id)) {
this.dataSource.viewManager.setCurrentView(id);

View File

@@ -1,14 +0,0 @@
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
import { DatabaseBlockAdapterExtensions } from './adapters/extension.js';
import { databaseSlashMenuConfig } from './configs/slash-menu.js';
export const DatabaseBlockSpec: ExtensionType[] = [
FlavourExtension('affine:database'),
BlockViewExtension('affine:database', literal`affine-database`),
DatabaseBlockAdapterExtensions,
SlashMenuConfigExtension('affine:database', databaseSlashMenuConfig),
].flat();

View File

@@ -4,7 +4,6 @@ export * from './config';
export * from './context';
export * from './data-source';
export * from './database-block';
export * from './database-spec';
export * from './detail-panel/block-renderer';
export * from './detail-panel/note-renderer';
export * from './properties';

View File

@@ -15,7 +15,7 @@ import { computed } from '@preact/signals-core';
import { html, nothing, type PropertyValues } from 'lit';
import { createRef, ref } from 'lit/directives/ref.js';
import { HostContextKey } from '../../context/host-context.js';
import { EditorHostKey } from '../../context/host-context.js';
import {
inlineLinkNodeStyle,
linkCellStyle,
@@ -88,7 +88,7 @@ export class LinkCell extends BaseCellRenderer<string, string> {
};
get std() {
const host = this.view.contextGet(HostContextKey);
const host = this.view.serviceGet(EditorHostKey);
return host?.std;
}

View File

@@ -24,7 +24,7 @@ import { computed, effect, signal } from '@preact/signals-core';
import { ref } from 'lit/directives/ref.js';
import { html } from 'lit/static-html.js';
import { HostContextKey } from '../../context/host-context.js';
import { EditorHostKey } from '../../context/host-context.js';
import type { DatabaseBlockComponent } from '../../database-block.js';
import {
richTextCellStyle,
@@ -87,7 +87,7 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
get inlineManager() {
return this.view
.contextGet(HostContextKey)
.serviceGet(EditorHostKey)
?.std.get(DefaultInlineManagerExtension.identifier);
}
@@ -98,7 +98,7 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
}
get host() {
return this.view.contextGet(HostContextKey);
return this.view.serviceGet(EditorHostKey);
}
private readonly richText$ = signal<RichText>();
@@ -398,7 +398,7 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
}
private get std() {
return this.view.contextGet(HostContextKey)?.std;
return this.view.serviceGet(EditorHostKey)?.std;
}
insertDelta = (delta: DeltaInsert<AffineTextAttributes>) => {

View File

@@ -5,7 +5,7 @@ import { Text } from '@blocksuite/store';
import * as Y from 'yjs';
import zod from 'zod';
import { HostContextKey } from '../../context/host-context.js';
import { EditorHostKey } from '../../context/host-context.js';
import { isLinkedDoc } from '../../utils/title-doc.js';
export const richTextColumnType = propertyType('rich-text');
@@ -43,7 +43,7 @@ export const richTextPropertyModelConfig = richTextColumnType.modelConfig({
},
toJson: ({ value, dataSource }) => {
if (!value) return null;
const host = dataSource.contextGet(HostContextKey);
const host = dataSource.serviceGet(EditorHostKey);
if (host) {
const collection = host.std.workspace;
const yText = toYText(value);

View File

@@ -3,7 +3,7 @@ import { Text } from '@blocksuite/store';
import { Doc } from 'yjs';
import zod from 'zod';
import { HostContextKey } from '../../context/host-context.js';
import { EditorHostKey } from '../../context/host-context.js';
import { isLinkedDoc } from '../../utils/title-doc.js';
export const titleColumnType = propertyType('title');
@@ -28,7 +28,7 @@ export const titlePropertyModelConfig = titleColumnType.modelConfig({
},
toJson: ({ value, dataSource }) => {
if (!value) return '';
const host = dataSource.contextGet(HostContextKey);
const host = dataSource.serviceGet(EditorHostKey);
if (host) {
const collection = host.std.workspace;
const deltas = value.deltas$.value;

View File

@@ -17,7 +17,7 @@ import { property } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { html } from 'lit/static-html.js';
import { HostContextKey } from '../../context/host-context.js';
import { EditorHostKey } from '../../context/host-context.js';
import type { DatabaseBlockComponent } from '../../database-block.js';
import { getSingleDocIdFromText } from '../../utils/title-doc.js';
import {
@@ -32,7 +32,7 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
docId$ = signal<string>();
get host() {
return this.view.contextGet(HostContextKey);
return this.view.serviceGet(EditorHostKey);
}
get inlineEditor() {
@@ -50,7 +50,7 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
}
get std() {
return this.view.contextGet(HostContextKey)?.std;
return this.view.serviceGet(EditorHostKey)?.std;
}
private readonly _onCopy = (e: ClipboardEvent) => {