index.hooks.ts 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { useAppDispatch } from '$app/stores/store';
  2. import React, { useCallback, useEffect, useMemo } from 'react';
  3. import { slashCommandActions } from '$app_reducers/document/slice';
  4. import { useSubscribeNode } from '$app/components/document/_shared/SubscribeNode.hooks';
  5. import { Op } from 'quill-delta';
  6. import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks';
  7. import { useSubscribeSlashState } from '$app/components/document/_shared/SubscribeSlash.hooks';
  8. export function useBlockSlash() {
  9. const dispatch = useAppDispatch();
  10. const { docId } = useSubscribeDocument();
  11. const { blockId, visible, slashText, hoverOption } = useSubscribeSlash();
  12. const [anchorPosition, setAnchorPosition] = React.useState<{
  13. top: number;
  14. left: number;
  15. }>();
  16. useEffect(() => {
  17. if (blockId && visible) {
  18. const blockEl = document.querySelector(`[data-block-id="${blockId}"]`) as HTMLElement;
  19. const el = blockEl.querySelector(`[role="textbox"]`) as HTMLElement;
  20. if (el) {
  21. const rect = el.getBoundingClientRect();
  22. setAnchorPosition({
  23. top: rect.top + rect.height,
  24. left: rect.left,
  25. });
  26. return;
  27. }
  28. }
  29. setAnchorPosition(undefined);
  30. }, [blockId, visible]);
  31. useEffect(() => {
  32. if (!slashText) {
  33. dispatch(slashCommandActions.closeSlashCommand(docId));
  34. }
  35. }, [dispatch, docId, slashText]);
  36. const searchText = useMemo(() => {
  37. if (!slashText) return '';
  38. if (slashText[0] !== '/') return slashText;
  39. return slashText.slice(1, slashText.length);
  40. }, [slashText]);
  41. const onClose = useCallback(() => {
  42. dispatch(slashCommandActions.closeSlashCommand(docId));
  43. }, [dispatch, docId]);
  44. const open = Boolean(anchorPosition);
  45. return {
  46. open,
  47. anchorPosition,
  48. onClose,
  49. blockId,
  50. searchText,
  51. hoverOption,
  52. };
  53. }
  54. export function useSubscribeSlash() {
  55. const slashCommandState = useSubscribeSlashState();
  56. const visible = slashCommandState.isSlashCommand;
  57. const blockId = slashCommandState.blockId;
  58. const { node } = useSubscribeNode(blockId || '');
  59. const slashText = useMemo(() => {
  60. if (!node) return '';
  61. const delta = node.data.delta || [];
  62. return delta
  63. .map((op: Op) => {
  64. if (typeof op.insert === 'string') {
  65. return op.insert;
  66. } else {
  67. return '';
  68. }
  69. })
  70. .join('');
  71. }, [node]);
  72. return {
  73. visible,
  74. blockId,
  75. slashText,
  76. hoverOption: slashCommandState.hoverOption,
  77. };
  78. }