UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

143 lines (142 loc) 5.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DomController = void 0; const tree_dump_1 = require("tree-dump"); const AvlMap_1 = require("sonic-forest/lib/avl/AvlMap"); const InputController_1 = require("./InputController"); const CursorController_1 = require("./CursorController"); const RichTextController_1 = require("./RichTextController"); const KeyController_1 = require("./KeyController"); const CompositionController_1 = require("./CompositionController"); const AnnalsController_1 = require("./annals/AnnalsController"); const constants_1 = require("../constants"); const constants_2 = require("../../../json-crdt-extensions/peritext/rga/constants"); const UiHandle_1 = require("../../events/defaults/ui/UiHandle"); const json_crdt_patch_1 = require("../../../json-crdt-patch"); class DomController { constructor(opts) { this.opts = opts; /** * Index of block HTML <div> elements keyed by the ID (timestamp) of the split * boundary that starts that block element. */ this.blocks = new AvlMap_1.AvlMap(json_crdt_patch_1.compare); /** * Index of inline HTML <span> elements keyed by the slice start {@link Point}. */ this.inlines = new AvlMap_1.AvlMap((a, b) => a.cmpSpatial(b)); const { source, events, log } = opts; const { txt } = events; this.txt = txt; const et = (this.et = opts.events.et); const keys = (this.keys = new KeyController_1.KeyController({ source })); const comp = (this.comp = new CompositionController_1.CompositionController({ et, source, txt })); this.input = new InputController_1.InputController({ et, source, txt, comp }); this.cursor = new CursorController_1.CursorController({ et, source, txt, keys }); this.richText = new RichTextController_1.RichTextController({ et, source, txt }); this.annals = new AnnalsController_1.AnnalsController({ et, txt, log }); const uiHandle = new UiHandle_1.UiHandle(txt, this); events.ui = uiHandle; events.undo = this.annals; } /** -------------------------------------------------- {@link UiLifeCycles} */ start() { const stopKeys = this.keys.start(); const stopComp = this.comp.start(); const stopInput = this.input.start(); const stopCursor = this.cursor.start(); const stopRichText = this.richText.start(); const stopAnnals = this.annals.start(); return () => { stopKeys(); stopComp(); stopInput(); stopCursor(); stopRichText(); stopAnnals(); }; } /** ------------------------------------------------- {@link PeritextUiApi} */ focus() { this.opts.source.focus(); } getSpans(blockInnerId) { let el; if (blockInnerId) { const txt = this.txt; const marker = txt.overlay.getOrNextLowerMarker(blockInnerId); const markerId = marker?.id ?? txt.str.id; el = this.blocks.get(markerId); } el ?? (el = this.opts.source); return el.querySelectorAll('.jsonjoy-peritext-inline'); } findSpanContaining(char) { const start = char.start; const overlayPoint = this.txt.overlay.getOrNextLower(start); if (overlayPoint) { const span = this.inlines.get(overlayPoint); if (span) { const inline = span[constants_1.ElementAttr.InlineOffset]; if (inline) { const contains = inline.contains(char); if (contains) return span; } } } const spans = this.getSpans(start); const length = spans.length; for (let i = 0; i < length; i++) { const span = spans[i]; const inline = span[constants_1.ElementAttr.InlineOffset]; if (inline) { const contains = inline.contains(char); if (contains) return span; } } return; } getCharRect(char) { const txt = this.opts.events.txt; const id = typeof char === 'number' ? txt.str.find(char) : char; if (!id) return; const start = txt.point(id, constants_2.Anchor.Before); const end = txt.point(id, constants_2.Anchor.After); const charRange = txt.range(start, end); const span = this.findSpanContaining(charRange); if (!span) return; const inline = span[constants_1.ElementAttr.InlineOffset]; if (!inline) return; const textNode = span.firstChild; if (!textNode) return; const range = document.createRange(); range.selectNode(textNode); const offset = Math.max(0, Math.min(textNode.length - 1, charRange.start.viewPos() - inline.start.viewPos())); range.setStart(textNode, offset); range.setEnd(textNode, offset + 1); const rects = range.getClientRects(); return rects[0]; } caretRect() { return document.getElementById(this.cursor.caretId)?.getBoundingClientRect?.(); } /** ----------------------------------------------------- {@link Printable} */ toString(tab) { return ('DOM' + (0, tree_dump_1.printTree)(tab, [ (tab) => 'blocks: ' + this.blocks.size(), (tab) => 'inlines: ' + this.inlines.size(), (tab) => this.cursor.toString(tab), (tab) => this.keys.toString(tab), (tab) => this.comp.toString(tab), (tab) => this.annals.toString(tab), ])); } } exports.DomController = DomController;