(feat): layout style modules

This commit is contained in:
Maksym Sadovnychyy 2022-05-01 20:45:08 +02:00
parent b4683bb70e
commit 138cd250dd
15 changed files with 229 additions and 297 deletions

View File

@ -36,7 +36,7 @@ const NestedRoutes = (routes: IRoute[], tag: string | undefined = undefined) =>
const App: FC = () => { const App: FC = () => {
const dispatch = useDispatch() const dispatch = useDispatch()
const { routes, serviceRoutes } = useSelector((state: IReduxState) => state.settings) const { routes, adminRoutes, serviceRoutes } = useSelector((state: IReduxState) => state.settings)
useEffect(() => { useEffect(() => {
dispatch(settingsActionCreators.requestSettings()) dispatch(settingsActionCreators.requestSettings())
@ -44,8 +44,9 @@ const App: FC = () => {
return <> return <>
<Routes> <Routes>
{NestedRoutes(routes, 'AdminLayout')} { NestedRoutes(routes, 'PublicLayout') }
{NestedRoutes(serviceRoutes)} { NestedRoutes(adminRoutes, 'AdminLayout') }
{ NestedRoutes(serviceRoutes) }
</Routes> </Routes>
</> </>
} }

View File

@ -1,16 +1,16 @@
import React, { FC, useState } from 'react' import React, { FC, useState } from 'react'
import { Button, Collapse, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { Button, Collapse, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap'
import { FeatherIcon } from '../../../components/FeatherIcons' import { FeatherIcon } from '../../../components/FeatherIcons'
import { IMenuItem, IReduxState } from '../../../interfaces' import { IMenuItem, IReduxState } from '../../../interfaces'
import { useSelector } from 'react-redux'
interface INavMenu { interface INavMenu {
toggleSidebar: () => void toggleSidebar: () => void
} }
const NavMenu : FC<INavMenu> = (props: INavMenu) => { const NavMenu : FC<INavMenu> = (props: INavMenu) => {
let { topMenu = [] } = useSelector((state: IReduxState) => state.settings) let { siteName, topMenu = [] } = useSelector((state: IReduxState) => state.settings)
const { toggleSidebar } = props const { toggleSidebar } = props
@ -24,31 +24,27 @@ const NavMenu : FC<INavMenu> = (props: INavMenu) => {
}) })
} }
return <Navbar className="navbar-expand-sm navbar-toggleable-sm fixed-top border-bottom box-shadow mb-3" light> return <header>
<Navbar className="navbar-expand-sm navbar-toggleable-sm fixed-top border-bottom box-shadow mb-3" light>
<Button color="light" onClick={toggleSidebar}> <Button color="light" onClick={toggleSidebar}>
<FeatherIcon icon="align-left" /> <FeatherIcon icon="align-left" />
</Button> </Button>
<NavbarBrand href="/">{siteName}</ NavbarBrand>
<NavbarBrand href="/">reactstrap</ NavbarBrand> <NavbarToggler onClick={toggle} className="mr-2"/>
<Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={state.isOpen} navbar>
<NavbarToggler onClick={toggle} className="mr-2"/> <ul className="navbar-nav flex-grow">
{topMenu.map((item: IMenuItem, index: number) => {
<Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={state.isOpen} navbar> return <NavItem key={index}>
<ul className="navbar-nav flex-grow"> <NavLink tag={Link} className="text-dark" to={item.target}>
{topMenu.map((item: IMenuItem, index: number) => { {item.icon ? <FeatherIcon icon={item.icon}/> : ''}
return <NavItem key={index}> {item.title}
<NavLink tag={Link} className="text-dark" to={item.target}> </NavLink>
{item.icon ? <FeatherIcon icon={item.icon}/> : ''} </NavItem>
{item.title} })}
</NavLink> </ul>
</NavItem> </Collapse>
})} </Navbar>
</ul> </header>
</Collapse>
</Navbar>
} }
export { export {

View File

@ -1,13 +1,11 @@
import React, { FC, useState } from 'react' import React, { FC, useState } from 'react'
import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import classNames from 'classnames' import classNames from 'classnames'
import { Collapse, Nav, NavItem, NavLink } from 'reactstrap' import { Collapse, Nav, NavItem, NavLink } from 'reactstrap'
import { Link } from 'react-router-dom'
import { FeatherIcon } from '../../../components/FeatherIcons' import { FeatherIcon } from '../../../components/FeatherIcons'
import { IMenuItem, IReduxState, ISubMenuItem } from '../../../interfaces' import { IMenuItem, IReduxState, ISubMenuItem } from '../../../interfaces'
import { useSelector } from 'react-redux' import style from './scss/style.module.scss'
interface ISubMenu { interface ISubMenu {
icon?: string, icon?: string,
@ -23,18 +21,18 @@ const SubMenu : FC<ISubMenu> = (props: ISubMenu) => {
return ( return (
<div> <div>
<NavItem onClick={toggle} className={classNames({ "menu-open": !collapsed })}> <NavItem onClick={toggle} className={classNames(style.navitem, !collapsed ? style.menuopen : '')}>
<NavLink className="dropdown-toggle"> <NavLink className={classNames("dropdown-toggle", `${style.navlink}`)}>
{icon ? <FeatherIcon icon={icon}/> : ''} {icon ? <FeatherIcon icon={icon}/> : ''}
<span className="link-title">{title}</span> <span className={style.linktitle}>{title}</span>
</NavLink> </NavLink>
</NavItem> </NavItem>
<Collapse isOpen={!collapsed} navbar className={classNames("items-menu", { "mb-1": !collapsed })}> <Collapse isOpen={!collapsed} navbar className={classNames(`${style.itemsmenu}`, { "mb-1": !collapsed })}>
{items.map((item: ISubMenuItem, index: number) => ( {items.map((item: ISubMenuItem, index: number) => (
<NavItem key={index} className="pl-4"> <NavItem key={index} className={classNames(style.navitem, "pl-4")}>
<NavLink tag={Link} to={item.target}> <NavLink tag={Link} to={item.target} className={style.navlink}>
{item.icon ? <FeatherIcon icon={item.icon}/> : ''} {item.icon ? <FeatherIcon icon={item.icon}/> : ''}
<span className="link-title">{item.title}</span> <span className={style.linktitle}>{item.title}</span>
</NavLink> </NavLink>
</NavItem> </NavItem>
))} ))}
@ -46,17 +44,17 @@ const SubMenu : FC<ISubMenu> = (props: ISubMenu) => {
const SideMenu : FC = () => { const SideMenu : FC = () => {
let { sideMenu = [] } = useSelector((state: IReduxState) => state.settings) let { sideMenu = [] } = useSelector((state: IReduxState) => state.settings)
return <div className="side-menu"> return <div className={style.sidemenu}>
<Nav vertical className="list-unstyled pb-3"> <Nav vertical className="list-unstyled pb-3">
{sideMenu.map((item: IMenuItem, index: number) => { {sideMenu.map((item: IMenuItem, index: number) => {
if(item.items) { if(item.items) {
return <SubMenu key={index} icon={item.icon} title={item.title} items={item.items} /> return <SubMenu key={index} icon={item.icon} title={item.title} items={item.items} />
} }
return <NavItem key={index}> return <NavItem key={index} className={style.navitem}>
<NavLink tag={Link} to={item.target}> <NavLink tag={Link} to={item.target} className={style.navlink}>
{item.icon ? <FeatherIcon icon={item.icon}/> : ''} {item.icon ? <FeatherIcon icon={item.icon}/> : ''}
<span className="link-title">{item.title}</span> <span className={style.linktitle}>{item.title}</span>
</NavLink> </NavLink>
</NavItem> </NavItem>
})} })}

View File

@ -0,0 +1,44 @@
.sidemenu {
.navitem {
cursor: pointer;
text-decoration: none;
transition: all 0.3s;
.navlink {
color: #fff;
&:hover {
color: #7386d5;
background: #fff;
}
.linktitle {
padding-left: 5px;
}
}
}
/* Collabsable menu nav item */
.menuopen {
background: #6d7fcc;
}
/* Collapsable menu items container */
.itemsmenu {
color: #fff;
background: #6d7fcc;
}
}
@media(max-width: 768px) {
.sidemenu {
.navitem {
.navlink {
.linktitle {
display: none;
}
}
}
}
}

View File

@ -7,12 +7,7 @@ import { SideMenu } from './SideMenu'
import { ILayout } from '../interfaces' import { ILayout } from '../interfaces'
import 'bootstrap/dist/css/bootstrap.min.css' import style from './scss/style.module.scss'
import './scss/style.scss'
const AdminLayout: FC<ILayout> = ({ children = null }) => { const AdminLayout: FC<ILayout> = ({ children = null }) => {
@ -21,11 +16,11 @@ const AdminLayout: FC<ILayout> = ({ children = null }) => {
return <> return <>
<NavMenu toggleSidebar={toggleSidebar} /> <NavMenu toggleSidebar={toggleSidebar} />
<div className="wrapper"> <div className={style.wrapper}>
<div className={classNames("sidebar", { "is-open": sidebarIsOpen })}> <div className={classNames(`${style.sidebar}`, sidebarIsOpen ? `${style.isopen}` : '')}>
<SideMenu /> <SideMenu />
</div> </div>
<Container fluid className={classNames("content", { "is-open": sidebarIsOpen }) }> <Container fluid className={classNames(`${style.content}`, sidebarIsOpen ? `${style.isopen}` : '') }>
{children} {children}
</Container> </Container>
</div> </div>

View File

@ -0,0 +1,55 @@
$background: #7386d5;
.wrapper {
padding-top: 57px;
.sidebar {
position: fixed;
background: $background;
min-width: 250px;
max-width: 250px;
height: 100%;
margin-left: -250px;
transition: all 0.5s;
&.isopen {
margin-left: 0;
transition: 0.5s;
}
}
.content {
transition: all 0.5s;
&.isopen {
padding-left: 255px;
}
}
}
@media(max-width: 768px) {
.wrapper {
padding-top: 51px;
.sidebar {
&.isopen {
min-width: 65px;
max-width: 65px;
margin-left: 0;
transition: all 0.5s, height 0s;
}
}
.content {
transition: all 0.5s;
&.isopen {
padding-left: 65px;
}
}
}
}

View File

@ -1,170 +0,0 @@
//colors
$color_science_blue_approx: #0366d6;
$color_cerise_approx: #e01a76;
$white: #fff;
$color_denim_approx: #1b6ec2;
$color_fun_blue_approx: #1861ac;
$black_5: rgba(0, 0, 0, .05);
html {
font-size: 14px;
}
a {
color: $color_science_blue_approx;
&.navbar-brand {
white-space: normal;
text-align: center;
//Instead of the line below you could use @include word-break($value)
word-break: break-all;
}
}
code {
color: $color_cerise_approx;
}
.btn-primary {
color: $white;
background-color: $color_denim_approx;
border-color: $color_fun_blue_approx;
}
.box-shadow {
//Instead of the line below you could use @include box-shadow($shadow-1, $shadow-2, $shadow-3, $shadow-4, $shadow-5, $shadow-6, $shadow-7, $shadow-8, $shadow-9, $shadow-10)
box-shadow: 0 .25rem .75rem $black_5;
}
@media(min-width: 768px) {
html {
font-size: 16px;
}
}
.wrapper {
padding-top: 57px;
.sidebar {
position: fixed;
background: #7386d5;
color: #fff;
min-width: 250px;
max-width: 250px;
height: 100%;
margin-left: -250px;
transition: all 0.5s;
&.is-open {
margin-left: 0;
transition: 0.5s;
}
a,
a:hover,
a:focus {
cursor: pointer;
color: inherit;
text-decoration: none;
transition: all 0.3s;
.link-title {
padding-left: 10px;
}
}
.side-menu {
.menu-title {
color: #fff;
padding: 10px;
}
.nav-item {
&:hover {
color: #7386d5;
background: #fff;
}
.dropdown-toggle {
&::after {
}
}
}
/* Collabsable menu nav item */
.menu-open {
background: #6d7fcc;
}
/* Collapsable menu items container */
.items-menu {
color: #fff;
background: #6d7fcc;
}
}
}
.content {
transition: all 0.5s;
&.is-open {
padding-left: 255px;
}
}
}
@media(max-width: 768px) {
.wrapper {
padding-top: 51px;
.sidebar {
&.is-open {
min-width: 60px;
max-width: 60px;
margin-left: 0;
transition: all 0.5s, height 0s;
}
.side-menu {
.menu-title {
display: none;
}
.nav-item {
.link-title {
display: none;
}
.dropdown-toggle {
&::after {
}
}
}
}
}
.content {
transition: all 0.5s;
&.is-open {
padding-left: 65px;
}
}
}
}

View File

@ -5,6 +5,9 @@ import { AdminLayout } from './admin'
import { ILayout } from './interfaces' import { ILayout } from './interfaces'
import 'bootstrap/dist/css/bootstrap.min.css'
import './scss/style.scss'
interface ILayouts { interface ILayouts {
[key: string]: React.FC<ILayout>; [key: string]: React.FC<ILayout>;
} }

View File

@ -1,8 +1,12 @@
import React, { useState } from 'react' import React, { FC, useState } from 'react'
import { Collapse, Container, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { Collapse, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap'
import { FeatherIcon } from '../../../components/FeatherIcons'
import { IMenuItem, IReduxState } from '../../../interfaces'
const NavMenu = () => { const NavMenu : FC = () => {
let { siteName, topMenu = [] } = useSelector((state: IReduxState) => state.settings)
const [state, hookState] = useState({ const [state, hookState] = useState({
isOpen: false isOpen: false
@ -16,20 +20,18 @@ const NavMenu = () => {
return <header> return <header>
<Navbar className="navbar-expand-sm navbar-toggleable-sm fixed-top border-bottom box-shadow mb-3" light> <Navbar className="navbar-expand-sm navbar-toggleable-sm fixed-top border-bottom box-shadow mb-3" light>
<NavbarBrand href="/">{siteName}</ NavbarBrand>
<NavbarBrand tag={Link} to="/">react-redux-template</NavbarBrand>
<NavbarToggler onClick={toggle} className="mr-2"/> <NavbarToggler onClick={toggle} className="mr-2"/>
<Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={state.isOpen} navbar> <Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={state.isOpen} navbar>
<ul className="navbar-nav flex-grow"> <ul className="navbar-nav flex-grow">
<NavItem> {topMenu.map((item: IMenuItem, index: number) => {
<NavLink tag={Link} className="text-dark" to="/">Home</NavLink> return <NavItem key={index}>
</NavItem> <NavLink tag={Link} className="text-dark" to={item.target}>
<NavItem> {item.icon ? <FeatherIcon icon={item.icon}/> : ''}
<NavLink tag={Link} className="text-dark" to="/counter">Counter</NavLink> {item.title}
</NavItem> </NavLink>
<NavItem> </NavItem>
<NavLink tag={Link} className="text-dark" to="/fetch-data">Fetch data</NavLink> })}
</NavItem>
</ul> </ul>
</Collapse> </Collapse>
</Navbar> </Navbar>

View File

@ -4,15 +4,16 @@ import { NavMenu } from './NavMenu'
import { ILayout } from '../interfaces' import { ILayout } from '../interfaces'
import 'bootstrap/dist/css/bootstrap.min.css' import s from './scss/style.module.scss'
import './scss/style.scss'
const PublicLayout: FC<ILayout> = ({ children = null }) => { const PublicLayout: FC<ILayout> = ({ children = null }) => {
return <> return <>
<NavMenu /> <NavMenu />
<Container> <div className={s.wrapper}>
{children} <Container fluid className={s.content}>
</Container> {children}
</Container>
</div>
</> </>
} }

View File

@ -0,0 +1,10 @@
.wrapper {
padding-top: 57px;
}
@media(max-width: 768px) {
.wrapper {
padding-top: 51px;
}
}

View File

@ -7,7 +7,7 @@ $color_fun_blue_approx: #1861ac;
$black_5: rgba(0, 0, 0, .05); $black_5: rgba(0, 0, 0, .05);
html { html {
font-size: 14px; font-size: 16px;
} }
a { a {
@ -35,8 +35,8 @@ code {
box-shadow: 0 .25rem .75rem $black_5; box-shadow: 0 .25rem .75rem $black_5;
} }
@media(min-width: 768px) { @media(max-width: 768px) {
html { html {
font-size: 16px; font-size: 14px;
} }
} }

View File

@ -0,0 +1,10 @@
import * as React from 'react'
const AdminHome = () => {
return <div>Admin Home</div>
}
export {
AdminHome
}

View File

@ -3,6 +3,9 @@ import React, { FC } from 'react'
import { Home } from './Home' import { Home } from './Home'
import { Counter } from './Counter' import { Counter } from './Counter'
import { FetchData } from './FetchData' import { FetchData } from './FetchData'
import { AdminHome } from './AdminHome'
import { Signin } from './Signin' import { Signin } from './Signin'
import { Signup } from './Signup' import { Signup } from './Signup'
@ -14,6 +17,9 @@ const pages: IPages = {
Home, Home,
Counter, Counter,
FetchData, FetchData,
AdminHome,
Signin, Signin,
Signup Signup
} }

View File

@ -3,7 +3,9 @@ import { AppThunkAction } from '../'
import { IMenuItem, IRoute } from '../../interfaces' import { IMenuItem, IRoute } from '../../interfaces'
export interface ISettingsState { export interface ISettingsState {
siteName: string,
routes: IRoute [], routes: IRoute [],
adminRoutes: IRoute [],
serviceRoutes: IRoute [], serviceRoutes: IRoute [],
sideMenu?: IMenuItem [], sideMenu?: IMenuItem [],
topMenu?: IMenuItem [], topMenu?: IMenuItem [],
@ -16,7 +18,9 @@ interface RequestSettingsAction {
interface ReceiveSettingsAction { interface ReceiveSettingsAction {
type: 'RECEIVE_SETTINGS', type: 'RECEIVE_SETTINGS',
siteName: string,
routes: IRoute [], routes: IRoute [],
adminRoutes: IRoute [],
serviceRoutes: IRoute [], serviceRoutes: IRoute [],
sideMenu?: IMenuItem [], sideMenu?: IMenuItem [],
topMenu?: IMenuItem [], topMenu?: IMenuItem [],
@ -31,6 +35,8 @@ export const actionCreators = {
const appState = getState() const appState = getState()
const siteName = "MAKS-IT"
const routes : IRoute[] = [ const routes : IRoute[] = [
{ path: "/", component: "Home" }, { path: "/", component: "Home" },
{ path: "/home", component: "Home" }, { path: "/home", component: "Home" },
@ -43,7 +49,10 @@ export const actionCreators = {
} }
] ]
}, },
]
const adminRoutes : IRoute [] = [
{ path: "/admin", component: "AdminHome" },
] ]
const serviceRoutes : IRoute[] = [ const serviceRoutes : IRoute[] = [
@ -53,33 +62,10 @@ export const actionCreators = {
const sideMenu : IMenuItem [] = [ const sideMenu : IMenuItem [] = [
{ {
icon: "activity", icon: "alert-triangle",
title: "Home", title: "Home",
items: [ target: "/admin"
{
icon: "activity",
title: "Home 1",
target: "/Home-1",
},
{
icon: "activity",
title: "Home 2",
target: "/Home-2",
},
{
icon: "activity",
title: "Home 3",
target: "/Home-3",
},
]
}, },
{
icon: "info",
title: "About",
target: "/about"
},
{ {
icon: "activity", icon: "activity",
title: "Page", title: "Page",
@ -96,17 +82,15 @@ export const actionCreators = {
}, },
] ]
}, },
{ {
icon: "alert-triangle", icon: "",
title: "Faq", title: "Counter",
target: "/faq" target: "/counter",
}, },
{ {
icon: "phone-call", icon: "",
title: "Contact", title: "Fetch data",
target: "/contact" target: "/fetch-data"
}, },
] ]
@ -116,16 +100,7 @@ export const actionCreators = {
title: "Home", title: "Home",
target: "/" target: "/"
}, },
{
icon: "",
title: "Counter",
target: "/counter",
},
{
icon: "",
title: "Fetch data",
target: "fetch-data"
},
{ {
icon: "", icon: "",
title: "Signin", title: "Signin",
@ -138,12 +113,14 @@ export const actionCreators = {
} }
] ]
dispatch({ type: 'RECEIVE_SETTINGS', routes, serviceRoutes, sideMenu, topMenu }) dispatch({ type: 'RECEIVE_SETTINGS', siteName, routes, adminRoutes, serviceRoutes, sideMenu, topMenu })
} }
} }
const unloadedState: ISettingsState = { const unloadedState: ISettingsState = {
siteName: "reactredux",
routes: [], routes: [],
adminRoutes: [],
serviceRoutes: [], serviceRoutes: [],
sideMenu: [], sideMenu: [],
topMenu: [], topMenu: [],
@ -159,7 +136,9 @@ export const reducer: Reducer<ISettingsState> = (state: ISettingsState | undefin
switch (action.type) { switch (action.type) {
case 'REQUEST_SETTINGS': case 'REQUEST_SETTINGS':
return { return {
siteName: state.siteName,
routes: state.routes, routes: state.routes,
adminRoutes: state.adminRoutes,
serviceRoutes: state.serviceRoutes, serviceRoutes: state.serviceRoutes,
sideMenu: state.sideMenu, sideMenu: state.sideMenu,
topMenu: state.topMenu, topMenu: state.topMenu,
@ -168,7 +147,9 @@ export const reducer: Reducer<ISettingsState> = (state: ISettingsState | undefin
case 'RECEIVE_SETTINGS': case 'RECEIVE_SETTINGS':
return { return {
siteName: action.siteName,
routes: action.routes, routes: action.routes,
adminRoutes: action.adminRoutes,
serviceRoutes: action.serviceRoutes, serviceRoutes: action.serviceRoutes,
sideMenu: action.sideMenu, sideMenu: action.sideMenu,
topMenu: action.topMenu, topMenu: action.topMenu,