モバイルメニュー

This commit is contained in:
Xeltica 2022-06-09 12:34:31 +09:00
parent 2301fe5eff
commit 28c2395a2c
4 changed files with 81 additions and 43 deletions

View File

@ -1,10 +1,13 @@
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
import { NavigationMenu } from './components/NavigationMenu';
import { useGetMetaQuery, useGetSessionQuery } from './services/session'; import { useGetMetaQuery, useGetSessionQuery } from './services/session';
import { useSelector } from './store'; import { useSelector } from './store';
import { setDrawerShown } from './store/slices/screen';
type IsMobileProp = { isMobile: boolean }; type IsMobileProp = { isMobile: boolean };
@ -43,16 +46,16 @@ const MobileHeader = styled.header`
export const GeneralLayout: React.FC = ({children}) => { export const GeneralLayout: React.FC = ({children}) => {
const { data: session } = useGetSessionQuery(undefined); const { data: session } = useGetSessionQuery(undefined);
const { data: meta } = useGetMetaQuery(undefined); const { data: meta } = useGetMetaQuery(undefined);
const { isMobile, title } = useSelector(state => state.screen); const { isMobile, title, isDrawerShown } = useSelector(state => state.screen);
const {t} = useTranslation(); const {t} = useTranslation();
const navLinkClassName = (isActive: boolean) => `item ${isActive ? 'active' : ''}`; const dispatch = useDispatch();
return ( return (
<Container isMobile={isMobile}> <Container isMobile={isMobile}>
{isMobile && ( {isMobile && (
<MobileHeader className="navbar hstack f-middle shadow-2 pl-2"> <MobileHeader className="navbar hstack f-middle shadow-2 pl-2">
<button className="btn flat"> <button className="btn flat" onClick={() => dispatch(setDrawerShown(true))}>
<i className="fas fa-bars"></i> <i className="fas fa-bars"></i>
</button> </button>
<h1>{t(title ?? 'title')}</h1> <h1>{t(title ?? 'title')}</h1>
@ -61,43 +64,7 @@ export const GeneralLayout: React.FC = ({children}) => {
<div> <div>
{!isMobile && ( {!isMobile && (
<Sidebar className="pa-2"> <Sidebar className="pa-2">
<h1 className="text-175 text-primary mb-2">{t('title')}</h1> <NavigationMenu />
<div className="menu">
<section>
<NavLink className={navLinkClassName} to="/" exact>
<i className="icon fas fa-home"></i>
{t('_sidebar.dashboard')}
</NavLink>
</section>
<section>
<h1>{t('_sidebar.tools')}</h1>
<NavLink className={navLinkClassName} to="/apps/miss-hai">
<i className="icon fas fa-tower-broadcast"></i>
{t('_sidebar.missHaiAlert')}
</NavLink>
<NavLink className={navLinkClassName} to="/apps/avatar-cropper">
<i className="icon fas fa-crop-simple"></i>
{t('_sidebar.cropper')}
</NavLink>
</section>
<section>
{session && <h1>{session.username}@{session.host}</h1>}
{session && (
<NavLink className={navLinkClassName} to="/account">
<i className="icon fas fa-circle-user"></i>
{t('_sidebar.accounts')}
</NavLink>
)}
<NavLink className={navLinkClassName} to="/settings">
<i className="icon fas fa-gear"></i>
{t('_sidebar.settings')}
</NavLink>
<NavLink className={navLinkClassName} to="/admin">
<i className="icon fas fa-lock"></i>
{t('_sidebar.admin')}
</NavLink>
</section>
</div>
</Sidebar> </Sidebar>
)} )}
<Main isMobile={isMobile}> <Main isMobile={isMobile}>
@ -113,6 +80,12 @@ export const GeneralLayout: React.FC = ({children}) => {
{children} {children}
</Main> </Main>
</div> </div>
<div className={`drawer-container ${isDrawerShown ? 'active' : ''}`}>
<div className="backdrop" onClick={() => dispatch(setDrawerShown(false))}></div>
<div className="drawer pa-2" onClick={e => e.stopPropagation()}>
<NavigationMenu />
</div>
</div>
</Container> </Container>
); );
}; };

View File

@ -0,0 +1,60 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { NavLink } from 'react-router-dom';
import { useGetSessionQuery } from '../services/session';
import { setDrawerShown } from '../store/slices/screen';
const navLinkClassName = (isActive: boolean) => `item ${isActive ? 'active' : ''}`;
export const NavigationMenu: React.VFC = () => {
const { data: session } = useGetSessionQuery(undefined);
const {t} = useTranslation();
const dispatch = useDispatch();
const onClickItem = () => {
dispatch(setDrawerShown(false));
};
return (
<>
<h1 className="text-175 text-dimmed mb-2">{t('title')}</h1>
<div className="menu">
<section>
<NavLink className={navLinkClassName} to="/" exact onClick={onClickItem}>
<i className="icon fas fa-home"></i>
{t('_sidebar.dashboard')}
</NavLink>
</section>
<section>
<h1>{t('_sidebar.tools')}</h1>
<NavLink className={navLinkClassName} to="/apps/miss-hai" onClick={onClickItem}>
<i className="icon fas fa-tower-broadcast"></i>
{t('_sidebar.missHaiAlert')}
</NavLink>
<NavLink className={navLinkClassName} to="/apps/avatar-cropper" onClick={onClickItem}>
<i className="icon fas fa-crop-simple"></i>
{t('_sidebar.cropper')}
</NavLink>
</section>
<section>
{session && <h1>{session.username}@{session.host}</h1>}
{session && (
<NavLink className={navLinkClassName} to="/account" onClick={onClickItem}>
<i className="icon fas fa-circle-user"></i>
{t('_sidebar.accounts')}
</NavLink>
)}
<NavLink className={navLinkClassName} to="/settings" onClick={onClickItem}>
<i className="icon fas fa-gear"></i>
{t('_sidebar.settings')}
</NavLink>
<NavLink className={navLinkClassName} to="/admin" onClick={onClickItem}>
<i className="icon fas fa-lock"></i>
{t('_sidebar.admin')}
</NavLink>
</section>
</div>
</>
);
};

View File

@ -119,8 +119,10 @@ export const SettingPage: React.VFC = () => {
</select> </select>
<div className="alert bg-info mt-2"> <div className="alert bg-info mt-2">
<i className="icon fas fa-language" /> <i className="icon fas fa-language" />
{t('translatedByTheCommunity')}&nbsp; <div>
<a href="https://crowdin.com/project/misskey-tools" target="_blank" rel="noopener noreferrer">{t('helpTranslation')}</a> {t('translatedByTheCommunity')}&nbsp;
<a href="https://crowdin.com/project/misskey-tools" target="_blank" rel="noopener noreferrer">{t('helpTranslation')}</a>
</div>
</div> </div>
</Card> </Card>
<div className="list-form"> <div className="list-form">

View File

@ -16,6 +16,7 @@ interface ScreenState {
accounts: IUser[]; accounts: IUser[];
accountTokens: string[]; accountTokens: string[];
isMobile: boolean; isMobile: boolean;
isDrawerShown: boolean;
} }
const initialState: ScreenState = { const initialState: ScreenState = {
@ -27,6 +28,7 @@ const initialState: ScreenState = {
accounts: [], accounts: [],
accountTokens: JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_ACCOUNTS) || '[]') as string[], accountTokens: JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_ACCOUNTS) || '[]') as string[],
isMobile: false, isMobile: false,
isDrawerShown: false,
}; };
/** /**
@ -64,9 +66,10 @@ export const screenSlice = createSlice({
}), }),
setMobile: generateSetter('isMobile'), setMobile: generateSetter('isMobile'),
setTitle: generateSetter('title'), setTitle: generateSetter('title'),
setDrawerShown: generateSetter('isDrawerShown'),
}, },
}); });
export const { showModal, hideModal, changeTheme, changeLang, setAccounts, setMobile, setTitle } = screenSlice.actions; export const { showModal, hideModal, changeTheme, changeLang, setAccounts, setMobile, setTitle, setDrawerShown } = screenSlice.actions;
export default screenSlice.reducer; export default screenSlice.reducer;