refactor(editor): extract data view block (#9452)

This commit is contained in:
Saul-Mirone
2024-12-31 06:15:35 +00:00
parent 0f03c3fc5e
commit 1e34ec8487
25 changed files with 153 additions and 34 deletions

View File

@@ -1,6 +1,7 @@
import { AttachmentBlockSpec } from '@blocksuite/affine-block-attachment';
import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark';
import { CodeBlockSpec } from '@blocksuite/affine-block-code';
import { DataViewBlockSpec } from '@blocksuite/affine-block-data-view';
import { DatabaseBlockSpec } from '@blocksuite/affine-block-database';
import { DividerBlockSpec } from '@blocksuite/affine-block-divider';
import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text';
@@ -34,7 +35,6 @@ import {
import type { ExtensionType } from '@blocksuite/block-std';
import { AdapterFactoryExtensions } from '../_common/adapters/extension.js';
import { DataViewBlockSpec } from '../data-view-block/data-view-spec.js';
export const CommonBlockSpecs: ExtensionType[] = [
DocDisplayMetaService,

View File

@@ -1,36 +0,0 @@
import type { PropertyMetaConfig } from '@blocksuite/data-view';
import type { Disposable } from '@blocksuite/global/utils';
import type { Block, BlockModel } from '@blocksuite/store';
type PropertyMeta<
T extends BlockModel = BlockModel,
Value = unknown,
ColumnData extends NonNullable<unknown> = NonNullable<unknown>,
> = {
name: string;
key: string;
metaConfig: PropertyMetaConfig<string, ColumnData, Value>;
getColumnData?: (block: T) => ColumnData;
setColumnData?: (block: T, data: ColumnData) => void;
get: (block: T) => Value;
set?: (block: T, value: Value) => void;
updated: (block: T, callback: () => void) => Disposable;
};
export type BlockMeta<T extends BlockModel = BlockModel> = {
selector: (block: Block) => boolean;
properties: PropertyMeta<T>[];
};
export const createBlockMeta = <T extends BlockModel>(
options: Omit<BlockMeta<T>, 'properties'>
) => {
const meta: BlockMeta = {
...options,
properties: [],
};
return {
...meta,
addProperty: <Value>(property: PropertyMeta<T, Value>) => {
meta.properties.push(property as PropertyMeta);
},
};
};

View File

@@ -1,6 +0,0 @@
import type { BlockMeta } from './base.js';
import { todoMeta } from './todo.js';
export const blockMetaMap = {
todo: todoMeta,
} satisfies Record<string, BlockMeta>;

View File

@@ -1,60 +0,0 @@
import { richTextColumnConfig } from '@blocksuite/affine-block-database';
import { type ListBlockModel, ListBlockSchema } from '@blocksuite/affine-model';
import { propertyPresets } from '@blocksuite/data-view/property-presets';
import { createBlockMeta } from './base.js';
export const todoMeta = createBlockMeta<ListBlockModel>({
selector: block => {
if (block.flavour !== ListBlockSchema.model.flavour) {
return false;
}
return (block.model as ListBlockModel).type === 'todo';
},
});
todoMeta.addProperty({
name: 'Content',
key: 'todo-title',
metaConfig: richTextColumnConfig,
get: block => block.text.yText,
set: (_block, _value) => {
//
},
updated: (block, callback) => {
block.text?.yText.observe(callback);
return {
dispose: () => {
block.text?.yText.unobserve(callback);
},
};
},
});
todoMeta.addProperty({
name: 'Checked',
key: 'todo-checked',
metaConfig: propertyPresets.checkboxPropertyConfig,
get: block => block.checked,
set: (block, value) => {
block.checked = value;
},
updated: (block, callback) => {
return block.propsUpdated.on(({ key }) => {
if (key === 'checked') {
callback();
}
});
},
});
todoMeta.addProperty({
name: 'Source',
key: 'todo-source',
metaConfig: propertyPresets.textPropertyConfig,
get: block => block.doc.meta?.title ?? '',
updated: (block, callback) => {
return block.doc.collection.meta.docMetaUpdated.on(() => {
callback();
});
},
});

View File

@@ -1,17 +0,0 @@
import { richTextColumnConfig } from '@blocksuite/affine-block-database';
import type { PropertyMetaConfig } from '@blocksuite/data-view';
import { propertyPresets } from '@blocksuite/data-view/property-presets';
export const queryBlockColumns = [
propertyPresets.datePropertyConfig,
propertyPresets.numberPropertyConfig,
propertyPresets.progressPropertyConfig,
propertyPresets.selectPropertyConfig,
propertyPresets.multiSelectPropertyConfig,
propertyPresets.checkboxPropertyConfig,
];
export const queryBlockHiddenColumns = [richTextColumnConfig];
const queryBlockAllColumns = [...queryBlockColumns, ...queryBlockHiddenColumns];
export const queryBlockAllColumnMap = Object.fromEntries(
queryBlockAllColumns.map(v => [v.type, v as PropertyMetaConfig])
);

View File

@@ -1,307 +0,0 @@
import {
databaseBlockAllPropertyMap,
databasePropertyConverts,
} from '@blocksuite/affine-block-database';
import type { Column } from '@blocksuite/affine-model';
import {
insertPositionToIndex,
type InsertToPosition,
} from '@blocksuite/affine-shared/utils';
import type { EditorHost } from '@blocksuite/block-std';
import { DataSourceBase, type PropertyMetaConfig } from '@blocksuite/data-view';
import { propertyPresets } from '@blocksuite/data-view/property-presets';
import { assertExists, Slot } from '@blocksuite/global/utils';
import type { Block, Doc } from '@blocksuite/store';
import type { BlockMeta } from './block-meta/base.js';
import { blockMetaMap } from './block-meta/index.js';
import { queryBlockAllColumnMap, queryBlockColumns } from './columns/index.js';
import type { DataViewBlockModel } from './data-view-model.js';
export type BlockQueryDataSourceConfig = {
type: keyof typeof blockMetaMap;
};
// @ts-expect-error FIXME: ts error
export class BlockQueryDataSource extends DataSourceBase {
private readonly columnMetaMap = new Map<
string,
PropertyMetaConfig<any, any, any>
>();
private readonly meta: BlockMeta;
blockMap = new Map<string, Block>();
docDisposeMap = new Map<string, () => void>();
slots = {
update: new Slot(),
};
private get blocks() {
return [...this.blockMap.values()];
}
get properties(): string[] {
return [
...this.meta.properties.map(v => v.key),
...this.block.columns.map(v => v.id),
];
}
get propertyMetas(): PropertyMetaConfig[] {
return queryBlockColumns as PropertyMetaConfig[];
}
get rows(): string[] {
return this.blocks.map(v => v.id);
}
get workspace() {
return this.host.doc.collection;
}
constructor(
private readonly host: EditorHost,
private readonly block: DataViewBlockModel,
config: BlockQueryDataSourceConfig
) {
super();
this.meta = blockMetaMap[config.type];
for (const property of this.meta.properties) {
this.columnMetaMap.set(property.metaConfig.type, property.metaConfig);
}
for (const collection of this.workspace.docs.values()) {
for (const block of Object.values(collection.getDoc().blocks.peek())) {
if (this.meta.selector(block)) {
this.blockMap.set(block.id, block);
}
}
}
this.workspace.docs.forEach(doc => {
this.listenToDoc(doc.getDoc());
});
this.workspace.slots.docAdded.on(id => {
const doc = this.workspace.getDoc(id);
if (doc) {
this.listenToDoc(doc);
}
});
this.workspace.slots.docRemoved.on(id => {
this.docDisposeMap.get(id)?.();
});
}
private getProperty(propertyId: string) {
const property = this.meta.properties.find(v => v.key === propertyId);
assertExists(property, `property ${propertyId} not found`);
return property;
}
private newColumnName() {
let i = 1;
while (this.block.columns.some(column => column.name === `Column ${i}`)) {
i++;
}
return `Column ${i}`;
}
cellValueChange(rowId: string, propertyId: string, value: unknown): void {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
this.block.cells[rowId] = {
...this.block.cells[rowId],
[propertyId]: value,
};
return;
}
const block = this.blockMap.get(rowId);
if (block) {
this.meta.properties
.find(v => v.key === propertyId)
?.set?.(block.model, value);
}
}
cellValueGet(rowId: string, propertyId: string): unknown {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
return this.block.cells[rowId]?.[propertyId];
}
const block = this.blockMap.get(rowId);
if (block) {
return this.getProperty(propertyId)?.get(block.model);
}
return;
}
getViewColumn(id: string) {
return this.block.columns.find(v => v.id === id);
}
listenToDoc(doc: Doc) {
this.docDisposeMap.set(
doc.id,
doc.slots.blockUpdated.on(v => {
if (v.type === 'add') {
const blockById = doc.getBlock(v.id);
if (blockById && this.meta.selector(blockById)) {
this.blockMap.set(v.id, blockById);
}
} else if (v.type === 'delete') {
this.blockMap.delete(v.id);
}
this.slots.update.emit();
}).dispose
);
}
propertyAdd(
insertToPosition: InsertToPosition,
type: string | undefined
): string {
const doc = this.block.doc;
doc.captureSync();
const column = databaseBlockAllPropertyMap[
type ?? propertyPresets.multiSelectPropertyConfig.type
].create(this.newColumnName());
const id = doc.generateBlockId();
if (this.block.columns.some(v => v.id === id)) {
return id;
}
doc.transact(() => {
const col: Column = {
...column,
id,
};
this.block.columns.splice(
insertPositionToIndex(insertToPosition, this.block.columns),
0,
col
);
});
return id;
}
propertyDataGet(propertyId: string): Record<string, unknown> {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
return viewColumn.data;
}
const property = this.getProperty(propertyId);
return (
property.getColumnData?.(this.blocks[0].model) ??
property.metaConfig.config.defaultData()
);
}
propertyDataSet(propertyId: string, data: Record<string, unknown>): void {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
viewColumn.data = data;
}
}
propertyDelete(_id: string): void {
const index = this.block.columns.findIndex(v => v.id === _id);
if (index >= 0) {
this.block.columns.splice(index, 1);
}
}
propertyDuplicate(_columnId: string): string {
throw new Error('Method not implemented.');
}
propertyMetaGet(type: string): PropertyMetaConfig {
const meta = this.columnMetaMap.get(type);
if (meta) {
return meta;
}
return queryBlockAllColumnMap[type];
}
propertyNameGet(propertyId: string): string {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
return viewColumn.name;
}
if (propertyId === 'type') {
return 'Block Type';
}
return this.getProperty(propertyId)?.name ?? '';
}
propertyNameSet(propertyId: string, name: string): void {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
viewColumn.name = name;
}
}
override propertyReadonlyGet(propertyId: string): boolean {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
return false;
}
if (propertyId === 'type') return true;
return this.getProperty(propertyId)?.set == null;
}
propertyTypeGet(propertyId: string): string {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
return viewColumn.type;
}
if (propertyId === 'type') {
return 'image';
}
return this.getProperty(propertyId).metaConfig.type;
}
propertyTypeSet(propertyId: string, toType: string): void {
const viewColumn = this.getViewColumn(propertyId);
if (viewColumn) {
const currentType = viewColumn.type;
const currentData = viewColumn.data;
const rows = this.rows$.value;
const currentCells = rows.map(rowId =>
this.cellValueGet(rowId, propertyId)
);
const convertFunction = databasePropertyConverts.find(
v => v.from === currentType && v.to === toType
)?.convert;
const result = convertFunction?.(
currentData as any,
currentCells as any
) ?? {
property: databaseBlockAllPropertyMap[toType].config.defaultData(),
cells: currentCells.map(() => undefined),
};
this.block.doc.captureSync();
viewColumn.type = toType;
viewColumn.data = result.property;
currentCells.forEach((value, i) => {
if (value != null || result.cells[i] != null) {
this.block.cells[rows[i]] = {
...this.block.cells[rows[i]],
[propertyId]: result.cells[i],
};
}
});
}
}
rowAdd(_insertPosition: InsertToPosition | number): string {
throw new Error('Method not implemented.');
}
rowDelete(_ids: string[]): void {
throw new Error('Method not implemented.');
}
rowMove(_rowId: string, _position: InsertToPosition): void {}
}

View File

@@ -1,329 +0,0 @@
import { BlockRenderer, NoteRenderer } from '@blocksuite/affine-block-database';
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import {
menu,
popMenu,
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import {
CopyIcon,
DeleteIcon,
MoreHorizontalIcon,
} from '@blocksuite/affine-components/icons';
import { PeekViewProvider } from '@blocksuite/affine-components/peek';
import { toast } from '@blocksuite/affine-components/toast';
import {
NotificationProvider,
type TelemetryEventMap,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { RANGE_SYNC_EXCLUDE_ATTR } from '@blocksuite/block-std';
import {
createRecordDetail,
createUniComponentFromWebComponent,
DatabaseSelection,
type DataSource,
DataView,
dataViewCommonStyle,
type DataViewProps,
type DataViewSelection,
type DataViewWidget,
type DataViewWidgetProps,
defineUniComponent,
renderUniLit,
uniMap,
} from '@blocksuite/data-view';
import { widgetPresets } from '@blocksuite/data-view/widget-presets';
import { Slice } from '@blocksuite/store';
import { computed, signal } from '@preact/signals-core';
import { css, nothing, unsafeCSS } from 'lit';
import { html } from 'lit/static-html.js';
import {
EdgelessRootBlockComponent,
type RootService,
} from '../root-block/index.js';
import { BlockQueryDataSource } from './data-source.js';
import type { DataViewBlockModel } from './data-view-model.js';
export class DataViewBlockComponent extends CaptionedBlockComponent<DataViewBlockModel> {
static override styles = css`
${unsafeCSS(dataViewCommonStyle('affine-database'))}
affine-database {
display: block;
border-radius: 8px;
background-color: var(--affine-background-primary-color);
padding: 8px;
margin: 8px -8px -8px;
}
.database-block-selected {
background-color: var(--affine-hover-color);
border-radius: 4px;
}
.database-ops {
padding: 2px;
border-radius: 4px;
display: flex;
cursor: pointer;
}
.database-ops svg {
width: 16px;
height: 16px;
color: var(--affine-icon-color);
}
.database-ops:hover {
background-color: var(--affine-hover-color);
}
@media print {
.database-ops {
display: none;
}
.database-header-bar {
display: none !important;
}
}
`;
private readonly _clickDatabaseOps = (e: MouseEvent) => {
popMenu(popupTargetFromElement(e.currentTarget as HTMLElement), {
options: {
items: [
menu.input({
initialValue: this.model.title,
placeholder: 'Untitled',
onChange: text => {
this.model.title = text;
},
}),
menu.action({
prefix: CopyIcon,
name: 'Copy',
select: () => {
const slice = Slice.fromModels(this.doc, [this.model]);
this.std.clipboard.copySlice(slice).catch(console.error);
},
}),
menu.group({
name: '',
items: [
menu.action({
prefix: DeleteIcon,
class: {
'delete-item': true,
},
name: 'Delete Database',
select: () => {
this.model.children.slice().forEach(block => {
this.doc.deleteBlock(block);
});
this.doc.deleteBlock(this.model);
},
}),
],
}),
],
},
});
};
private _dataSource?: DataSource;
private readonly dataView = new DataView();
_bindHotkey: DataViewProps['bindHotkey'] = hotkeys => {
return {
dispose: this.host.event.bindHotkey(hotkeys, {
blockId: this.topContenteditableElement?.blockId ?? this.blockId,
}),
};
};
_handleEvent: DataViewProps['handleEvent'] = (name, handler) => {
return {
dispose: this.host.event.add(name, handler, {
blockId: this.blockId,
}),
};
};
getRootService = () => {
return this.std.getService<RootService>('affine:page');
};
headerWidget: DataViewWidget = defineUniComponent(
(props: DataViewWidgetProps) => {
return html`
<div style="margin-bottom: 16px;display:flex;flex-direction: column">
<div style="display:flex;gap:8px;padding: 0 6px;margin-bottom: 8px;">
<div>${this.model.title}</div>
${this.renderDatabaseOps()}
</div>
<div
style="display:flex;align-items:center;justify-content: space-between;gap: 12px"
class="database-header-bar"
>
<div style="flex:1">
${renderUniLit(widgetPresets.viewBar, props)}
</div>
${renderUniLit(this.toolsWidget, props)}
</div>
${renderUniLit(widgetPresets.quickSettingBar, props)}
</div>
`;
}
);
selection$ = computed(() => {
const databaseSelection = this.selection.value.find(
(selection): selection is DatabaseSelection => {
if (selection.blockId !== this.blockId) {
return false;
}
return selection instanceof DatabaseSelection;
}
);
return databaseSelection?.viewSelection;
});
setSelection = (selection: DataViewSelection | undefined) => {
this.selection.setGroup(
'note',
selection
? [
new DatabaseSelection({
blockId: this.blockId,
viewSelection: selection,
}),
]
: []
);
};
toolsWidget: DataViewWidget = widgetPresets.createTools({
table: [
widgetPresets.tools.filter,
widgetPresets.tools.search,
widgetPresets.tools.viewOptions,
widgetPresets.tools.tableAddRow,
],
kanban: [
widgetPresets.tools.filter,
widgetPresets.tools.search,
widgetPresets.tools.viewOptions,
],
});
get dataSource(): DataSource {
if (!this._dataSource) {
this._dataSource = new BlockQueryDataSource(this.host, this.model, {
type: 'todo',
});
}
return this._dataSource;
}
override get topContenteditableElement() {
if (this.rootComponent instanceof EdgelessRootBlockComponent) {
const note = this.closest<NoteBlockComponent>('affine-note');
return note;
}
return this.rootComponent;
}
get view() {
return this.dataView.expose;
}
private renderDatabaseOps() {
if (this.doc.readonly) {
return nothing;
}
return html` <div class="database-ops" @click="${this._clickDatabaseOps}">
${MoreHorizontalIcon}
</div>`;
}
override connectedCallback() {
super.connectedCallback();
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
}
override renderBlock() {
const peekViewService = this.std.getOptional(PeekViewProvider);
const telemetryService = this.std.getOptional(TelemetryProvider);
return html`
<div contenteditable="false" style="position: relative">
${this.dataView.render({
virtualPadding$: signal(0),
bindHotkey: this._bindHotkey,
handleEvent: this._handleEvent,
selection$: this.selection$,
setSelection: this.setSelection,
dataSource: this.dataSource,
headerWidget: this.headerWidget,
clipboard: this.std.clipboard,
notification: {
toast: message => {
const notification = this.std.getOptional(NotificationProvider);
if (notification) {
notification.toast(message);
} else {
toast(this.host, message);
}
},
},
eventTrace: (key, params) => {
telemetryService?.track(key, {
...(params as TelemetryEventMap[typeof key]),
blockId: this.blockId,
});
},
detailPanelConfig: {
openDetailPanel: (target, data) => {
if (peekViewService) {
const template = createRecordDetail({
...data,
openDoc: () => {},
detail: {
header: uniMap(
createUniComponentFromWebComponent(BlockRenderer),
props => ({
...props,
host: this.host,
})
),
note: uniMap(
createUniComponentFromWebComponent(NoteRenderer),
props => ({
...props,
model: this.model,
host: this.host,
})
),
},
});
return peekViewService.peek({ target, template });
} else {
return Promise.resolve();
}
},
},
})}
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'affine-data-view': DataViewBlockComponent;
}
}

View File

@@ -1,95 +0,0 @@
import type { Column } from '@blocksuite/affine-model';
import {
arrayMove,
insertPositionToIndex,
type InsertToPosition,
} from '@blocksuite/affine-shared/utils';
import type { DataViewDataType } from '@blocksuite/data-view';
import { BlockModel, defineBlockSchema } from '@blocksuite/store';
type Props = {
title: string;
views: DataViewDataType[];
columns: Column[];
cells: Record<string, Record<string, unknown>>;
};
export class DataViewBlockModel extends BlockModel<Props> {
constructor() {
super();
}
applyViewsUpdate() {
this.doc.updateBlock(this, {
views: this.views,
});
}
deleteView(id: string) {
this.doc.captureSync();
this.doc.transact(() => {
this.views = this.views.filter(v => v.id !== id);
});
}
duplicateView(id: string): string {
const newId = this.doc.generateBlockId();
this.doc.transact(() => {
const index = this.views.findIndex(v => v.id === id);
const view = this.views[index];
if (view) {
this.views.splice(
index + 1,
0,
JSON.parse(JSON.stringify({ ...view, id: newId }))
);
}
});
return newId;
}
moveViewTo(id: string, position: InsertToPosition) {
this.doc.transact(() => {
this.views = arrayMove(
this.views,
v => v.id === id,
arr => insertPositionToIndex(position, arr)
);
});
this.applyViewsUpdate();
}
updateView(
id: string,
update: (data: DataViewDataType) => Partial<DataViewDataType>
) {
this.doc.transact(() => {
this.views = this.views.map(v => {
if (v.id !== id) {
return v;
}
return { ...v, ...(update(v) as DataViewDataType) };
});
});
this.applyViewsUpdate();
}
}
export const DataViewBlockSchema = defineBlockSchema({
flavour: 'affine:data-view',
props: (): Props => ({
views: [],
title: '',
columns: [],
cells: {},
}),
metadata: {
role: 'hub',
version: 1,
parent: ['affine:note'],
children: ['affine:paragraph', 'affine:list'],
},
toModel: () => {
return new DataViewBlockModel();
},
});

View File

@@ -1,11 +0,0 @@
import {
BlockViewExtension,
type ExtensionType,
FlavourExtension,
} from '@blocksuite/block-std';
import { literal } from 'lit/static-html.js';
export const DataViewBlockSpec: ExtensionType[] = [
FlavourExtension('affine:data-view'),
BlockViewExtension('affine:data-view', literal`affine-data-view`),
];

View File

@@ -1,13 +0,0 @@
import type { DataViewBlockModel } from './data-view-model.js';
export * from './data-view-block.js';
export * from './data-view-model.js';
export * from './data-view-spec.js';
declare global {
namespace BlockSuite {
interface BlockModels {
'affine:data-view': DataViewBlockModel;
}
}
}

View File

@@ -1,11 +0,0 @@
import type { ViewMeta } from '@blocksuite/data-view';
import { viewPresets } from '@blocksuite/data-view/view-presets';
export const blockQueryViews: ViewMeta[] = [
viewPresets.tableViewMeta,
viewPresets.kanbanViewMeta,
];
export const blockQueryViewMap = Object.fromEntries(
blockQueryViews.map(view => [view.type, view])
);

View File

@@ -1,6 +1,7 @@
import { effects as blockAttachmentEffects } from '@blocksuite/affine-block-attachment/effects';
import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookmark/effects';
import { effects as blockCodeEffects } from '@blocksuite/affine-block-code/effects';
import { effects as blockDataViewEffects } from '@blocksuite/affine-block-data-view/effects';
import { effects as blockDatabaseEffects } from '@blocksuite/affine-block-database/effects';
import { effects as blockDividerEffects } from '@blocksuite/affine-block-divider/effects';
import { effects as blockEdgelessTextEffects } from '@blocksuite/affine-block-edgeless-text/effects';
@@ -38,7 +39,6 @@ import { effects as inlineEffects } from '@blocksuite/inline/effects';
import type { BlockModel } from '@blocksuite/store';
import { registerSpecs } from './_specs/register-specs.js';
import { DataViewBlockComponent } from './data-view-block/index.js';
import { EdgelessAutoCompletePanel } from './root-block/edgeless/components/auto-complete/auto-complete-panel.js';
import { EdgelessAutoComplete } from './root-block/edgeless/components/auto-complete/edgeless-auto-complete.js';
import { EdgelessToolIconButton } from './root-block/edgeless/components/buttons/tool-icon-button.js';
@@ -209,6 +209,7 @@ export function effects() {
blockLatexEffects();
blockEdgelessTextEffects();
blockDividerEffects();
blockDataViewEffects();
blockCodeEffects();
componentCaptionEffects();
@@ -234,7 +235,6 @@ export function effects() {
customElements.define('affine-preview-root', PreviewRootBlockComponent);
customElements.define('mini-mindmap-preview', MiniMindmapPreview);
customElements.define('mini-mindmap-surface-block', MindmapSurfaceBlock);
customElements.define('affine-data-view', DataViewBlockComponent);
customElements.define('affine-edgeless-root', EdgelessRootBlockComponent);
customElements.define('edgeless-copilot-panel', EdgelessCopilotPanel);
customElements.define(

View File

@@ -17,7 +17,6 @@ export * from './_common/test-utils/test-utils.js';
export * from './_common/transformers/index.js';
export { type AbstractEditor } from './_common/types.js';
export * from './_specs/index.js';
export * from './data-view-block';
export { EdgelessTemplatePanel } from './root-block/edgeless/components/toolbar/template/template-panel.js';
export type {
Template,
@@ -42,6 +41,7 @@ export {
export * from '@blocksuite/affine-block-attachment';
export * from '@blocksuite/affine-block-bookmark';
export * from '@blocksuite/affine-block-code';
export * from '@blocksuite/affine-block-data-view';
export * from '@blocksuite/affine-block-database';
export * from '@blocksuite/affine-block-divider';
export * from '@blocksuite/affine-block-edgeless-text';

View File

@@ -1,5 +1,6 @@
import { addSiblingAttachmentBlocks } from '@blocksuite/affine-block-attachment';
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-block-bookmark';
import type { DataViewBlockComponent } from '@blocksuite/affine-block-data-view';
import {
FigmaIcon,
GithubIcon,
@@ -51,7 +52,6 @@ import type { BlockModel } from '@blocksuite/store';
import { Slice, Text } from '@blocksuite/store';
import type { TemplateResult } from 'lit';
import type { DataViewBlockComponent } from '../../../data-view-block/index.js';
import type { RootBlockComponent } from '../../types.js';
import { formatDate, formatTime } from '../../utils/misc.js';
import type { AffineLinkedDocWidget } from '../linked-doc/index.js';

View File

@@ -1,4 +1,5 @@
// Import models only, the bundled file should not include anything else.
import { DataViewBlockSchema } from '@blocksuite/affine-block-data-view';
import { SurfaceBlockSchema } from '@blocksuite/affine-block-surface';
import {
AttachmentBlockSchema,
@@ -26,8 +27,6 @@ import {
import type { BlockSchema } from '@blocksuite/store';
import type { z } from 'zod';
import { DataViewBlockSchema } from './data-view-block/data-view-model.js';
/** Built-in first party block models built for affine */
export const AffineSchemas: z.infer<typeof BlockSchema>[] = [
CodeBlockSchema,