モバイルメニュー
This commit is contained in:
parent
2301fe5eff
commit
28c2395a2c
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
60
src/frontend/components/NavigationMenu.tsx
Normal file
60
src/frontend/components/NavigationMenu.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -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')}
|
<div>
|
||||||
<a href="https://crowdin.com/project/misskey-tools" target="_blank" rel="noopener noreferrer">{t('helpTranslation')}</a>
|
{t('translatedByTheCommunity')}
|
||||||
|
<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">
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user