component.tsx 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. import { NodeViewWrapper, NodeViewContent } from '@tiptap/react';
  2. import React, { useRef, useLayoutEffect, useState, useEffect } from 'react';
  3. import { strings } from '@douyinfe/semi-foundation/aiChatInput/constants';
  4. export default (props: any) => {
  5. const isEmpty = props.node.textContent === strings.ZERO_WIDTH_CHAR;
  6. const placeholder = props.node.attrs.placeholder || '';
  7. const placeholderRef = useRef<HTMLSpanElement>(null);
  8. const [placeholderWidth, setPlaceholderWidth] = useState<number | undefined>(
  9. undefined,
  10. );
  11. useLayoutEffect(() => {
  12. if (isEmpty && placeholderRef.current) {
  13. const timer = setTimeout(() => {
  14. setPlaceholderWidth(placeholderRef.current?.offsetWidth);
  15. });
  16. return () => {
  17. clearTimeout(timer);
  18. };
  19. }
  20. return undefined;
  21. }, [isEmpty, placeholder]);
  22. return (
  23. <NodeViewWrapper
  24. as="span"
  25. className="input-slot"
  26. style={{ minWidth: isEmpty && placeholderWidth ? `${placeholderWidth}px` : undefined }}
  27. >
  28. {isEmpty && (
  29. <span
  30. ref={placeholderRef}
  31. data-placeholder={true}
  32. // contentEditable 必须传入 boolean,strings 导致控制台报错
  33. // contentEditable' s value must be boolean,strings will cause warning
  34. contentEditable={false}
  35. className="input-slot-placeholder"
  36. >
  37. {placeholder}
  38. </span>
  39. )}
  40. <NodeViewContent as="div" className="content" />
  41. </NodeViewWrapper>
  42. );
  43. };