All files / mutxt-react/src/MuTxt/behavior file.ts

83.6% Statements 51/61
61.29% Branches 19/31
92.3% Functions 12/13
91.48% Lines 43/47

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 761x 1x     1x 5x   2x         1x 2x         2x 2x     1x 2x 2x 2x   4x   2x     1x 2x 2x 2x 2x 2x 2x 2x     1x 1x 1x 1x 1x 1x     1x 2x 2x     1x 6x 9x 6x 1x     6x       6x 1x 1x 1x       6x    
import {Editor, Element as SlateElement, Node, Path, Transforms} from 'slate';
import {insertVoidBlock} from './voidInsert';
import type {CustomElement, CustomText, FileElement} from '../types';
 
export const isFileElement = (node: unknown): node is FileElement =>
  SlateElement.isElement(node) && (node as any).type === 'file';
 
const createParagraphElement = (): CustomElement => ({
  type: 'p',
  children: [{text: ''}] as CustomText[],
});
 
const createFileElement = (thingId: string, caption?: string): FileElement => {
  const el: FileElement = {
    type: 'file',
    '@thing': thingId,
    children: [{text: ''}] as CustomText[],
  };
  if (caption && caption.trim()) el.caption = caption.trim();
  return el;
};
 
const getActiveFileEntry = (editor: Editor): [FileElement, Path] | undefined => {
  const {selection} = editor;
  Iif (!selection) return;
  const match = Editor.above(editor, {
    at: Editor.unhangRange(editor, selection),
    match: (node) => isFileElement(node),
  });
  return (match as [FileElement, Path] | undefined) ?? void 0;
};
 
const insertParagraphNearActiveFile = (editor: Editor, position: 'above' | 'below' = 'below'): Path | null => {
  const entry = getActiveFileEntry(editor);
  Iif (!entry) return null;
  const [, path] = entry;
  const targetPath = position === 'above' ? path : Path.next(path);
  Transforms.insertNodes(editor, createParagraphElement(), {at: targetPath});
  Transforms.select(editor, Editor.start(editor, targetPath));
  return targetPath;
};
 
export const removeFileAtPath = (editor: Editor, path: Path): boolean => {
  Iif (!Node.has(editor, path)) return false;
  const node = Node.get(editor, path);
  Iif (!isFileElement(node)) return false;
  Transforms.removeNodes(editor, {at: path});
  return true;
};
 
export const insertFile = (editor: Editor, thingId: string, caption?: string): FileElement | null => {
  Iif (!thingId) return null;
  return insertVoidBlock(editor, createFileElement(thingId, caption));
};
 
export const withFile = <T extends Editor>(editor: T): T => {
  const {isVoid, insertBreak, insertSoftBreak, insertText} = editor;
  editor.isVoid = (element) => ((element as any).type === 'file' ? true : isVoid(element));
  editor.insertBreak = () => {
    Eif (insertParagraphNearActiveFile(editor, 'below')) return;
    insertBreak();
  };
  editor.insertSoftBreak = () => {
    if (insertParagraphNearActiveFile(editor, 'above')) return;
    insertSoftBreak();
  };
  editor.insertText = (text) => {
    Eif (text && insertParagraphNearActiveFile(editor, 'below')) {
      insertText(text);
      return;
    }
    insertText(text);
  };
  return editor;
};