mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
feat(editor): unify block props api (#10888)
Closes: [BS-2707](https://linear.app/affine-design/issue/BS-2707/统一使用props获取和更新block-prop)
This commit is contained in:
@@ -104,8 +104,8 @@ test('init block without props should add default props', () => {
|
||||
const model = block.model as RootModel;
|
||||
|
||||
expect(yBlock.get('prop:count')).toBe(0);
|
||||
expect(model.count).toBe(0);
|
||||
expect(model.style).toEqual({});
|
||||
expect(model.props.count).toBe(0);
|
||||
expect(model.props.style).toEqual({});
|
||||
});
|
||||
|
||||
describe('block model should has signal props', () => {
|
||||
@@ -120,38 +120,38 @@ describe('block model should has signal props', () => {
|
||||
const block = new Block(doc.schema, yBlock, doc);
|
||||
const model = block.model as RootModel;
|
||||
|
||||
const isOdd = computed(() => model.count$.value % 2 === 1);
|
||||
const isOdd = computed(() => model.props.count$.value % 2 === 1);
|
||||
|
||||
expect(model.count$.value).toBe(0);
|
||||
expect(model.props.count$.value).toBe(0);
|
||||
expect(isOdd.peek()).toBe(false);
|
||||
|
||||
// set prop
|
||||
model.count = 1;
|
||||
expect(model.count$.value).toBe(1);
|
||||
model.props.count = 1;
|
||||
expect(model.props.count$.value).toBe(1);
|
||||
expect(isOdd.peek()).toBe(true);
|
||||
expect(yBlock.get('prop:count')).toBe(1);
|
||||
|
||||
// set signal
|
||||
model.count$.value = 2;
|
||||
expect(model.count).toBe(2);
|
||||
model.props.count$.value = 2;
|
||||
expect(model.props.count).toBe(2);
|
||||
expect(isOdd.peek()).toBe(false);
|
||||
expect(yBlock.get('prop:count')).toBe(2);
|
||||
|
||||
// set prop
|
||||
yBlock.set('prop:count', 3);
|
||||
expect(model.count).toBe(3);
|
||||
expect(model.count$.value).toBe(3);
|
||||
expect(model.props.count).toBe(3);
|
||||
expect(model.props.count$.value).toBe(3);
|
||||
expect(isOdd.peek()).toBe(true);
|
||||
|
||||
const toggleEffect = vi.fn();
|
||||
effect(() => {
|
||||
toggleEffect(model.toggle$.value);
|
||||
toggleEffect(model.props.toggle$.value);
|
||||
});
|
||||
expect(toggleEffect).toHaveBeenCalledTimes(1);
|
||||
const runToggle = () => {
|
||||
const next = !model.toggle;
|
||||
model.toggle = next;
|
||||
expect(model.toggle$.value).toBe(next);
|
||||
const next = !model.props.toggle;
|
||||
model.props.toggle = next;
|
||||
expect(model.props.toggle$.value).toBe(next);
|
||||
};
|
||||
const times = 10;
|
||||
for (let i = 0; i < times; i++) {
|
||||
@@ -159,9 +159,9 @@ describe('block model should has signal props', () => {
|
||||
}
|
||||
expect(toggleEffect).toHaveBeenCalledTimes(times + 1);
|
||||
const runToggleReverse = () => {
|
||||
const next = !model.toggle;
|
||||
model.toggle$.value = next;
|
||||
expect(model.toggle).toBe(next);
|
||||
const next = !model.props.toggle;
|
||||
model.props.toggle$.value = next;
|
||||
expect(model.props.toggle).toBe(next);
|
||||
};
|
||||
for (let i = 0; i < times; i++) {
|
||||
runToggleReverse();
|
||||
@@ -179,22 +179,22 @@ describe('block model should has signal props', () => {
|
||||
|
||||
const block = new Block(doc.schema, yBlock, doc);
|
||||
const model = block.model as RootModel;
|
||||
expect(model.style).toEqual({});
|
||||
expect(model.props.style).toEqual({});
|
||||
|
||||
model.style = { color: 'red' };
|
||||
model.props.style = { color: 'red' };
|
||||
expect((yBlock.get('prop:style') as Y.Map<unknown>).toJSON()).toEqual({
|
||||
color: 'red',
|
||||
});
|
||||
expect(model.style$.value).toEqual({ color: 'red' });
|
||||
expect(model.props.style$.value).toEqual({ color: 'red' });
|
||||
|
||||
model.style.color = 'yellow';
|
||||
model.props.style.color = 'yellow';
|
||||
expect((yBlock.get('prop:style') as Y.Map<unknown>).toJSON()).toEqual({
|
||||
color: 'yellow',
|
||||
});
|
||||
expect(model.style$.value).toEqual({ color: 'yellow' });
|
||||
expect(model.props.style$.value).toEqual({ color: 'yellow' });
|
||||
|
||||
model.style$.value = { color: 'blue' };
|
||||
expect(model.style.color).toBe('blue');
|
||||
model.props.style$.value = { color: 'blue' };
|
||||
expect(model.props.style.color).toBe('blue');
|
||||
expect((yBlock.get('prop:style') as Y.Map<unknown>).toJSON()).toEqual({
|
||||
color: 'blue',
|
||||
});
|
||||
@@ -202,8 +202,8 @@ describe('block model should has signal props', () => {
|
||||
const map = new Y.Map();
|
||||
map.set('color', 'green');
|
||||
yBlock.set('prop:style', map);
|
||||
expect(model.style.color).toBe('green');
|
||||
expect(model.style$.value).toEqual({ color: 'green' });
|
||||
expect(model.props.style.color).toBe('green');
|
||||
expect(model.props.style$.value).toEqual({ color: 'green' });
|
||||
});
|
||||
|
||||
test('with stash and pop', () => {
|
||||
@@ -218,34 +218,34 @@ describe('block model should has signal props', () => {
|
||||
const block = new Block(doc.schema, yBlock, doc, { onChange });
|
||||
const model = block.model as RootModel;
|
||||
|
||||
expect(model.count).toBe(0);
|
||||
expect(model.props.count).toBe(0);
|
||||
model.stash('count');
|
||||
|
||||
onChange.mockClear();
|
||||
model.count = 1;
|
||||
expect(model.count$.value).toBe(1);
|
||||
model.props.count = 1;
|
||||
expect(model.props.count$.value).toBe(1);
|
||||
expect(yBlock.get('prop:count')).toBe(0);
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
|
||||
model.count$.value = 2;
|
||||
expect(model.count).toBe(2);
|
||||
model.props.count$.value = 2;
|
||||
expect(model.props.count).toBe(2);
|
||||
expect(yBlock.get('prop:count')).toBe(0);
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
|
||||
model.pop('count');
|
||||
expect(yBlock.get('prop:count')).toBe(2);
|
||||
expect(model.count).toBe(2);
|
||||
expect(model.count$.value).toBe(2);
|
||||
expect(model.props.count).toBe(2);
|
||||
expect(model.props.count$.value).toBe(2);
|
||||
expect(onChange).toHaveBeenCalledTimes(3);
|
||||
|
||||
model.stash('count');
|
||||
yBlock.set('prop:count', 3);
|
||||
expect(model.count).toBe(3);
|
||||
expect(model.count$.value).toBe(3);
|
||||
expect(model.props.count).toBe(3);
|
||||
expect(model.props.count$.value).toBe(3);
|
||||
|
||||
model.count$.value = 4;
|
||||
model.props.count$.value = 4;
|
||||
expect(yBlock.get('prop:count')).toBe(3);
|
||||
expect(model.count).toBe(4);
|
||||
expect(model.props.count).toBe(4);
|
||||
|
||||
model.pop('count');
|
||||
expect(yBlock.get('prop:count')).toBe(4);
|
||||
@@ -266,22 +266,22 @@ test('on change', () => {
|
||||
});
|
||||
const model = block.model as RootModel;
|
||||
|
||||
model.title = internalPrimitives.Text('abc');
|
||||
model.props.title = internalPrimitives.Text('abc');
|
||||
expect(onPropsUpdated).toHaveBeenCalledWith(expect.anything(), 'title', true);
|
||||
expect(model.title$.value.toDelta()).toEqual([{ insert: 'abc' }]);
|
||||
expect(model.props.title$.value.toDelta()).toEqual([{ insert: 'abc' }]);
|
||||
|
||||
onPropsUpdated.mockClear();
|
||||
|
||||
model.title.insert('d', 1);
|
||||
model.props.title.insert('d', 1);
|
||||
expect(onPropsUpdated).toHaveBeenCalledWith(expect.anything(), 'title', true);
|
||||
|
||||
expect(model.title$.value.toDelta()).toEqual([{ insert: 'adbc' }]);
|
||||
expect(model.props.title$.value.toDelta()).toEqual([{ insert: 'adbc' }]);
|
||||
|
||||
onPropsUpdated.mockClear();
|
||||
|
||||
model.boxed.getValue()!.set('foo', 0);
|
||||
model.props.boxed.getValue()!.set('foo', 0);
|
||||
expect(onPropsUpdated).toHaveBeenCalledWith(expect.anything(), 'boxed', true);
|
||||
expect(model.boxed$.value.getValue()!.toJSON()).toEqual({
|
||||
expect(model.props.boxed$.value.getValue()!.toJSON()).toEqual({
|
||||
foo: 0,
|
||||
});
|
||||
});
|
||||
@@ -299,33 +299,33 @@ test('deep sync', () => {
|
||||
onChange: onPropsUpdated,
|
||||
});
|
||||
const model = block.model as TableModel;
|
||||
expect(model.cols).toEqual({});
|
||||
expect(model.rows).toEqual([]);
|
||||
expect(model.props.cols).toEqual({});
|
||||
expect(model.props.rows).toEqual([]);
|
||||
|
||||
model.cols = {
|
||||
model.props.cols = {
|
||||
'1': { color: 'red' },
|
||||
};
|
||||
const onColsUpdated = vi.fn();
|
||||
const onRowsUpdated = vi.fn();
|
||||
effect(() => {
|
||||
onColsUpdated(model.cols$.value);
|
||||
onColsUpdated(model.props.cols$.value);
|
||||
});
|
||||
effect(() => {
|
||||
onRowsUpdated(model.rows$.value);
|
||||
onRowsUpdated(model.props.rows$.value);
|
||||
});
|
||||
const getColsMap = () => yBlock.get('prop:cols') as Y.Map<unknown>;
|
||||
const getRowsArr = () => yBlock.get('prop:rows') as Y.Array<unknown>;
|
||||
expect(getColsMap().toJSON()).toEqual({
|
||||
'1': { color: 'red' },
|
||||
});
|
||||
expect(model.cols$.value).toEqual({
|
||||
expect(model.props.cols$.value).toEqual({
|
||||
'1': { color: 'red' },
|
||||
});
|
||||
|
||||
onPropsUpdated.mockClear();
|
||||
onColsUpdated.mockClear();
|
||||
|
||||
model.cols['2'] = { color: 'blue' };
|
||||
model.props.cols['2'] = { color: 'blue' };
|
||||
expect(getColsMap().toJSON()).toEqual({
|
||||
'1': { color: 'red' },
|
||||
'2': { color: 'blue' },
|
||||
@@ -355,7 +355,7 @@ test('deep sync', () => {
|
||||
onPropsUpdated.mockClear();
|
||||
onRowsUpdated.mockClear();
|
||||
|
||||
model.rows.push({ color: 'yellow' });
|
||||
model.props.rows.push({ color: 'yellow' });
|
||||
expect(onPropsUpdated).toHaveBeenCalledWith(expect.anything(), 'rows', true);
|
||||
expect(onRowsUpdated).toHaveBeenCalledWith([{ color: 'yellow' }]);
|
||||
expect(onPropsUpdated).toHaveBeenCalledTimes(1);
|
||||
@@ -368,7 +368,7 @@ test('deep sync', () => {
|
||||
row1.set('color', 'green');
|
||||
expect(onRowsUpdated).toHaveBeenCalledWith([{ color: 'green' }]);
|
||||
expect(onPropsUpdated).toHaveBeenCalledWith(expect.anything(), 'rows', true);
|
||||
expect(model.rows$.value).toEqual([{ color: 'green' }]);
|
||||
expect(model.props.rows$.value).toEqual([{ color: 'green' }]);
|
||||
expect(onPropsUpdated).toHaveBeenCalledTimes(1);
|
||||
expect(onRowsUpdated).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -49,37 +49,37 @@ test('trigger props updated', () => {
|
||||
const getItems = () => rootModel.yBlock.get('prop:items') as Y.Array<unknown>;
|
||||
const getCount = () => rootModel.yBlock.get('prop:count');
|
||||
|
||||
rootModel.count = 1;
|
||||
rootModel.props.count = 1;
|
||||
expect(onPropsUpdated).toBeCalledTimes(1);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(1, { key: 'count' });
|
||||
expect(getCount()).toBe(1);
|
||||
|
||||
rootModel.count = 2;
|
||||
rootModel.props.count = 2;
|
||||
expect(onPropsUpdated).toBeCalledTimes(2);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(2, { key: 'count' });
|
||||
expect(getCount()).toBe(2);
|
||||
|
||||
rootModel.style.color = 'blue';
|
||||
rootModel.props.style.color = 'blue';
|
||||
expect(onPropsUpdated).toBeCalledTimes(3);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(3, { key: 'style' });
|
||||
expect(getColor()).toBe('blue');
|
||||
|
||||
rootModel.style = { color: 'red' };
|
||||
rootModel.props.style = { color: 'red' };
|
||||
expect(onPropsUpdated).toBeCalledTimes(4);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(4, { key: 'style' });
|
||||
expect(getColor()).toBe('red');
|
||||
|
||||
rootModel.style.color = 'green';
|
||||
rootModel.props.style.color = 'green';
|
||||
expect(onPropsUpdated).toBeCalledTimes(5);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(5, { key: 'style' });
|
||||
expect(getColor()).toBe('green');
|
||||
|
||||
rootModel.items.push(1);
|
||||
rootModel.props.items.push(1);
|
||||
expect(onPropsUpdated).toBeCalledTimes(6);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(6, { key: 'items' });
|
||||
expect(getItems().get(0)).toBe(1);
|
||||
|
||||
rootModel.items[0] = { id: '1' };
|
||||
rootModel.props.items[0] = { id: '1' };
|
||||
expect(onPropsUpdated).toBeCalledTimes(7);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(7, { key: 'items' });
|
||||
expect(getItems().get(0)).toBeInstanceOf(Y.Map);
|
||||
@@ -107,13 +107,13 @@ test('stash and pop', () => {
|
||||
const getColor = () =>
|
||||
(rootModel.yBlock.get('prop:style') as Y.Map<string>).get('color');
|
||||
|
||||
rootModel.count = 1;
|
||||
rootModel.props.count = 1;
|
||||
expect(onPropsUpdated).toBeCalledTimes(1);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(1, { key: 'count' });
|
||||
expect(getCount()).toBe(1);
|
||||
|
||||
rootModel.stash('count');
|
||||
rootModel.count = 2;
|
||||
rootModel.props.count = 2;
|
||||
expect(onPropsUpdated).toBeCalledTimes(3);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(3, { key: 'count' });
|
||||
expect(rootModel.yBlock.get('prop:count')).toBe(1);
|
||||
@@ -123,13 +123,13 @@ test('stash and pop', () => {
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(4, { key: 'count' });
|
||||
expect(rootModel.yBlock.get('prop:count')).toBe(2);
|
||||
|
||||
rootModel.style.color = 'blue';
|
||||
rootModel.props.style.color = 'blue';
|
||||
expect(getColor()).toBe('blue');
|
||||
expect(onPropsUpdated).toBeCalledTimes(5);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(5, { key: 'style' });
|
||||
|
||||
rootModel.stash('style');
|
||||
rootModel.style = {
|
||||
rootModel.props.style = {
|
||||
color: 'red',
|
||||
};
|
||||
expect(getColor()).toBe('blue');
|
||||
@@ -145,7 +145,7 @@ test('stash and pop', () => {
|
||||
expect(onPropsUpdated).toBeCalledTimes(9);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(9, { key: 'style' });
|
||||
|
||||
rootModel.style.color = 'green';
|
||||
rootModel.props.style.color = 'green';
|
||||
expect(onPropsUpdated).toBeCalledTimes(10);
|
||||
expect(onPropsUpdated).toHaveBeenNthCalledWith(10, { key: 'style' });
|
||||
expect(getColor()).toBe('red');
|
||||
@@ -173,33 +173,33 @@ test('always get latest value in onChange', () => {
|
||||
let value: unknown;
|
||||
rootModel.propsUpdated.subscribe(({ key }) => {
|
||||
// @ts-expect-error ignore
|
||||
value = rootModel[key];
|
||||
value = rootModel.props[key];
|
||||
});
|
||||
|
||||
rootModel.count = 1;
|
||||
rootModel.props.count = 1;
|
||||
expect(value).toBe(1);
|
||||
|
||||
rootModel.stash('count');
|
||||
|
||||
rootModel.count = 2;
|
||||
rootModel.props.count = 2;
|
||||
expect(value).toBe(2);
|
||||
|
||||
rootModel.pop('count');
|
||||
|
||||
rootModel.count = 3;
|
||||
rootModel.props.count = 3;
|
||||
expect(value).toBe(3);
|
||||
|
||||
rootModel.style.color = 'blue';
|
||||
rootModel.props.style.color = 'blue';
|
||||
expect(value).toEqual({ color: 'blue' });
|
||||
|
||||
rootModel.stash('style');
|
||||
rootModel.style = { color: 'red' };
|
||||
rootModel.props.style = { color: 'red' };
|
||||
expect(value).toEqual({ color: 'red' });
|
||||
rootModel.style.color = 'green';
|
||||
rootModel.props.style.color = 'green';
|
||||
expect(value).toEqual({ color: 'green' });
|
||||
|
||||
rootModel.pop('style');
|
||||
rootModel.style.color = 'yellow';
|
||||
rootModel.props.style.color = 'yellow';
|
||||
expect(value).toEqual({ color: 'yellow' });
|
||||
});
|
||||
|
||||
|
||||
@@ -11,28 +11,10 @@ import type { BlockSchemaType } from './zod.js';
|
||||
type SignaledProps<Props> = Props & {
|
||||
[P in keyof Props & string as `${P}$`]: Signal<Props[P]>;
|
||||
};
|
||||
/**
|
||||
* The MagicProps function is used to append the props to the class.
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* class MyBlock extends MagicProps()<{ foo: string }> {}
|
||||
* const myBlock = new MyBlock();
|
||||
* // You'll get type checking for the foo prop
|
||||
* myBlock.foo = 'bar';
|
||||
* ```
|
||||
*/
|
||||
function MagicProps(): { new <Props>(): Props } {
|
||||
return class {} as never;
|
||||
}
|
||||
|
||||
const modelLabel = Symbol('model_label');
|
||||
|
||||
// @ts-expect-error allow magic props
|
||||
export class BlockModel<
|
||||
Props extends object = object,
|
||||
PropsSignal extends object = SignaledProps<Props>,
|
||||
> extends MagicProps()<PropsSignal> {
|
||||
export class BlockModel<Props extends object = object> {
|
||||
private readonly _children = signal<string[]>([]);
|
||||
|
||||
private _store!: Store;
|
||||
@@ -82,8 +64,15 @@ export class BlockModel<
|
||||
|
||||
stash!: (prop: keyof Props & string) => void;
|
||||
|
||||
// text is optional
|
||||
text?: Text;
|
||||
get text(): Text | undefined {
|
||||
return (this.props as { text?: Text }).text;
|
||||
}
|
||||
|
||||
set text(text: Text) {
|
||||
if (this.keys.includes('text')) {
|
||||
(this.props as { text?: Text }).text = text;
|
||||
}
|
||||
}
|
||||
|
||||
yBlock!: YBlock;
|
||||
|
||||
@@ -125,7 +114,6 @@ export class BlockModel<
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._onCreated = {
|
||||
dispose: this.created.pipe(take(1)).subscribe(() => {
|
||||
this._children.value = this.yBlock.get('sys:children').toArray();
|
||||
|
||||
@@ -11,18 +11,17 @@ export type DraftModel<Model extends BlockModel = BlockModel> = Pick<
|
||||
PropsInDraft
|
||||
> & {
|
||||
children: DraftModel[];
|
||||
} & ModelProps<Model> & {
|
||||
[draftModelSymbol]: true;
|
||||
};
|
||||
props: ModelProps<Model>;
|
||||
[draftModelSymbol]: true;
|
||||
};
|
||||
|
||||
export function toDraftModel<Model extends BlockModel = BlockModel>(
|
||||
origin: Model
|
||||
): DraftModel<Model> {
|
||||
const { id, version, flavour, role, keys, text, children } = origin;
|
||||
|
||||
const isFlatData = origin.schema.model.isFlatData;
|
||||
const props = origin.keys.reduce((acc, key) => {
|
||||
const target = isFlatData ? origin.props : origin;
|
||||
const target = origin.props;
|
||||
const value = target[key as keyof typeof target];
|
||||
return {
|
||||
...acc,
|
||||
@@ -38,6 +37,6 @@ export function toDraftModel<Model extends BlockModel = BlockModel>(
|
||||
keys,
|
||||
text,
|
||||
children: children.map(toDraftModel),
|
||||
...props,
|
||||
props,
|
||||
} as DraftModel<Model>;
|
||||
}
|
||||
|
||||
@@ -55,12 +55,12 @@ export class SyncController {
|
||||
const proxy = this._getPropsProxy(keyName, value);
|
||||
this._byPassUpdate(() => {
|
||||
// @ts-expect-error allow magic props
|
||||
this.model[keyName] = proxy;
|
||||
this.model.props[keyName] = proxy;
|
||||
const signalKey = `${keyName}$`;
|
||||
this._mutex(() => {
|
||||
if (signalKey in this.model) {
|
||||
if (signalKey in this.model.props) {
|
||||
// @ts-expect-error allow magic props
|
||||
this.model[signalKey].value = y2Native(value);
|
||||
this.model.props[signalKey].value = y2Native(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -71,10 +71,10 @@ export class SyncController {
|
||||
const keyName = key.replace('prop:', '');
|
||||
this._byPassUpdate(() => {
|
||||
// @ts-expect-error allow magic props
|
||||
delete this.model[keyName];
|
||||
if (`${keyName}$` in this.model) {
|
||||
delete this.model.props[keyName];
|
||||
if (`${keyName}$` in this.model.props) {
|
||||
// @ts-expect-error allow magic props
|
||||
this.model[`${keyName}$`].value = undefined;
|
||||
this.model.props[`${keyName}$`].value = undefined;
|
||||
}
|
||||
});
|
||||
this.onChange?.(keyName, isLocal);
|
||||
@@ -146,7 +146,7 @@ export class SyncController {
|
||||
if (!this.model) return;
|
||||
_mutex(() => {
|
||||
// @ts-expect-error allow magic props
|
||||
this.model[key] = value;
|
||||
this.model.props[key] = value;
|
||||
});
|
||||
});
|
||||
const subscription = model.deleted.subscribe(() => {
|
||||
@@ -161,7 +161,6 @@ export class SyncController {
|
||||
},
|
||||
{} as Record<string, unknown>
|
||||
);
|
||||
Object.assign(model, signalWithProps);
|
||||
|
||||
model.id = this.id;
|
||||
model.keys = Object.keys(props);
|
||||
@@ -172,7 +171,7 @@ export class SyncController {
|
||||
model.doc = this.doc;
|
||||
}
|
||||
|
||||
const proxy = new Proxy(model, {
|
||||
const proxy = new Proxy(signalWithProps, {
|
||||
has: (target, p) => {
|
||||
return Reflect.has(target, p);
|
||||
},
|
||||
@@ -217,14 +216,15 @@ export class SyncController {
|
||||
return Reflect.deleteProperty(target, p);
|
||||
},
|
||||
});
|
||||
model._props = proxy;
|
||||
|
||||
function setValue(target: BlockModel, p: string, value: unknown) {
|
||||
function setValue(target: UnRecord, p: string, value: unknown) {
|
||||
_mutex(() => {
|
||||
// @ts-expect-error allow magic props
|
||||
target[`${p}$`].value = value;
|
||||
});
|
||||
}
|
||||
return proxy;
|
||||
return model;
|
||||
}
|
||||
|
||||
private _getPropsProxy(name: string, value: unknown) {
|
||||
@@ -232,10 +232,10 @@ export class SyncController {
|
||||
onChange: (_, isLocal) => {
|
||||
this.onChange?.(name, isLocal);
|
||||
const signalKey = `${name}$`;
|
||||
if (signalKey in this.model) {
|
||||
if (signalKey in this.model.props) {
|
||||
this._mutex(() => {
|
||||
// @ts-expect-error allow magic props
|
||||
this.model[signalKey].value = y2Native(value);
|
||||
this.model.props[signalKey].value = y2Native(value);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -331,13 +331,13 @@ export class SyncController {
|
||||
private _popProp(prop: string) {
|
||||
const model = this.model as BlockModel<Record<string, unknown>>;
|
||||
|
||||
const value = model[prop];
|
||||
const value = model.props[prop];
|
||||
this._stashed.delete(prop);
|
||||
model[prop] = value;
|
||||
model.props[prop] = value;
|
||||
}
|
||||
|
||||
private _stashProp(prop: string) {
|
||||
(this.model as BlockModel<Record<string, unknown>>)[prop] = y2Native(
|
||||
(this.model as BlockModel<Record<string, unknown>>).props[prop] = y2Native(
|
||||
this.yBlock.get(`prop:${prop}`),
|
||||
{
|
||||
transform: (value, origin) => {
|
||||
|
||||
@@ -39,7 +39,7 @@ function getBlockViewType(query: Query, block: Block): BlockViewType {
|
||||
(acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]: block.model[key as keyof BlockModel],
|
||||
[key]: block.model.props[key as keyof BlockModel['props']],
|
||||
};
|
||||
},
|
||||
{} as Record<string, unknown>
|
||||
|
||||
@@ -19,7 +19,7 @@ export function syncBlockProps(
|
||||
if (value === undefined) return;
|
||||
|
||||
// @ts-expect-error allow props
|
||||
model[key] = value;
|
||||
model.props[key] = value;
|
||||
});
|
||||
|
||||
// set default value
|
||||
|
||||
@@ -51,7 +51,7 @@ export class BaseBlockTransformer<Props extends object = object> {
|
||||
}
|
||||
return Object.fromEntries(
|
||||
draftModel.keys.map(key => {
|
||||
const value = draftModel[key as keyof typeof draftModel];
|
||||
const value = draftModel.props[key as keyof typeof draftModel.props];
|
||||
return [key, toJSON(value)];
|
||||
})
|
||||
);
|
||||
|
||||
@@ -457,8 +457,8 @@ export class Transformer {
|
||||
id: flat.snapshot.id,
|
||||
flavour: flat.snapshot.flavour,
|
||||
children: [],
|
||||
...props,
|
||||
} as DraftModel;
|
||||
props,
|
||||
} as unknown as DraftModel;
|
||||
} catch (error) {
|
||||
console.error(`Error when transforming snapshot to model data:`);
|
||||
console.error(error);
|
||||
@@ -529,7 +529,7 @@ export class Transformer {
|
||||
|
||||
const actualIndex =
|
||||
startIndex !== undefined ? startIndex + index : undefined;
|
||||
doc.addBlock(flavour, draft as object, parentId, actualIndex);
|
||||
doc.addBlock(flavour, { id, ...draft.props }, parentId, actualIndex);
|
||||
|
||||
const model = doc.getBlock(id)?.model;
|
||||
if (!model) {
|
||||
|
||||
Reference in New Issue
Block a user