diff --git a/blocksuite/affine/blocks/data-view/src/data-source.ts b/blocksuite/affine/blocks/data-view/src/data-source.ts index e631f3d88..c5ec2cc51 100644 --- a/blocksuite/affine/blocks/data-view/src/data-source.ts +++ b/blocksuite/affine/blocks/data-view/src/data-source.ts @@ -171,8 +171,12 @@ export class BlockQueryDataSource extends DataSourceBase { propertyAdd( insertToPosition: InsertToPosition, - type: string | undefined + ops?: { + type?: string; + name?: string; + } ): string { + const { type } = ops ?? {}; const doc = this.block.store; doc.captureSync(); const column = DatabaseBlockDataSource.propertiesMap.value[ diff --git a/blocksuite/affine/blocks/database/src/data-source.ts b/blocksuite/affine/blocks/database/src/data-source.ts index e97fbf0cd..3301d3819 100644 --- a/blocksuite/affine/blocks/database/src/data-source.ts +++ b/blocksuite/affine/blocks/database/src/data-source.ts @@ -56,10 +56,12 @@ type SpacialProperty = { valueSet: (rowId: string, propertyId: string, value: unknown) => void; valueGet: (rowId: string, propertyId: string) => unknown; }; + export class DatabaseBlockDataSource extends DataSourceBase { override get parentProvider() { return this._model.store.provider; } + spacialProperties: Record = { 'created-time': { valueSet: () => {}, @@ -101,11 +103,17 @@ export class DatabaseBlockDataSource extends DataSourceBase { }, }, }; - isSpacialProperty(propertyId: string): boolean { - return this.spacialProperties[propertyId] !== undefined; + + isSpacialProperty(propertyType: string): boolean { + return this.spacialProperties[propertyType] !== undefined; } - spacialValueGet(rowId: string, propertyId: string): unknown { - return this.spacialProperties[propertyId]?.valueGet(rowId, propertyId); + + spacialValueGet( + rowId: string, + propertyId: string, + propertyType: string + ): unknown { + return this.spacialProperties[propertyType]?.valueGet(rowId, propertyId); } static externalProperties = signal([]); @@ -213,16 +221,20 @@ export class DatabaseBlockDataSource extends DataSourceBase { return this._model.children[this._model.childMap.value.get(rowId) ?? -1]; } - private newPropertyName() { + private newPropertyName(prefix = 'Column'): string { let i = 1; - while ( - this._model.props.columns$.value.some( - column => column.name === `Column ${i}` - ) - ) { + const hasSameName = (name: string) => { + return this._model.props.columns$.value.some( + column => column.name === name + ); + }; + while (true) { + let name = i === 1 ? prefix : `${prefix} ${i}`; + if (!hasSameName(name)) { + return name; + } i++; } - return `Column ${i}`; } cellValueChange(rowId: string, propertyId: string, value: unknown): void { @@ -257,14 +269,14 @@ export class DatabaseBlockDataSource extends DataSourceBase { cellValueGet(rowId: string, propertyId: string): unknown { if (this.isSpacialProperty(propertyId)) { - return this.spacialValueGet(rowId, propertyId); + return this.spacialValueGet(rowId, propertyId, propertyId); } const type = this.propertyTypeGet(propertyId); if (!type) { return; } - if (type === 'title') { - return this.spacialValueGet(rowId, 'title'); + if (this.isSpacialProperty(type)) { + return this.spacialValueGet(rowId, propertyId, type); } const meta = this.propertyMetaGet(type); if (!meta) { @@ -283,9 +295,13 @@ export class DatabaseBlockDataSource extends DataSourceBase { propertyAdd( insertToPosition: InsertToPosition, - type?: string + ops?: { + type?: string; + name?: string; + } ): string | undefined { this.doc.captureSync(); + const { type, name } = ops ?? {}; const property = this.propertyMetaGet( type ?? propertyPresets.multiSelectPropertyConfig.type ); @@ -295,7 +311,7 @@ export class DatabaseBlockDataSource extends DataSourceBase { const result = addProperty( this._model, insertToPosition, - property.create(this.newPropertyName()) + property.create(this.newPropertyName(name)) ); return result; } diff --git a/blocksuite/affine/blocks/database/src/properties/created-time/define.ts b/blocksuite/affine/blocks/database/src/properties/created-time/define.ts index dafc264ad..e211d19c9 100644 --- a/blocksuite/affine/blocks/database/src/properties/created-time/define.ts +++ b/blocksuite/affine/blocks/database/src/properties/created-time/define.ts @@ -24,12 +24,7 @@ export const createdTimePropertyModelConfig = createdTimeColumnType.modelConfig( return { value: null }; }, toJson: ({ value }) => value, - fromJson: ({ value }) => value, - }, - fixed: { - defaultData: {}, - defaultShow: false, - defaultOrder: 'end', + setValue: () => {}, }, } ); diff --git a/blocksuite/affine/data-view/src/core/data-source/base.ts b/blocksuite/affine/data-view/src/core/data-source/base.ts index ac5cd8b08..814dfe01a 100644 --- a/blocksuite/affine/data-view/src/core/data-source/base.ts +++ b/blocksuite/affine/data-view/src/core/data-source/base.ts @@ -61,7 +61,10 @@ export interface DataSource { propertyMetaGet(type: string): PropertyMetaConfig | undefined; propertyAdd( insertToPosition: InsertToPosition, - type?: string + ops?: { + type?: string; + name?: string; + } ): string | undefined; propertyDuplicate(propertyId: string): string | undefined; @@ -179,7 +182,10 @@ export abstract class DataSourceBase implements DataSource { abstract propertyAdd( insertToPosition: InsertToPosition, - type?: string + ops?: { + type?: string; + name?: string; + } ): string | undefined; abstract propertyDataGet(propertyId: string): Record; diff --git a/blocksuite/affine/data-view/src/core/detail/detail.ts b/blocksuite/affine/data-view/src/core/detail/detail.ts index 1947cc367..35d100606 100644 --- a/blocksuite/affine/data-view/src/core/detail/detail.ts +++ b/blocksuite/affine/data-view/src/core/detail/detail.ts @@ -122,7 +122,10 @@ export class RecordDetail extends SignalWatcher( name: meta.config.name, prefix: renderUniLit(meta.renderer.icon), select: () => { - this.view.propertyAdd('end', meta.type); + this.view.propertyAdd('end', { + type: meta.type, + name: meta.config.name, + }); }, }); }), diff --git a/blocksuite/affine/data-view/src/core/group-by/define.ts b/blocksuite/affine/data-view/src/core/group-by/define.ts index a72c54113..345e20922 100644 --- a/blocksuite/affine/data-view/src/core/group-by/define.ts +++ b/blocksuite/affine/data-view/src/core/group-by/define.ts @@ -8,6 +8,7 @@ import { NumberGroupView } from './renderer/number-group.js'; import { SelectGroupView } from './renderer/select-group.js'; import { StringGroupView } from './renderer/string-group.js'; import type { GroupByConfig } from './types.js'; + export const createGroupByConfig = < Data extends Record, MatchType extends TypeInstance, @@ -25,7 +26,7 @@ export const groupByMatchers = [ createGroupByConfig({ name: 'select', matchType: t.tag.instance(), - groupName: (type, value) => { + groupName: (type, value: string | null) => { if (t.tag.is(type) && type.data) { return type.data.find(v => v.id === value)?.value ?? ''; } @@ -54,6 +55,7 @@ export const groupByMatchers = [ }, ]; }, + addToGroup: v => v, view: createUniComponentFromWebComponent(SelectGroupView), }), createGroupByConfig({ @@ -106,7 +108,7 @@ export const groupByMatchers = [ createGroupByConfig({ name: 'text', matchType: t.string.instance(), - groupName: (_type, value) => { + groupName: (_type, value: string | null) => { return `${value ?? ''}`; }, defaultKeys: _type => { @@ -123,6 +125,7 @@ export const groupByMatchers = [ }, ]; }, + addToGroup: v => v, view: createUniComponentFromWebComponent(StringGroupView), }), createGroupByConfig({ @@ -151,7 +154,7 @@ export const groupByMatchers = [ createGroupByConfig({ name: 'boolean', matchType: t.boolean.instance(), - groupName: (_type, value) => { + groupName: (_type, value: boolean | null) => { return `${value?.toString() ?? ''}`; }, defaultKeys: _type => { @@ -176,6 +179,7 @@ export const groupByMatchers = [ }, ]; }, + addToGroup: v => v, view: createUniComponentFromWebComponent(BooleanGroupView), }), ]; diff --git a/blocksuite/affine/data-view/src/core/group-by/trait.ts b/blocksuite/affine/data-view/src/core/group-by/trait.ts index 2758cd09c..7fa752cac 100644 --- a/blocksuite/affine/data-view/src/core/group-by/trait.ts +++ b/blocksuite/affine/data-view/src/core/group-by/trait.ts @@ -31,6 +31,7 @@ export class Group< Data extends Record = Record, > { rows: Row[] = []; + constructor( public readonly key: string, public readonly value: JsonValue, @@ -57,6 +58,7 @@ export class Group< get tType() { return this.groupInfo.tType; } + get view() { return this.config.view; } @@ -185,7 +187,10 @@ export class GroupTrait { if (!groupMap || !groupInfo) { return; } - const addTo = groupInfo.config.addToGroup ?? (value => value); + const addTo = groupInfo.config.addToGroup; + if (addTo === false) { + return; + } const v = groupMap[key]?.value; if (v != null) { const newValue = addTo( @@ -268,8 +273,10 @@ export class GroupTrait { this.view.cellGetOrCreate(rowId, propertyId).jsonValue$.value ); } - const addTo = - this.groupInfo$.value?.config.addToGroup ?? (value => value); + const addTo = this.groupInfo$.value?.config.addToGroup; + if (addTo === false || addTo == null) { + return; + } newValue = addTo(groupMap[toGroupKey]?.value ?? null, newValue); this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(newValue); } diff --git a/blocksuite/affine/data-view/src/core/group-by/types.ts b/blocksuite/affine/data-view/src/core/group-by/types.ts index 906736793..ace0f5cbc 100644 --- a/blocksuite/affine/data-view/src/core/group-by/types.ts +++ b/blocksuite/affine/data-view/src/core/group-by/types.ts @@ -9,7 +9,10 @@ export interface GroupRenderProps< group: Group; readonly: boolean; } - +type AddToGroup = ( + value: GroupValue | null, + oldValue: ValueTypeOf | null +) => ValueTypeOf | null; export type GroupByConfig< Data extends NonNullable = NonNullable, MatchType extends TypeInstance = TypeInstance, @@ -29,10 +32,7 @@ export type GroupByConfig< key: string; value: GroupValue | null; }[]; - addToGroup?: ( - value: GroupValue | null, - oldValue: ValueTypeOf | null - ) => ValueTypeOf | null; + addToGroup: AddToGroup | false; removeFromGroup?: ( value: GroupValue | null, oldValue: ValueTypeOf | null diff --git a/blocksuite/affine/data-view/src/core/view-manager/single-view.ts b/blocksuite/affine/data-view/src/core/view-manager/single-view.ts index 593f1fd5b..6777c5dc5 100644 --- a/blocksuite/affine/data-view/src/core/view-manager/single-view.ts +++ b/blocksuite/affine/data-view/src/core/view-manager/single-view.ts @@ -58,7 +58,10 @@ export interface SingleView { propertyAdd( toAfterOfProperty: InsertToPosition, - type?: string + ops?: { + type?: string; + name?: string; + } ): string | undefined; serviceGet(key: GeneralServiceIdentifier): T | null; @@ -236,8 +239,14 @@ export abstract class SingleViewBase< }); } - propertyAdd(position: InsertToPosition, type?: string): string | undefined { - const id = this.dataSource.propertyAdd(position, type); + propertyAdd( + position: InsertToPosition, + ops?: { + type?: string; + name?: string; + } + ): string | undefined { + const id = this.dataSource.propertyAdd(position, ops); if (!id) { return; } diff --git a/blocksuite/affine/data-view/src/view-presets/table/pc-virtual/group/top/header/column-header.ts b/blocksuite/affine/data-view/src/view-presets/table/pc-virtual/group/top/header/column-header.ts index ad94518f1..cfef40ff7 100644 --- a/blocksuite/affine/data-view/src/view-presets/table/pc-virtual/group/top/header/column-header.ts +++ b/blocksuite/affine/data-view/src/view-presets/table/pc-virtual/group/top/header/column-header.ts @@ -1,3 +1,8 @@ +import { + menu, + popMenu, + popupTargetFromElement, +} from '@blocksuite/affine-components/context-menu'; import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit'; import { PlusIcon } from '@blocksuite/icons/lit'; import { ShadowlessElement } from '@blocksuite/std'; @@ -7,6 +12,7 @@ import { repeat } from 'lit/directives/repeat.js'; import { styleMap } from 'lit/directives/style-map.js'; import { html } from 'lit/static-html.js'; +import { renderUniLit } from '../../../../../../core'; import type { TableSingleView } from '../../../../table-view-manager'; import * as styles from './column-header-css'; @@ -15,19 +21,50 @@ export class VirtualTableHeader extends SignalWatcher( ) { private readonly _onAddColumn = (e: MouseEvent) => { if (this.readonly) return; - this.tableViewManager.propertyAdd('end'); const ele = e.currentTarget as HTMLElement; - requestAnimationFrame(() => { - this.editLastColumnTitle(); - ele.scrollIntoView({ block: 'nearest', inline: 'nearest' }); + popMenu(popupTargetFromElement(ele), { + options: { + title: { + text: 'Property type', + }, + items: [ + menu.group({ + items: this.tableViewManager.propertyMetas$.value.map(config => { + return menu.action({ + name: config.config.name, + prefix: renderUniLit(config.renderer.icon), + select: () => { + const id = this.tableViewManager.propertyAdd('end', { + type: config.type, + name: config.config.name, + }); + if (id) { + requestAnimationFrame(() => { + ele.scrollIntoView({ + block: 'nearest', + inline: 'nearest', + }); + this.openPropertyMenuById(id); + }); + } + }, + }); + }), + }), + ], + }, }); }; - editLastColumnTitle = () => { - const columns = this.querySelectorAll('affine-database-header-column'); - const column = columns.item(columns.length - 1); - column.scrollIntoView({ block: 'nearest', inline: 'nearest' }); - column.editTitle(); + openPropertyMenuById = (id: string) => { + const column = this.querySelectorAll('virtual-database-header-column'); + for (const item of column) { + if (item.dataset.columnId === id) { + item.scrollIntoView({ block: 'nearest', inline: 'nearest' }); + item.editTitle(); + return; + } + } }; private get readonly() { @@ -44,7 +81,7 @@ export class VirtualTableHeader extends SignalWatcher(
${this.readonly ? nothing - : html`
`} + : html`
`} ${repeat( this.tableViewManager.properties$.value, column => column.id, @@ -54,14 +91,14 @@ export class VirtualTableHeader extends SignalWatcher( border: index === 0 ? 'none' : undefined, }); return html` - + >
`; } diff --git a/blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts b/blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts index 70dd58f70..86f1bf18d 100644 --- a/blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts +++ b/blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts @@ -241,7 +241,7 @@ export class TableSingleView extends SingleViewBase { }); } - if (groupKey) { + if (groupKey && id) { this.groupTrait.addToGroup(id, groupKey); } return id; diff --git a/blocksuite/playground/apps/starter/data/database.ts b/blocksuite/playground/apps/starter/data/database.ts index 9d174ce12..9e32ea798 100644 --- a/blocksuite/playground/apps/starter/data/database.ts +++ b/blocksuite/playground/apps/starter/data/database.ts @@ -45,10 +45,10 @@ export const database: InitFn = (collection: Workspace, id: string) => { const datasource = new DatabaseBlockDataSource(database); datasource.viewManager.viewAdd('table'); database.props.title = new Text(title); - const richTextId = datasource.propertyAdd( - 'end', - databaseBlockProperties.richTextColumnConfig.type - ); + const richTextId = datasource.propertyAdd('end', { + type: databaseBlockProperties.richTextColumnConfig.type, + name: 'Rich Text', + }); Object.values([ propertyPresets.multiSelectPropertyConfig, propertyPresets.datePropertyConfig, @@ -57,7 +57,10 @@ export const database: InitFn = (collection: Workspace, id: string) => { propertyPresets.checkboxPropertyConfig, propertyPresets.progressPropertyConfig, ]).forEach(column => { - datasource.propertyAdd('end', column.type); + datasource.propertyAdd('end', { + type: column.type, + name: column.config.name, + }); }); if (group) { const groupTrait = diff --git a/packages/frontend/core/src/blocksuite/database-block/group-by/index.tsx b/packages/frontend/core/src/blocksuite/database-block/group-by/index.tsx index a5cc7831d..69669e267 100644 --- a/packages/frontend/core/src/blocksuite/database-block/group-by/index.tsx +++ b/packages/frontend/core/src/blocksuite/database-block/group-by/index.tsx @@ -97,6 +97,7 @@ export const groupByConfigList = [ }, ]; }, + addToGroup: v => v, view: uniReactRoot.createUniComponent(MemberGroupView), }), createGroupByConfig({ @@ -122,7 +123,7 @@ export const groupByConfigList = [ return [ungroups]; }, valuesGroup: (value, _type) => { - if (!Array.isArray(value)) { + if (!Array.isArray(value) || value.length === 0) { return [ungroups]; } return value.map(id => ({ diff --git a/packages/frontend/core/src/blocksuite/database-block/properties/created-by/define.ts b/packages/frontend/core/src/blocksuite/database-block/properties/created-by/define.ts index ac5b54fb6..97404c93f 100644 --- a/packages/frontend/core/src/blocksuite/database-block/properties/created-by/define.ts +++ b/packages/frontend/core/src/blocksuite/database-block/properties/created-by/define.ts @@ -16,11 +16,6 @@ export const createdByPropertyModelConfig = createdByColumnType.modelConfig({ schema: zod.object({}), default: () => ({}), }, - fixed: { - defaultData: {}, - defaultOrder: 'end', - defaultShow: false, - }, rawValue: { schema: zod.string().nullable(), default: () => null, @@ -29,7 +24,7 @@ export const createdByPropertyModelConfig = createdByColumnType.modelConfig({ return { value: null }; }, toJson: ({ value }) => value, - fromJson: ({ value }) => value, + setValue: () => {}, }, jsonValue: { schema: zod.string().nullable(), diff --git a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx index 8d4861871..9d00d98ec 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx @@ -1,4 +1,4 @@ -import { Scrollable } from '@affine/component'; +import { Scrollable, uniReactRoot } from '@affine/component'; import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor'; import { EditorOutlineViewer } from '@affine/core/blocksuite/outline-viewer'; import { useActiveBlocksuiteEditor } from '@affine/core/components/hooks/use-block-suite-editor'; @@ -269,6 +269,7 @@ const SharePageInner = ({
+ diff --git a/tests/affine-local/e2e/blocksuite/database/utils.ts b/tests/affine-local/e2e/blocksuite/database/utils.ts index 8a4deb0b1..21a967865 100644 --- a/tests/affine-local/e2e/blocksuite/database/utils.ts +++ b/tests/affine-local/e2e/blocksuite/database/utils.ts @@ -79,8 +79,12 @@ export async function selectColumnType( const typeMenu = page.locator('affine-menu').getByText('Type'); await page.waitForTimeout(100); await typeMenu.hover(); + await page.mouse.move(0, 0); await page.waitForTimeout(100); - await page.keyboard.type(columnType); + for (const char of columnType.split('')) { + await page.keyboard.type(char); + await page.waitForTimeout(10); + } await page.waitForTimeout(100); for (let i = 0; i < nth; i++) { await page.keyboard.press('ArrowDown'); diff --git a/tests/blocksuite/e2e/database/column.spec.ts b/tests/blocksuite/e2e/database/column.spec.ts index 6ab6b333b..162d996d6 100644 --- a/tests/blocksuite/e2e/database/column.spec.ts +++ b/tests/blocksuite/e2e/database/column.spec.ts @@ -65,7 +65,7 @@ test.describe('column operations', () => { await initDatabaseDynamicRowWithData(page, '123', true); await pressEscape(page); const { text: title1 } = await getDatabaseHeaderColumn(page, 1); - expect(title1).toBe('Column 1'); + expect(title1).toBe('Column'); const selected = getDatabaseCell(page, { rowIndex: 0, diff --git a/tests/blocksuite/e2e/database/database.spec.ts b/tests/blocksuite/e2e/database/database.spec.ts index 295271c25..836f67251 100644 --- a/tests/blocksuite/e2e/database/database.spec.ts +++ b/tests/blocksuite/e2e/database/database.spec.ts @@ -90,7 +90,7 @@ test('edit column title', async ({ page }) => { expect(await column.innerText()).toBe('1'); await undoByClick(page); - expect(await column.innerText()).toBe('Column 1'); + expect(await column.innerText()).toBe('Column'); }); test('should modify the value when the input loses focus', async ({ page }) => { @@ -359,7 +359,7 @@ test('should title column support quick renaming', async ({ page }) => { expect(await textElement.innerText()).toBe('123'); await undoByClick(page); - expect(await textElement.innerText()).toBe('Column 1'); + expect(await textElement.innerText()).toBe('Column'); await textElement.click(); await waitNextFrame(page); await selectAllByKeyboard(page); diff --git a/tests/blocksuite/e2e/utils/actions/misc.ts b/tests/blocksuite/e2e/utils/actions/misc.ts index d9ddd4333..5691660a7 100644 --- a/tests/blocksuite/e2e/utils/actions/misc.ts +++ b/tests/blocksuite/e2e/utils/actions/misc.ts @@ -392,7 +392,9 @@ export async function initKanbanViewState( return rowId; }); config.columns.forEach(column => { - const columnId = datasource.propertyAdd('end', column.type); + const columnId = datasource.propertyAdd('end', { + type: column.type, + }); if (!columnId) { return; }