Skip to content

Commit 3c4a344

Browse files
committed
feat(json-crdt-extensions): 🎸 add "format" event "set" action
1 parent da100fd commit 3c4a344

File tree

4 files changed

+59
-8
lines changed

4 files changed

+59
-8
lines changed

src/json-crdt-extensions/peritext/events/__tests__/format.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,41 @@ const testSuite = (getKit: () => Kit) => {
202202
});
203203
});
204204

205+
describe('"set" action', () => {
206+
test('can overwrite formatting data', () => {
207+
const kit = setup();
208+
kit.et.cursor({at: [10, 20]});
209+
kit.et.format({action: 'ins', type: SliceTypeCon.col, data: {color: 'green', opacity: .5}});
210+
expect(kit.toHtml()).toBe('<p>abcdefghij<col data-attr=\'{"color":"green","opacity":0.5}\'>klmnopqrst</col>uvwxyz</p>');
211+
const slice = kit.peritext.savedSlices.each().find(({type}) => type === SliceTypeCon.col)!;
212+
expect(slice.data()).toEqual({color: 'green', opacity: 0.5});
213+
kit.et.format({action: 'set', slice, data: {"color":"red"}});
214+
expect(slice.data()).toEqual({color: 'red'});
215+
});
216+
217+
test('can overwrite formatting data with non-object', () => {
218+
const kit = setup();
219+
kit.et.cursor({at: [10, 20]});
220+
kit.et.format({action: 'ins', type: SliceTypeCon.col, data: {color: 'green', opacity: .5}});
221+
expect(kit.toHtml()).toBe('<p>abcdefghij<col data-attr=\'{"color":"green","opacity":0.5}\'>klmnopqrst</col>uvwxyz</p>');
222+
const slice = kit.peritext.savedSlices.each().find(({type}) => type === SliceTypeCon.col)!;
223+
expect(slice.data()).toEqual({color: 'green', opacity: 0.5});
224+
kit.et.format({action: 'set', slice, data: 123});
225+
expect(slice.data()).toEqual(123);
226+
});
227+
228+
test('can overwrite non-object with object', () => {
229+
const kit = setup();
230+
kit.et.cursor({at: [10, 20]});
231+
kit.et.format({action: 'ins', type: SliceTypeCon.col, data: true});
232+
expect(kit.toHtml()).toBe('<p>abcdefghij<col data-attr=\'true\'>klmnopqrst</col>uvwxyz</p>');
233+
const slice = kit.peritext.savedSlices.each().find(({type}) => type === SliceTypeCon.col)!;
234+
expect(slice.data()).toEqual(true);
235+
kit.et.format({action: 'set', slice, data: {col: '#fff'}});
236+
expect(slice.data()).toEqual({col: '#fff'});
237+
});
238+
});
239+
205240
describe('data', () => {
206241
test('can retrive formatting data, when specified as "obj"', () => {
207242
const kit = setup();

src/json-crdt-extensions/peritext/events/defaults/PeritextEventDefaults.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,14 @@ export class PeritextEventDefaults implements PeritextEventHandlerMap {
248248
else slices.insErase(tag, detail.data, selection);
249249
break;
250250
}
251-
case 'upd': {
251+
case 'upd':
252+
case 'set': {
252253
const {slice} = detail;
253254
if (slice) {
254255
const persistedSlice = slice instanceof PersistedSlice ? slice : this.txt.getSlice(slice);
255256
if (persistedSlice instanceof PersistedSlice) {
256-
persistedSlice.mergeData(detail.data);
257+
if (action === 'set') persistedSlice.setData(detail.data);
258+
else persistedSlice.mergeData(detail.data);
257259
}
258260
}
259261
break;

src/json-crdt-extensions/peritext/events/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,10 @@ export interface FormatDetail extends RangeEventDetail, SliceDetailPart {
298298
* updated to the specified slice. If the `slice` field is not specified,
299299
* the `data` will be updated on the first annotation in the selection set
300300
* with the same `type`.
301+
* - The `'set'` action ovewrites the formatting data of the slice specified
302+
* by the `slice` field with the `data` field.
301303
*/
302-
action: 'ins' | 'tog' | 'del' | 'erase' | 'upd';
304+
action: 'ins' | 'tog' | 'del' | 'erase' | 'upd' | 'set';
303305

304306
/**
305307
* Type of the annotation. The type is used to determine the visual style of

src/json-crdt-extensions/peritext/slice/PersistedSlice.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,30 @@ export class PersistedSlice<T = string> extends Range<T> implements MutableSlice
174174
return this.dataNode() as unknown as ObjApi<ObjNode>;
175175
}
176176

177+
/**
178+
* Overwrites the data of this slice with the given data.
179+
*
180+
* @param data Data to set for this slice. The data can be any JSON value, but
181+
* it is recommended to use an object.
182+
*/
183+
public setData(data: unknown): void {
184+
this.tupleApi().set([[SliceTupleIndex.Data, s.jsonCon(data)]]);
185+
}
186+
187+
/**
188+
* Merges object data into the slice's data using JSON CRDT diffing.
189+
*
190+
* @param data Data to merge into the slice. If the data is an object, it will be
191+
* merged with the existing data of the slice using JSON CRDT diffing.
192+
*/
177193
public mergeData(data: unknown): void {
178194
const {model} = this;
179195
const diff = new JsonCrdtDiff(model);
180196
if (this.dataNode() instanceof ObjApi && !!data && typeof data === 'object' && !Array.isArray(data)) {
181197
const dataNode = this.dataAsObj();
182198
const patch = diff.diff(dataNode.node, data);
183199
model.applyPatch(patch);
184-
} else {
185-
this.tupleApi().set([
186-
[SliceTupleIndex.Data, s.jsonCon(data)],
187-
]);
188-
}
200+
} else this.setData(data);
189201
}
190202

191203
public getStore(): Slices<T> | undefined {

0 commit comments

Comments
 (0)