@@ -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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,77 +505,54 @@ 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
|
||||||
options: [
|
|
||||||
{
|
|
||||||
id: ids[0],
|
|
||||||
color: getTagColor(),
|
|
||||||
value: 'TODO',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: ids[1],
|
|
||||||
color: getTagColor(),
|
|
||||||
value: 'In Progress',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: ids[2],
|
|
||||||
color: getTagColor(),
|
|
||||||
value: 'Done',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
for (let i = 0; i < 4; i++) {
|
datasource.propertyNameSet(statusId, 'Status');
|
||||||
const rowId = model.doc.addBlock(
|
datasource.propertyDataSet(statusId, {
|
||||||
'affine:paragraph',
|
options: [
|
||||||
{
|
{
|
||||||
text: new Text(`Task ${i + 1}`),
|
id: ids[0],
|
||||||
|
color: getTagColor(),
|
||||||
|
value: 'TODO',
|
||||||
},
|
},
|
||||||
model.id
|
{
|
||||||
);
|
id: ids[1],
|
||||||
updateCell(model, rowId, {
|
color: getTagColor(),
|
||||||
columnId: statusId,
|
value: 'In Progress',
|
||||||
value: ids[i],
|
},
|
||||||
});
|
{
|
||||||
|
id: ids[2],
|
||||||
|
color: getTagColor(),
|
||||||
|
value: 'Done',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
datasource.cellValueChange(rowId, statusId, ids[i]);
|
||||||
|
// const rowId = model.doc.addBlock(
|
||||||
|
// 'affine:paragraph',
|
||||||
|
// {
|
||||||
|
// text: new Text(`Task ${i + 1}`),
|
||||||
|
// },
|
||||||
|
// model.id
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
databaseViewInitEmpty(model, viewType);
|
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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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])
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
@@ -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?.();
|
||||||
},
|
},
|
||||||
|
|||||||
1
blocksuite/affine/data-view/src/core/group-by/index.ts
Normal file
1
blocksuite/affine/data-view/src/core/group-by/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './trait.js';
|
||||||
@@ -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';
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 ?? '',
|
||||||
|
|||||||
@@ -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?.();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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?.();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,105 +37,74 @@ export const database: InitFn = (collection: Workspace, id: string) => {
|
|||||||
},
|
},
|
||||||
noteId
|
noteId
|
||||||
);
|
);
|
||||||
|
const database = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
||||||
|
const datasource = new DatabaseBlockDataSource(database);
|
||||||
|
datasource.viewManager.viewAdd('table');
|
||||||
|
database.title = new Text(title);
|
||||||
|
const richTextId = datasource.propertyAdd(
|
||||||
|
'end',
|
||||||
|
databaseBlockColumns.richTextColumnConfig.type
|
||||||
|
);
|
||||||
|
Object.values([
|
||||||
|
propertyPresets.multiSelectPropertyConfig,
|
||||||
|
propertyPresets.datePropertyConfig,
|
||||||
|
propertyPresets.numberPropertyConfig,
|
||||||
|
databaseBlockColumns.linkColumnConfig,
|
||||||
|
propertyPresets.checkboxPropertyConfig,
|
||||||
|
propertyPresets.progressPropertyConfig,
|
||||||
|
]).forEach(column => {
|
||||||
|
datasource.propertyAdd('end', column.type);
|
||||||
|
});
|
||||||
|
if (group) {
|
||||||
|
const groupTrait =
|
||||||
|
datasource.viewManager.currentView$.value?.traitGet(groupTraitKey);
|
||||||
|
groupTrait?.changeGroup(database.columns[1].id);
|
||||||
|
}
|
||||||
|
const paragraphTypes: ParagraphType[] = [
|
||||||
|
'text',
|
||||||
|
'quote',
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
];
|
||||||
|
paragraphTypes.forEach(type => {
|
||||||
|
const id = doc.addBlock(
|
||||||
|
'affine:paragraph',
|
||||||
|
{ type: type, text: new Text(`Paragraph type ${type}`) },
|
||||||
|
databaseId
|
||||||
|
);
|
||||||
|
datasource.cellValueChange(
|
||||||
|
id,
|
||||||
|
richTextId,
|
||||||
|
new Text(`Paragraph type ${type}`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle'];
|
||||||
|
|
||||||
new Promise(resolve => requestAnimationFrame(resolve))
|
listTypes.forEach(type => {
|
||||||
.then(() => {
|
const id = doc.addBlock(
|
||||||
const service = window.host.std.getService('affine:database');
|
'affine:list',
|
||||||
if (!service) return;
|
{ type: type, text: new Text(`List type ${type}`) },
|
||||||
service.initDatabaseBlock(
|
databaseId
|
||||||
doc,
|
);
|
||||||
model,
|
datasource.cellValueChange(
|
||||||
databaseId,
|
id,
|
||||||
viewPresets.tableViewMeta.type,
|
richTextId,
|
||||||
true
|
new Text(`List type ${type}`)
|
||||||
);
|
);
|
||||||
const database = doc.getBlockById(databaseId) as DatabaseBlockModel;
|
});
|
||||||
database.title = new Text(title);
|
// Add a paragraph after database
|
||||||
const richTextId = service.addColumn(
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
database,
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
'end',
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
databaseBlockColumns.richTextColumnConfig.create(
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
databaseBlockColumns.richTextColumnConfig.config.name
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
)
|
datasource.viewManager.viewAdd(viewPresets.kanbanViewMeta.type);
|
||||||
);
|
|
||||||
Object.values([
|
|
||||||
propertyPresets.multiSelectPropertyConfig,
|
|
||||||
propertyPresets.datePropertyConfig,
|
|
||||||
propertyPresets.numberPropertyConfig,
|
|
||||||
databaseBlockColumns.linkColumnConfig,
|
|
||||||
propertyPresets.checkboxPropertyConfig,
|
|
||||||
propertyPresets.progressPropertyConfig,
|
|
||||||
]).forEach(column => {
|
|
||||||
service.addColumn(
|
|
||||||
database,
|
|
||||||
'end',
|
|
||||||
column.create(column.config.name)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
service.updateView(database, database.views[0].id, () => {
|
|
||||||
return {
|
|
||||||
groupBy: group
|
|
||||||
? {
|
|
||||||
columnId: database.columns[1].id,
|
|
||||||
type: 'groupBy',
|
|
||||||
name: 'select',
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
} as Partial<ViewBasicDataType>;
|
|
||||||
});
|
|
||||||
const paragraphTypes: ParagraphType[] = [
|
|
||||||
'text',
|
|
||||||
'quote',
|
|
||||||
'h1',
|
|
||||||
'h2',
|
|
||||||
'h3',
|
|
||||||
'h4',
|
|
||||||
'h5',
|
|
||||||
'h6',
|
|
||||||
];
|
|
||||||
paragraphTypes.forEach(type => {
|
|
||||||
const id = doc.addBlock(
|
|
||||||
'affine:paragraph',
|
|
||||||
{ type: type, text: new Text(`Paragraph type ${type}`) },
|
|
||||||
databaseId
|
|
||||||
);
|
|
||||||
service.updateCell(database, id, {
|
|
||||||
columnId: richTextId,
|
|
||||||
value: new Text(`Paragraph type ${type}`),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const listTypes: ListType[] = [
|
|
||||||
'numbered',
|
|
||||||
'bulleted',
|
|
||||||
'todo',
|
|
||||||
'toggle',
|
|
||||||
];
|
|
||||||
|
|
||||||
listTypes.forEach(type => {
|
doc.resetHistory();
|
||||||
const id = doc.addBlock(
|
|
||||||
'affine:list',
|
|
||||||
{ type: type, text: new Text(`List type ${type}`) },
|
|
||||||
databaseId
|
|
||||||
);
|
|
||||||
service.updateCell(database, id, {
|
|
||||||
columnId: richTextId,
|
|
||||||
value: new Text(`List type ${type}`),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// Add a paragraph after database
|
|
||||||
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(
|
|
||||||
database,
|
|
||||||
viewPresets.kanbanViewMeta.type
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
datasource.viewManager.viewAdd('table');
|
||||||
databaseService.databaseViewInitEmpty(
|
|
||||||
model,
|
|
||||||
databaseService.viewPresets.tableViewMeta.type
|
|
||||||
);
|
|
||||||
databaseService.applyColumnUpdate(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.captureSync();
|
doc.captureSync();
|
||||||
return { rootId, noteId, databaseId };
|
return { rootId, noteId, databaseId };
|
||||||
}, rootId);
|
}, rootId);
|
||||||
@@ -570,43 +563,34 @@ 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',
|
{ type: 'text', text: new window.$blocksuite.store.Text(rowText) },
|
||||||
{ type: 'text', text: new window.$blocksuite.store.Text(rowText) },
|
databaseId
|
||||||
databaseId
|
|
||||||
);
|
|
||||||
return rowId;
|
|
||||||
});
|
|
||||||
config.columns.forEach(column => {
|
|
||||||
const columnId = databaseService.addColumn(model, 'end', {
|
|
||||||
data: {},
|
|
||||||
name: column.type,
|
|
||||||
type: column.type,
|
|
||||||
});
|
|
||||||
rowIds.forEach((rowId, index) => {
|
|
||||||
const value = column.value?.[index];
|
|
||||||
if (value !== undefined) {
|
|
||||||
databaseService.updateCell(model, rowId, {
|
|
||||||
columnId,
|
|
||||||
value:
|
|
||||||
column.type === 'rich-text'
|
|
||||||
? new window.$blocksuite.store.Text(value as string)
|
|
||||||
: value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
databaseService.databaseViewInitEmpty(
|
|
||||||
model,
|
|
||||||
databaseService.viewPresets.kanbanViewMeta.type
|
|
||||||
);
|
);
|
||||||
databaseService.applyColumnUpdate(model);
|
return rowId;
|
||||||
}
|
});
|
||||||
|
config.columns.forEach(column => {
|
||||||
|
const columnId = datasource.propertyAdd('end', column.type);
|
||||||
|
datasource.propertyNameSet(columnId, column.type);
|
||||||
|
rowIds.forEach((rowId, index) => {
|
||||||
|
const value = column.value?.[index];
|
||||||
|
if (value !== undefined) {
|
||||||
|
datasource.cellValueChange(
|
||||||
|
rowId,
|
||||||
|
columnId,
|
||||||
|
column.type === 'rich-text'
|
||||||
|
? new window.$blocksuite.store.Text(value as string)
|
||||||
|
: value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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) {
|
datasource.viewManager.viewAdd('table');
|
||||||
databaseService.databaseViewInitEmpty(
|
|
||||||
model,
|
|
||||||
databaseService.viewPresets.tableViewMeta.type
|
|
||||||
);
|
|
||||||
databaseService.applyColumnUpdate(model);
|
|
||||||
}
|
|
||||||
doc.addBlock('affine:paragraph', {}, noteId);
|
doc.addBlock('affine:paragraph', {}, noteId);
|
||||||
|
|
||||||
doc.captureSync();
|
doc.captureSync();
|
||||||
return { rootId, noteId, databaseId };
|
return { rootId, noteId, databaseId };
|
||||||
}, rootId);
|
}, rootId);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user