refactor(editor): flat data for table block (#10010)

close: BS-2548
This commit is contained in:
Saul-Mirone
2025-02-10 19:09:33 +00:00
parent a5f36eb1d8
commit c6b8f2b584
9 changed files with 125 additions and 98 deletions
+19 -30
View File
@@ -1,7 +1,12 @@
import { TableModelFlavour } from '@blocksuite/affine-model'; import {
import { generateFractionalIndexingKeyBetween } from '@blocksuite/affine-shared/utils'; type TableBlockModel,
TableModelFlavour,
} from '@blocksuite/affine-model';
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
import { type BlockModel, nanoid, Text } from '@blocksuite/store'; import { type BlockModel } from '@blocksuite/store';
import { TableDataManager } from './table-data-manager';
export const insertTableBlockCommand: Command< export const insertTableBlockCommand: Command<
{ {
place?: 'after' | 'before'; place?: 'after' | 'before';
@@ -22,41 +27,25 @@ export const insertTableBlockCommand: Command<
if (!targetModel) return; if (!targetModel) return;
const row1Id = nanoid();
const row2Id = nanoid();
const col1Id = nanoid();
const col2Id = nanoid();
const order1 = generateFractionalIndexingKeyBetween(null, null);
const order2 = generateFractionalIndexingKeyBetween(order1, null);
const initialTableData = {
rows: {
[row1Id]: { rowId: row1Id, order: order1 },
[row2Id]: { rowId: row2Id, order: order2 },
},
columns: {
[col1Id]: { columnId: col1Id, order: order1 },
[col2Id]: { columnId: col2Id, order: order2 },
},
cells: {
[`${row1Id}:${col1Id}`]: { text: new Text() },
[`${row1Id}:${col2Id}`]: { text: new Text() },
[`${row2Id}:${col1Id}`]: { text: new Text() },
[`${row2Id}:${col2Id}`]: { text: new Text() },
},
};
const result = std.store.addSiblingBlocks( const result = std.store.addSiblingBlocks(
targetModel, targetModel,
[{ flavour: TableModelFlavour, ...initialTableData }], [{ flavour: TableModelFlavour }],
place place
); );
const blockId = result[0]; const blockId = result[0];
if (blockId == null) return; if (blockId == null) return;
if (removeEmptyLine && targetModel.text?.length === 0) { const model = std.store.getBlock(blockId)?.model as TableBlockModel;
std.store.deleteBlock(targetModel); if (model == null) return;
const dataManager = new TableDataManager(model);
dataManager.addNRow(2);
dataManager.addNColumn(2);
if (removeEmptyLine && model.text?.length === 0) {
std.store.deleteBlock(model);
} }
next({ insertedTableBlockId: blockId }); next({ insertedTableBlockId: blockId });
@@ -23,13 +23,13 @@ export class TableDataManager {
`${this.virtualRowCount$.value + this.rows$.value.length} x ${this.virtualColumnCount$.value + this.columns$.value.length}` `${this.virtualRowCount$.value + this.rows$.value.length} x ${this.virtualColumnCount$.value + this.columns$.value.length}`
); );
rows$ = computed(() => { rows$ = computed(() => {
return Object.values(this.model.rows$.value).sort((a, b) => return Object.values(this.model.props.rows$.value).sort((a, b) =>
a.order > b.order ? 1 : -1 a.order > b.order ? 1 : -1
); );
}); });
columns$ = computed(() => { columns$ = computed(() => {
return Object.values(this.model.columns$.value).sort((a, b) => return Object.values(this.model.props.columns$.value).sort((a, b) =>
a.order > b.order ? 1 : -1 a.order > b.order ? 1 : -1
); );
}); });
@@ -72,19 +72,20 @@ export class TableDataManager {
}); });
getCell(rowId: string, columnId: string): TableCell | undefined { getCell(rowId: string, columnId: string): TableCell | undefined {
return this.model.cells$.value[`${rowId}:${columnId}`]; return this.model.props.cells$.value[`${rowId}:${columnId}`];
} }
addRow(after?: number) { addRow(after?: number) {
const order = this.getOrder(this.rows$.value, after); const order = this.getOrder(this.rows$.value, after);
const rowId = nanoid(); const rowId = nanoid();
this.model.doc.transact(() => { this.model.doc.transact(() => {
this.model.rows[rowId] = { this.model.props.rows[rowId] = {
rowId, rowId,
order, order,
}; };
this.columns$.value.forEach(column => { this.columns$.value.forEach(column => {
this.model.cells[`${rowId}:${column.columnId}`] = { this.model.props.cells[`${rowId}:${column.columnId}`] = {
text: new Text(), text: new Text(),
}; };
}); });
@@ -148,12 +149,12 @@ export class TableDataManager {
const order = this.getOrder(this.columns$.value, after); const order = this.getOrder(this.columns$.value, after);
const columnId = nanoid(); const columnId = nanoid();
this.model.doc.transact(() => { this.model.doc.transact(() => {
this.model.columns[columnId] = { this.model.props.columns[columnId] = {
columnId, columnId,
order, order,
}; };
this.rows$.value.forEach(row => { this.rows$.value.forEach(row => {
this.model.cells[`${row.rowId}:${columnId}`] = { this.model.props.cells[`${row.rowId}:${columnId}`] = {
text: new Text(), text: new Text(),
}; };
}); });
@@ -163,14 +164,14 @@ export class TableDataManager {
deleteRow(rowId: string) { deleteRow(rowId: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
Object.keys(this.model.rows).forEach(id => { Object.keys(this.model.props.rows).forEach(id => {
if (id === rowId) { if (id === rowId) {
delete this.model.rows[id]; delete this.model.props.rows[id];
} }
}); });
Object.keys(this.model.cells).forEach(id => { Object.keys(this.model.props.cells).forEach(id => {
if (id.startsWith(rowId)) { if (id.startsWith(rowId)) {
delete this.model.cells[id]; delete this.model.props.cells[id];
} }
}); });
}); });
@@ -178,14 +179,14 @@ export class TableDataManager {
deleteColumn(columnId: string) { deleteColumn(columnId: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
Object.keys(this.model.columns).forEach(id => { Object.keys(this.model.props.columns).forEach(id => {
if (id === columnId) { if (id === columnId) {
delete this.model.columns[id]; delete this.model.props.columns[id];
} }
}); });
Object.keys(this.model.cells).forEach(id => { Object.keys(this.model.props.cells).forEach(id => {
if (id.endsWith(`:${columnId}`)) { if (id.endsWith(`:${columnId}`)) {
delete this.model.cells[id]; delete this.model.props.cells[id];
} }
}); });
}); });
@@ -193,51 +194,51 @@ export class TableDataManager {
updateRowOrder(rowId: string, newOrder: string) { updateRowOrder(rowId: string, newOrder: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
if (this.model.rows[rowId]) { if (this.model.props.rows[rowId]) {
this.model.rows[rowId].order = newOrder; this.model.props.rows[rowId].order = newOrder;
} }
}); });
} }
updateColumnOrder(columnId: string, newOrder: string) { updateColumnOrder(columnId: string, newOrder: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
if (this.model.columns[columnId]) { if (this.model.props.columns[columnId]) {
this.model.columns[columnId].order = newOrder; this.model.props.columns[columnId].order = newOrder;
} }
}); });
} }
setRowBackgroundColor(rowId: string, color?: string) { setRowBackgroundColor(rowId: string, color?: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
if (this.model.rows[rowId]) { if (this.model.props.rows[rowId]) {
this.model.rows[rowId].backgroundColor = color; this.model.props.rows[rowId].backgroundColor = color;
} }
}); });
} }
setColumnBackgroundColor(columnId: string, color?: string) { setColumnBackgroundColor(columnId: string, color?: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
if (this.model.columns[columnId]) { if (this.model.props.columns[columnId]) {
this.model.columns[columnId].backgroundColor = color; this.model.props.columns[columnId].backgroundColor = color;
} }
}); });
} }
setColumnWidth(columnId: string, width: number) { setColumnWidth(columnId: string, width: number) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
if (this.model.columns[columnId]) { if (this.model.props.columns[columnId]) {
this.model.columns[columnId].width = width; this.model.props.columns[columnId].width = width;
} }
}); });
} }
clearRow(rowId: string) { clearRow(rowId: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
Object.keys(this.model.cells).forEach(id => { Object.keys(this.model.props.cells).forEach(id => {
if (id.startsWith(rowId)) { if (id.startsWith(rowId)) {
this.model.cells[id]?.text.replace( this.model.props.cells[id]?.text.replace(
0, 0,
this.model.cells[id]?.text.length, this.model.props.cells[id]?.text.length,
'' ''
); );
} }
@@ -247,11 +248,11 @@ export class TableDataManager {
clearColumn(columnId: string) { clearColumn(columnId: string) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
Object.keys(this.model.cells).forEach(id => { Object.keys(this.model.props.cells).forEach(id => {
if (id.endsWith(`:${columnId}`)) { if (id.endsWith(`:${columnId}`)) {
this.model.cells[id]?.text.replace( this.model.props.cells[id]?.text.replace(
0, 0,
this.model.cells[id]?.text.length, this.model.props.cells[id]?.text.length,
'' ''
); );
} }
@@ -286,7 +287,7 @@ export class TableDataManager {
clearCells(cells: { rowId: string; columnId: string }[]) { clearCells(cells: { rowId: string; columnId: string }[]) {
this.model.doc.transact(() => { this.model.doc.transact(() => {
cells.forEach(({ rowId, columnId }) => { cells.forEach(({ rowId, columnId }) => {
const text = this.model.cells[`${rowId}:${columnId}`]?.text; const text = this.model.props.cells[`${rowId}:${columnId}`]?.text;
if (text) { if (text) {
text.replace(0, text.length, ''); text.replace(0, text.length, '');
} }
@@ -308,7 +309,7 @@ export class TableDataManager {
if (!column) return; if (!column) return;
const order = this.getOrder(columns, after); const order = this.getOrder(columns, after);
this.model.doc.transact(() => { this.model.doc.transact(() => {
const realColumn = this.model.columns[column.columnId]; const realColumn = this.model.props.columns[column.columnId];
if (realColumn) { if (realColumn) {
realColumn.order = order; realColumn.order = order;
} }
@@ -321,7 +322,7 @@ export class TableDataManager {
if (!row) return; if (!row) return;
const order = this.getOrder(rows, after); const order = this.getOrder(rows, after);
this.model.doc.transact(() => { this.model.doc.transact(() => {
const realRow = this.model.rows[row.rowId]; const realRow = this.model.props.rows[row.rowId];
if (realRow) { if (realRow) {
realRow.order = order; realRow.order = order;
} }
@@ -334,15 +335,15 @@ export class TableDataManager {
const order = this.getOrder(this.columns$.value, index); const order = this.getOrder(this.columns$.value, index);
const newColumnId = nanoid(); const newColumnId = nanoid();
this.model.doc.transact(() => { this.model.doc.transact(() => {
this.model.columns[newColumnId] = { this.model.props.columns[newColumnId] = {
...oldColumn, ...oldColumn,
columnId: newColumnId, columnId: newColumnId,
order, order,
}; };
this.rows$.value.forEach(row => { this.rows$.value.forEach(row => {
this.model.cells[`${row.rowId}:${newColumnId}`] = { this.model.props.cells[`${row.rowId}:${newColumnId}`] = {
text: text:
this.model.cells[ this.model.props.cells[
`${row.rowId}:${oldColumn.columnId}` `${row.rowId}:${oldColumn.columnId}`
]?.text.clone() ?? new Text(), ]?.text.clone() ?? new Text(),
}; };
@@ -357,15 +358,15 @@ export class TableDataManager {
const order = this.getOrder(this.rows$.value, index); const order = this.getOrder(this.rows$.value, index);
const newRowId = nanoid(); const newRowId = nanoid();
this.model.doc.transact(() => { this.model.doc.transact(() => {
this.model.rows[newRowId] = { this.model.props.rows[newRowId] = {
...oldRow, ...oldRow,
rowId: newRowId, rowId: newRowId,
order, order,
}; };
this.columns$.value.forEach(column => { this.columns$.value.forEach(column => {
this.model.cells[`${newRowId}:${column.columnId}`] = { this.model.props.cells[`${newRowId}:${column.columnId}`] = {
text: text:
this.model.cells[ this.model.props.cells[
`${oldRow.rowId}:${column.columnId}` `${oldRow.rowId}:${column.columnId}`
]?.text.clone() ?? new Text(), ]?.text.clone() ?? new Text(),
}; };
@@ -48,6 +48,7 @@ export const TableBlockSchema = defineBlockSchema({
cells: {}, cells: {},
}), }),
metadata: { metadata: {
isFlatData: true,
role: 'content', role: 'content',
version: 1, version: 1,
parent: ['affine:note'], parent: ['affine:note'],
+1 -1
View File
@@ -4,4 +4,4 @@ export const SCHEMA_NOT_FOUND_MESSAGE =
export const TEXT_UNIQ_IDENTIFIER = '$blocksuite:internal:text$'; export const TEXT_UNIQ_IDENTIFIER = '$blocksuite:internal:text$';
export const NATIVE_UNIQ_IDENTIFIER = '$blocksuite:internal:native$'; export const NATIVE_UNIQ_IDENTIFIER = '$blocksuite:internal:native$';
export const SYS_KEYS = new Set(['id', 'flavour', 'children']); export const SYS_KEYS = new Set(['id', 'flavour', 'children', 'version']);
@@ -44,6 +44,9 @@ export class Block {
const onChange = !options.onChange const onChange = !options.onChange
? undefined ? undefined
: (key: string, value: unknown) => { : (key: string, value: unknown) => {
if (!this._syncController || !this.model) {
return;
}
options.onChange?.(this, key, value); options.onChange?.(this, key, value);
}; };
const flavour = yBlock.get('sys:flavour') as string; const flavour = yBlock.get('sys:flavour') as string;
@@ -50,7 +50,7 @@ export class FlatSyncController {
model.schema = schema; model.schema = schema;
model.id = this.id; model.id = this.id;
model.keys = Array.from(props).map(key => key.replace('prop:', '')); model.keys = Array.from(props);
model.yBlock = this.yBlock; model.yBlock = this.yBlock;
const reactive = new ReactiveFlatYMap( const reactive = new ReactiveFlatYMap(
this.yBlock, this.yBlock,
@@ -69,8 +69,7 @@ export class FlatSyncController {
const defaultProps = schema.model.props?.(internalPrimitives); const defaultProps = schema.model.props?.(internalPrimitives);
if (defaultProps) { if (defaultProps) {
Object.entries(defaultProps).forEach(([key, value]) => { Object.entries(defaultProps).forEach(([key, value]) => {
const keyWithProp = `prop:${key}`; if (key in proxy) {
if (keyWithProp in proxy) {
return; return;
} }
proxy[key] = value; proxy[key] = value;
@@ -149,9 +148,8 @@ export class FlatSyncController {
// Set default props if not exists // Set default props if not exists
if (defaultProps) { if (defaultProps) {
Object.keys(defaultProps).forEach(key => { Object.keys(defaultProps).forEach(key => {
const keyWithProp = `prop:${key}`; if (props.has(key)) return;
if (props.has(keyWithProp)) return; props.add(key);
props.add(keyWithProp);
}); });
} }
@@ -1,7 +1,7 @@
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import * as Y from 'yjs'; import * as Y from 'yjs';
import { native2Y } from '../../reactive/index.js'; import { isPureObject, native2Y } from '../../reactive/index.js';
import type { Schema } from '../../schema/index.js'; import type { Schema } from '../../schema/index.js';
import type { BlockModel } from '../block/block-model.js'; import type { BlockModel } from '../block/block-model.js';
import type { YBlock } from '../block/types.js'; import type { YBlock } from '../block/types.js';
@@ -95,11 +95,26 @@ export class DocCRUD {
delete props.flavour; delete props.flavour;
delete props.children; delete props.children;
Object.entries(props).forEach(([key, value]) => { const isFlatData = schema.model.isFlatData;
if (value === undefined) return; if (isFlatData) {
const run = (obj: unknown, basePath: string) => {
if (isPureObject(obj)) {
Object.entries(obj).forEach(([key, value]) => {
const fullPath = basePath ? `${basePath}.${key}` : key;
run(value, fullPath);
});
} else {
yBlock.set(`prop:${basePath}`, native2Y(obj));
}
};
run(props, '');
} else {
Object.entries(props).forEach(([key, value]) => {
if (value === undefined) return;
yBlock.set(`prop:${key}`, native2Y(value)); yBlock.set(`prop:${key}`, native2Y(value));
}); });
}
const parentId = const parentId =
parent ?? (schema.model.role === 'root' ? null : this.root); parent ?? (schema.model.role === 'root' ? null : this.root);
@@ -1,7 +1,6 @@
import type { z } from 'zod'; import type { z } from 'zod';
import { SYS_KEYS } from '../../consts.js'; import { SYS_KEYS } from '../../consts.js';
import { native2Y } from '../../reactive/index.js';
import type { BlockModel } from '../block/block-model.js'; import type { BlockModel } from '../block/block-model.js';
import type { BlockProps, YBlock } from '../block/types.js'; import type { BlockProps, YBlock } from '../block/types.js';
import type { BlockSchema } from '../block/zod.js'; import type { BlockSchema } from '../block/zod.js';
@@ -32,6 +31,6 @@ export function syncBlockProps(
} }
// @ts-expect-error allow props // @ts-expect-error allow props
model[key] = native2Y(value); model[key] = value;
}); });
} }
@@ -33,6 +33,7 @@ type CreateProxyOptions = {
shouldByPassSignal: () => boolean; shouldByPassSignal: () => boolean;
byPassSignalUpdate: (fn: () => void) => void; byPassSignalUpdate: (fn: () => void) => void;
stashed: Set<string | number>; stashed: Set<string | number>;
initialized: () => boolean;
}; };
const proxySymbol = Symbol('proxy'); const proxySymbol = Symbol('proxy');
@@ -60,6 +61,7 @@ function createProxy(
byPassSignalUpdate, byPassSignalUpdate,
basePath, basePath,
onChange, onChange,
initialized,
transform = (_key, value) => value, transform = (_key, value) => value,
stashed, stashed,
} = options; } = options;
@@ -73,7 +75,7 @@ function createProxy(
if (isPureObject(value) && !isProxy(value)) { if (isPureObject(value) && !isProxy(value)) {
const proxy = createProxy(yMap, value as UnRecord, root, { const proxy = createProxy(yMap, value as UnRecord, root, {
...options, ...options,
basePath: `${basePath}.${key}`, basePath: basePath ? `${basePath}.${key}` : key,
}); });
base[key] = proxy; base[key] = proxy;
} }
@@ -111,6 +113,9 @@ function createProxy(
root[signalKey] = signalData; root[signalKey] = signalData;
onDispose.once( onDispose.once(
signalData.subscribe(next => { signalData.subscribe(next => {
if (!initialized()) {
return;
}
byPassSignalUpdate(() => { byPassSignalUpdate(() => {
proxy[p] = next; proxy[p] = next;
onChange?.(firstKey, next); onChange?.(firstKey, next);
@@ -137,7 +142,7 @@ function createProxy(
if (isPureObject(value)) { if (isPureObject(value)) {
const syncYMap = () => { const syncYMap = () => {
yMap.forEach((_, key) => { yMap.forEach((_, key) => {
if (keyWithoutPrefix(key).startsWith(fullPath)) { if (initialized() && keyWithoutPrefix(key).startsWith(fullPath)) {
yMap.delete(key); yMap.delete(key);
} }
}); });
@@ -148,13 +153,13 @@ function createProxy(
run(value, fullPath); run(value, fullPath);
} else { } else {
list.push(() => { list.push(() => {
yMap.set(keyWithPrefix(fullPath), value); yMap.set(keyWithPrefix(fullPath), native2Y(value));
}); });
} }
}); });
}; };
run(value, fullPath); run(value, fullPath);
if (list.length) { if (list.length && initialized()) {
yMap.doc?.transact( yMap.doc?.transact(
() => { () => {
list.forEach(fn => fn()); list.forEach(fn => fn());
@@ -180,7 +185,7 @@ function createProxy(
const yValue = native2Y(value); const yValue = native2Y(value);
const next = transform(firstKey, value, yValue); const next = transform(firstKey, value, yValue);
if (!isStashed) { if (!isStashed && initialized()) {
yMap.doc?.transact( yMap.doc?.transact(
() => { () => {
yMap.set(keyWithPrefix(fullPath), yValue); yMap.set(keyWithPrefix(fullPath), yValue);
@@ -233,7 +238,7 @@ function createProxy(
}); });
}; };
if (!isStashed) { if (!isStashed && initialized()) {
yMap.doc?.transact( yMap.doc?.transact(
() => { () => {
const fullKey = keyWithPrefix(fullPath); const fullKey = keyWithPrefix(fullPath);
@@ -268,6 +273,8 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
protected readonly _source: UnRecord; protected readonly _source: UnRecord;
protected readonly _options?: ProxyOptions<UnRecord>; protected readonly _options?: ProxyOptions<UnRecord>;
private readonly _initialized;
private readonly _observer = (event: YMapEvent<unknown>) => { private readonly _observer = (event: YMapEvent<unknown>) => {
const yMap = this._ySource; const yMap = this._ySource;
const proxy = this._proxy; const proxy = this._proxy;
@@ -344,9 +351,12 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
}; };
private readonly _createDefaultData = (): UnRecord => { private readonly _createDefaultData = (): UnRecord => {
const data: UnRecord = {}; const root: UnRecord = {};
const transform = this._transform; const transform = this._transform;
Array.from(this._ySource.entries()).forEach(([key, value]) => { Array.from(this._ySource.entries()).forEach(([key, value]) => {
if (key.startsWith('sys')) {
return;
}
const keys = keyWithoutPrefix(key).split('.'); const keys = keyWithoutPrefix(key).split('.');
const firstKey = keys[0]; const firstKey = keys[0];
@@ -356,7 +366,8 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
} else if (value instanceof YArray) { } else if (value instanceof YArray) {
finalData = transform(firstKey, value.toArray(), value); finalData = transform(firstKey, value.toArray(), value);
} else if (value instanceof YText) { } else if (value instanceof YText) {
finalData = transform(firstKey, new Text(value), value); const next = new Text(value);
finalData = transform(firstKey, next, value);
} else if (value instanceof YMap) { } else if (value instanceof YMap) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.ReactiveProxyError, ErrorCode.ReactiveProxyError,
@@ -369,21 +380,25 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
void keys.reduce((acc: UnRecord, key, index) => { void keys.reduce((acc: UnRecord, key, index) => {
if (!acc[key] && index !== allLength - 1) { if (!acc[key] && index !== allLength - 1) {
const path = keys.slice(0, index + 1).join('.'); const path = keys.slice(0, index + 1).join('.');
const data = this._getProxy({} as UnRecord, path); const data = this._getProxy({} as UnRecord, root, path);
acc[key] = data; acc[key] = data;
} }
if (index === allLength - 1) { if (index === allLength - 1) {
acc[key] = finalData; acc[key] = finalData;
} }
return acc[key] as UnRecord; return acc[key] as UnRecord;
}, data); }, root);
}); });
return data; return root;
}; };
private readonly _getProxy = (source: UnRecord, path?: string): UnRecord => { private readonly _getProxy = (
return createProxy(this._ySource, source, source, { source: UnRecord,
root: UnRecord,
path?: string
): UnRecord => {
return createProxy(this._ySource, source, root, {
onDispose: this._onDispose, onDispose: this._onDispose,
shouldByPassSignal: () => this._skipNext, shouldByPassSignal: () => this._skipNext,
byPassSignalUpdate: this._updateWithSkip, byPassSignalUpdate: this._updateWithSkip,
@@ -391,6 +406,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
onChange: this._onChange, onChange: this._onChange,
transform: this._transform, transform: this._transform,
stashed: this._stashed, stashed: this._stashed,
initialized: () => this._initialized,
}); });
}; };
@@ -400,16 +416,20 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
private readonly _onChange?: OnChange private readonly _onChange?: OnChange
) { ) {
super(); super();
this._initialized = false;
const source = this._createDefaultData(); const source = this._createDefaultData();
this._source = source; this._source = source;
const proxy = this._getProxy(source); const proxy = this._getProxy(source, source);
Object.entries(source).forEach(([key, value]) => { Object.entries(source).forEach(([key, value]) => {
const signalData = signal(value); const signalData = signal(value);
source[`${key}$`] = signalData; source[`${key}$`] = signalData;
_onDispose.once( _onDispose.once(
signalData.subscribe(next => { signalData.subscribe(next => {
if (!this._initialized) {
return;
}
this._updateWithSkip(() => { this._updateWithSkip(() => {
proxy[key] = next; proxy[key] = next;
this._onChange?.(key, next); this._onChange?.(key, next);
@@ -420,6 +440,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
this._proxy = proxy; this._proxy = proxy;
this._ySource.observe(this._observer); this._ySource.observe(this._observer);
this._initialized = true;
} }
pop = (prop: string): void => { pop = (prop: string): void => {