@/utils#scrollBlockEleIntoView TypeScript Examples

The following examples show how to use @/utils#scrollBlockEleIntoView. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: useBlock.ts    From easy-email with MIT License 4 votes vote down vote up
export function useBlock() {
  const {
    formState: { values },
    formHelpers: { getState, change },
  } = useEditorContext();

  const { focusIdx, setFocusIdx } = useFocusIdx();

  const { autoComplete } = useEditorProps();

  const focusBlock = get(values, focusIdx) as IBlockData | null;

  const { redo, undo, redoable, undoable, reset } = useContext(RecordContext);

  const addBlock = useCallback(
    (params: {
      type: string;
      parentIdx: string;
      positionIndex?: number;
      payload?: any;
      canReplace?: boolean;
    }) => {
      const start = console.time();

      let { type, parentIdx, positionIndex, payload } = params;
      let nextFocusIdx: string;
      const values = cloneDeep(getState().values) as IEmailTemplate;
      const parent = get(values, parentIdx) as IBlockData | null;
      if (!parent) {
        console.error(`Invalid ${type} block`);
        return;
      }

      let child = createBlockDataByType(type, payload);

      if (typeof positionIndex === 'undefined') {
        positionIndex = parent.children.length;
      }
      nextFocusIdx = `${parentIdx}.children.[${positionIndex}]`;
      const block = BlockManager.getBlockByType(type);
      if (!block) {
        console.error(`Invalid ${type} block`);
        return;
      }
      const parentBlock = BlockManager.getBlockByType(parent.type)!;

      if (autoComplete) {
        const autoCompletePaths = BlockManager.getAutoCompletePath(
          type,
          parent.type
        );
        if (autoCompletePaths) {
          autoCompletePaths.forEach((item) => {
            child = createBlockDataByType(item, {
              children: [child],
            });
            nextFocusIdx += '.children.[0]';
          });
        }
      }

      // Replace
      if (params.canReplace) {
        const parentIndex = getIndexByIdx(parentIdx);
        const upParent = getParentByIdx(values, parentIdx);
        if (upParent) {
          upParent.children.splice(parentIndex, 1, child);

          return change(getParentIdx(parentIdx)!, { ...upParent });
        }
      }

      const fixedBlock = BlockManager.getBlockByType(child.type);
      if (!fixedBlock?.validParentType.includes(parent.type)) {
        console.error(
          `${block.type} cannot be used inside ${
            parentBlock.type
          }, only inside: ${block.validParentType.join(', ')}`
        );
        return;
      }

      parent.children.splice(positionIndex, 0, child);
      console.timeLog();
      change(parentIdx, parent); // listeners not notified
      setFocusIdx(nextFocusIdx);
      scrollBlockEleIntoView({
        idx: nextFocusIdx,
      });
      console.timeEnd();
    },
    [autoComplete, change, getState, setFocusIdx]
  );

  const moveBlock = useCallback(
    (sourceIdx: string, destinationIdx: string) => {
      if (sourceIdx === destinationIdx) return null;

      let nextFocusIdx: string;

      const values = cloneDeep(getState().values) as IEmailTemplate;
      const source = getValueByIdx(values, sourceIdx)!;
      const sourceParentIdx = getParentIdx(sourceIdx);
      const destinationParentIdx = getParentIdx(destinationIdx);
      if (!sourceParentIdx || !destinationParentIdx) return;
      const sourceParent = getValueByIdx(values, sourceParentIdx)!;
      const destinationParent = getValueByIdx(values, destinationParentIdx)!;

      const sourceIndex = getIndexByIdx(sourceIdx);
      let [removed] = sourceParent.children.splice(sourceIndex, 1);
      if (autoComplete) {
        const autoCompletePaths = BlockManager.getAutoCompletePath(
          source.type,
          destinationParent.type
        );
        if (autoCompletePaths) {
          autoCompletePaths.forEach((item) => {
            removed = createBlockDataByType(item, {
              children: [removed],
            });
            nextFocusIdx += '.children.[0]';
          });
        } else {
          console.error('Something when wrong');
        }
      }

      const positionIndex = getIndexByIdx(destinationIdx);
      if (sourceParent === destinationParent) {
        destinationParent.children.splice(positionIndex, 0, removed);

        nextFocusIdx =
          destinationParentIdx +
          `.children.[${destinationParent.children.findIndex(
            (item: IBlockData) => item === removed
          )}]`;
      } else {
        destinationParent.children.splice(positionIndex, 0, removed);
        nextFocusIdx = destinationIdx;
      }

      change(getPageIdx(), { ...values.content });

      setTimeout(() => {
        setFocusIdx(nextFocusIdx);
      }, 50);

      scrollBlockEleIntoView({
        idx: nextFocusIdx,
      });
    },
    [autoComplete, change, getState, setFocusIdx]
  );

  const copyBlock = useCallback(
    (idx: string) => {
      let nextFocusIdx: string;
      const values = cloneDeep(getState().values) as IEmailTemplate;

      const parentIdx = getParentIdx(idx);
      if (!parentIdx) return;
      const parent = get(values, getParentIdx(idx) || '') as IBlockData | null;
      if (!parent) {
        console.error('Invalid block');
        return;
      }
      const copyBlock = cloneDeep(get(values, idx));
      const index = getIndexByIdx(idx) + 1;

      parent.children.splice(index, 0, copyBlock);
      change(parentIdx, parent);
      nextFocusIdx = `${parentIdx}.children.[${index}]`;

      setFocusIdx(nextFocusIdx);
    },
    [change, getState, setFocusIdx]
  );

  const removeBlock = useCallback(
    (idx: string) => {
      let nextFocusIdx: string;
      const values = cloneDeep(getState().values) as IEmailTemplate;

      const block = getValueByIdx(values, idx);
      if (!block) {
        console.error('Invalid block');
        return;
      }
      const parentIdx = getParentIdx(idx);
      const parent = get(values, getParentIdx(idx) || '') as IBlockData | null;
      const blockIndex = getIndexByIdx(idx);
      if (!parentIdx || !parent) {
        if (block.type === BasicType.PAGE) {
          console.error('Page node can not remove');
          return;
        }
        console.error('Invalid block');
        return;
      }
      nextFocusIdx = parentIdx;

      parent.children.splice(blockIndex, 1);
      change(parentIdx, parent);
      setFocusIdx(nextFocusIdx);
    },
    [change, getState, setFocusIdx]
  );

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const setValueByIdx = useCallback(
    debounce(<T extends IBlockData>(idx: string, newVal: T) => {
      change(idx, {
        ...newVal,
      });
    }),
    [change]
  );

  const isExistBlock = useCallback(
    (idx: string) => {
      return Boolean(get(values, idx));
    },
    [values]
  );

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const setFocusBlock = useCallback(
    debounce((val) => {
      change(focusIdx, { ...val });
    }),
    [focusBlock, focusIdx, change]
  );

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const setFocusBlockValue = useCallback(
    debounce((val) => {
      if (!focusBlock) return;
      focusBlock.data.value = val;
      change(focusIdx, { ...focusBlock });
    }),
    [focusBlock, focusIdx]
  );

  return {
    values,
    change,
    focusBlock,
    setFocusBlock,
    setFocusBlockValue,
    setValueByIdx,
    addBlock,
    moveBlock,
    copyBlock,
    removeBlock,
    isExistBlock,
    redo,
    undo,
    reset,
    redoable,
    undoable,
  };
}