refactor(editor): remove database-service (#9769)

close: BS-2426
This commit is contained in:
zzj3720
2025-01-18 05:36:15 +00:00
parent ad814a0f4f
commit 95c0f59d96
30 changed files with 448 additions and 427 deletions

View File

@@ -211,7 +211,7 @@ export class BlockQueryDataSource extends DataSourceBase {
} }
} }
propertyDuplicate(_columnId: string): string { propertyDuplicate(_columnId: string): string | undefined {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@@ -1,4 +1,11 @@
import type { DatabaseBlockModel } from '@blocksuite/affine-model';
import type { BlockCommands, Command } from '@blocksuite/block-std'; import type { BlockCommands, Command } from '@blocksuite/block-std';
import type { BlockModel, Store } from '@blocksuite/store';
import {
DatabaseBlockDataSource,
databaseViewInitTemplate,
} from './data-source';
export const insertDatabaseBlockCommand: Command< export const insertDatabaseBlockCommand: Command<
'selectedModels', 'selectedModels',
@@ -17,8 +24,7 @@ export const insertDatabaseBlockCommand: Command<
? selectedModels[0] ? selectedModels[0]
: selectedModels[selectedModels.length - 1]; : selectedModels[selectedModels.length - 1];
const service = std.getService('affine:database'); if (!targetModel) return;
if (!service || !targetModel) return;
const result = std.store.addSiblingBlocks( const result = std.store.addSiblingBlocks(
targetModel, targetModel,
@@ -29,7 +35,7 @@ export const insertDatabaseBlockCommand: Command<
if (string == null) return; if (string == null) return;
service.initDatabaseBlock(std.store, targetModel, string, viewType, false); initDatabaseBlock(std.store, targetModel, string, viewType, false);
if (removeEmptyLine && targetModel.text?.length === 0) { if (removeEmptyLine && targetModel.text?.length === 0) {
std.store.deleteBlock(targetModel); std.store.deleteBlock(targetModel);
@@ -38,6 +44,28 @@ export const insertDatabaseBlockCommand: Command<
next({ insertedDatabaseBlockId: string }); next({ insertedDatabaseBlockId: string });
}; };
export const initDatabaseBlock = (
doc: Store,
model: BlockModel,
databaseId: string,
viewType: string,
isAppendNewRow = true
) => {
const blockModel = doc.getBlock(databaseId)?.model as
| DatabaseBlockModel
| undefined;
if (!blockModel) {
return;
}
const datasource = new DatabaseBlockDataSource(blockModel);
databaseViewInitTemplate(datasource, viewType);
if (isAppendNewRow) {
const parent = doc.getParent(model);
if (!parent) return;
doc.addBlock('affine:paragraph', {}, parent.id);
}
};
export const commands: BlockCommands = { export const commands: BlockCommands = {
insertDatabaseBlock: insertDatabaseBlockCommand, insertDatabaseBlock: insertDatabaseBlockCommand,
}; };

View File

@@ -1,4 +1,8 @@
import type { DatabaseBlockModel } from '@blocksuite/affine-model'; import type {
Column,
ColumnUpdater,
DatabaseBlockModel,
} from '@blocksuite/affine-model';
import { FeatureFlagService } from '@blocksuite/affine-shared/services'; import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { import {
insertPositionToIndex, insertPositionToIndex,
@@ -19,7 +23,6 @@ import {
import { propertyPresets } from '@blocksuite/data-view/property-presets'; import { propertyPresets } from '@blocksuite/data-view/property-presets';
import { IS_MOBILE } from '@blocksuite/global/env'; import { IS_MOBILE } from '@blocksuite/global/env';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { assertExists } from '@blocksuite/global/utils';
import { type BlockModel, nanoid, Text } from '@blocksuite/store'; import { type BlockModel, nanoid, Text } from '@blocksuite/store';
import { computed, type ReadonlySignal } from '@preact/signals-core'; import { computed, type ReadonlySignal } from '@preact/signals-core';
@@ -29,7 +32,6 @@ import {
databaseBlockPropertyList, databaseBlockPropertyList,
databasePropertyConverts, databasePropertyConverts,
} from './properties/index.js'; } from './properties/index.js';
import { titlePropertyModelConfig } from './properties/title/define.js';
import { import {
addProperty, addProperty,
applyCellsUpdate, applyCellsUpdate,
@@ -38,7 +40,6 @@ import {
deleteRows, deleteRows,
deleteView, deleteView,
duplicateView, duplicateView,
findPropertyIndex,
getCell, getCell,
getProperty, getProperty,
moveViewTo, moveViewTo,
@@ -69,7 +70,17 @@ export class DatabaseBlockDataSource extends DataSourceBase {
}); });
properties$: ReadonlySignal<string[]> = computed(() => { properties$: ReadonlySignal<string[]> = computed(() => {
return this._model.columns$.value.map(column => column.id); const fixedPropertiesSet = new Set(this.fixedProperties$.value);
const properties: string[] = [];
this._model.columns$.value.forEach(column => {
if (fixedPropertiesSet.has(column.type)) {
fixedPropertiesSet.delete(column.type);
}
properties.push(column.id);
});
const result = [...fixedPropertiesSet, ...properties];
return result;
}); });
readonly$: ReadonlySignal<boolean> = computed(() => { readonly$: ReadonlySignal<boolean> = computed(() => {
@@ -98,9 +109,15 @@ export class DatabaseBlockDataSource extends DataSourceBase {
return this._model.doc; return this._model.doc;
} }
get propertyMetas(): PropertyMetaConfig<any, any, any>[] { allPropertyMetas$ = computed<PropertyMetaConfig<any, any, any>[]>(() => {
return databaseBlockPropertyList; return databaseBlockPropertyList;
} });
propertyMetas$ = computed<PropertyMetaConfig[]>(() => {
return this.allPropertyMetas$.value.filter(
v => !v.config.fixed && !v.config.hide
);
});
constructor(model: DatabaseBlockModel) { constructor(model: DatabaseBlockModel) {
super(); super();
@@ -147,13 +164,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
newValue: value, newValue: value,
}); });
} }
if (type === 'title' && newValue instanceof Text) {
this._model.doc.transact(() => {
this._model.text?.clear();
this._model.text?.join(newValue);
});
return;
}
if (this._model.columns$.value.some(v => v.id === propertyId)) { if (this._model.columns$.value.some(v => v.id === propertyId)) {
updateCell(this._model, rowId, { updateCell(this._model, rowId, {
columnId: propertyId, columnId: propertyId,
@@ -193,34 +203,108 @@ export class DatabaseBlockDataSource extends DataSourceBase {
return result; return result;
} }
propertyDataGet(propertyId: string): Record<string, unknown> { protected override getNormalPropertyAndIndex(propertyId: string):
return ( | {
this._model.columns$.value.find(v => v.id === propertyId)?.data ?? {} column: Column<Record<string, unknown>>;
index: number;
}
| undefined {
const index = this._model.columns$.value.findIndex(
v => v.id === propertyId
); );
if (index >= 0) {
const column = this._model.columns$.value[index];
if (!column) {
return;
}
return {
column,
index,
};
}
return;
}
private getPropertyAndIndex(propertyId: string):
| {
column: Column<Record<string, unknown>>;
index: number;
}
| undefined {
const result = this.getNormalPropertyAndIndex(propertyId);
if (result) {
return result;
}
if (this.isFixedProperty(propertyId)) {
const meta = this.propertyMetaGet(propertyId);
const defaultData = meta.config.fixed?.defaultData ?? {};
return {
column: {
data: defaultData,
id: propertyId,
type: propertyId,
name: meta.config.name,
},
index: -1,
};
}
return undefined;
}
private updateProperty(id: string, updater: ColumnUpdater) {
const result = this.getPropertyAndIndex(id);
if (!result) {
return;
}
const { column: prevColumn, index } = result;
this._model.doc.transact(() => {
if (index >= 0) {
const result = updater(prevColumn);
this._model.columns[index] = { ...prevColumn, ...result };
} else {
const result = updater(prevColumn);
this._model.columns = [
...this._model.columns,
{ ...prevColumn, ...result },
];
}
});
return id;
}
propertyDataGet(propertyId: string): Record<string, unknown> {
const result = this.getPropertyAndIndex(propertyId);
if (!result) {
return {};
}
return result.column.data;
} }
propertyDataSet(propertyId: string, data: Record<string, unknown>): void { propertyDataSet(propertyId: string, data: Record<string, unknown>): void {
this._runCapture(); this._runCapture();
this.updateProperty(propertyId, () => ({ data }));
updateProperty(this._model, propertyId, () => ({ data }));
applyPropertyUpdate(this._model); applyPropertyUpdate(this._model);
} }
propertyDataTypeGet(propertyId: string): TypeInstance | undefined { propertyDataTypeGet(propertyId: string): TypeInstance | undefined {
const data = this._model.columns$.value.find(v => v.id === propertyId); const result = this.getPropertyAndIndex(propertyId);
if (!data) { if (!result) {
return; return;
} }
const meta = this.propertyMetaGet(data.type); const { column } = result;
const meta = this.propertyMetaGet(column.type);
return meta.config.type({ return meta.config.type({
data: data.data, data: column.data,
dataSource: this, dataSource: this,
}); });
} }
propertyDelete(id: string): void { propertyDelete(id: string): void {
if (this.isFixedProperty(id)) {
return;
}
this.doc.captureSync(); this.doc.captureSync();
const index = findPropertyIndex(this._model, id); const index = this._model.columns.findIndex(v => v.id === id);
if (index < 0) return; if (index < 0) return;
this.doc.transact(() => { this.doc.transact(() => {
@@ -228,10 +312,15 @@ export class DatabaseBlockDataSource extends DataSourceBase {
}); });
} }
propertyDuplicate(propertyId: string): string { propertyDuplicate(propertyId: string): string | undefined {
if (this.isFixedProperty(propertyId)) {
return;
}
this.doc.captureSync(); this.doc.captureSync();
const currentSchema = getProperty(this._model, propertyId); const currentSchema = getProperty(this._model, propertyId);
assertExists(currentSchema); if (!currentSchema) {
return;
}
const { id: copyId, ...nonIdProps } = currentSchema; const { id: copyId, ...nonIdProps } = currentSchema;
const names = new Set(this._model.columns$.value.map(v => v.name)); const names = new Set(this._model.columns$.value.map(v => v.name));
let index = 1; let index = 1;
@@ -267,14 +356,16 @@ export class DatabaseBlockDataSource extends DataSourceBase {
if (propertyId === 'type') { if (propertyId === 'type') {
return 'Block Type'; return 'Block Type';
} }
return ( const result = this.getPropertyAndIndex(propertyId);
this._model.columns$.value.find(v => v.id === propertyId)?.name ?? '' if (!result) {
); return '';
}
return result.column.name;
} }
propertyNameSet(propertyId: string, name: string): void { propertyNameSet(propertyId: string, name: string): void {
this.doc.captureSync(); this.doc.captureSync();
updateProperty(this._model, propertyId, () => ({ name })); this.updateProperty(propertyId, () => ({ name }));
applyPropertyUpdate(this._model); applyPropertyUpdate(this._model);
} }
@@ -287,12 +378,17 @@ export class DatabaseBlockDataSource extends DataSourceBase {
if (propertyId === 'type') { if (propertyId === 'type') {
return 'image'; return 'image';
} }
return ( const result = this.getPropertyAndIndex(propertyId);
this._model.columns$.value.find(v => v.id === propertyId)?.type ?? '' if (!result) {
); return '';
}
return result.column.type;
} }
propertyTypeSet(propertyId: string, toType: string): void { propertyTypeSet(propertyId: string, toType: string): void {
if (this.isFixedProperty(propertyId)) {
return;
}
const currentType = this.propertyTypeGet(propertyId); const currentType = this.propertyTypeGet(propertyId);
const currentData = this.propertyDataGet(propertyId); const currentData = this.propertyDataGet(propertyId);
const rows = this.rows$.value; const rows = this.rows$.value;
@@ -409,44 +505,18 @@ export class DatabaseBlockDataSource extends DataSourceBase {
} }
} }
export const databaseViewAddView = (
model: DatabaseBlockModel,
viewType: string
) => {
const dataSource = new DatabaseBlockDataSource(model);
dataSource.viewManager.viewAdd(viewType);
};
export const databaseViewInitEmpty = (
model: DatabaseBlockModel,
viewType: string
) => {
addProperty(
model,
'start',
titlePropertyModelConfig.create(titlePropertyModelConfig.config.name)
);
databaseViewAddView(model, viewType);
};
export const databaseViewInitConvert = (
model: DatabaseBlockModel,
viewType: string
) => {
addProperty(
model,
'end',
propertyPresets.multiSelectPropertyConfig.create('Tag', { options: [] })
);
databaseViewInitEmpty(model, viewType);
};
export const databaseViewInitTemplate = ( export const databaseViewInitTemplate = (
model: DatabaseBlockModel, datasource: DatabaseBlockDataSource,
viewType: string viewType: string
) => { ) => {
const ids = [nanoid(), nanoid(), nanoid()] as const; const ids = [nanoid(), nanoid(), nanoid()] as const;
const statusId = addProperty( const titleId = datasource.properties$.value[0];
model, const statusId = datasource.propertyAdd(
'end', 'end',
propertyPresets.selectPropertyConfig.create('Status', { propertyPresets.selectPropertyConfig.type
);
datasource.propertyNameSet(statusId, 'Status');
datasource.propertyDataSet(statusId, {
options: [ options: [
{ {
id: ids[0], id: ids[0],
@@ -464,22 +534,25 @@ export const databaseViewInitTemplate = (
value: 'Done', value: 'Done',
}, },
], ],
})
);
for (let i = 0; i < 4; i++) {
const rowId = model.doc.addBlock(
'affine:paragraph',
{
text: new Text(`Task ${i + 1}`),
},
model.id
);
updateCell(model, rowId, {
columnId: statusId,
value: ids[i],
}); });
for (let i = 0; i < 4; i++) {
const rowId = datasource.rowAdd('end');
if (titleId) {
const text = datasource.cellValueGet(rowId, titleId);
if (text instanceof Text) {
text.replace(0, text.length, `Task ${i + 1}`);
} }
databaseViewInitEmpty(model, viewType); }
datasource.cellValueChange(rowId, statusId, ids[i]);
// const rowId = model.doc.addBlock(
// 'affine:paragraph',
// {
// text: new Text(`Task ${i + 1}`),
// },
// model.id
// );
}
datasource.viewManager.viewAdd(viewType);
}; };
export const convertToDatabase = (host: EditorHost, viewType: string) => { export const convertToDatabase = (host: EditorHost, viewType: string) => {
const [_, ctx] = host.std.command const [_, ctx] = host.std.command
@@ -511,8 +584,8 @@ export const convertToDatabase = (host: EditorHost, viewType: string) => {
if (!databaseModel) { if (!databaseModel) {
return; return;
} }
databaseViewInitConvert(databaseModel, viewType); const datasource = new DatabaseBlockDataSource(databaseModel);
applyPropertyUpdate(databaseModel); datasource.viewManager.viewAdd(viewType);
host.doc.moveBlocks(selectedModels, databaseModel); host.doc.moveBlocks(selectedModels, databaseModel);
const selectionManager = host.selection; const selectionManager = host.selection;

View File

@@ -51,17 +51,13 @@ import { popSideDetail } from './components/layout.js';
import type { DatabaseOptionsConfig } from './config.js'; import type { DatabaseOptionsConfig } from './config.js';
import { HostContextKey } from './context/host-context.js'; import { HostContextKey } from './context/host-context.js';
import { DatabaseBlockDataSource } from './data-source.js'; import { DatabaseBlockDataSource } from './data-source.js';
import type { DatabaseBlockService } from './database-service.js';
import { BlockRenderer } from './detail-panel/block-renderer.js'; import { BlockRenderer } from './detail-panel/block-renderer.js';
import { NoteRenderer } from './detail-panel/note-renderer.js'; import { NoteRenderer } from './detail-panel/note-renderer.js';
import { DatabaseSelection } from './selection.js'; import { DatabaseSelection } from './selection.js';
import { currentViewStorage } from './utils/current-view.js'; import { currentViewStorage } from './utils/current-view.js';
import { getSingleDocIdFromText } from './utils/title-doc.js'; import { getSingleDocIdFromText } from './utils/title-doc.js';
export class DatabaseBlockComponent extends CaptionedBlockComponent< export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBlockModel> {
DatabaseBlockModel,
DatabaseBlockService
> {
static override styles = css` static override styles = css`
${unsafeCSS(dataViewCommonStyle('affine-database'))} ${unsafeCSS(dataViewCommonStyle('affine-database'))}
affine-database { affine-database {

View File

@@ -1,59 +0,0 @@
import {
type DatabaseBlockModel,
DatabaseBlockSchema,
} from '@blocksuite/affine-model';
import { BlockService } from '@blocksuite/block-std';
import { viewPresets } from '@blocksuite/data-view/view-presets';
import type { BlockModel, Store } from '@blocksuite/store';
import {
databaseViewAddView,
databaseViewInitEmpty,
databaseViewInitTemplate,
} from './data-source.js';
import {
addProperty,
applyPropertyUpdate,
updateCell,
updateView,
} from './utils/block-utils.js';
export class DatabaseBlockService extends BlockService {
static override readonly flavour = DatabaseBlockSchema.model.flavour;
addColumn = addProperty;
applyColumnUpdate = applyPropertyUpdate;
databaseViewAddView = databaseViewAddView;
databaseViewInitEmpty = databaseViewInitEmpty;
updateCell = updateCell;
updateView = updateView;
viewPresets = viewPresets;
initDatabaseBlock(
doc: Store,
model: BlockModel,
databaseId: string,
viewType: string,
isAppendNewRow = true
) {
const blockModel = doc.getBlock(databaseId)?.model as
| DatabaseBlockModel
| undefined;
if (!blockModel) {
return;
}
databaseViewInitTemplate(blockModel, viewType);
if (isAppendNewRow) {
const parent = doc.getParent(model);
if (!parent) return;
doc.addBlock('affine:paragraph', {}, parent.id);
}
applyPropertyUpdate(blockModel);
}
}

View File

@@ -8,11 +8,9 @@ import { literal } from 'lit/static-html.js';
import { DatabaseBlockAdapterExtensions } from './adapters/extension.js'; import { DatabaseBlockAdapterExtensions } from './adapters/extension.js';
import { commands } from './commands.js'; import { commands } from './commands.js';
import { DatabaseBlockService } from './database-service.js';
export const DatabaseBlockSpec: ExtensionType[] = [ export const DatabaseBlockSpec: ExtensionType[] = [
FlavourExtension('affine:database'), FlavourExtension('affine:database'),
DatabaseBlockService,
CommandExtension(commands), CommandExtension(commands),
BlockViewExtension('affine:database', literal`affine-database`), BlockViewExtension('affine:database', literal`affine-database`),
DatabaseBlockAdapterExtensions, DatabaseBlockAdapterExtensions,

View File

@@ -3,7 +3,6 @@ import { CenterPeek } from './components/layout';
import { DatabaseTitle } from './components/title'; import { DatabaseTitle } from './components/title';
import type { DatabaseOptionsConfig } from './config'; import type { DatabaseOptionsConfig } from './config';
import { DatabaseBlockComponent } from './database-block'; import { DatabaseBlockComponent } from './database-block';
import type { DatabaseBlockService } from './database-service';
import { BlockRenderer } from './detail-panel/block-renderer'; import { BlockRenderer } from './detail-panel/block-renderer';
import { NoteRenderer } from './detail-panel/note-renderer'; import { NoteRenderer } from './detail-panel/note-renderer';
import { LinkCell, LinkCellEditing } from './properties/link/cell-renderer'; import { LinkCell, LinkCellEditing } from './properties/link/cell-renderer';
@@ -60,9 +59,5 @@ declare global {
*/ */
insertDatabaseBlock: typeof insertDatabaseBlockCommand; insertDatabaseBlock: typeof insertDatabaseBlockCommand;
} }
interface BlockServices {
'affine:database': DatabaseBlockService;
}
} }
} }

View File

@@ -6,7 +6,6 @@ export * from './adapters';
export type { DatabaseOptionsConfig } from './config'; export type { DatabaseOptionsConfig } from './config';
export * from './data-source'; export * from './data-source';
export * from './database-block'; export * from './database-block';
export * from './database-service';
export * from './database-spec'; export * from './database-spec';
export * from './detail-panel/block-renderer'; export * from './detail-panel/block-renderer';
export * from './detail-panel/note-renderer'; export * from './detail-panel/note-renderer';

View File

@@ -21,18 +21,12 @@ export const databaseBlockColumns = {
numberColumnConfig: numberPropertyConfig, numberColumnConfig: numberPropertyConfig,
progressColumnConfig: progressPropertyConfig, progressColumnConfig: progressPropertyConfig,
selectColumnConfig: selectPropertyConfig, selectColumnConfig: selectPropertyConfig,
imageColumnConfig: propertyPresets.imagePropertyConfig,
linkColumnConfig, linkColumnConfig,
richTextColumnConfig, richTextColumnConfig,
titleColumnConfig,
}; };
export const databaseBlockPropertyList = Object.values(databaseBlockColumns); export const databaseBlockPropertyList = Object.values(databaseBlockColumns);
export const databaseBlockHiddenColumns = [
propertyPresets.imagePropertyConfig,
titleColumnConfig,
];
const databaseBlockAllColumns = [
...databaseBlockPropertyList,
...databaseBlockHiddenColumns,
];
export const databaseBlockAllPropertyMap = Object.fromEntries( export const databaseBlockAllPropertyMap = Object.fromEntries(
databaseBlockAllColumns.map(v => [v.type, v as PropertyMetaConfig]) databaseBlockPropertyList.map(v => [v.type, v as PropertyMetaConfig])
); );

View File

@@ -3,7 +3,6 @@ import type {
RichText, RichText,
} from '@blocksuite/affine-components/rich-text'; } from '@blocksuite/affine-components/rich-text';
import { DefaultInlineManagerExtension } from '@blocksuite/affine-components/rich-text'; import { DefaultInlineManagerExtension } from '@blocksuite/affine-components/rich-text';
import type { RootBlockModel } from '@blocksuite/affine-model';
import { import {
ParseDocUrlProvider, ParseDocUrlProvider,
TelemetryProvider, TelemetryProvider,
@@ -23,8 +22,7 @@ import { assertExists } from '@blocksuite/global/utils';
import type { DeltaInsert } from '@blocksuite/inline'; import type { DeltaInsert } from '@blocksuite/inline';
import type { BlockSnapshot } from '@blocksuite/store'; import type { BlockSnapshot } from '@blocksuite/store';
import { Text } from '@blocksuite/store'; import { Text } from '@blocksuite/store';
import { computed, signal } from '@preact/signals-core'; import { css } from 'lit';
import { css, nothing } from 'lit';
import { query } from 'lit/decorators.js'; import { query } from 'lit/decorators.js';
import { keyed } from 'lit/directives/keyed.js'; import { keyed } from 'lit/directives/keyed.js';
import { html } from 'lit/static-html.js'; import { html } from 'lit/static-html.js';
@@ -143,12 +141,6 @@ abstract class BaseRichTextCell extends BaseCellRenderer<Text> {
?.std.get(DefaultInlineManagerExtension.identifier); ?.std.get(DefaultInlineManagerExtension.identifier);
} }
get service() {
return this.view
.contextGet(HostContextKey)
?.std.getService('affine:database');
}
get topContenteditableElement() { get topContenteditableElement() {
const databaseBlock = const databaseBlock =
this.closest<DatabaseBlockComponent>('affine-database'); this.closest<DatabaseBlockComponent>('affine-database');
@@ -172,19 +164,6 @@ abstract class BaseRichTextCell extends BaseCellRenderer<Text> {
@query('.affine-database-rich-text') @query('.affine-database-rich-text')
accessor _richTextElement!: HTMLElement; accessor _richTextElement!: HTMLElement;
docId$ = signal<string>();
isLinkedDoc$ = computed(() => false);
linkedDocTitle$ = computed(() => {
if (!this.docId$.value) {
return this.value;
}
const doc = this.host?.std.workspace.getDoc(this.docId$.value);
const root = doc?.root as RootBlockModel;
return root.title;
});
} }
export class RichTextCell extends BaseRichTextCell { export class RichTextCell extends BaseRichTextCell {
@@ -232,7 +211,6 @@ export class RichTextCell extends BaseRichTextCell {
} }
override render() { override render() {
if (!this.service) return nothing;
if (!this.value || !(this.value instanceof Text)) { if (!this.value || !(this.value instanceof Text)) {
return html`<div class="affine-database-rich-text"></div>`; return html`<div class="affine-database-rich-text"></div>`;
} }
@@ -492,7 +470,6 @@ export class RichTextCellEditing extends BaseRichTextCell {
override connectedCallback() { override connectedCallback() {
super.connectedCallback(); super.connectedCallback();
if (!this.value || typeof this.value === 'string') { if (!this.value || typeof this.value === 'string') {
this._initYText(this.value); this._initYText(this.value);
} }
@@ -540,7 +517,6 @@ export class RichTextCellEditing extends BaseRichTextCell {
} }
override render() { override render() {
if (!this.service) return nothing;
return html`<rich-text return html`<rich-text
.yText=${this.value} .yText=${this.value}
.inlineEventSource=${this.topContenteditableElement} .inlineEventSource=${this.topContenteditableElement}

View File

@@ -21,6 +21,7 @@ export const richTextPropertyModelConfig =
}; };
}, },
cellToJson: ({ value, dataSource }) => { cellToJson: ({ value, dataSource }) => {
if (!value) return null;
const host = dataSource.contextGet(HostContextKey); const host = dataSource.contextGet(HostContextKey);
if (host) { if (host) {
const collection = host.std.workspace; const collection = host.std.workspace;

View File

@@ -8,6 +8,10 @@ export const titleColumnType = propertyType('title');
export const titlePropertyModelConfig = titleColumnType.modelConfig<Text>({ export const titlePropertyModelConfig = titleColumnType.modelConfig<Text>({
name: 'Title', name: 'Title',
fixed: {
defaultData: {},
defaultShow: true,
},
type: () => t.richText.instance(), type: () => t.richText.instance(),
defaultData: () => ({}), defaultData: () => ({}),
cellToString: ({ value }) => value?.toString() ?? '', cellToString: ({ value }) => value?.toString() ?? '',
@@ -17,6 +21,7 @@ export const titlePropertyModelConfig = titleColumnType.modelConfig<Text>({
}; };
}, },
cellToJson: ({ value, dataSource }) => { cellToJson: ({ value, dataSource }) => {
if (!value) return null;
const host = dataSource.contextGet(HostContextKey); const host = dataSource.contextGet(HostContextKey);
if (host) { if (host) {
const collection = host.std.workspace; const collection = host.std.workspace;

View File

@@ -77,7 +77,7 @@ export function deleteColumn(
model: DatabaseBlockModel, model: DatabaseBlockModel,
columnId: Column['id'] columnId: Column['id']
) { ) {
const index = findPropertyIndex(model, columnId); const index = model.columns.findIndex(v => v.id === columnId);
if (index < 0) return; if (index < 0) return;
model.doc.transact(() => { model.doc.transact(() => {
@@ -116,10 +116,6 @@ export function duplicateView(model: DatabaseBlockModel, id: string): string {
return newId; return newId;
} }
export function findPropertyIndex(model: DatabaseBlockModel, id: Column['id']) {
return model.columns.findIndex(v => v.id === id);
}
export function getCell( export function getCell(
model: DatabaseBlockModel, model: DatabaseBlockModel,
rowId: BlockModel['id'], rowId: BlockModel['id'],
@@ -228,7 +224,8 @@ export function updateCells(
export function updateProperty( export function updateProperty(
model: DatabaseBlockModel, model: DatabaseBlockModel,
id: string, id: string,
updater: ColumnUpdater updater: ColumnUpdater,
defaultValue?: Record<string, unknown>
) { ) {
const index = model.columns.findIndex(v => v.id === id); const index = model.columns.findIndex(v => v.id === id);
if (index == null) { if (index == null) {
@@ -240,7 +237,7 @@ export function updateProperty(
return; return;
} }
const result = updater(column); const result = updater(column);
model.columns[index] = { ...column, ...result }; model.columns[index] = { ...defaultValue, ...column, ...result };
}); });
return id; return id;
} }

View File

@@ -136,16 +136,15 @@ export class DataViewPropertiesSettingView extends SignalWatcher(
}); });
renderProperty = (property: Property) => { renderProperty = (property: Property) => {
const isTitle = property.type$.value === 'title';
const icon = property.hide$.value ? InvisibleIcon() : ViewIcon(); const icon = property.hide$.value ? InvisibleIcon() : ViewIcon();
const changeVisible = () => { const changeVisible = () => {
if (property.type$.value !== 'title') { if (property.hideCanSet) {
property.hideSet(!property.hide$.value); property.hideSet(!property.hide$.value);
} }
}; };
const classList = classMap({ const classList = classMap({
'property-item-op-icon': true, 'property-item-op-icon': true,
disabled: isTitle, disabled: !property.hideCanSet,
}); });
return html` <div return html` <div
${dragHandler(property.id)} ${dragHandler(property.id)}
@@ -251,7 +250,7 @@ export const popPropertiesSetting = (
const isAllShowed = items.every(v => !v.hide$.value); const isAllShowed = items.every(v => !v.hide$.value);
const clickChangeAll = () => { const clickChangeAll = () => {
props.view.propertiesWithoutFilter$.value.forEach(id => { props.view.propertiesWithoutFilter$.value.forEach(id => {
if (props.view.propertyTypeGet(id) !== 'title') { if (props.view.propertyCanHide(id)) {
props.view.propertyHideSet(id, isAllShowed); props.view.propertyHideSet(id, isAllShowed);
} }
}); });

View File

@@ -36,13 +36,13 @@ export const typeConfig = (property: Property) => {
items: [ items: [
menu.subMenu({ menu.subMenu({
name: 'Type', name: 'Type',
hide: () => !property.typeSet || property.type$.value === 'title', hide: () => !property.typeCanSet,
postfix: html` <div postfix: html` <div
class="affine-database-column-type-icon" class="affine-database-column-type-icon"
style="color: var(--affine-text-secondary-color);gap:4px;font-size: 14px;" style="color: var(--affine-text-secondary-color);gap:4px;font-size: 14px;"
> >
${renderUniLit(property.icon)} ${renderUniLit(property.icon)}
${property.view.propertyMetas.find( ${property.view.propertyMetas$.value.find(
v => v.type === property.type$.value v => v.type === property.type$.value
)?.config.name} )?.config.name}
</div>`, </div>`,
@@ -52,7 +52,7 @@ export const typeConfig = (property: Property) => {
}, },
items: [ items: [
menu.group({ menu.group({
items: property.view.propertyMetas.map(config => { items: property.view.propertyMetas$.value.map(config => {
return menu.action({ return menu.action({
isSelected: config.type === property.type$.value, isSelected: config.type === property.type$.value,
name: config.config.name, name: config.config.name,

View File

@@ -1,3 +1,4 @@
import type { Column } from '@blocksuite/affine-model';
import type { InsertToPosition } from '@blocksuite/affine-shared/utils'; import type { InsertToPosition } from '@blocksuite/affine-shared/utils';
import { computed, type ReadonlySignal } from '@preact/signals-core'; import { computed, type ReadonlySignal } from '@preact/signals-core';
@@ -26,7 +27,8 @@ export interface DataSource {
rowDelete(ids: string[]): void; rowDelete(ids: string[]): void;
rowMove(rowId: string, position: InsertToPosition): void; rowMove(rowId: string, position: InsertToPosition): void;
propertyMetas: PropertyMetaConfig[]; propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
allPropertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
propertyNameGet$(propertyId: string): ReadonlySignal<string | undefined>; propertyNameGet$(propertyId: string): ReadonlySignal<string | undefined>;
propertyNameGet(propertyId: string): string; propertyNameGet(propertyId: string): string;
@@ -35,6 +37,7 @@ export interface DataSource {
propertyTypeGet(propertyId: string): string | undefined; propertyTypeGet(propertyId: string): string | undefined;
propertyTypeGet$(propertyId: string): ReadonlySignal<string | undefined>; propertyTypeGet$(propertyId: string): ReadonlySignal<string | undefined>;
propertyTypeSet(propertyId: string, type: string): void; propertyTypeSet(propertyId: string, type: string): void;
propertyTypeCanSet(propertyId: string): boolean;
propertyDataGet(propertyId: string): Record<string, unknown>; propertyDataGet(propertyId: string): Record<string, unknown>;
propertyDataGet$( propertyDataGet$(
@@ -52,8 +55,12 @@ export interface DataSource {
propertyMetaGet(type: string): PropertyMetaConfig; propertyMetaGet(type: string): PropertyMetaConfig;
propertyAdd(insertToPosition: InsertToPosition, type?: string): string; propertyAdd(insertToPosition: InsertToPosition, type?: string): string;
propertyDuplicate(propertyId: string): string;
propertyDuplicate(propertyId: string): string | undefined;
propertyCanDuplicate(propertyId: string): boolean;
propertyDelete(id: string): void; propertyDelete(id: string): void;
propertyCanDelete(propertyId: string): boolean;
contextGet<T>(key: DataViewContextKey<T>): T; contextGet<T>(key: DataViewContextKey<T>): T;
@@ -82,13 +89,24 @@ export interface DataSource {
} }
export abstract class DataSourceBase implements DataSource { export abstract class DataSourceBase implements DataSource {
propertyTypeCanSet(propertyId: string): boolean {
return !this.isFixedProperty(propertyId);
}
propertyCanDuplicate(propertyId: string): boolean {
return !this.isFixedProperty(propertyId);
}
propertyCanDelete(propertyId: string): boolean {
return !this.isFixedProperty(propertyId);
}
context = new Map<symbol, unknown>(); context = new Map<symbol, unknown>();
abstract featureFlags$: ReadonlySignal<DatabaseFlags>; abstract featureFlags$: ReadonlySignal<DatabaseFlags>;
abstract properties$: ReadonlySignal<string[]>; abstract properties$: ReadonlySignal<string[]>;
abstract propertyMetas: PropertyMetaConfig[]; abstract propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
abstract allPropertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
abstract readonly$: ReadonlySignal<boolean>; abstract readonly$: ReadonlySignal<boolean>;
@@ -159,7 +177,7 @@ export abstract class DataSourceBase implements DataSource {
abstract propertyDelete(id: string): void; abstract propertyDelete(id: string): void;
abstract propertyDuplicate(propertyId: string): string; abstract propertyDuplicate(propertyId: string): string | undefined;
abstract propertyMetaGet(type: string): PropertyMetaConfig; abstract propertyMetaGet(type: string): PropertyMetaConfig;
@@ -223,4 +241,31 @@ export abstract class DataSourceBase implements DataSource {
viewMetaGetById$(viewId: string): ReadonlySignal<ViewMeta | undefined> { viewMetaGetById$(viewId: string): ReadonlySignal<ViewMeta | undefined> {
return computed(() => this.viewMetaGetById(viewId)); return computed(() => this.viewMetaGetById(viewId));
} }
fixedProperties$ = computed(() => {
return this.allPropertyMetas$.value
.filter(v => v.config.fixed)
.map(v => v.type);
});
fixedPropertySet = computed(() => {
return new Set(this.fixedProperties$.value);
});
protected abstract getNormalPropertyAndIndex(propertyId: string):
| {
column: Column<Record<string, unknown>>;
index: number;
}
| undefined;
isFixedProperty(propertyId: string) {
if (this.fixedPropertySet.value.has(propertyId)) {
return true;
}
const result = this.getNormalPropertyAndIndex(propertyId);
if (result) {
return this.fixedPropertySet.value.has(result.column.type);
}
return false;
}
} }

View File

@@ -119,7 +119,7 @@ export class RecordDetail extends SignalWatcher(
}, },
items: [ items: [
menu.group({ menu.group({
items: this.view.propertyMetas.map(meta => { items: this.view.propertyMetas$.value.map(meta => {
return menu.action({ return menu.action({
name: meta.config.name, name: meta.config.name,
prefix: renderUniLit(this.view.propertyIconGet(meta.type)), prefix: renderUniLit(this.view.propertyIconGet(meta.type)),

View File

@@ -184,8 +184,7 @@ export class RecordField extends SignalWatcher(
menu.action({ menu.action({
name: 'Duplicate', name: 'Duplicate',
prefix: DuplicateIcon(), prefix: DuplicateIcon(),
hide: () => hide: () => !this.column.canDuplicate,
!this.column.duplicate || this.column.type$.value === 'title',
select: () => { select: () => {
this.column.duplicate?.(); this.column.duplicate?.();
}, },
@@ -193,8 +192,7 @@ export class RecordField extends SignalWatcher(
menu.action({ menu.action({
name: 'Delete', name: 'Delete',
prefix: DeleteIcon(), prefix: DeleteIcon(),
hide: () => hide: () => !this.column.canDelete,
!this.column.delete || this.column.type$.value === 'title',
select: () => { select: () => {
this.column.delete?.(); this.column.delete?.();
}, },

View File

@@ -0,0 +1 @@
export * from './trait.js';

View File

@@ -3,6 +3,7 @@ export * from './component/index.js';
export { DataSourceBase } from './data-source/base.js'; export { DataSourceBase } from './data-source/base.js';
export { DataView } from './data-view.js'; export { DataView } from './data-view.js';
export * from './filter/index.js'; export * from './filter/index.js';
export * from './group-by';
export * from './logical/index.js'; export * from './logical/index.js';
export * from './property/index.js'; export * from './property/index.js';
export type { DataViewSelection } from './types.js'; export type { DataViewSelection } from './types.js';

View File

@@ -15,6 +15,12 @@ export type PropertyConfig<
Value = unknown, Value = unknown,
> = { > = {
name: string; name: string;
hide?: boolean;
fixed?: {
defaultData: Data;
defaultOrder?: string;
defaultShow?: boolean;
};
defaultData: () => Data; defaultData: () => Data;
type: ( type: (
config: WithCommonPropertyConfig<{ config: WithCommonPropertyConfig<{
@@ -50,7 +56,7 @@ export type PropertyConfig<
}; };
cellToJson: ( cellToJson: (
config: WithCommonPropertyConfig<{ config: WithCommonPropertyConfig<{
value: Value; value?: Value;
data: Data; data: Data;
}> }>
) => DVJSON; ) => DVJSON;

View File

@@ -23,7 +23,10 @@ export interface Property<
readonly icon?: UniComponent; readonly icon?: UniComponent;
readonly delete?: () => void; readonly delete?: () => void;
get canDelete(): boolean;
readonly duplicate?: () => void; readonly duplicate?: () => void;
get canDuplicate(): boolean;
cellGet(rowId: string): Cell<Value>; cellGet(rowId: string): Cell<Value>;
@@ -32,12 +35,14 @@ export interface Property<
readonly type$: ReadonlySignal<string>; readonly type$: ReadonlySignal<string>;
readonly typeSet?: (type: string) => void; readonly typeSet?: (type: string) => void;
get typeCanSet(): boolean;
readonly name$: ReadonlySignal<string>; readonly name$: ReadonlySignal<string>;
nameSet(name: string): void; nameSet(name: string): void;
readonly hide$: ReadonlySignal<boolean>; readonly hide$: ReadonlySignal<boolean>;
hideSet(hide: boolean): void; hideSet(hide: boolean): void;
get hideCanSet(): boolean;
valueGet(rowId: string): Value | undefined; valueGet(rowId: string): Value | undefined;
valueSet(rowId: string, value: Value | undefined): void; valueSet(rowId: string, value: Value | undefined): void;
@@ -123,6 +128,18 @@ export abstract class PropertyBase<
public view: SingleView, public view: SingleView,
public propertyId: string public propertyId: string
) {} ) {}
get canDelete(): boolean {
return this.view.propertyCanDelete(this.id);
}
get canDuplicate(): boolean {
return this.view.propertyCanDuplicate(this.id);
}
get typeCanSet(): boolean {
return this.view.propertyTypeCanSet(this.id);
}
get hideCanSet(): boolean {
return this.view.propertyCanHide(this.id);
}
cellGet(rowId: string): Cell<Value> { cellGet(rowId: string): Cell<Value> {
return this.view.cellGet(rowId, this.id) as Cell<Value>; return this.view.cellGet(rowId, this.id) as Cell<Value>;

View File

@@ -80,13 +80,15 @@ export interface SingleView {
rowNextGet(rowId: string): string | undefined; rowNextGet(rowId: string): string | undefined;
readonly propertyMetas: PropertyMetaConfig[]; readonly propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
propertyAdd(toAfterOfProperty: InsertToPosition, type?: string): string; propertyAdd(toAfterOfProperty: InsertToPosition, type?: string): string;
propertyDelete(propertyId: string): void; propertyDelete(propertyId: string): void;
propertyCanDelete(propertyId: string): boolean;
propertyDuplicate(propertyId: string): void; propertyDuplicate(propertyId: string): void;
propertyCanDuplicate(propertyId: string): boolean;
propertyGet(propertyId: string): Property; propertyGet(propertyId: string): Property;
@@ -103,10 +105,12 @@ export interface SingleView {
propertyTypeGet(propertyId: string): string | undefined; propertyTypeGet(propertyId: string): string | undefined;
propertyTypeSet(propertyId: string, type: string): void; propertyTypeSet(propertyId: string, type: string): void;
propertyTypeCanSet(propertyId: string): boolean;
propertyHideGet(propertyId: string): boolean; propertyHideGet(propertyId: string): boolean;
propertyHideSet(propertyId: string, hide: boolean): void; propertyHideSet(propertyId: string, hide: boolean): void;
propertyCanHide(propertyId: string): boolean;
propertyDataGet(propertyId: string): Record<string, unknown>; propertyDataGet(propertyId: string): Record<string, unknown>;
@@ -215,8 +219,8 @@ export abstract class SingleViewBase<
return this.dataSource.viewMetaGet(this.type); return this.dataSource.viewMetaGet(this.type);
} }
get propertyMetas(): PropertyMetaConfig[] { get propertyMetas$() {
return this.dataSource.propertyMetas; return this.dataSource.propertyMetas$;
} }
abstract get type(): string; abstract get type(): string;
@@ -225,6 +229,18 @@ export abstract class SingleViewBase<
public manager: ViewManager, public manager: ViewManager,
public id: string public id: string
) {} ) {}
propertyCanDelete(propertyId: string): boolean {
return this.dataSource.propertyCanDelete(propertyId);
}
propertyCanDuplicate(propertyId: string): boolean {
return this.dataSource.propertyCanDuplicate(propertyId);
}
propertyTypeCanSet(propertyId: string): boolean {
return this.dataSource.propertyTypeCanSet(propertyId);
}
propertyCanHide(propertyId: string): boolean {
return this.propertyTypeGet(propertyId) !== 'title';
}
private searchRowsMapping(rows: string[], searchString: string): string[] { private searchRowsMapping(rows: string[], searchString: string): string[] {
return rows.filter(id => { return rows.filter(id => {
@@ -370,6 +386,9 @@ export abstract class SingleViewBase<
propertyDuplicate(propertyId: string): void { propertyDuplicate(propertyId: string): void {
const id = this.dataSource.propertyDuplicate(propertyId); const id = this.dataSource.propertyDuplicate(propertyId);
if (!id) {
return;
}
this.propertyMove(id, { this.propertyMove(id, {
before: false, before: false,
id: propertyId, id: propertyId,

View File

@@ -5,6 +5,7 @@ export const imagePropertyType = propertyType('image');
export const imagePropertyModelConfig = imagePropertyType.modelConfig<string>({ export const imagePropertyModelConfig = imagePropertyType.modelConfig<string>({
name: 'image', name: 'image',
hide: true,
type: () => t.image.instance(), type: () => t.image.instance(),
defaultData: () => ({}), defaultData: () => ({}),
cellToString: ({ value }) => value ?? '', cellToString: ({ value }) => value ?? '',

View File

@@ -123,9 +123,7 @@ export class MobileTableColumnHeader extends SignalWatcher(
menu.action({ menu.action({
name: 'Hide In View', name: 'Hide In View',
prefix: ViewIcon(), prefix: ViewIcon(),
hide: () => hide: () => !this.column.hideCanSet,
this.column.hide$.value ||
this.column.type$.value === 'title',
select: () => { select: () => {
this.column.hideSet(true); this.column.hideSet(true);
}, },
@@ -220,8 +218,7 @@ export class MobileTableColumnHeader extends SignalWatcher(
menu.action({ menu.action({
name: 'Duplicate', name: 'Duplicate',
prefix: DuplicateIcon(), prefix: DuplicateIcon(),
hide: () => hide: () => !this.column.canDuplicate,
!this.column.duplicate || this.column.type$.value === 'title',
select: () => { select: () => {
this.column.duplicate?.(); this.column.duplicate?.();
}, },
@@ -229,8 +226,7 @@ export class MobileTableColumnHeader extends SignalWatcher(
menu.action({ menu.action({
name: 'Delete', name: 'Delete',
prefix: DeleteIcon(), prefix: DeleteIcon(),
hide: () => hide: () => !this.column.canDelete,
!this.column.delete || this.column.type$.value === 'title',
select: () => { select: () => {
this.column.delete?.(); this.column.delete?.();
}, },

View File

@@ -80,7 +80,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
event.stopPropagation(); event.stopPropagation();
popMenu(popupTargetFromElement(this), { popMenu(popupTargetFromElement(this), {
options: { options: {
items: this.tableViewManager.propertyMetas.map(config => { items: this.tableViewManager.propertyMetas$.value.map(config => {
return menu.action({ return menu.action({
name: config.config.name, name: config.config.name,
isSelected: config.type === this.column.type$.value, isSelected: config.type === this.column.type$.value,
@@ -254,9 +254,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
menu.action({ menu.action({
name: 'Hide In View', name: 'Hide In View',
prefix: ViewIcon(), prefix: ViewIcon(),
hide: () => hide: () => !this.column.hideCanSet,
this.column.hide$.value ||
this.column.type$.value === 'title',
select: () => { select: () => {
this.column.hideSet(true); this.column.hideSet(true);
}, },
@@ -370,8 +368,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
menu.action({ menu.action({
name: 'Duplicate', name: 'Duplicate',
prefix: DuplicateIcon(), prefix: DuplicateIcon(),
hide: () => hide: () => !this.column.canDuplicate,
!this.column.duplicate || this.column.type$.value === 'title',
select: () => { select: () => {
this.column.duplicate?.(); this.column.duplicate?.();
}, },
@@ -379,8 +376,7 @@ export class DatabaseHeaderColumn extends SignalWatcher(
menu.action({ menu.action({
name: 'Delete', name: 'Delete',
prefix: DeleteIcon(), prefix: DeleteIcon(),
hide: () => hide: () => !this.column.canDelete,
!this.column.delete || this.column.type$.value === 'title',
select: () => { select: () => {
this.column.delete?.(); this.column.delete?.();
}, },

View File

@@ -1,16 +1,16 @@
import { import {
databaseBlockColumns, databaseBlockColumns,
DatabaseBlockDataSource,
type DatabaseBlockModel, type DatabaseBlockModel,
type ListType, type ListType,
type ParagraphType, type ParagraphType,
type ViewBasicDataType,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import { groupTraitKey } from '@blocksuite/data-view';
import { propertyPresets } from '@blocksuite/data-view/property-presets';
import { viewPresets } from '@blocksuite/data-view/view-presets'; import { viewPresets } from '@blocksuite/data-view/view-presets';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import { Text, type Workspace } from '@blocksuite/store'; import { Text, type Workspace } from '@blocksuite/store';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { propertyPresets } from '../../../../affine/data-view/src/property-presets';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const database: InitFn = (collection: Workspace, id: string) => { export const database: InitFn = (collection: Workspace, id: string) => {
@@ -37,26 +37,13 @@ export const database: InitFn = (collection: Workspace, id: string) => {
}, },
noteId noteId
); );
new Promise(resolve => requestAnimationFrame(resolve))
.then(() => {
const service = window.host.std.getService('affine:database');
if (!service) return;
service.initDatabaseBlock(
doc,
model,
databaseId,
viewPresets.tableViewMeta.type,
true
);
const database = doc.getBlockById(databaseId) as DatabaseBlockModel; const database = doc.getBlockById(databaseId) as DatabaseBlockModel;
const datasource = new DatabaseBlockDataSource(database);
datasource.viewManager.viewAdd('table');
database.title = new Text(title); database.title = new Text(title);
const richTextId = service.addColumn( const richTextId = datasource.propertyAdd(
database,
'end', 'end',
databaseBlockColumns.richTextColumnConfig.create( databaseBlockColumns.richTextColumnConfig.type
databaseBlockColumns.richTextColumnConfig.config.name
)
); );
Object.values([ Object.values([
propertyPresets.multiSelectPropertyConfig, propertyPresets.multiSelectPropertyConfig,
@@ -66,23 +53,13 @@ export const database: InitFn = (collection: Workspace, id: string) => {
propertyPresets.checkboxPropertyConfig, propertyPresets.checkboxPropertyConfig,
propertyPresets.progressPropertyConfig, propertyPresets.progressPropertyConfig,
]).forEach(column => { ]).forEach(column => {
service.addColumn( datasource.propertyAdd('end', column.type);
database,
'end',
column.create(column.config.name)
);
}); });
service.updateView(database, database.views[0].id, () => { if (group) {
return { const groupTrait =
groupBy: group datasource.viewManager.currentView$.value?.traitGet(groupTraitKey);
? { groupTrait?.changeGroup(database.columns[1].id);
columnId: database.columns[1].id,
type: 'groupBy',
name: 'select',
} }
: undefined,
} as Partial<ViewBasicDataType>;
});
const paragraphTypes: ParagraphType[] = [ const paragraphTypes: ParagraphType[] = [
'text', 'text',
'quote', 'quote',
@@ -99,17 +76,13 @@ export const database: InitFn = (collection: Workspace, id: string) => {
{ type: type, text: new Text(`Paragraph type ${type}`) }, { type: type, text: new Text(`Paragraph type ${type}`) },
databaseId databaseId
); );
service.updateCell(database, id, { datasource.cellValueChange(
columnId: richTextId, id,
value: new Text(`Paragraph type ${type}`), richTextId,
new Text(`Paragraph type ${type}`)
);
}); });
}); const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle'];
const listTypes: ListType[] = [
'numbered',
'bulleted',
'todo',
'toggle',
];
listTypes.forEach(type => { listTypes.forEach(type => {
const id = doc.addBlock( const id = doc.addBlock(
@@ -117,10 +90,11 @@ export const database: InitFn = (collection: Workspace, id: string) => {
{ type: type, text: new Text(`List type ${type}`) }, { type: type, text: new Text(`List type ${type}`) },
databaseId databaseId
); );
service.updateCell(database, id, { datasource.cellValueChange(
columnId: richTextId, id,
value: new Text(`List type ${type}`), richTextId,
}); new Text(`List type ${type}`)
);
}); });
// Add a paragraph after database // Add a paragraph after database
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);
@@ -128,14 +102,9 @@ export const database: InitFn = (collection: Workspace, id: string) => {
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);
service.databaseViewAddView( datasource.viewManager.viewAdd(viewPresets.kanbanViewMeta.type);
database,
viewPresets.kanbanViewMeta.type
);
doc.resetHistory(); doc.resetHistory();
})
.catch(console.error);
}; };
// Add database block inside note block // Add database block inside note block
addDatabase('Database 1', false); addDatabase('Database 1', false);

View File

@@ -237,16 +237,9 @@ test.describe('Embed synced doc', () => {
noteId noteId
); );
const model = doc2.getBlockById(databaseId) as DatabaseBlockModel; const model = doc2.getBlockById(databaseId) as DatabaseBlockModel;
await new Promise(resolve => setTimeout(resolve, 100)); const datasource =
const databaseBlock = document.querySelector('affine-database'); new window.$blocksuite.blocks.DatabaseBlockDataSource(model);
const databaseService = databaseBlock?.service; datasource.viewManager.viewAdd('table');
if (databaseService) {
databaseService.databaseViewInitEmpty(
model,
databaseService.viewPresets.tableViewMeta.type
);
databaseService.applyColumnUpdate(model);
}
}); });
// go back to previous doc // go back to previous doc

View File

@@ -526,17 +526,10 @@ export async function initEmptyDatabaseState(page: Page, rootId?: string) {
noteId noteId
); );
const model = doc.getBlockById(databaseId) as DatabaseBlockModel; const model = doc.getBlockById(databaseId) as DatabaseBlockModel;
await new Promise(resolve => setTimeout(resolve, 100)); const datasource = new window.$blocksuite.blocks.DatabaseBlockDataSource(
const databaseBlock = document.querySelector('affine-database'); model
const databaseService = databaseBlock?.service;
if (databaseService) {
databaseService.databaseViewInitEmpty(
model,
databaseService.viewPresets.tableViewMeta.type
); );
databaseService.applyColumnUpdate(model); datasource.viewManager.viewAdd('table');
}
doc.captureSync(); doc.captureSync();
return { rootId, noteId, databaseId }; return { rootId, noteId, databaseId };
}, rootId); }, rootId);
@@ -570,10 +563,9 @@ export async function initKanbanViewState(
noteId noteId
); );
const model = doc.getBlockById(databaseId) as DatabaseBlockModel; const model = doc.getBlockById(databaseId) as DatabaseBlockModel;
await new Promise(resolve => setTimeout(resolve, 100)); const datasource = new window.$blocksuite.blocks.DatabaseBlockDataSource(
const databaseBlock = document.querySelector('affine-database'); model
const databaseService = databaseBlock?.service; );
if (databaseService) {
const rowIds = config.rows.map(rowText => { const rowIds = config.rows.map(rowText => {
const rowId = doc.addBlock( const rowId = doc.addBlock(
'affine:paragraph', 'affine:paragraph',
@@ -583,30 +575,22 @@ export async function initKanbanViewState(
return rowId; return rowId;
}); });
config.columns.forEach(column => { config.columns.forEach(column => {
const columnId = databaseService.addColumn(model, 'end', { const columnId = datasource.propertyAdd('end', column.type);
data: {}, datasource.propertyNameSet(columnId, column.type);
name: column.type,
type: column.type,
});
rowIds.forEach((rowId, index) => { rowIds.forEach((rowId, index) => {
const value = column.value?.[index]; const value = column.value?.[index];
if (value !== undefined) { if (value !== undefined) {
databaseService.updateCell(model, rowId, { datasource.cellValueChange(
rowId,
columnId, columnId,
value:
column.type === 'rich-text' column.type === 'rich-text'
? new window.$blocksuite.store.Text(value as string) ? new window.$blocksuite.store.Text(value as string)
: value, : value
});
}
});
});
databaseService.databaseViewInitEmpty(
model,
databaseService.viewPresets.kanbanViewMeta.type
); );
databaseService.applyColumnUpdate(model);
} }
});
});
datasource.viewManager.viewAdd('kanban');
doc.captureSync(); doc.captureSync();
return { rootId, noteId, databaseId }; return { rootId, noteId, databaseId };
}, },
@@ -636,18 +620,11 @@ export async function initEmptyDatabaseWithParagraphState(
noteId noteId
); );
const model = doc.getBlockById(databaseId) as DatabaseBlockModel; const model = doc.getBlockById(databaseId) as DatabaseBlockModel;
await new Promise(resolve => setTimeout(resolve, 100)); const datasource = new window.$blocksuite.blocks.DatabaseBlockDataSource(
const databaseBlock = document.querySelector('affine-database'); model
const databaseService = databaseBlock?.service;
if (databaseService) {
databaseService.databaseViewInitEmpty(
model,
databaseService.viewPresets.tableViewMeta.type
); );
databaseService.applyColumnUpdate(model); datasource.viewManager.viewAdd('table');
}
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);
doc.captureSync(); doc.captureSync();
return { rootId, noteId, databaseId }; return { rootId, noteId, databaseId };
}, rootId); }, rootId);

View File

@@ -19,26 +19,26 @@ declare global {
* the following instance are initialized in `packages/playground/apps/starter/main.ts` * the following instance are initialized in `packages/playground/apps/starter/main.ts`
*/ */
$blocksuite: { $blocksuite: {
store: typeof import('../../packages/framework/store/src/index.js'); store: typeof import('../../framework/store/src/index.js');
blocks: typeof import('../../packages/blocks/src/index.js'); blocks: typeof import('../../blocks/src/index.js');
global: { global: {
utils: typeof import('../../packages/framework/global/src/utils.js'); utils: typeof import('../../framework/global/src/utils.js');
}; };
editor: typeof import('../../packages/presets/src/index.js'); editor: typeof import('../../presets/src/index.js');
identifiers: { identifiers: {
WidgetViewMapIdentifier: typeof WidgetViewMapIdentifier; WidgetViewMapIdentifier: typeof WidgetViewMapIdentifier;
QuickSearchProvider: typeof import('../../packages/affine/shared/src/services/quick-search-service.js').QuickSearchProvider; QuickSearchProvider: typeof import('../../affine/shared/src/services/quick-search-service.js').QuickSearchProvider;
DocModeProvider: typeof import('../../packages/affine/shared/src/services/doc-mode-service.js').DocModeProvider; DocModeProvider: typeof import('../../affine/shared/src/services/doc-mode-service.js').DocModeProvider;
ThemeProvider: typeof import('../../packages/affine/shared/src/services/theme-service.js').ThemeProvider; ThemeProvider: typeof import('../../affine/shared/src/services/theme-service.js').ThemeProvider;
RefNodeSlotsProvider: typeof RefNodeSlotsProvider; RefNodeSlotsProvider: typeof RefNodeSlotsProvider;
ParseDocUrlService: typeof import('../../packages/affine/shared/src/services/parse-url-service.js').ParseDocUrlProvider; ParseDocUrlService: typeof import('../../affine/shared/src/services/parse-url-service.js').ParseDocUrlProvider;
}; };
defaultExtensions: () => ExtensionType[]; defaultExtensions: () => ExtensionType[];
extensions: { extensions: {
WidgetViewMapExtension: typeof import('../../packages/framework/block-std/src/extension/widget-view-map.js').WidgetViewMapExtension; WidgetViewMapExtension: typeof import('../../framework/block-std/src/extension/widget-view-map.js').WidgetViewMapExtension;
}; };
mockServices: { mockServices: {
mockDocModeService: typeof import('../../packages/playground/apps/_common/mock-services.js').mockDocModeService; mockDocModeService: typeof import('../../playground/apps/_common/mock-services.js').mockDocModeService;
}; };
}; };
collection: Workspace; collection: Workspace;