Commit 8cf939c0 authored by XieZhiXiong's avatar XieZhiXiong

feat: 添加 AnchorPage 带锚点头部页面布局组件

parent e9600160
/*
* @Author: XieZhiXiong
* @Date: 2021-05-11 09:44:16
* @LastEditors: XieZhiXiong
* @LastEditTime: 2021-05-11 10:14:07
* @Description: 获取 DOM 节点的位置 或是 大小 hook
*/
import { useState, useCallback } from 'react';
interface RectData {
/**
* 元素高度
*/
height: number,
/**
* 元素宽度
*/
width: number,
/**
* 元素顶部距离视口的距离
*/
top: number,
/**
* 元素右侧距离视口的距离
*/
right: number,
/**
* 元素底部距离视口的距离
*/
bottom: number,
/**
* 元素左侧距离视口的距离
*/
left: number,
/**
* 元素距离左上角坐标 (0, 0)的 x 坐标值
*/
x: number,
/**
* 元素距离左上角坐标 (0, 0)的 y 坐标值
*/
y: number,
}
const useClientRect = (): [RectData, any] => {
const [rect, setRect] = useState({
height: 0,
width: 0,
top: 0,
right: 0,
bottom: 0,
left: 0,
x: 0,
y: 0,
});
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [rect, ref];
}
export default useClientRect;
@import '~antd/es/style/themes/default.less';
.anchor-page-header {
display: flex;
background: #FFFFFF;
&-left {
flex: 1;
overflow: hidden;
}
&-right {
padding-top: 14px;
flex-shrink: 0;
margin-left: 12px;
}
.anchor-page {
&-header {
background: #FFFFFF;
&-heading {
padding: 14px 0;
display: flex;
align-items: center;
&-title {
margin-bottom: 0;
color: rgba(0, 0, 0, 0.85);
font-weight: 600;
font-size: 18px;
line-height: 32px;
&-main {
padding: 0 16px;
display: flex;
}
&-left {
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&-right {
padding-top: 14px;
flex-shrink: 0;
margin-left: 12px;
}
&-heading {
padding: 14px 0 9px 0;
display: flex;
align-items: center;
&-title {
margin-bottom: 0;
color: rgba(0, 0, 0, 0.85);
font-weight: 600;
font-size: 18px;
line-height: 32px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&-back {
margin-right: 12px;
font-size: 16px;
cursor: pointer;
&:hover {
color: @primary-color;
}
}
&-anchors {
display: flex;
align-items: center;
}
:global {
.ant-anchor {
&-link {
padding: 0 16px;
font-size: 14px;
font-weight: 400;
color: @text-color;
&-back {
margin-right: 12px;
font-size: 16px;
cursor: pointer;
&-title {
padding: 16px 0;
border-bottom: 2px solid transparent;
}
&-active,
&:hover {
font-weight: 500;
color: @text-color;
&:hover {
color: @primary-color;
.ant-anchor-link-title {
border-bottom: 2px solid @primary-color;
}
}
}
&-ink {
display: none;
}
}
}
}
&-content {
padding: 16px;
}
}
\ No newline at end of file
......@@ -2,13 +2,14 @@
* @Author: XieZhiXiong
* @Date: 2021-05-10 11:36:58
* @LastEditors: XieZhiXiong
* @LastEditTime: 2021-05-10 15:05:20
* @LastEditTime: 2021-05-11 10:16:12
* @Description: 页面公用锚点头部
*/
import React from 'react';
import { Anchor, Button } from 'antd';
import React, { useState } from 'react';
import { Anchor } from 'antd';
import { ArrowLeftOutlined } from '@ant-design/icons';
import { history } from 'umi';
import useClientRect from '@/hooks/useClientRect';
import styles from './index.less';
export interface AnchorsItem {
......@@ -35,6 +36,12 @@ interface IProps {
* 锚点数据
*/
anchors: AnchorsItem[],
/**
* 自定义样式
*/
customStyle?: React.CSSProperties,
children?: React.ReactNode,
}
const AnchorPageHeader: React.FC<IProps> = (props: IProps) => {
......@@ -42,39 +49,90 @@ const AnchorPageHeader: React.FC<IProps> = (props: IProps) => {
title,
extra,
anchors,
customStyle,
children,
} = props;
const firstKey = anchors.length ? `#${anchors[0].key}` : '';
const [current, setCurrent] = useState(firstKey);
const [rect, measuredRef] = useClientRect();
const handleBack = () => {
history.goBack();
};
const handleAnchorChange = (currentActiveLink: string) => {
if (currentActiveLink) {
setCurrent('');
return;
}
if (!currentActiveLink) {
setCurrent(firstKey);
}
};
const handleAnchorClick = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
};
return (
<div className={styles['anchor-page-header']}>
<div className={styles['anchor-page-header-left']}>
<div className={styles['anchor-page-header-heading']}>
<ArrowLeftOutlined
className={styles['anchor-page-header-back']}
onClick={handleBack}
/>
<span className={styles['anchor-page-header-heading-title']}>
{title}
</span>
</div>
<div className={styles['anchor-page-header-content']}>
123
</div>
<div
className={styles['anchor-page']}
style={customStyle}
>
<div
className={styles['anchor-page-header']}
style={customStyle}
ref={measuredRef}
>
<Anchor
showInkInFixed={false}
targetOffset={rect.height}
onChange={handleAnchorChange}
onClick={handleAnchorClick}
>
<div className={styles['anchor-page-header-main']}>
<div className={styles['anchor-page-header-left']}>
<div className={styles['anchor-page-header-heading']}>
<ArrowLeftOutlined
className={styles['anchor-page-header-back']}
onClick={handleBack}
/>
<span className={styles['anchor-page-header-heading-title']}>
{title}
</span>
</div>
<div className={styles['anchor-page-header-content']}>
<div className={styles['anchor-page-header-anchors']}>
{anchors.map((item, index) => (
<Anchor.Link
className={current && index === 0 ? 'ant-anchor-link-active' : null}
key={item.key}
href={`#${item.key}`}
title={item.name}
/>
))}
</div>
</div>
</div>
{extra ? (
<div className={styles['anchor-page-header-right']}>
{extra}
</div>
) : null}
</div>
</Anchor>
</div>
<div className={styles['anchor-page-content']}>
{children}
</div>
{extra ? (
<div className={styles['anchor-page-header-right']}>
{extra}
</div>
) : null}
</div>
);
};
AnchorPageHeader.defaultProps = {
extra: null,
customStyle: {},
children: null,
};
export default AnchorPageHeader;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment