Antd-Typography排版组件

364 阅读1分钟

1.何时使用

  • 当需要展示标题、段落、列表内容时使用,如文章/博客/日志的文本样式。
  • 当需要一列基于文本的基础操作时,如拷贝/省略/可编辑。

2.代码演示

ant-design.antgroup.com/components/…

3.源码学习

3.1 Typography

 const Typography = React.forwardRef<
  HTMLElement,
  InternalTypographyProps<keyof JSX.IntrinsicElements>
>((props, ref) => {
 const {
    prefixCls: customizePrefixCls,
    component: Component = 'article',   //属性是HTMLElement的要定个首字母大写
    className,
    rootClassName,
    setContentRef,
    children,
    direction: typographyDirection,
    style,
    ...restProps
  } = props;
  
  return wrapCSSVar(
    // @ts-expect-error: Expression produces a union type that is too complex to represent.
    <Component className={componentClassName} style={mergedStyle} ref={mergedRef} {...restProps}>
      {children}
    </Component>,
  );

3.2 Title

const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
  //属性要给予默认值
  const { level = 1, ...restProps } = props;
  let component: keyof JSX.IntrinsicElements;
  
  ...
  
  //严格判断输入的属性是否合法
  if (TITLE_ELE_LIST.includes(level)) {
    component = `h${level}`;
  } else {
    component = 'h1';
  }

  return <Base ref={ref} {...restProps} component={component} />;
});

3.3 Text

const Text: React.ForwardRefRenderFunction<HTMLSpanElement, TextProps> = (
  { ellipsis, ...restProps },
  ref,
) => {
  const mergedEllipsis = React.useMemo(() => {
    if (ellipsis && typeof ellipsis === 'object') {
      return omit(ellipsis as EllipsisConfig, ['expandable', 'rows']);
    }

    return ellipsis;
  }, [ellipsis]);
  ...

  return <Base ref={ref} {...restProps} ellipsis={mergedEllipsis} component="span" />;
};

3.4 Paragraph

const Paragraph = React.forwardRef<HTMLElement, ParagraphProps>((props, ref) => (
  <Base ref={ref} {...props} component="div" />
));

3.5 Link

const Link = React.forwardRef<HTMLElement, LinkProps>(({ ellipsis, rel, ...restProps }, ref) => {

  return <Base {...mergedProps} ref={ref} ellipsis={!!ellipsis} component="a" />;
});

3.6 Editable组件

  const inComposition = React.useRef(false);
  const lastKeyCode = React.useRef<number>();

  const [current, setCurrent] = React.useState(value);

  React.useEffect(() => {
   setCurrent(value);
  }, [value]);

  const onChange: React.ChangeEventHandler<HTMLTextAreaElement> = ({ target }) => {
   setCurrent(target.value.replace(/[\n\r]/g, ''));
  };

  const onCompositionStart = () => {
    inComposition.current = true;    //改变 `ref.current` 属性时,React 不会重新渲染组件。React 不知道它何时会发生改变,因为 ref 是一个普通的 JavaScript 对象
  };

  const onCompositionEnd = () => {
    inComposition.current = false;
  };
  
  const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = ({ keyCode }) => {
    // We don't record keyCode when IME is using
    if (inComposition.current) return;

    lastKeyCode.current = keyCode;  //存值,直到组件销毁才丢弃
  };
  
  const onKeyUp: React.KeyboardEventHandler<HTMLTextAreaElement> = ({
    keyCode,
    ctrlKey,
    altKey,
    metaKey,
    shiftKey,
  }) => {
    // Check if it's a real key
    if (
      lastKeyCode.current === keyCode &&
      !inComposition.current &&
      !ctrlKey &&
      !altKey &&
      !metaKey &&
      !shiftKey
    ) {
      if (keyCode === KeyCode.ENTER) {
        confirmChange();
        onEnd?.();
      } else if (keyCode === KeyCode.ESC) {
        onCancel();
      }
    }
  };
  
  const confirmChange = () => {
    onSave(current.trim());
  };
  
  const onBlur: React.FocusEventHandler<HTMLTextAreaElement> = () => {
    confirmChange();
  };
  
  return wrapCSSVar(
    <div className={textAreaClassName} style={style}>
      <TextArea
        ref={ref}
        maxLength={maxLength}
        value={current}
        onChange={onChange}
        onKeyDown={onKeyDown}
        onKeyUp={onKeyUp}
        onCompositionStart={onCompositionStart}
        onCompositionEnd={onCompositionEnd}
        onBlur={onBlur}
        aria-label={ariaLabel}
        rows={1}
        autoSize={autoSize}
      />
      {enterIcon !== null
        ? cloneElement(enterIcon, { className: `${prefixCls}-edit-content-confirm` })
        : null}
    </div>,
  );

3.7 定时器

  // Simple module exposing `copy` function that will try to use [execCommand] with fallback to IE-specific `clipboardData` interface and finally, resort to usual `prompt` with proper text content and message.
  // https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
  import copy from 'copy-to-clipboard';
  
  const copyIdRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
  const cleanCopyId = () => {
    if (copyIdRef.current) {
      clearTimeout(copyIdRef.current);
    }
  };

  const onCopyClick = (e?: React.MouseEvent<HTMLDivElement>) => {
    e?.preventDefault();  //阻止默认事件
    e?.stopPropagation();  //阻止冒泡

    copy(copyConfig.text || String(children) || '', copyOptions);

    setCopied(true);

    // Trigger tips update
    cleanCopyId();  //每次点击都要先清除掉之前的定时器会导致内存溢出,同时可以防抖
    copyIdRef.current = setTimeout(() => {
      setCopied(false);
    }, 3000);

    copyConfig.onCopy?.(e);
  };

  React.useEffect(() => cleanCopyId, []);  返回值会在unmount时调用,清除定时器
  

3.8 aria-hidden 属性

developer.mozilla.org/zh-CN/docs/…

aria-hidden是一个用于辅助功能(Accessibility)的HTML属性,用于指示元素是否对辅助技术(如屏幕阅读器)隐藏。

辅助功能是一种设计和开发网页的方法,旨在使网页对于残障人士和使用辅助技术的人更易访问。aria-hidden属性可以应用于任何HTML元素,用于指示该元素是否应该在辅助技术中隐藏或忽略。