diff --git a/src/assets/css/components/common/sidebar.scss b/src/assets/css/components/common/sidebar.scss new file mode 100644 index 0000000..b94d972 --- /dev/null +++ b/src/assets/css/components/common/sidebar.scss @@ -0,0 +1,301 @@ +@use "@/assets/css/constants" as constants; +@use "@/assets/css/mixins" as mixins; + +.sidebar { + display: flex; + flex-direction: column; + height: 100%; + user-select: none; + transition: all .3s; + white-space: nowrap; + + .title { + display: flex; + align-items: center; + font-weight: bold; + padding: 10px 14px; + color: constants.$main-color; + overflow: hidden; + + .icon-box { + display: flex; + justify-content: center; + align-items: center; + padding: 10px; + width: 40px; + height: 40px; + font-size: constants.$SIZE_ICON_SM; + border-radius: 8px; + cursor: pointer; + span { + transform: rotateZ(180deg); + transition: all .3s; + } + + &:hover { + background-color: constants.$background-color; + } + } + + .text { + flex: 1; + font-size: 2em; + text-align: center; + letter-spacing: 0.2em; + transform: translateX(0.1em); + } + } + + .content { + display: flex; + min-height: 0; + flex-direction: column; + flex: 1; + + .scroll { + min-height: 0; + flex: 1; + } + + ul { + > li { + &.item { + position: relative; + margin: 4px 14px; + font-size: 1.4em; + + >.menu-bt { + border-radius: 8px; + overflow: hidden; + height: 40px; + + .icon-box { + display: flex; + justify-content: center; + align-items: center; + padding: 0 10px; + width: 40px; + height: 40px; + font-size: constants.$SIZE_ICON_SM; + cursor: pointer; + } + + a { + display: flex; + align-items: center; + height: 100%; + width: 100%; + transition: all 0.2s; + + .text { + flex: 1; + padding-left: 8px; + } + + &.active { + color: constants.$origin-color; + background-color: constants.$main-color !important; + } + } + } + + .submenu { + display: none; + position: fixed; + padding-left: 20px; + z-index: 10000; + + .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; + } + } + } + } + + &:hover { + >.menu-bt { + a { + background-color: constants.$background-color; + } + } + + .submenu { + display: block; + animation: 0.3s ease; + @include mixins.unique-keyframes { + 0% { + transform: translateX(-10px); + opacity: 0; + } + 100% { + transform: translateX(0); + opacity: 1; + } + } + } + } + } + } + } + } + + .separate { + height: 0; + margin: 10px 5px; + border: { + width: 1px; + color: constants.$font-secondary-color; + style: solid; + }; + opacity: 0.4; + } + + .footer { + display: flex; + align-items: center; + font-weight: bold; + padding: 8px 14px; + color: constants.$main-color; + + .icon-user { + display: flex; + justify-content: center; + align-items: center; + margin-left: 4px; + padding: 10px; + width: 36px; + height: 36px; + font-size: constants.$SIZE_ICON_XS; + border: 2px constants.$font-secondary-color solid; + color: constants.$font-secondary-color; + border-radius: 50%; + cursor: pointer; + } + + .text { + flex: 1; + padding-left: 10px; + font-size: 1.4em; + color: constants.$font-main-color; + user-select: text; + + a{ + color: constants.$main-color; + text-decoration: underline; + } + } + + .icon-exit { + font-size: constants.$SIZE_ICON_XS; + color: constants.$error-color; + padding: 6px 10px; + cursor: pointer; + + &:hover { + border-radius: 8px; + background-color: constants.$background-color; + } + } + } + + &.hide { + width: 68px !important; + + .title { + .icon-box { + span { + transform: rotateZ(360deg); + transition: all .3s; + } + } + .text { + display: none; + } + } + + .menu-bt { + .text { + display: none; + } + } + + .submenu { + .menu-bt { + .text { + display: block; + } + } + } + + .footer { + position: relative; + .text { + display: none; + } + + .submenu-exit { + display: none; + position: absolute; + padding-left: 6px; + left: 100%; + + .content { + padding: 8px; + border-radius: 8px; + background-color: constants.$origin-color; + + .icon-exit { + padding: 4px 8px; + &:hover { + border-radius: 8px; + background-color: constants.$background-color; + } + } + } + + &.hide { + display: none!important; + } + } + + &:hover .submenu-exit { + display: block; + animation: 0.3s ease; + @include mixins.unique-keyframes { + 0% { + transform: translateX(-10px); + opacity: 0; + } + 100% { + transform: translateX(0); + opacity: 1; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/assets/css/pages/tools-framework.scss b/src/assets/css/pages/tools-framework.scss index f9e08cf..9906a03 100644 --- a/src/assets/css/pages/tools-framework.scss +++ b/src/assets/css/pages/tools-framework.scss @@ -6,295 +6,7 @@ body { } .left-panel { - display: flex; - flex-direction: column; - width: clamp(180px, 20vw, 240px); background-color: constants.$origin-color; - user-select: none; - transition: all .3s; - white-space: nowrap; - - .title { - display: flex; - align-items: center; - font-weight: bold; - padding: 10px 14px; - color: constants.$main-color; - overflow: hidden; - - .icon-box { - display: flex; - justify-content: center; - align-items: center; - padding: 10px; - width: 40px; - height: 40px; - font-size: constants.$SIZE_ICON_SM; - border-radius: 8px; - cursor: pointer; - span { - transform: rotateZ(180deg); - transition: all .3s; - } - - &:hover { - background-color: constants.$background-color; - } - } - - .text { - flex: 1; - font-size: 2em; - text-align: center; - letter-spacing: 0.6em; - transform: translateX(0.3em); - } - } - - .content { - display: flex; - min-height: 0; - flex-direction: column; - flex: 1; - - .toolsMenu { - min-height: 0; - flex: 1; - } - - ul { - > li { - &.item { - position: relative; - margin: 4px 14px; - font-size: 1.4em; - - .menu-bt { - border-radius: 8px; - overflow: hidden; - height: 40px; - - .icon-box { - display: flex; - justify-content: center; - align-items: center; - padding: 0 10px; - width: 40px; - height: 40px; - font-size: constants.$SIZE_ICON_SM; - cursor: pointer; - } - - a { - display: flex; - align-items: center; - height: 100%; - width: 100%; - transition: all 0.2s; - - .text { - flex: 1; - padding-left: 8px; - } - - &.active { - color: constants.$origin-color; - background-color: constants.$main-color !important; - } - } - } - - .submenu { - display: none; - position: fixed; - padding-left: 20px; - z-index: 10000; - - .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; - } - } - } - } - - &:hover { - .menu-bt { - a { - background-color: constants.$background-color; - } - } - - .submenu { - display: block; - animation: 0.3s ease; - @include mixins.unique-keyframes { - 0% { - transform: translateX(-10px); - opacity: 0; - } - 100% { - transform: translateX(0); - opacity: 1; - } - } - } - } - } - } - } - } - - .separate { - height: 0; - margin: 10px 5px; - border: { - width: 1px; - color: constants.$font-secondary-color; - style: solid; - }; - opacity: 0.4; - } - - .footer { - display: flex; - align-items: center; - font-weight: bold; - padding: 8px 14px; - color: constants.$main-color; - - .icon-user { - display: flex; - justify-content: center; - align-items: center; - margin-left: 4px; - padding: 10px; - width: 36px; - height: 36px; - font-size: constants.$SIZE_ICON_XS; - border: 2px constants.$font-secondary-color solid; - color: constants.$font-secondary-color; - border-radius: 50%; - cursor: pointer; - } - - .text { - flex: 1; - padding-left: 10px; - font-size: 1.4em; - color: constants.$font-main-color; - user-select: text; - - a{ - color: constants.$main-color; - text-decoration: underline; - } - } - - .icon-exit { - font-size: constants.$SIZE_ICON_XS; - color: constants.$error-color; - padding: 6px 10px; - cursor: pointer; - - &:hover { - border-radius: 8px; - background-color: constants.$background-color; - } - } - } - - &.hide { - width: 68px; - - .title { - .icon-box { - span { - transform: rotateZ(360deg); - transition: all .3s; - } - } - .text { - display: none; - } - } - - .menu-bt { - .text { - display: none; - } - } - - .footer { - position: relative; - .text { - display: none; - } - - .submenu-exit { - display: none; - position: absolute; - padding-left: 6px; - left: 100%; - - .content { - padding: 8px; - border-radius: 8px; - background-color: constants.$origin-color; - - .icon-exit { - padding: 4px 8px; - &:hover { - border-radius: 8px; - background-color: constants.$background-color; - } - } - } - - &.hide { - display: none!important; - } - } - - &:hover .submenu-exit { - display: block; - animation: 0.3s ease; - @include mixins.unique-keyframes { - 0% { - transform: translateX(-10px); - opacity: 0; - } - 100% { - transform: translateX(0); - opacity: 1; - } - } - } - } - } } .right-panel { diff --git a/src/components/common/sidebar/SidebarFooter.tsx b/src/components/common/sidebar/SidebarFooter.tsx new file mode 100644 index 0000000..55e1d24 --- /dev/null +++ b/src/components/common/sidebar/SidebarFooter.tsx @@ -0,0 +1,64 @@ +import React from 'react' +import Icon from '@ant-design/icons' +import { getLoginStatus, logout } from '@/utils/auth' +import { getRedirectUrl } from '@/utils/common' + +const SidebarFooter: React.FC = () => { + const matches = useMatches() + const lastMatch = matches.reduce((_, second) => second) + const location = useLocation() + const navigate = useNavigate() + const [exiting, setExiting] = useState(false) + const handleClickAvatar = () => { + if (getLoginStatus()) { + navigate('/user') + } else { + navigate(getRedirectUrl('/login', `${lastMatch.pathname}${location.search}`)) + } + } + + const handleLogout = () => { + if (exiting) { + return + } + + setExiting(true) + void logout().finally(() => { + setTimeout(() => { + window.location.reload() + }, 1500) + }) + } + + return ( +