All files / src/shared/HFInputs/HFRichInput RichTextUtils.ts

11.53% Statements 6/52
8.33% Branches 1/12
6.66% Functions 1/15
12% Lines 6/50

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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180                        45x                                                                                       45x                                                                                           270x 270x 270x   270x                                                                                                                                                  
import { safeParse } from "@utils/index";
import {
  EditorState,
  Entity,
  convertToRaw,
  convertFromRaw,
  RawDraftContentState,
  RawDraftContentBlock,
  DraftInlineStyleType,
  SelectionState,
} from "draft-js";
 
export const convertEditorToADB = (editorState: EditorState) => {
  const contentState = editorState.getCurrentContent();
  const rawContent = convertToRaw(contentState);
 
  const adbContent = rawContent.blocks.map((block) => {
    const blockData = {
      type: block.type,
      text: block.text,
      styles: block.inlineStyleRanges.map((styleRange) => ({
        style: styleRange.style,
        offset: styleRange.offset,
        length: styleRange.length,
      })),
      entities: block.entityRanges.map((entityRange) => {
        const entity = Entity.get(entityRange.key.toString());
        const entityData = entity.getData();
 
        return {
          entityKey: entityRange.key.toString(),
          offset: entityRange.offset,
          length: entityRange.length,
          entityData,
        };
      }),
    };
 
    return blockData;
  });
 
  return adbContent;
};
 
type ADBContent = {
  type: string;
  text: string;
  styles: { style: string; offset: number; length: number }[];
  entities: {
    entityKey: string;
    offset: number;
    length: number;
    entityData: any;
  }[];
}[];
 
export const convertADBToEditorState = (
  adbContent: ADBContent,
): EditorState => {
  // Create content blocks with required properties (key and depth)
  const blocks: RawDraftContentBlock[] = adbContent.map((block, index) => {
    return {
      key: `block-${index}`, // Create a unique key for each block
      text: block.text,
      type: block.type,
      entityRanges: block.entities.map((entity) => ({
        key: parseInt(entity.entityKey, 10), // Convert the entityKey to a number
        offset: entity.offset,
        length: entity.length,
      })),
      inlineStyleRanges: block.styles.map((style) => ({
        style: style.style as DraftInlineStyleType, // Cast to DraftInlineStyleType
        offset: style.offset,
        length: style.length,
      })),
      data: {},
      depth: 0, // Set depth to 0 (unless dealing with nested content, like lists)
    };
  });
 
  // Prepare the raw content state to be converted into Draft.js format
  const rawContent: RawDraftContentState = {
    blocks,
    entityMap: {}, // You can populate the entityMap if you need to map entity data here
  };
 
  // Recreate the editor state from raw content
  const contentState = convertFromRaw(rawContent);
 
  // Set entity data back (if applicable)
  adbContent.forEach((block) => {
    block.entities.forEach((entity) => {
      const entityKey = parseInt(entity.entityKey, 10); // Convert entityKey to a number
      // Create or update the entity in the entity map
      Entity.create(entityKey.toString(), "MUTABLE", entity.entityData);
    });
  });
 
  return EditorState.createWithContent(contentState);
};
 
export function parseDescription(description: any) {
  try {
    const parsed = safeParse(description);
    return (parsed.blocks?.[0]?.text ?? description)?.replaceAll("<br>", "\n");
  } catch (e) {
    return description?.replaceAll("<br>", "\n");
  }
}
 
function isAnyTextSelected(editorState: EditorState) {
  const selection = editorState.getSelection();
  const anyTextSelected = !selection.isCollapsed();
 
  return anyTextSelected;
}
 
export function selectAllText(editorState: EditorState) {
  const anyTextSelected = isAnyTextSelected(editorState);
  // If there is any selection, we should skip selecting all text to update them
  if (anyTextSelected) {
    return editorState;
  }
 
  const contentState = editorState.getCurrentContent();
  const firstBlock = contentState.getFirstBlock();
  const lastBlock = contentState.getLastBlock();
 
  // Create a selection from the very start to the very end
  const entireSelection = new SelectionState({
    anchorKey: firstBlock.getKey(),
    anchorOffset: 0,
    focusKey: lastBlock.getKey(),
    focusOffset: lastBlock.getLength(),
  });
 
  // Force the editor to use that selection
  return EditorState.forceSelection(editorState, entireSelection);
}
 
function deselectAllText(editorState: EditorState) {
  const content = editorState.getCurrentContent();
  const lastBlock = content.getLastBlock();
 
  // Create a selection at the very end of the last block, with no range
  const collapsedSelection = new SelectionState({
    anchorKey: lastBlock.getKey(),
    anchorOffset: lastBlock.getLength(),
    focusKey: lastBlock.getKey(),
    focusOffset: lastBlock.getLength(),
    isBackward: false,
  });
 
  // Force the editor to use that collapsed selection
  return EditorState.forceSelection(editorState, collapsedSelection);
}
 
export function updateRichEditorText(
  editorState: EditorState,
  updateStyleCb: any,
  updateState?: (newState: EditorState) => void,
  isEmpty?: boolean,
) {
  if (isEmpty) {
    const newState = updateStyleCb(editorState);
    if (updateState) updateState(newState);
    return;
  }
 
  const selectedState = selectAllText(editorState);
 
  const newState = updateStyleCb(selectedState);
 
  const updatedState = isAnyTextSelected(editorState)
    ? newState
    : deselectAllText(newState);
 
  if (updateState) updateState(updatedState);
}