import { reduce, difference, size, map } from 'lodash-es'; import classNames from 'classnames'; import { Tooltip, Menu, Input } from 'antd'; import { useTranslation } from 'react-i18next'; import React, { useState, useEffect, ReactNode, useRef } from 'react'; import Icon, { createFromIconfontCN, SearchOutlined } from '@ant-design/icons'; import type { TreeItem } from './PlayGround'; import CollaspeAllSvg from '../images/collapse-all.svg'; import styles from './PlayGrounds.module.less'; // menu icon const MenuIcon = createFromIconfontCN({ scriptUrl: '//at.alicdn.com/t/font_470089_1lnym745udm.js', // generated by iconfont.cn }); const { SubMenu } = Menu; export interface PlayGroundItemProps { source: string; examples: PlayGroundItemProps[]; babeledSource: string; absolutePath?: string; relativePath?: string; screenshot?: string; recommended?: boolean; filename: string; title?: string; location?: Location; playground?: { container?: string; playgroundDidMount?: string; playgroundWillUnmount?: string; dependencies?: { [key: string]: string; }; devDependencies?: { [key: string]: string; }; htmlCodeTemplate?: string; }; } interface PlayGroundsProps { getPath: (currentExample: PlayGroundItemProps) => string; // 获得当前选中的示例 key 值 currentExample: PlayGroundItemProps; updateCurrentExample: (val: PlayGroundItemProps) => void; treeData: TreeItem[]; showExampleDemoTitle: boolean; } const PlayGrounds: React.FC = ({ getPath, currentExample, updateCurrentExample, treeData, showExampleDemoTitle, }) => { const { t } = useTranslation(); // 菜单栏展开keys const [openKeys, setOpenKeys] = useState([]); const menuRef = useRef(null); // 初始化点击进来的示例按钮a的dom const [aRef, setARef] = useState(); // input 搜索框的value const [searchValue, setSearchValue] = useState(''); // 查找符合条件的数据 从title和 searchValue 可以匹配 就返回 否自返回[] const findSearchTreeData = (data: TreeItem[]): TreeItem[] => reduce( data, (value: TreeItem[], item: TreeItem) => { if (item.title?.match(searchValue)) { return [...value, item]; } if (item.children) { const searchData = findSearchTreeData(item.children); return size(searchData) ? [...value, { ...item, children: searchData }] : value; } return value; }, [], ); // 获取最新的 TreeData 数据 const getTreeData = () => searchValue ? findSearchTreeData(treeData) : treeData; // 控制 菜单栏展开key 保证二级菜单唯一 const onOpenChange = (keys: any[]) => { let newKeys = keys; const diffKey = difference(keys, openKeys)[0]; if (diffKey && /^secondaryKey-/.test(diffKey)) { newKeys = [ ...newKeys.filter((key) => !/^secondaryKey-/.test(key)), diffKey, ]; } setOpenKeys(newKeys); }; // 获取默认展开的keys数组 传入treeData 和 底层的 key 返回符合条件的 keys const getDefaultOpenKeys = (data: TreeItem[], key: string): string[] => reduce( data, (value: any[], item: TreeItem) => { if (item.children) { const keys = getDefaultOpenKeys(item.children, key); return keys.length ? [...value, item.value, ...keys] : value; } return key === item.value ? [item.value] : value; }, [], ); // 初始化菜单栏展开keys useEffect(() => { const exampleKey = getPath(currentExample); setOpenKeys(getDefaultOpenKeys(getTreeData(), exampleKey)); }, [currentExample]); // 初始化滚动到中间 useEffect(() => { if (aRef) { aRef.scrollIntoView({ block: 'center', behavior: 'smooth', }); } }, [aRef]); // 获取搜索后的文本结构 左文本 + 搜索文本 + 右文本 const getSearchValueTitle = (title: string): ReactNode => searchValue && title.match(searchValue) ? ( <> {title.replace(new RegExp(`${searchValue}.*`), '')} {searchValue} {title.replace(new RegExp(`.*?${searchValue}`), '')} ) : ( title ); // 图例按钮 + img + tooltip文本 const example = (item: TreeItem) => ( {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} { if (dom && !aRef && item.value === getPath(currentExample)) { setARef(dom); } }} className={classNames(styles.card, { [styles.current]: currentExample && item.relativePath === currentExample.relativePath, })} >
{(showExampleDemoTitle || !item.screenshot) && (
{item.title}
)}
); // 导航栏 const getMenuSub = (data: TreeItem[]) => map(data, (item: TreeItem) => item.children ? ( {item.icon && ( )} {item.title && getSearchValueTitle(item.title)} } > {getMenuSub(item.children)} ) : ( { if (item.value?.match(window.location.pathname)) { window.history.replaceState({}, '', `${item.value}`); updateCurrentExample(item as any); } else { window.location.href = `${window.location.origin}${item.value}`; } }} > {example(item)} ), ); // 搜索栏 const searchSider = () => (
} value={searchValue} onChange={(e: any) => setSearchValue(e.target.value)} /> setOpenKeys([])} />
); return (
{searchSider()} {openKeys && ( {getMenuSub(getTreeData())} )}
); }; export default PlayGrounds;