Skip to content

Commit 6b4b675

Browse files
committed
feat(json-crdt-peritext-ui): 🎸 add support for tag updates in "marker" events
1 parent ab7384a commit 6b4b675

File tree

6 files changed

+64
-18
lines changed

6 files changed

+64
-18
lines changed

‎src/json-crdt-extensions/peritext/editor/Editor.ts‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {MarkerOverlayPoint} from '../overlay/MarkerOverlayPoint';
1414
import {UndEndIterator, type UndEndNext} from '../../../util/iterator';
1515
import {tick, Timespan, type ITimespanStruct} from '../../../json-crdt-patch';
1616
import {CursorAnchor, SliceStacking, SliceHeaderMask, SliceHeaderShift, SliceTypeCon} from '../slice/constants';
17+
import {ArrApi} from '../../../json-crdt/model';
1718
import type {Point} from '../rga/Point';
1819
import type {Range} from '../rga/Range';
1920
import type {Printable} from 'tree-dump';
@@ -30,6 +31,7 @@ import type {
3031
ViewSlice,
3132
EditorUi,
3233
EditorSelection,
34+
MarkerUpdateTarget,
3335
} from './types';
3436
import type {ApiOperation} from '../../../json-crdt/model/api/types';
3537

@@ -951,18 +953,24 @@ export class Editor<T = string> implements Printable {
951953

952954
public updMarkerSlice(
953955
marker: MarkerSlice<T>,
954-
target: 'type',
956+
target: MarkerUpdateTarget,
955957
ops: ApiOperation[],
956958
): void {
957-
const node = target === 'type' ? marker.nestedType().asArr() : void 0;
959+
const node = target === 'type'
960+
? marker.nestedType().asArr()
961+
: target[0] === 'tag'
962+
? marker.nestedType().tag(target[1]).asVec()
963+
: target[0] === 'data'
964+
? marker.nestedType().tag(target[1])?.data()
965+
: void 0;
958966
if (!node) return;
959967
for (const op of ops) node.op(op);
960-
if (node.length() === 0) marker.del();
968+
if (target === 'type' && node instanceof ArrApi && node.length() === 0) marker.del();
961969
}
962970

963971
public updMarkerAt(
964972
point: Point<T>,
965-
target: 'type',
973+
target: MarkerUpdateTarget,
966974
ops: ApiOperation[],
967975
slices: EditorSlices<T> = this.saved,
968976
): void {
@@ -974,7 +982,7 @@ export class Editor<T = string> implements Printable {
974982

975983
public updMarker(
976984
selection: Range<T>[] | IterableIterator<Range<T>> = this.cursors(),
977-
target: 'type',
985+
target: MarkerUpdateTarget,
978986
ops: ApiOperation[],
979987
slices: EditorSlices<T> = this.saved,
980988
): void {

‎src/json-crdt-extensions/peritext/editor/types.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,5 @@ export interface EditorUi<T = string> {
5252
*/
5353
vert?(point: Point<T>, steps: number): Point<T> | undefined;
5454
}
55+
56+
export type MarkerUpdateTarget = 'type' | ['tag', index?: number] | ['data', index?: number];

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,43 @@ const testSuite = (getKit: () => Kit) => {
141141
expect(kit.toHtml()).toBe('<p>abcdefghijklmnopqrstuvwxyz</p>');
142142
});
143143
});
144+
145+
describe('tag', () => {
146+
test('can update tag name', () => {
147+
const kit = setup();
148+
kit.et.cursor({at: [8]});
149+
kit.et.marker({action: 'ins', type: SliceTypeCon.blockquote});
150+
expect(kit.toHtml()).toBe('<p>abcdefgh</p><blockquote>ijklmnopqrstuvwxyz</blockquote>');
151+
kit.et.marker({
152+
action: 'upd',
153+
target: ['tag', 0],
154+
ops: [
155+
['replace', '/0', SliceTypeCon.p],
156+
],
157+
});
158+
expect(kit.toHtml()).toBe('<p>abcdefgh</p><p>ijklmnopqrstuvwxyz</p>');
159+
});
160+
161+
test('can update discriminant', () => {
162+
const kit = setup();
163+
kit.et.cursor({at: [8]});
164+
kit.et.marker({action: 'ins', type: SliceTypeCon.p});
165+
expect(kit.toHtml()).toBe('<p>abcdefgh</p><p>ijklmnopqrstuvwxyz</p>');
166+
const slice = kit.peritext.savedSlices.each().find((slice) => slice.type() === SliceTypeCon.p);
167+
expect(slice?.nestedType().tag(0).discriminant()).toBe(0);
168+
kit.et.cursor({at: [12]});
169+
kit.et.marker({
170+
action: 'upd',
171+
target: ['tag', 0],
172+
ops: [
173+
['replace', '/1', 1],
174+
],
175+
});
176+
expect(slice?.nestedType().tag(0).discriminant()).toBe(1);
177+
slice?.nestedType().tag(0).setDiscriminant(2);
178+
expect(slice?.nestedType().tag(0).discriminant()).toBe(2);
179+
});
180+
});
144181
});
145182

146183
describe('scenarios', () => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ export class PeritextEventDefaults implements PeritextEventHandlerMap {
288288
case 'upd': {
289289
const {target, ops} = detail;
290290
if (target && ops) {
291-
editor.updMarker(selection, target as any, ops);
291+
editor.updMarker(selection, target, ops);
292292
}
293293
break;
294294
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ export interface MarkerDetail extends RangeEventDetail, SliceDetailPart {
484484
* in the type array. The target node to which the operations are applied
485485
* are enfored to be an "obj" node.
486486
*/
487-
target?: 'type' | ['tag', index: number] | ['data', index: number];
487+
target?: 'type' | ['tag', index?: number] | ['data', index?: number];
488488

489489
/**
490490
* The list of operations to perform on the target node, when the action is

‎src/json-crdt/model/api/nodes.ts‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,9 @@ export class NodeApi<N extends JsonNode = JsonNode> implements Printable {
240240
if (index !== index) break ADD;
241241
if (index < 0) index = 0;
242242
if (index > length) index = length;
243-
if (node instanceof ArrApi) {
244-
node.ins(index, Array.isArray(value) ? value : [value]);
245-
} else if (node instanceof StrApi) {
246-
node.ins(index, value + '');
247-
} else if (node instanceof BinApi) {
243+
if (node instanceof ArrApi) node.ins(index, Array.isArray(value) ? value : [value]);
244+
else if (node instanceof StrApi) node.ins(index, value + '');
245+
else if (node instanceof BinApi) {
248246
if (!(value instanceof Uint8Array)) break ADD;
249247
node.ins(index, value);
250248
}
@@ -272,11 +270,11 @@ export class NodeApi<N extends JsonNode = JsonNode> implements Printable {
272270
index = ~~key;
273271
if (index + '' !== key) break REPLACE;
274272
}
275-
if (index !== index || index < 0 || index > length - 1) break REPLACE;
276-
node.upd(index, value);
277-
} else if (node instanceof VecApi) {
278-
node.set([[~~key, value]]);
279-
} else break REPLACE;
273+
if (index !== index || index < 0 || index > length) break REPLACE;
274+
if (index === length) node.ins(index, [value]);
275+
else node.upd(index, value);
276+
} else if (node instanceof VecApi) node.set([[~~key, value]]);
277+
else break REPLACE;
280278
return true;
281279
} catch {}
282280
return false;
@@ -343,7 +341,8 @@ export class NodeApi<N extends JsonNode = JsonNode> implements Printable {
343341
}
344342

345343
public toString(tab: string = ''): string {
346-
return 'api' + printTree(tab, [(tab) => this.node.toString(tab)]);
344+
const name = this.constructor === NodeApi ? '*' : this.node.name();
345+
return 'api(' + name + ')' + printTree(tab, [(tab) => this.node.toString(tab)]);
347346
}
348347
}
349348

0 commit comments

Comments
 (0)