mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
site: improve overview page (#40126)
* site: imp * update fontsize * add scrollTo(0) * revert * add style return type * add resize Event when input onChange * docs: fix affix height and revert resize event * fix: justifycontent => justify-content * fix style Co-authored-by: afc163 <afc163@gmail.com>
This commit is contained in:
parent
267b40c93e
commit
1c8f6507ce
@ -1,8 +1,9 @@
|
||||
import React, { memo, useMemo, useState } from 'react';
|
||||
import React, { memo, useMemo, useRef, useState } from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { Link, useIntl, useSidebarData, useLocation } from 'dumi';
|
||||
import { css } from '@emotion/react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { Card, Col, Divider, Input, Row, Space, Tag, Typography } from 'antd';
|
||||
import { Card, Col, Divider, Input, Row, Space, Tag, Typography, Affix } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import type { Component } from './ProComponentsList';
|
||||
import proComponentsList from './ProComponentsList';
|
||||
@ -10,7 +11,6 @@ import useSiteToken from '../../../hooks/useSiteToken';
|
||||
|
||||
const useStyle = () => {
|
||||
const { token } = useSiteToken();
|
||||
|
||||
return {
|
||||
componentsOverviewGroupTitle: css`
|
||||
margin-bottom: 24px !important;
|
||||
@ -33,22 +33,25 @@ const useStyle = () => {
|
||||
box-shadow: 0 6px 16px -8px #00000014, 0 9px 28px #0000000d, 0 12px 48px 16px #00000008;
|
||||
}
|
||||
`,
|
||||
componentsOverviewAffix: css`
|
||||
display: flex;
|
||||
transition: all 0.3s;
|
||||
justify-content: space-between;
|
||||
`,
|
||||
componentsOverviewSearch: css`
|
||||
font-size: ${token.fontSizeXL}px;
|
||||
padding: 0;
|
||||
|
||||
.anticon-search {
|
||||
color: ${token.colorTextDisabled};
|
||||
}
|
||||
`,
|
||||
componentsOverviewContent: css`
|
||||
&:empty:after {
|
||||
content: 'Not Found';
|
||||
text-align: center;
|
||||
padding: 16px 0 40px;
|
||||
display: block;
|
||||
padding: 16px 0 40px;
|
||||
color: ${token.colorTextDisabled};
|
||||
text-align: center;
|
||||
border-bottom: 1px solid ${token.colorSplit};
|
||||
content: 'Not Found';
|
||||
}
|
||||
`,
|
||||
};
|
||||
@ -76,13 +79,27 @@ const { Title } = Typography;
|
||||
|
||||
const Overview: React.FC = () => {
|
||||
const style = useStyle();
|
||||
|
||||
const data = useSidebarData();
|
||||
const [searchBarAffixed, setSearchBarAffixed] = useState<boolean>(false);
|
||||
|
||||
const { token } = useSiteToken();
|
||||
const { borderRadius, colorBgContainer, fontSizeXL } = token;
|
||||
|
||||
const affixedStyle: CSSProperties = {
|
||||
boxShadow: 'rgba(50, 50, 93, 0.25) 0 6px 12px -2px, rgba(0, 0, 0, 0.3) 0 3px 7px -3px',
|
||||
padding: 8,
|
||||
margin: -8,
|
||||
borderRadius,
|
||||
backgroundColor: colorBgContainer,
|
||||
};
|
||||
|
||||
const { search: urlSearch } = useLocation();
|
||||
const { locale, formatMessage } = useIntl();
|
||||
|
||||
const [search, setSearch] = useState<string>('');
|
||||
|
||||
const sectionRef = React.useRef<HTMLElement>(null);
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
|
||||
const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
|
||||
if (event.keyCode === 13 && search.trim().length) {
|
||||
@ -114,23 +131,27 @@ const Overview: React.FC = () => {
|
||||
]),
|
||||
[data, locale],
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="markdown" ref={sectionRef}>
|
||||
<Divider />
|
||||
<Input
|
||||
value={search}
|
||||
placeholder={formatMessage({ id: 'app.components.overview.search' })}
|
||||
css={style.componentsOverviewSearch}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value);
|
||||
reportSearch(e.target.value);
|
||||
}}
|
||||
onKeyDown={onKeyDown}
|
||||
bordered={false}
|
||||
autoFocus
|
||||
suffix={<SearchOutlined />}
|
||||
/>
|
||||
<Affix offsetTop={24} onChange={setSearchBarAffixed}>
|
||||
<div css={style.componentsOverviewAffix} style={searchBarAffixed ? affixedStyle : {}}>
|
||||
<Input
|
||||
autoFocus
|
||||
value={search}
|
||||
placeholder={formatMessage({ id: 'app.components.overview.search' })}
|
||||
css={style.componentsOverviewSearch}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value);
|
||||
reportSearch(e.target.value);
|
||||
}}
|
||||
onKeyDown={onKeyDown}
|
||||
bordered={false}
|
||||
suffix={<SearchOutlined />}
|
||||
style={{ fontSize: searchBarAffixed ? fontSizeXL - 2 : fontSizeXL }}
|
||||
/>
|
||||
</div>
|
||||
</Affix>
|
||||
<Divider />
|
||||
<div css={style.componentsOverviewContent}>
|
||||
{groups
|
||||
|
@ -1,6 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import Icon, * as AntdIcons from '@ant-design/icons';
|
||||
import type { SegmentedProps } from 'antd';
|
||||
import type { IntlShape } from 'react-intl';
|
||||
import { Segmented, Input, Empty, Affix } from 'antd';
|
||||
import { css } from '@emotion/react';
|
||||
import { useIntl } from 'dumi';
|
||||
import debounce from 'lodash/debounce';
|
||||
import Category from './Category';
|
||||
@ -17,6 +21,32 @@ export enum ThemeType {
|
||||
|
||||
const allIcons: { [key: string]: any } = AntdIcons;
|
||||
|
||||
const useStyle = () => ({
|
||||
iconSearchAffix: css`
|
||||
display: flex;
|
||||
transition: all 0.3s;
|
||||
justify-content: space-between;
|
||||
`,
|
||||
});
|
||||
|
||||
const options = (intl: IntlShape): SegmentedProps['options'] => [
|
||||
{
|
||||
value: ThemeType.Outlined,
|
||||
icon: <Icon component={OutlinedIcon} />,
|
||||
label: intl.formatMessage({ id: 'app.docs.components.icon.outlined' }),
|
||||
},
|
||||
{
|
||||
value: ThemeType.Filled,
|
||||
icon: <Icon component={FilledIcon} />,
|
||||
label: intl.formatMessage({ id: 'app.docs.components.icon.filled' }),
|
||||
},
|
||||
{
|
||||
value: ThemeType.TwoTone,
|
||||
icon: <Icon component={TwoToneIcon} />,
|
||||
label: intl.formatMessage({ id: 'app.docs.components.icon.two-tone' }),
|
||||
},
|
||||
];
|
||||
|
||||
interface IconSearchState {
|
||||
theme: ThemeType;
|
||||
searchKey: string;
|
||||
@ -24,25 +54,23 @@ interface IconSearchState {
|
||||
|
||||
const IconSearch: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [displayState, setDisplayState] = React.useState<IconSearchState>({
|
||||
theme: ThemeType.Outlined,
|
||||
const { iconSearchAffix } = useStyle();
|
||||
const [displayState, setDisplayState] = useState<IconSearchState>({
|
||||
searchKey: '',
|
||||
theme: ThemeType.Outlined,
|
||||
});
|
||||
|
||||
const newIconNames: string[] = [];
|
||||
|
||||
const handleSearchIcon = React.useCallback(
|
||||
debounce((searchKey: string) => {
|
||||
setDisplayState((prevState) => ({ ...prevState, searchKey }));
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const handleSearchIcon = debounce((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setDisplayState((prevState) => ({ ...prevState, searchKey: e.target.value }));
|
||||
}, 300);
|
||||
|
||||
const handleChangeTheme = React.useCallback((value) => {
|
||||
const handleChangeTheme = useCallback((value) => {
|
||||
setDisplayState((prevState) => ({ ...prevState, theme: value as ThemeType }));
|
||||
}, []);
|
||||
|
||||
const renderCategories = React.useMemo<React.ReactNode | React.ReactNode[]>(() => {
|
||||
const renderCategories = useMemo<React.ReactNode | React.ReactNode[]>(() => {
|
||||
const { searchKey = '', theme } = displayState;
|
||||
|
||||
const categoriesResult = Object.keys(categories)
|
||||
@ -77,62 +105,38 @@ const IconSearch: React.FC = () => {
|
||||
newIcons={newIconNames}
|
||||
/>
|
||||
));
|
||||
return categoriesResult.length === 0 ? <Empty style={{ margin: '2em 0' }} /> : categoriesResult;
|
||||
return categoriesResult.length ? categoriesResult : <Empty style={{ margin: '2em 0' }} />;
|
||||
}, [displayState.searchKey, displayState.theme]);
|
||||
|
||||
const [searchBarAffixed, setSearchBarAffixed] = React.useState(false);
|
||||
const [searchBarAffixed, setSearchBarAffixed] = useState<boolean>(false);
|
||||
const { token } = useSiteToken();
|
||||
const { borderRadius, colorBgContainer } = token;
|
||||
const affixedStyle = searchBarAffixed
|
||||
? {
|
||||
boxShadow: 'rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px',
|
||||
padding: 8,
|
||||
margin: -8,
|
||||
borderRadius,
|
||||
background: colorBgContainer,
|
||||
}
|
||||
: {};
|
||||
|
||||
const affixedStyle: CSSProperties = {
|
||||
boxShadow: 'rgba(50, 50, 93, 0.25) 0 6px 12px -2px, rgba(0, 0, 0, 0.3) 0 3px 7px -3px',
|
||||
padding: 8,
|
||||
margin: -8,
|
||||
borderRadius,
|
||||
backgroundColor: colorBgContainer,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="markdown">
|
||||
<Affix offsetTop={24} onChange={(affixed) => setSearchBarAffixed(affixed)}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
transition: 'all .3s',
|
||||
...affixedStyle,
|
||||
}}
|
||||
>
|
||||
<Affix offsetTop={24} onChange={setSearchBarAffixed}>
|
||||
<div css={iconSearchAffix} style={searchBarAffixed ? affixedStyle : {}}>
|
||||
<Segmented
|
||||
value={displayState.theme}
|
||||
onChange={handleChangeTheme}
|
||||
size="large"
|
||||
options={[
|
||||
{
|
||||
icon: <Icon component={OutlinedIcon} />,
|
||||
label: intl.formatMessage({ id: 'app.docs.components.icon.outlined' }),
|
||||
value: ThemeType.Outlined,
|
||||
},
|
||||
{
|
||||
icon: <Icon component={FilledIcon} />,
|
||||
label: intl.formatMessage({ id: 'app.docs.components.icon.filled' }),
|
||||
value: ThemeType.Filled,
|
||||
},
|
||||
{
|
||||
icon: <Icon component={TwoToneIcon} />,
|
||||
label: intl.formatMessage({ id: 'app.docs.components.icon.two-tone' }),
|
||||
value: ThemeType.TwoTone,
|
||||
},
|
||||
]}
|
||||
value={displayState.theme}
|
||||
options={options(intl)}
|
||||
onChange={handleChangeTheme}
|
||||
/>
|
||||
<Input.Search
|
||||
placeholder={intl.formatMessage({ id: 'app.docs.components.icon.search.placeholder' })}
|
||||
style={{ flex: 1, marginInlineStart: 16 }}
|
||||
allowClear
|
||||
onChange={(e) => handleSearchIcon(e.currentTarget.value)}
|
||||
size="large"
|
||||
autoFocus
|
||||
size="large"
|
||||
onChange={handleSearchIcon}
|
||||
/>
|
||||
</div>
|
||||
</Affix>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useSidebarData } from 'dumi';
|
||||
import { Affix, Col, ConfigProvider, Menu } from 'antd';
|
||||
import { Col, ConfigProvider, Menu } from 'antd';
|
||||
import MobileMenu from 'rc-drawer';
|
||||
import { css } from '@emotion/react';
|
||||
import SiteContext from '../SiteContext';
|
||||
@ -107,6 +107,9 @@ const useStyle = () => {
|
||||
z-index: 1;
|
||||
|
||||
.main-menu-inner {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
@ -115,11 +118,6 @@ const useStyle = () => {
|
||||
&:hover .main-menu-inner {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
> div,
|
||||
> div > div {
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
};
|
||||
};
|
||||
@ -153,11 +151,7 @@ const Sidebar: React.FC = () => {
|
||||
<MobileMenu key="Mobile-menu">{menuChild}</MobileMenu>
|
||||
) : (
|
||||
<Col xxl={4} xl={5} lg={6} md={6} sm={24} xs={24} css={styles.mainMenu}>
|
||||
<Affix>
|
||||
<section style={{ width: '100%' }} className="main-menu-inner">
|
||||
{menuChild}
|
||||
</section>
|
||||
</Affix>
|
||||
<section className="main-menu-inner">{menuChild}</section>
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
@ -7,8 +7,14 @@ export interface LoadingIconProps {
|
||||
existIcon: boolean;
|
||||
loading?: boolean | object;
|
||||
}
|
||||
const getCollapsedWidth = () => ({ width: 0, opacity: 0, transform: 'scale(0)' });
|
||||
const getRealWidth = (node: HTMLElement) => ({
|
||||
|
||||
const getCollapsedWidth = (): React.CSSProperties => ({
|
||||
width: 0,
|
||||
opacity: 0,
|
||||
transform: 'scale(0)',
|
||||
});
|
||||
|
||||
const getRealWidth = (node: HTMLElement): React.CSSProperties => ({
|
||||
width: node.scrollWidth,
|
||||
opacity: 1,
|
||||
transform: 'scale(1)',
|
||||
|
Loading…
Reference in New Issue
Block a user