Add ToolsFramework #32

Merged
FatttSnake merged 26 commits from FatttSnake into dev 2023-10-14 18:36:50 +08:00
6 changed files with 367 additions and 211 deletions
Showing only changes of commit ac3c1dbc2c - Show all commits

View File

@@ -14,7 +14,6 @@
.hide-scrollbar-content { .hide-scrollbar-content {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
min-width: 900px;
} }
} }

View File

@@ -1,4 +1,5 @@
@use "@/assets/css/constants" as constants; @use "@/assets/css/constants" as constants;
@use "@/assets/css/mixins" as mixins;
body { body {
background-color: constants.$background-color; background-color: constants.$background-color;
@@ -21,23 +22,32 @@ body {
.content { .content {
> ul { > ul {
> li { > li {
margin: 4px 14px;
&.item { &.item {
position: relative;
margin: 4px 14px;
font-size: 1.4em; font-size: 1.4em;
.menu-bt {
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
.menu-bt { .icon-box {
cursor: pointer;
width: 26px;
height: 26px;
.icon { .icon {
margin-right: 16px; margin-right: 16px;
} }
}
a { a {
display: flex; display: flex;
padding: 8px 16px; padding: 8px 16px;
height: 100%; height: 100%;
width: 100%; width: 100%;
transition: all 0.2s;
.text { .text {
flex: 1; flex: 1;
@@ -50,54 +60,60 @@ body {
} }
} }
&:hover { .submenu {
display: none;
position: fixed;
padding-left: 20px;
left: 100%;
top: 0;
.content {
display: flex;
flex-direction: column;
gap: 2px;
padding: 10px 10px;
background-color: constants.$origin-color;
border-radius: 8px;
.item {
border-radius: 8px;
white-space: nowrap;
overflow: hidden;
a {
display: block;
padding: 8px 16px;
transition: all 0.2s;
&.active {
color: constants.$origin-color;
background-color: constants.$main-color !important;
}
}
&:hover a {
background-color: constants.$background-color; background-color: constants.$background-color;
} }
} }
&.multiple-item {
font-size: 1.4em;
border-radius: 8px;
overflow: hidden;
.menu-bt {
display: flex;
.icon-box {
cursor: pointer;
padding: 8px 16px;
.icon {
transition: all 0.3s ease;
transform: rotate(180deg);
} }
} }
a { &:hover {
display: flex; background-color: constants.$background-color;
padding: {
top: 8px;
bottom: 8px;
right: 16px;
};
height: 100%;
width: 100%;
.text {
flex: 1;
}
}
}
.submenu { .submenu {
display: block;
animation: 0.3s ease;
@include mixins.unique-keyframes {
0% {
display: block;
transform: translateX(-10px);
opacity: 0;
} }
100% {
&.show { display: block;
.menu-bt { transform: translateX(0);
.icon-box { opacity: 1;
.icon {
transform: rotate(360deg);
} }
} }
} }
@@ -106,7 +122,7 @@ body {
.separate { .separate {
height: 0; height: 0;
margin: 10px 0; margin: 10px 5px;
border: { border: {
width: 1px; width: 1px;
color: constants.$font-secondary-color; color: constants.$font-secondary-color;

View File

@@ -10,6 +10,7 @@ interface HideScrollbarProps
isHiddenVerticalScrollbarWhenFull?: boolean isHiddenVerticalScrollbarWhenFull?: boolean
isShowHorizontalScrollbar?: boolean isShowHorizontalScrollbar?: boolean
isHiddenHorizontalScrollbarWhenFull?: boolean isHiddenHorizontalScrollbarWhenFull?: boolean
minWidth?: string | number
} }
export interface HideScrollbarElement { export interface HideScrollbarElement {
@@ -138,7 +139,6 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((prop
const lastScrollbarClickPositionRef = useRef({ x: -1, y: -1 }) const lastScrollbarClickPositionRef = useRef({ x: -1, y: -1 })
const lastScrollbarTouchPositionRef = useRef({ x: -1, y: -1 }) const lastScrollbarTouchPositionRef = useRef({ x: -1, y: -1 })
const lastTouchPositionRef = useRef({ x: -1, y: -1 }) const lastTouchPositionRef = useRef({ x: -1, y: -1 })
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
const [verticalScrollbarLength, setVerticalScrollbarLength] = useState(100) const [verticalScrollbarLength, setVerticalScrollbarLength] = useState(100)
const [verticalScrollbarPosition, setVerticalScrollbarPosition] = useState(0) const [verticalScrollbarPosition, setVerticalScrollbarPosition] = useState(0)
const [verticalScrollbarOnClick, setVerticalScrollbarOnClick] = useState(false) const [verticalScrollbarOnClick, setVerticalScrollbarOnClick] = useState(false)
@@ -157,6 +157,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((prop
isHiddenVerticalScrollbarWhenFull, isHiddenVerticalScrollbarWhenFull,
isShowHorizontalScrollbar, isShowHorizontalScrollbar,
isHiddenHorizontalScrollbarWhenFull, isHiddenHorizontalScrollbarWhenFull,
minWidth,
..._props ..._props
} = props } = props
@@ -365,9 +366,6 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((prop
useEffect(() => { useEffect(() => {
const windowResizeListener = () => { const windowResizeListener = () => {
setVerticalScrollbarWidth(
(rootRef.current?.offsetWidth ?? 0) - (rootRef.current?.clientWidth ?? 0)
)
setHorizontalScrollbarWidth( setHorizontalScrollbarWidth(
(rootRef.current?.offsetHeight ?? 0) - (rootRef.current?.clientHeight ?? 0) (rootRef.current?.offsetHeight ?? 0) - (rootRef.current?.clientHeight ?? 0)
) )
@@ -457,7 +455,6 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((prop
className={'hide-scrollbar-selection'} className={'hide-scrollbar-selection'}
tabIndex={0} tabIndex={0}
style={{ style={{
width: `calc(100vw + ${verticalScrollbarWidth}px)`,
height: `calc(100vh + ${horizontalScrollbarWidth}px)`, height: `calc(100vh + ${horizontalScrollbarWidth}px)`,
touchAction: isPreventAnyScroll ? 'none' : '', touchAction: isPreventAnyScroll ? 'none' : '',
msTouchAction: isPreventAnyScroll ? 'none' : '' msTouchAction: isPreventAnyScroll ? 'none' : ''
@@ -469,7 +466,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>((prop
onTouchMove={isPreventAnyScroll ? handleDefaultTouchmove : undefined} onTouchMove={isPreventAnyScroll ? handleDefaultTouchmove : undefined}
onScroll={handleDefaultScroll} onScroll={handleDefaultScroll}
> >
<div className={'hide-scrollbar-content'} ref={contentRef}> <div className={'hide-scrollbar-content'} ref={contentRef} style={{ minWidth }}>
{props.children} {props.children}
</div> </div>
</div> </div>

View File

@@ -81,6 +81,7 @@ const HomeFramework: React.FC = () => {
ref={hideScrollbarRef} ref={hideScrollbarRef}
isPreventVerticalScroll={preventScroll} isPreventVerticalScroll={preventScroll}
isShowHorizontalScrollbar={true} isShowHorizontalScrollbar={true}
minWidth={'900px'}
> >
<div className={'body'}> <div className={'body'}>
<div> <div>

View File

@@ -3,41 +3,14 @@ import FitFullScreen from '@/components/common/FitFullScreen'
import '@/assets/css/pages/tools-framework.scss' import '@/assets/css/pages/tools-framework.scss'
import Icon from '@ant-design/icons' import Icon from '@ant-design/icons'
import { toolsJsonObjects } from '@/router/tools.tsx' import { toolsJsonObjects } from '@/router/tools.tsx'
import _ from 'lodash' import HideScrollbar from '@/components/common/HideScrollbar.tsx'
const ToolsFramework: React.FC = () => { const ToolsFramework: React.FC = () => {
const location = useLocation()
const [multipleMenuShown, setMultipleMenuShown] = useState(
toolsJsonObjects.map((value) => ({
id: value.id,
path: value.path,
shown: `${location.pathname}/`.startsWith(`/tools/${value.path}/`)
}))
)
useEffect(() => {
const temp = _.clone(multipleMenuShown)
temp.forEach((value) => {
value.shown = `${location.pathname}/`.startsWith(`/tools/${value.path}/`)
})
setMultipleMenuShown(temp)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location])
const switchSubmenu = (menuId: string) => {
return () => {
const temp = _.clone(multipleMenuShown)
const menu = temp.find(({ id }) => id === menuId)
menu && (menu.shown = !menu.shown)
setMultipleMenuShown(temp)
}
}
return ( return (
<> <>
<FitFullScreen className={'flex-horizontal'}> <FitFullScreen className={'flex-horizontal'}>
<div className={'left-panel'}> <div className={'left-panel'}>
<HideScrollbar>
<div className={'title'}></div> <div className={'title'}></div>
<div className={'content'}> <div className={'content'}>
<ul> <ul>
@@ -53,11 +26,15 @@ const ToolsFramework: React.FC = () => {
isPending ? 'pending' : isActive ? 'active' : '' isPending ? 'pending' : isActive ? 'active' : ''
} }
> >
<div className={'icon-box'}>
<Icon <Icon
className={'icon'} className={'icon'}
component={toolsJsonObjects[0].icon} component={toolsJsonObjects[0].icon}
/> />
<span className={'text'}>{toolsJsonObjects[0].name}</span> </div>
<span className={'text'}>
{toolsJsonObjects[0].name}
</span>
</NavLink> </NavLink>
</div> </div>
</li> </li>
@@ -69,11 +46,15 @@ const ToolsFramework: React.FC = () => {
isPending ? ' pending' : isActive ? ' active' : '' isPending ? ' pending' : isActive ? ' active' : ''
} }
> >
<div className={'icon-box'}>
<Icon <Icon
className={'icon'} className={'icon'}
component={toolsJsonObjects[1].icon} component={toolsJsonObjects[1].icon}
/> />
<span className={'text'}>{toolsJsonObjects[1].name}</span> </div>
<span className={'text'}>
{toolsJsonObjects[1].name}
</span>
</NavLink> </NavLink>
</div> </div>
</li> </li>
@@ -84,52 +65,38 @@ const ToolsFramework: React.FC = () => {
return tool.menu && return tool.menu &&
tool.id !== 'tools' && tool.id !== 'tools' &&
tool.id !== 'tools-all' ? ( tool.id !== 'tools-all' ? (
<li <li className={'item'} key={tool.id}>
className={
tool.children
? `multiple-item${
multipleMenuShown.find(
({ id }) => id === tool.id
)?.shown ?? false
? ' show'
: ''
}`
: 'item'
}
key={tool.id}
>
<div className={'menu-bt'}> <div className={'menu-bt'}>
{tool.children ? (
<div
className={'icon-box'}
onClick={switchSubmenu(tool.id)}
>
<Icon
className={'icon'}
component={IconFatwebDown}
/>
</div>
) : undefined}
<NavLink <NavLink
to={tool.path} to={tool.path}
className={({ isActive, isPending }) => className={({ isActive, isPending }) =>
isPending ? 'pending' : isActive ? 'active' : '' isPending
? 'pending'
: isActive
? 'active'
: ''
} }
> >
{tool.children ? undefined : tool.icon ? ( <div className={'icon-box'}>
{tool.icon ? (
<Icon <Icon
className={'icon'} className={'icon'}
component={tool.icon} component={tool.icon}
/> />
) : undefined} ) : undefined}
</div>
<span className={'text'}>{tool.name}</span> <span className={'text'}>{tool.name}</span>
</NavLink> </NavLink>
</div> </div>
{tool.children ? ( {tool.children ? (
<ul className={'submenu'}> <ul className={'submenu'}>
<div className={'content'}>
{tool.children.map((subTool) => { {tool.children.map((subTool) => {
return subTool.menu ? ( return subTool.menu ? (
<li className={'item'} key={subTool.id}> <li
className={'item'}
key={subTool.id}
>
<NavLink <NavLink
to={`${tool.path}/${subTool.path}`} to={`${tool.path}/${subTool.path}`}
className={({ className={({
@@ -143,12 +110,6 @@ const ToolsFramework: React.FC = () => {
: '' : ''
} }
> >
{subTool.icon ? (
<Icon
className={'icon'}
component={subTool.icon}
/>
) : undefined}
<span className={'text'}> <span className={'text'}>
{subTool.name} {subTool.name}
</span> </span>
@@ -156,6 +117,7 @@ const ToolsFramework: React.FC = () => {
</li> </li>
) : undefined ) : undefined
})} })}
</div>
</ul> </ul>
) : undefined} ) : undefined}
</li> </li>
@@ -163,6 +125,7 @@ const ToolsFramework: React.FC = () => {
})} })}
</ul> </ul>
</div> </div>
</HideScrollbar>
</div> </div>
<div className={'right-panel'}></div> <div className={'right-panel'}></div>
</FitFullScreen> </FitFullScreen>

View File

@@ -8,7 +8,6 @@ export const toolsJsonObjects: ToolsJsonObject[] = [
id: 'tools', id: 'tools',
component: React.lazy(() => import('@/pages/tools')), component: React.lazy(() => import('@/pages/tools')),
name: '主页', name: '主页',
icon: React.lazy(() => import('~icons/fatweb/logo.jsx')),
menu: true, menu: true,
auth: false auth: false
}, },
@@ -35,6 +34,7 @@ export const toolsJsonObjects: ToolsJsonObject[] = [
path: '1', path: '1',
id: '1', id: '1',
name: '翻译1', name: '翻译1',
icon: React.lazy(() => import('~icons/fatweb/logo.jsx')),
menu: true, menu: true,
auth: false auth: false
}, },
@@ -80,6 +80,186 @@ export const toolsJsonObjects: ToolsJsonObject[] = [
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')), icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true, menu: true,
auth: false auth: false
},
{
path: 'translation--1',
id: 'tools-translation--1',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--1',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--2',
id: 'tools-translation--2',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--2',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--3',
id: 'tools-translation--3',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--3',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--4',
id: 'tools-translation--4',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--4',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--5',
id: 'tools-translation--5',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--5',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--6',
id: 'tools-translation--6',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--6',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--7',
id: 'tools-translation--7',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--7',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--8',
id: 'tools-translation--8',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--8',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--9',
id: 'tools-translation--9',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--9',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--10',
id: 'tools-translation--10',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--10',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--1-',
id: 'tools-translation--1-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--1-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--2-',
id: 'tools-translation--2-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--2-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--3-',
id: 'tools-translation--3-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--3-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--4-',
id: 'tools-translation--4-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--4-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--5-',
id: 'tools-translation--5-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--5-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--6-',
id: 'tools-translation--6-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--6-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--7-',
id: 'tools-translation--7-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--7-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--8-',
id: 'tools-translation--8-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--8-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--9-',
id: 'tools-translation--9-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--9-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
},
{
path: 'translation--10-',
id: 'tools-translation--10-',
component: React.lazy(() => import('@/pages/tools/Translation')),
name: '翻译--10-',
icon: React.lazy(() => import('~icons/fatweb/jenkins.jsx')),
menu: true,
auth: false
} }
] ]