Skip to content

Commit 8a8ac43

Browse files
committed
feat(json-crdt-peritext-ui): 🎸 add support for selections to "format" events
1 parent 92e2b84 commit 8a8ac43

File tree

4 files changed

+35
-32
lines changed

4 files changed

+35
-32
lines changed

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export class Editor<T = string> implements Printable {
177177
* the contents is removed and the cursor is set at the start of the range
178178
* as caret.
179179
*/
180-
public collapseCursor(cursor: Cursor<T>): void {
180+
public collapseCursor(cursor: Range<T>): void {
181181
this.delRange(cursor);
182182
cursor.collapseToStart();
183183
}
@@ -630,11 +630,11 @@ export class Editor<T = string> implements Printable {
630630

631631
// --------------------------------------------------------------- formatting
632632

633-
public eraseFormatting(store: EditorSlices<T> = this.saved): void {
633+
public eraseFormatting(store: EditorSlices<T> = this.saved, selection: Range<T>[] | IterableIterator<Range<T>> = this.cursors()): void {
634634
const overlay = this.txt.overlay;
635-
for (let i = this.cursors0(), cursor = i(); cursor; cursor = i()) {
635+
for (const range of selection) {
636636
overlay.refresh();
637-
const contained = overlay.findContained(cursor);
637+
const contained = overlay.findContained(range);
638638
for (const slice of contained) {
639639
if (slice instanceof PersistedSlice) {
640640
switch (slice.behavior) {
@@ -646,7 +646,7 @@ export class Editor<T = string> implements Printable {
646646
}
647647
}
648648
overlay.refresh();
649-
const overlapping = overlay.findOverlapping(cursor);
649+
const overlapping = overlay.findOverlapping(range);
650650
for (const slice of overlapping) {
651651
switch (slice.behavior) {
652652
case SliceBehavior.One:
@@ -658,11 +658,11 @@ export class Editor<T = string> implements Printable {
658658
}
659659
}
660660

661-
public clearFormatting(store: EditorSlices<T> = this.saved): void {
661+
public clearFormatting(store: EditorSlices<T> = this.saved, selection: Range<T>[] | IterableIterator<Range<T>> = this.cursors()): void {
662662
const overlay = this.txt.overlay;
663663
overlay.refresh();
664-
for (let i = this.cursors0(), cursor = i(); cursor; cursor = i()) {
665-
const overlapping = overlay.findOverlapping(cursor);
664+
for (const range of selection) {
665+
const overlapping = overlay.findOverlapping(range);
666666
for (const slice of overlapping) store.del(slice.id);
667667
}
668668
}
@@ -709,18 +709,19 @@ export class Editor<T = string> implements Printable {
709709
type: CommonSliceType | string | number,
710710
data?: unknown,
711711
store: EditorSlices<T> = this.saved,
712+
selection: Range<T>[] | IterableIterator<Range<T>> = this.cursors(),
712713
): void {
713714
// TODO: handle mutually exclusive slices (<sub>, <sub>)
714715
this.txt.overlay.refresh();
715-
CURSORS: for (let i = this.cursors0(), cursor = i(); cursor; cursor = i()) {
716-
if (cursor.isCollapsed()) {
716+
SELECTION: for (const range of selection) {
717+
if (range.isCollapsed()) {
717718
const pending = this.pending.value;
718719
if (pending.has(type)) pending.delete(type);
719720
else pending.set(type, data);
720721
this.pending.next(pending);
721-
continue CURSORS;
722+
continue SELECTION;
722723
}
723-
this.toggleRangeExclFmt(cursor, type, data, store);
724+
this.toggleRangeExclFmt(range, type, data, store);
724725
}
725726
}
726727

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,37 @@ import type {SliceType} from '../slice/types';
44
import type {MarkerSlice} from '../slice/MarkerSlice';
55
import type {Slices} from '../slice/Slices';
66
import type {ITimestampStruct} from '../../../json-crdt-patch';
7-
import type {Cursor} from './Cursor';
7+
import type {Range} from '../rga/Range';
88

99
export class EditorSlices<T = string> {
1010
constructor(
1111
protected readonly txt: Peritext<T>,
1212
public readonly slices: Slices<T>,
1313
) {}
1414

15-
protected insAtCursors<S extends PersistedSlice<T>>(callback: (cursor: Cursor<T>) => S): S[] {
15+
protected insAtCursors<S extends PersistedSlice<T>>(selection: Range<T>[] | IterableIterator<Range<T>>, callback: (range: Range<T>) => S): S[] {
1616
const slices: S[] = [];
17-
this.txt.editor.forCursor((cursor) => {
17+
for (const cursor of selection) {
1818
const slice = callback(cursor);
1919
slices.push(slice);
20-
});
20+
}
2121
return slices;
2222
}
2323

24-
public insStack(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T>[] {
25-
return this.insAtCursors((cursor) => this.slices.insStack(cursor.range(), type, data));
24+
public insStack(type: SliceType, data?: unknown | ITimestampStruct, selection: Range<T>[] | IterableIterator<Range<T>> = this.txt.editor.cursors()): PersistedSlice<T>[] {
25+
return this.insAtCursors(selection, (cursor) => this.slices.insStack(cursor.range(), type, data));
2626
}
2727

28-
public insOne(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T>[] {
29-
return this.insAtCursors((cursor) => this.slices.insOne(cursor.range(), type, data));
28+
public insOne(type: SliceType, data?: unknown | ITimestampStruct, selection: Range<T>[] | IterableIterator<Range<T>> = this.txt.editor.cursors()): PersistedSlice<T>[] {
29+
return this.insAtCursors(selection, (cursor) => this.slices.insOne(cursor.range(), type, data));
3030
}
3131

32-
public insErase(type: SliceType, data?: unknown | ITimestampStruct): PersistedSlice<T>[] {
33-
return this.insAtCursors((cursor) => this.slices.insErase(cursor.range(), type, data));
32+
public insErase(type: SliceType, data?: unknown | ITimestampStruct, selection: Range<T>[] | IterableIterator<Range<T>> = this.txt.editor.cursors()): PersistedSlice<T>[] {
33+
return this.insAtCursors(selection, (cursor) => this.slices.insErase(cursor.range(), type, data));
3434
}
3535

36-
public insMarker(type: SliceType, data?: unknown, separator?: string): MarkerSlice<T>[] {
37-
return this.insAtCursors((cursor) => {
36+
public insMarker(type: SliceType, data?: unknown, separator?: string, selection: Range<T>[] | IterableIterator<Range<T>> = this.txt.editor.cursors()): MarkerSlice<T>[] {
37+
return this.insAtCursors(selection, (cursor) => {
3838
this.txt.editor.collapseCursor(cursor);
3939
const after = cursor.start.clone();
4040
after.refAfter();

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,28 +168,30 @@ export class PeritextEventDefaults implements PeritextEventHandlerMap {
168168
}
169169
};
170170

171-
public readonly format = (event: CustomEvent<events.FormatDetail>) => {
172-
const {type, store = 'saved', behavior = 'one', data} = event.detail;
171+
public readonly format = ({detail}: CustomEvent<events.FormatDetail>) => {
172+
const selection = [...this.getSelSet(detail)];
173+
this.moveSelSet(selection, detail);
174+
const {type, store = 'saved', behavior = 'one', data} = detail;
173175
const editor = this.txt.editor;
174176
const slices: EditorSlices = store === 'saved' ? editor.saved : store === 'extra' ? editor.extra : editor.local;
175177
switch (behavior) {
176178
case 'many': {
177179
if (type === undefined) throw new Error('TYPE_REQUIRED');
178-
slices.insStack(type, data);
180+
slices.insStack(type, data, selection);
179181
break;
180182
}
181183
case 'one': {
182184
if (type === undefined) throw new Error('TYPE_REQUIRED');
183-
editor.toggleExclFmt(type, data, slices);
185+
editor.toggleExclFmt(type, data, slices, selection);
184186
break;
185187
}
186188
case 'erase': {
187-
if (type === undefined) editor.eraseFormatting(slices);
188-
else slices.insErase(type, data);
189+
if (type === undefined) editor.eraseFormatting(slices, selection);
190+
else slices.insErase(type, data, selection);
189191
break;
190192
}
191193
case 'clear': {
192-
editor.clearFormatting(slices);
194+
editor.clearFormatting(slices, selection);
193195
break;
194196
}
195197
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ export interface CursorDetail extends SelectionDetailPart, SelectionMoveDetailPa
232232
/**
233233
* Event dispatched to insert an inline rich-text annotation into the document.
234234
*/
235-
export interface FormatDetail {
235+
export interface FormatDetail extends SelectionDetailPart, SelectionMoveDetailPart {
236236
/**
237237
* Type of the annotation. The type is used to determine the visual style of
238238
* the annotation, for example, the type `'bold'` may render the text in bold.

0 commit comments

Comments
 (0)