# 实现标签功能

Tapd的标签功能是通过往body上插入一个组件来实现的

image-20230901105303487

样式

.entity-card-tag-panel {
    cursor: default;
    position: fixed;
    width: 300px;
    z-index: 100;
    background-color: #fff;
    box-shadow: 0 4px 20px 0 rgba(24,43,80,.1);
    border: 1px solid #e8e9ec;
    border-radius: 4px;
    padding-bottom: 8px;
}
1
2
3
4
5
6
7
8
9
10
11

给标签那一列的单元格添加点击事件

const columns: ProColumns<any>[] = [
        {
            title: 'BAV应用名称',
            dataIndex: 'checkName',
            width: 150,
        },
        {
            title: '标签',
            dataIndex: 'tags',
            width: 150,
            onCell: () => ({
                onClick: (e: React.MouseEvent<HTMLTableCellElement>) => {
                    const rect = e.currentTarget.getBoundingClientRect();
                    setRect(rect);
                    setEditStatus(true);
                    e.stopPropagation() // 这里需要阻止事件冒泡
                },
            }),
        },
  ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

另外需要给document.body添加点击事件,点击单元格区域以外就隐藏输入框

useEffect(() => {
        setScrollY(getTableScrollY({ extraHeight: 70, ref: node }));
        const listener = () => setEditStatus(false);
        document.body.addEventListener('click', listener);
        return () => {
            document.body.removeEventListener('click', listener);
        };
    }, []);
1
2
3
4
5
6
7
8

需求:使用createProtal创建组件,当点击页面其他部分时,隐藏组件

interface ModalProps {
  show: boolean;
  onClose: (e: MouseEvent) => void;
}

const Modal: React.FC<ModalProps> = ({ show, onClose }) => {
  useEffect(() => {
    const handleClose = (e: MouseEvent) => {
      const el = (e.target as HTMLDivElement).closest('.modal');
      console.log(el);
      if (el === null) {
        onClose();
      }
    };
    window.addEventListener('click', handleClose);
    return () => {
      window.removeEventListener('click', handleClose);
    };
  }, [onClose]);

  if (!show) return null;

  return createPortal(
    <div className="modal">
      <div className="modal-content">
        <p>This is a modal</p>
      </div>
    </div>,
    document.body
  );
};
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

父组件部分

export default function App() {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const openModal = (e: React.MouseEvent) => {
    setIsModalOpen(true);
    e.stopPropagation();
  };

  const closeModal = () => setIsModalOpen(false);
  return (
    <div className="App">
      <button onClick={openModal}>Open Modal</button>
      {/* {createPortal(
        <Modal show={isModalOpen} onClose={closeModal} />,
        document.body
      )} */}
      {/* {<Modal show={isModalOpen} onClose={closeModal} />} */}
      <Modal show={isModalOpen} onClose={closeModal} />
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Elemnt.closet

这里用到了Element.closest() (opens new window)

该方法用来获取:匹配特定选择器且离当前元素最近的祖先元素(也可以是当前元素本身)。如果匹配不到,则返回 null。

CodeSandBox (opens new window)

image-20230905184815334