All files / collaborative-slate/src/sync toSlate.ts

91.66% Statements 77/84
83.33% Branches 50/60
100% Functions 3/3
96.92% Lines 63/65

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 1006x 6x 6x     3000x   6x 2227x 1381x 1381x 1882x 1882x 1882x       1882x 606x 606x 606x 606x 606x 5x 5x 5x     1882x 5x 5x       5x 5x 5x     1877x 1645x 1645x 1645x 601x 601x 601x 601x 601x 601x 601x 601x 585x   1645x         1381x 38x   1343x 1343x 1343x 309x           1381x       1381x 1381x 1381x   846x 846x 846x 1516x 846x 846x         846x       6x 711x 711x 711x 711x 711x    
import {Slice} from 'json-joy/lib/json-crdt-extensions/peritext/slice/Slice';
import {SliceStacking} from 'json-joy/lib/json-crdt-extensions/peritext/slice/constants';
import {type Block, type Inline, LeafBlock, type Peritext} from 'json-joy/lib/json-crdt-extensions/peritext';
import type {SlateDescendantNode, SlateDocument, SlateElementNode, SlateTextNode} from '../types';
 
const isText = (node: SlateDescendantNode): node is SlateTextNode => typeof (node as SlateTextNode).text === 'string';
 
const blockToSlateNode = (block: Block | LeafBlock): SlateElementNode => {
  if (block instanceof LeafBlock) {
    const children: SlateDescendantNode[] = [];
    for (let iterator = block.texts0(), inline: Inline | undefined; (inline = iterator()); ) {
      const text = inline.text();
      const attr = inline.attr();
      const attrKeys = Object.keys(attr);
 
      let atomicSlice: Slice | undefined;
      let atomicTag: string | undefined;
      for (const tag of attrKeys) {
        const stack = attr[tag];
        Iif (!stack || stack.length <= 0) continue;
        const slice = stack[0].slice;
        Iif (!(slice instanceof Slice)) continue;
        if (slice.stacking === SliceStacking.Atomic) {
          atomicSlice = slice;
          atomicTag = tag;
          break;
        }
      }
      if (atomicSlice && atomicTag !== undefined) {
        const data = atomicSlice.data();
        const inlineEl: SlateElementNode = {
          type: atomicTag,
          children: [{text: ''}],
        };
        Eif (data && typeof data === 'object' && !Array.isArray(data)) Object.assign(inlineEl, data);
        children.push(inlineEl);
        continue;
      }
 
      if (!text && attrKeys.length === 0) continue;
      const textNode: SlateTextNode = {text: text || ''};
      const length = attrKeys.length;
      ATTRS: for (let i = 0; i < length; i++) {
        const tag = attrKeys[i];
        const stack = attr[tag];
        Iif (!stack || stack.length <= 0) continue ATTRS;
        const slice = stack[0].slice;
        Iif (!(slice instanceof Slice)) continue ATTRS;
        Iif (slice.stacking === SliceStacking.Atomic) continue ATTRS;
        const data = slice.data();
        if (data && typeof data === 'object' && !Array.isArray(data)) Object.assign(textNode, {[tag]: data});
        else textNode[tag] = data !== undefined ? data : true;
      }
      children.push(textNode);
    }
 
    // Slate requires text nodes around inline elements; bracket the run
    // with empty text nodes when needed.
    if (children.length === 0) {
      children.push({text: ''});
    } else {
      if (!isText(children[0])) children.unshift({text: ''});
      if (!isText(children[children.length - 1])) children.push({text: ''});
      for (let i = 1; i < children.length; i++) {
        Iif (!isText(children[i]) && !isText(children[i - 1])) {
          children.splice(i, 0, {text: ''});
          i++;
        }
      }
    }
    const node: SlateElementNode = {
      type: block.tag() + '',
      children: children as any,
    };
    const attr = block.attr();
    Eif (typeof attr === 'object') Object.assign(node, attr);
    return node;
  } else {
    const children: SlateElementNode[] = [];
    const blockChildren = block.children;
    const length = blockChildren.length;
    for (let i = 0; i < length; i++) children.push(blockToSlateNode(blockChildren[i]));
    const attr = block.attr();
    const node: SlateElementNode = {
      ...(attr && typeof attr === 'object' ? attr : {}),
      type: block.tag() + '',
      children: children.length ? children : [{text: ''}],
    };
    return node;
  }
};
 
export const toSlate = (txt: Peritext): SlateDocument => {
  txt.refresh();
  const block = txt.blocks.root;
  const node = blockToSlateNode(block);
  const content: SlateDocument = (node?.children ?? []) as SlateDocument;
  return content;
};