-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #737 from streamich/peritext-events
Peritext events
- Loading branch information
Showing
5 changed files
with
428 additions
and
0 deletions.
There are no files selected for viewing
112 changes: 112 additions & 0 deletions
112
src/json-crdt-peritext-ui/events/PeritextEventDefaults.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import type {Peritext} from '../../json-crdt-extensions/peritext'; | ||
import type {EditorSlices} from '../../json-crdt-extensions/peritext/editor/EditorSlices'; | ||
import type {PeritextEventHandlerMap, PeritextEventTarget} from './PeritextEventTarget'; | ||
import type * as events from './types'; | ||
|
||
export class PeritextEventDefaults implements PeritextEventHandlerMap { | ||
public constructor( | ||
protected readonly txt: Peritext, | ||
protected readonly et: PeritextEventTarget, | ||
) {} | ||
|
||
public readonly change = (event: CustomEvent<events.ChangeDetail>) => { | ||
// console.log('change', event?.detail.ev?.type, event?.detail.ev?.detail); | ||
}; | ||
|
||
public readonly insert = (event: CustomEvent<events.InsertDetail>) => { | ||
if (event.defaultPrevented) return; | ||
const text = event.detail.text; | ||
this.txt.editor.insert(text); | ||
this.et.change(event); | ||
}; | ||
|
||
public readonly delete = (event: CustomEvent<events.DeleteDetail>) => { | ||
if (event.defaultPrevented) return; | ||
const {direction = -1, unit = 'char'} = event.detail; | ||
this.txt.editor.delete(direction, unit); | ||
this.et.change(event); | ||
}; | ||
|
||
public readonly cursor = (event: CustomEvent<events.CursorDetail>) => { | ||
const {at, edge, len = 0, unit} = event.detail; | ||
const txt = this.txt; | ||
const editor = txt.editor; | ||
|
||
// If `at` is specified. | ||
if (typeof at === 'number' && at >= 0) { | ||
const point = editor.point(at); | ||
switch (edge) { | ||
case 'focus': | ||
case 'anchor': { | ||
editor.cursor.setEndpoint(point, edge === 'focus' ? 0 : 1); | ||
break; | ||
} | ||
case 'new': { | ||
editor.addCursor(txt.range(point, point.clone())); | ||
break; | ||
} | ||
default: { | ||
// both | ||
if (!!len && typeof len === 'number') { | ||
const point2 = point.clone(); | ||
point2.step(len); | ||
const range = txt.rangeFromPoints(point, point2); | ||
editor.cursor.set(range.start, range.end); | ||
} else { | ||
editor.cursor.set(point); | ||
} | ||
} | ||
} | ||
this.et.change(event); | ||
return; | ||
} | ||
|
||
// If `edge` is specified. | ||
const isSpecificEdgeSelected = edge === 'focus' || edge === 'anchor'; | ||
if (isSpecificEdgeSelected) { | ||
editor.move(len, unit ?? 'char', edge === 'focus' ? 0 : 1, false); | ||
this.et.change(event); | ||
return; | ||
} | ||
|
||
// If `len` is specified. | ||
if (len) { | ||
const cursor = editor.cursor; | ||
if (cursor.isCollapsed()) editor.move(len, unit ?? 'char'); | ||
else { | ||
if (len > 0) cursor.collapseToEnd(); | ||
else cursor.collapseToStart(); | ||
} | ||
this.et.change(event); | ||
return; | ||
} | ||
|
||
// If `unit` is specified. | ||
if (unit) { | ||
editor.select(unit); | ||
this.et.change(event); | ||
return; | ||
} | ||
}; | ||
|
||
public readonly inline = (event: CustomEvent<events.InlineDetail>) => { | ||
const {type, store = 'saved', behavior = 'overwrite', data, pos} = event.detail; | ||
const editor = this.txt.editor; | ||
const slices: EditorSlices = store === 'saved' ? editor.saved : store === 'extra' ? editor.extra : editor.local; | ||
switch (behavior) { | ||
case 'stack': | ||
slices.insStack(type, data); | ||
break; | ||
case 'erase': | ||
slices.insErase(type, data); | ||
break; | ||
default: | ||
slices.insOverwrite(type, data); | ||
} | ||
this.et.change(event); | ||
}; | ||
|
||
public readonly marker = (event: CustomEvent<events.MarkerDetail>) => { | ||
throw new Error('Not implemented'); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import {TypedEventTarget} from './TypedEventTarget'; | ||
import {type PeritextEventMap, CursorDetail, InlineDetail, DeleteDetail} from './types'; | ||
|
||
export type PeritextEventHandlerMap = { | ||
[K in keyof PeritextEventMap]: (event: CustomEvent<PeritextEventMap[K]>) => void; | ||
}; | ||
|
||
let id = 0; | ||
|
||
export class PeritextEventTarget extends TypedEventTarget<PeritextEventMap> { | ||
public readonly id: number = id++; | ||
|
||
public defaults: Partial<PeritextEventHandlerMap> = {}; | ||
|
||
public dispatch<K extends keyof PeritextEventMap>(type: K, detail: PeritextEventMap[K]): void { | ||
const event = new CustomEvent<PeritextEventMap[K]>(type, {detail}); | ||
this.dispatchEvent(event); | ||
this.defaults[type]?.(event); | ||
} | ||
|
||
public change(ev?: CustomEvent<any>): void { | ||
this.dispatch('change', {ev}); | ||
} | ||
|
||
public insert(text: string): void { | ||
this.dispatch('insert', {text}); | ||
} | ||
|
||
public delete(direction?: -1 | 0 | 1, unit?: DeleteDetail['unit']): void { | ||
this.dispatch('delete', {direction, unit}); | ||
} | ||
|
||
public cursor(detail: CursorDetail): void { | ||
this.dispatch('cursor', detail); | ||
} | ||
|
||
public move(len: number, unit?: CursorDetail['unit'], edge?: CursorDetail['edge']): void { | ||
this.cursor({len, unit, edge}); | ||
} | ||
|
||
public inline(detail: InlineDetail): void { | ||
this.dispatch('inline', detail); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Peritext Events | ||
|
||
## Editing | ||
|
||
### The `insert` event | ||
|
||
- [ ] Insert a character. | ||
- [ ] Insert a string. | ||
|
||
### The `delete` event | ||
|
||
- [ ] Delete one character backward. | ||
- [ ] Delete one character forward. | ||
- [ ] Delete one word backward. | ||
- [ ] Delete one word forward. | ||
|
||
### Clipboard events | ||
|
||
- [ ] Export range. | ||
- [ ] Import range. | ||
- [ ] Insert plain text. | ||
- [ ] Insert HTML. | ||
|
||
### Imperative API | ||
|
||
- [ ] Delete range. | ||
- [ ] Delete all annotations in range. | ||
- [ ] Find block by position. | ||
- [ ] Find annotations by position. | ||
- [ ] Get block range. | ||
|
||
## Overlays | ||
|
||
- Events | ||
- [ ] `mark` event | ||
- [ ] Annotate range. | ||
- [ ] Insert block split. | ||
- [ ] `mark-update` event | ||
- [ ] Change annotation metadata. | ||
- [ ] Change annotation range. | ||
- [ ] Change block metadata. | ||
- [ ] `mark-delete` event | ||
- [ ] Delete block split. | ||
- [ ] Delete annotation. | ||
|
||
### The `inline` event | ||
|
||
### The `marker` event | ||
|
||
### Imperative API | ||
|
||
- [ ] Insert an overlay. | ||
- [ ] Update overlay metadata. | ||
- [ ] Delete an overlay. | ||
- [ ] Change overlay range. | ||
- [ ] Assign a set of overlays. | ||
- [ ] Remove an overlay set. | ||
|
||
## Navigation | ||
|
||
### The `cursor` event | ||
|
||
- [ ] Move one character left. | ||
- [ ] Move one character right. | ||
- [ ] Move one word left. | ||
- [ ] Move one word right. | ||
- [ ] Move left until end of line. | ||
- [ ] Move right until end of line. | ||
- [ ] Move up one line. | ||
- [ ] Move down one line. | ||
|
||
### History | ||
|
||
- [ ] Undo. | ||
- [ ] Redo. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
const buildTypedEventTargetType = () => { | ||
const klass = class TypedEventTarget<EventMap> { | ||
addEventListener<K extends keyof EventMap>( | ||
type: K, | ||
listener: (this: HTMLElement, ev: EventMap[K]) => any, | ||
options?: boolean | AddEventListenerOptions, | ||
): void; | ||
addEventListener( | ||
type: string, | ||
callback: EventListenerOrEventListenerObject | null, | ||
options?: AddEventListenerOptions | boolean, | ||
): void; | ||
addEventListener(): void {} | ||
dispatchEvent(event: EventMap[keyof EventMap]): boolean; | ||
dispatchEvent(event: Event): boolean; | ||
dispatchEvent(): boolean { | ||
return true; | ||
} | ||
removeEventListener<K extends keyof EventMap>( | ||
type: K, | ||
listener: (this: HTMLElement, ev: EventMap[K]) => any, | ||
options?: boolean | EventListenerOptions, | ||
): void; | ||
removeEventListener( | ||
type: string, | ||
callback: EventListenerOrEventListenerObject | null, | ||
options?: EventListenerOptions | boolean, | ||
): void; | ||
removeEventListener(): void {} | ||
}; | ||
return EventTarget as unknown as typeof klass; | ||
}; | ||
|
||
export const TypedEventTarget = buildTypedEventTargetType(); | ||
|
||
export interface TypedEventTarget<EventMap> { | ||
addEventListener<K extends keyof EventMap>( | ||
type: K, | ||
listener: (this: HTMLElement, ev: EventMap[K]) => any, | ||
options?: boolean | AddEventListenerOptions, | ||
): void; | ||
addEventListener( | ||
type: string, | ||
callback: EventListenerOrEventListenerObject | null, | ||
options?: AddEventListenerOptions | boolean, | ||
): void; | ||
dispatchEvent(event: EventMap[keyof EventMap]): boolean; | ||
dispatchEvent(event: Event): boolean; | ||
removeEventListener<K extends keyof EventMap>( | ||
type: K, | ||
listener: (this: HTMLElement, ev: EventMap[K]) => any, | ||
options?: boolean | EventListenerOptions, | ||
): void; | ||
removeEventListener( | ||
type: string, | ||
callback: EventListenerOrEventListenerObject | null, | ||
options?: EventListenerOptions | boolean, | ||
): void; | ||
} |
Oops, something went wrong.