feat(editor): support delete in flat block proxy (#9994)
This commit is contained in:
@@ -452,6 +452,33 @@ describe('flat', () => {
|
|||||||
expect.anything()
|
expect.anything()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onChange.mockClear();
|
||||||
|
onColUpdated.mockClear();
|
||||||
|
delete (model.props.cols.a as Record<string, unknown>).color;
|
||||||
|
expect(yBlock.get('prop:cols.a.color')).toBe(undefined);
|
||||||
|
expect(model.props.cols.a).toEqual({});
|
||||||
|
expect(model.props.cols$.value).toEqual({ a: {} });
|
||||||
|
expect(onColUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onChange).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
'cols',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
|
||||||
|
model.props.cols = {
|
||||||
|
a: { color: 'red' },
|
||||||
|
b: { color: 'blue' },
|
||||||
|
};
|
||||||
|
onChange.mockClear();
|
||||||
|
onColUpdated.mockClear();
|
||||||
|
delete (model.props as Record<string, unknown>).cols;
|
||||||
|
expect(model.props.cols$.value).toBeUndefined();
|
||||||
|
expect(yBlock.get('prop:cols.a.color')).toBe(undefined);
|
||||||
|
expect(yBlock.get('prop:cols.b.color')).toBe(undefined);
|
||||||
|
expect(onColUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onChange).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
onChange.mockClear();
|
onChange.mockClear();
|
||||||
model.props.title.insert('test', 0);
|
model.props.title.insert('test', 0);
|
||||||
expect((yBlock.get('prop:title') as Y.Text).toJSON()).toBe('test');
|
expect((yBlock.get('prop:title') as Y.Text).toJSON()).toBe('test');
|
||||||
@@ -483,6 +510,16 @@ describe('flat', () => {
|
|||||||
'labels',
|
'labels',
|
||||||
expect.anything()
|
expect.anything()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onChange.mockClear();
|
||||||
|
model.props.labels.splice(0, 1);
|
||||||
|
expect(getLabels().toJSON()).toEqual([]);
|
||||||
|
expect(model.props.labels$.value).toEqual([]);
|
||||||
|
expect(onChange).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
'labels',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('stash and pop', () => {
|
test('stash and pop', () => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
type YMapEvent,
|
type YMapEvent,
|
||||||
} from 'yjs';
|
} from 'yjs';
|
||||||
|
|
||||||
|
import { SYS_KEYS } from '../consts';
|
||||||
import { BaseReactiveYData } from './base-reactive-data';
|
import { BaseReactiveYData } from './base-reactive-data';
|
||||||
import { Boxed, type OnBoxedChange } from './boxed';
|
import { Boxed, type OnBoxedChange } from './boxed';
|
||||||
import { isPureObject } from './is-pure-object';
|
import { isPureObject } from './is-pure-object';
|
||||||
@@ -18,6 +19,9 @@ import type { ProxyOptions, UnRecord } from './types';
|
|||||||
|
|
||||||
const keyWithoutPrefix = (key: string) => key.replace(/(prop|sys):/, '');
|
const keyWithoutPrefix = (key: string) => key.replace(/(prop|sys):/, '');
|
||||||
|
|
||||||
|
const keyWithPrefix = (key: string) =>
|
||||||
|
SYS_KEYS.has(key) ? `sys:${key}` : `prop:${key}`;
|
||||||
|
|
||||||
type OnChange = (key: string, value: unknown) => void;
|
type OnChange = (key: string, value: unknown) => void;
|
||||||
type Transform = (key: string, value: unknown, origin: unknown) => unknown;
|
type Transform = (key: string, value: unknown, origin: unknown) => unknown;
|
||||||
|
|
||||||
@@ -144,7 +148,7 @@ function createProxy(
|
|||||||
run(value, fullPath);
|
run(value, fullPath);
|
||||||
} else {
|
} else {
|
||||||
list.push(() => {
|
list.push(() => {
|
||||||
yMap.set(`prop:${fullPath}`, value);
|
yMap.set(keyWithPrefix(fullPath), value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -179,7 +183,7 @@ function createProxy(
|
|||||||
if (!isStashed) {
|
if (!isStashed) {
|
||||||
yMap.doc?.transact(
|
yMap.doc?.transact(
|
||||||
() => {
|
() => {
|
||||||
yMap.set(`prop:${fullPath}`, yValue);
|
yMap.set(keyWithPrefix(fullPath), yValue);
|
||||||
},
|
},
|
||||||
{ proxy: true }
|
{ proxy: true }
|
||||||
);
|
);
|
||||||
@@ -191,6 +195,64 @@ function createProxy(
|
|||||||
}
|
}
|
||||||
return Reflect.set(target, p, value, receiver);
|
return Reflect.set(target, p, value, receiver);
|
||||||
},
|
},
|
||||||
|
deleteProperty: (target, p) => {
|
||||||
|
if (typeof p === 'string') {
|
||||||
|
const fullPath = basePath ? `${basePath}.${p}` : p;
|
||||||
|
const firstKey = fullPath.split('.')[0];
|
||||||
|
if (!firstKey) {
|
||||||
|
throw new Error(`Invalid key for: ${fullPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isStashed = stashed.has(firstKey);
|
||||||
|
|
||||||
|
const updateSignal = () => {
|
||||||
|
if (shouldByPassSignal()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signalKey = `${firstKey}$`;
|
||||||
|
if (!(signalKey in root)) {
|
||||||
|
if (!isRoot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete root[signalKey];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byPassSignalUpdate(() => {
|
||||||
|
const prev = root[firstKey];
|
||||||
|
const next = isRoot
|
||||||
|
? prev
|
||||||
|
: isPureObject(prev)
|
||||||
|
? { ...prev }
|
||||||
|
: Array.isArray(prev)
|
||||||
|
? [...prev]
|
||||||
|
: prev;
|
||||||
|
// @ts-expect-error allow magic props
|
||||||
|
root[signalKey].value = next;
|
||||||
|
onChange?.(firstKey, next);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isStashed) {
|
||||||
|
yMap.doc?.transact(
|
||||||
|
() => {
|
||||||
|
const fullKey = keyWithPrefix(fullPath);
|
||||||
|
yMap.forEach((_, key) => {
|
||||||
|
if (key.startsWith(fullKey)) {
|
||||||
|
yMap.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ proxy: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = Reflect.deleteProperty(target, p);
|
||||||
|
updateSignal();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Reflect.deleteProperty(target, p);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
markProxy(proxy);
|
markProxy(proxy);
|
||||||
@@ -217,7 +279,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
|
|||||||
}
|
}
|
||||||
if (type.action === 'update' || type.action === 'add') {
|
if (type.action === 'update' || type.action === 'add') {
|
||||||
const value = yMap.get(key);
|
const value = yMap.get(key);
|
||||||
const keyName: string = key.replace('prop:', '');
|
const keyName: string = keyWithoutPrefix(key);
|
||||||
const keys = keyName.split('.');
|
const keys = keyName.split('.');
|
||||||
const firstKey = keys[0];
|
const firstKey = keys[0];
|
||||||
if (this._stashed.has(firstKey)) {
|
if (this._stashed.has(firstKey)) {
|
||||||
@@ -232,7 +294,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type.action === 'delete') {
|
if (type.action === 'delete') {
|
||||||
const keyName: string = key.replace('prop:', '');
|
const keyName: string = keyWithoutPrefix(key);
|
||||||
const keys = keyName.split('.');
|
const keys = keyName.split('.');
|
||||||
const firstKey = keys[0];
|
const firstKey = keys[0];
|
||||||
if (this._stashed.has(firstKey)) {
|
if (this._stashed.has(firstKey)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user