mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
fix(editor): missing signal of optional flat props (#13762)
Close https://github.com/toeverything/AFFiNE/issues/13750 #### PR Dependency Tree * **PR #13762** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Optional block properties are now supported (e.g., flat-table), with default values applied automatically when not set. * **Bug Fixes** * More reliable initialization and syncing of block properties, ensuring defaults appear consistently. * Change notifications now correctly reflect updates to optional/defaulted properties. * **Tests** * Added tests verifying optional property behavior, default application, syncing, and change events. <!-- end of auto-generated comment: release notes by coderabbit.ai --> #### PR Dependency Tree * **PR #13762** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal)
This commit is contained in:
@@ -51,6 +51,7 @@ const flatTableSchema = defineBlockSchema({
|
|||||||
textCols: {} as Record<string, Text>,
|
textCols: {} as Record<string, Text>,
|
||||||
rows: {} as Record<string, { color: string }>,
|
rows: {} as Record<string, { color: string }>,
|
||||||
labels: [] as Array<string>,
|
labels: [] as Array<string>,
|
||||||
|
optional: undefined as string | undefined,
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
role: 'content',
|
role: 'content',
|
||||||
@@ -494,6 +495,16 @@ describe('flat', () => {
|
|||||||
expect(model.props.textCols$.value.a.toDelta()).toEqual([
|
expect(model.props.textCols$.value.a.toDelta()).toEqual([
|
||||||
{ insert: 'test' },
|
{ insert: 'test' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
onChange.mockClear();
|
||||||
|
expect(model.props).not.toHaveProperty('optional');
|
||||||
|
expect(model.props).toHaveProperty('optional$');
|
||||||
|
model.props.optional$.value = 'test';
|
||||||
|
expect(model.props.optional).toBe('test');
|
||||||
|
expect(model.props.optional$.value).toBe('test');
|
||||||
|
expect(onChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onChange).toHaveBeenCalledWith(expect.anything(), 'optional', true);
|
||||||
|
expect(yBlock.get('prop:optional')).toBe('test');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('stash and pop', () => {
|
test('stash and pop', () => {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export class FlatSyncController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const model = schema.model.toModel?.() ?? new BlockModel<object>();
|
const model = schema.model.toModel?.() ?? new BlockModel<object>();
|
||||||
|
const defaultProps = schema.model.props?.(internalPrimitives);
|
||||||
model.schema = schema;
|
model.schema = schema;
|
||||||
|
|
||||||
model.id = this.id;
|
model.id = this.id;
|
||||||
@@ -55,7 +56,8 @@ export class FlatSyncController {
|
|||||||
const reactive = new ReactiveFlatYMap(
|
const reactive = new ReactiveFlatYMap(
|
||||||
this.yBlock,
|
this.yBlock,
|
||||||
model.deleted,
|
model.deleted,
|
||||||
this.onChange
|
this.onChange,
|
||||||
|
defaultProps
|
||||||
);
|
);
|
||||||
this._reactive = reactive;
|
this._reactive = reactive;
|
||||||
const proxy = reactive.proxy;
|
const proxy = reactive.proxy;
|
||||||
@@ -66,17 +68,6 @@ export class FlatSyncController {
|
|||||||
model.store = this.doc;
|
model.store = this.doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps = schema.model.props?.(internalPrimitives);
|
|
||||||
if (defaultProps) {
|
|
||||||
Object.entries(defaultProps).forEach(([key, value]) => {
|
|
||||||
if (key in proxy) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value === undefined) return;
|
|
||||||
proxy[key] = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
|
|||||||
constructor(
|
constructor(
|
||||||
protected readonly _ySource: YMap<unknown>,
|
protected readonly _ySource: YMap<unknown>,
|
||||||
private readonly _onDispose: Subject<void>,
|
private readonly _onDispose: Subject<void>,
|
||||||
private readonly _onChange?: OnChange
|
private readonly _onChange?: OnChange,
|
||||||
|
defaultProps?: Record<string, unknown>
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._initialized = false;
|
this._initialized = false;
|
||||||
@@ -112,7 +113,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
|
|||||||
|
|
||||||
const proxy = this._getProxy(source, source);
|
const proxy = this._getProxy(source, source);
|
||||||
|
|
||||||
Object.entries(source).forEach(([key, value]) => {
|
const initSignals = (key: string, value: unknown) => {
|
||||||
const signalData = signal(value);
|
const signalData = signal(value);
|
||||||
source[`${key}$`] = signalData;
|
source[`${key}$`] = signalData;
|
||||||
const unsubscribe = signalData.subscribe(next => {
|
const unsubscribe = signalData.subscribe(next => {
|
||||||
@@ -128,11 +129,30 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
|
|||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(source).forEach(([key, value]) => {
|
||||||
|
initSignals(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (defaultProps) {
|
||||||
|
Object.entries(defaultProps).forEach(([key, value]) => {
|
||||||
|
if (!(key in proxy) && value === undefined) {
|
||||||
|
initSignals(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this._proxy = proxy;
|
this._proxy = proxy;
|
||||||
this._ySource.observe(this._observer);
|
this._ySource.observe(this._observer);
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
|
||||||
|
if (defaultProps) {
|
||||||
|
Object.entries(defaultProps).forEach(([key, value]) => {
|
||||||
|
if (key in proxy || value === undefined) return;
|
||||||
|
proxy[key] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pop = (prop: string): void => {
|
pop = (prop: string): void => {
|
||||||
|
|||||||
Reference in New Issue
Block a user