menu moved to config and context
This commit is contained in:
1 parent
e8fb2e817b
commit
1dbbe04d59
20 files changed
+62
-249
No files matched your search
@@ -13,7 +13,6 @@ const Navigation = dynamic(() => import('../Navigation').then(mod => mod.Navigat
|
||||
})
|
||||
|
||||
export interface IAppProps {
|
||||
menu: any
|
||||
showAuthor: boolean
|
||||
wide?: boolean
|
||||
contentStyle?: React.CSSProperties
|
||||
@@ -27,9 +26,7 @@ export const App: React.FC<IAppProps> = ({ wide = false, ...props }) => {
|
||||
<Wrapper
|
||||
header={(
|
||||
<Header>
|
||||
<Navigation
|
||||
items={props.menu.items}
|
||||
/>
|
||||
<Navigation />
|
||||
</Header>
|
||||
)}
|
||||
footer={(
|
||||
|
||||
@@ -2,20 +2,21 @@ import './styles.css'
|
||||
|
||||
import cx from 'classnames'
|
||||
import { MenuItem } from './MenuItem'
|
||||
import { IMenu } from 'src/types'
|
||||
|
||||
export type MenuProps = {
|
||||
layout: 'desktop' | 'mobile'
|
||||
items: any[]
|
||||
items: IMenu[]
|
||||
}
|
||||
|
||||
export const Menu: React.FC<MenuProps> = props => (
|
||||
<menu className={cx('menu', props.layout)}>
|
||||
{props.items.map((item, index) => (
|
||||
<MenuItem key={index}
|
||||
href={item.url}
|
||||
href={item.href}
|
||||
layout={props.layout}
|
||||
>
|
||||
{item.text}
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</menu>
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import * as React from 'react'
|
||||
import { MenuToggle } from '../MenuToggle'
|
||||
import { useScreenType } from '../../hooks/useScreenType'
|
||||
import { useScreenType } from 'src/hooks/useScreenType'
|
||||
import { Menu } from '../Menu'
|
||||
import { Overlay } from '../Overlay'
|
||||
import { useState, useCallback, useContext } from 'react'
|
||||
import { ConfigContext } from 'src/context/ConfigContext'
|
||||
|
||||
export interface INavigationProps {
|
||||
items: any[]
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export const Navigation: React.FC<INavigationProps> = props => {
|
||||
const isMobile = useScreenType(['phone', 'tablet'])
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const onClick = React.useCallback(
|
||||
const [open, setOpen] = useState(false)
|
||||
const onClick = useCallback(
|
||||
() => {
|
||||
setOpen(!open)
|
||||
},
|
||||
[open]
|
||||
)
|
||||
const { menu } = useContext(ConfigContext)
|
||||
|
||||
return (
|
||||
<nav>
|
||||
<nav style={props.style}>
|
||||
<style jsx>{`
|
||||
nav {
|
||||
display: flex;
|
||||
@@ -44,7 +46,7 @@ export const Navigation: React.FC<INavigationProps> = props => {
|
||||
) : (
|
||||
<Menu
|
||||
layout={'desktop'}
|
||||
items={props.items}
|
||||
items={menu}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -58,7 +60,7 @@ export const Navigation: React.FC<INavigationProps> = props => {
|
||||
}}>
|
||||
<Menu
|
||||
layout={'mobile'}
|
||||
items={props.items}
|
||||
items={menu}
|
||||
/>
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
@@ -29,5 +29,43 @@ export default {
|
||||
value: 'https://www.instagram.com/hudozka',
|
||||
title: 'Инстаграм'
|
||||
}
|
||||
],
|
||||
menu: [
|
||||
{
|
||||
name: 'Школа',
|
||||
href: '/',
|
||||
},
|
||||
{
|
||||
name: 'Расписание',
|
||||
href: '/schedule',
|
||||
},
|
||||
{
|
||||
name: 'Поступление',
|
||||
href: '/join',
|
||||
},
|
||||
{
|
||||
name: 'Предметы',
|
||||
href: '/courses',
|
||||
},
|
||||
{
|
||||
name: 'Коллектив',
|
||||
href: '/collective',
|
||||
},
|
||||
{
|
||||
name: 'Галерея',
|
||||
href: '/gallery',
|
||||
},
|
||||
{
|
||||
name: '«Мы соседи»',
|
||||
href: '/neighbors',
|
||||
},
|
||||
{
|
||||
name: 'Документы',
|
||||
href: '/documents',
|
||||
},
|
||||
{
|
||||
name: 'Сведения об образовательной организации',
|
||||
href: '/info/',
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import url from 'url'
|
||||
import { getPathWithNoTrailingSlash } from '../lib/url'
|
||||
import { urlToPattern } from '../lib/url'
|
||||
import { isMatchPathPattern } from '../lib/url'
|
||||
import { isEqualPath } from '../lib/url'
|
||||
import { compose, any } from '../lib/common'
|
||||
|
||||
function isActive(path, menuItem) {
|
||||
return isEqualPath(path, menuItem.url, true)
|
||||
}
|
||||
|
||||
function isHighlighted(path, menuItem) {
|
||||
return any(flatMenuItem(menuItem)
|
||||
.map(i => isMatchPathPattern(
|
||||
compose(urlToPattern, getPathWithNoTrailingSlash)(i.url),
|
||||
path,
|
||||
true
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
function flatMenuItem(item) {
|
||||
const rootUrl = item.url
|
||||
const flatUrls = items => {
|
||||
return items.map(item => ({
|
||||
...item,
|
||||
url: url.resolve(rootUrl, item.url)
|
||||
}))
|
||||
}
|
||||
|
||||
return !item.items
|
||||
? [item]
|
||||
: item.items.reduce((acc, i) => [...acc, ...compose(flatUrls, flatMenuItem)(i)], [item])
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes plain menu model and sample path and defines is an item of menu should be active
|
||||
*
|
||||
* @param path
|
||||
* @param menu
|
||||
* @return {{items}}
|
||||
*/
|
||||
export function buildMenu(path, menu) {
|
||||
const items = menu.map(item => ({
|
||||
...item,
|
||||
active: isActive(path, item),
|
||||
highlighted: isHighlighted(path, item),
|
||||
}))
|
||||
return { items }
|
||||
}
|
||||
@@ -20,21 +20,6 @@ function isMatchPathPattern(pattern, path, ignoreTrailingSlash = false) {
|
||||
return pattern.test(test)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* /article/* -> ^\/article\/[/\w\d-_]+\/?$
|
||||
*
|
||||
* @param url
|
||||
* @return {RegExp}
|
||||
*/
|
||||
function urlToPattern(url) {
|
||||
const u = url.replace('/*', '[/\\w\\d-_]+')
|
||||
const pattern = `^${u}/?$`
|
||||
|
||||
return new RegExp(pattern)
|
||||
}
|
||||
|
||||
exports.getPathWithNoTrailingSlash = getPathWithNoTrailingSlash
|
||||
exports.isMatchPathPattern = isMatchPathPattern
|
||||
exports.urlToPattern = urlToPattern
|
||||
exports.isEqualPath = isEqualPath
|
||||
@@ -1,102 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
url: '/',
|
||||
text: 'Школа',
|
||||
color: 'blue',
|
||||
items: [
|
||||
{url: '/summer-school/2017', text: 'Летние каникулы 2017'},
|
||||
{url: '/articles/*', text: 'Статьи'},
|
||||
{url: '/article/*', text: ''},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Расписание',
|
||||
url: '/schedule/',
|
||||
color: 'green',
|
||||
items: [
|
||||
{url: '*', text: ''},
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
'text': 'Поступление',
|
||||
'url': '/join',
|
||||
'color': 'yellow'
|
||||
},
|
||||
{
|
||||
url: '/courses/',
|
||||
text: 'Предметы',
|
||||
color: 'magenta',
|
||||
items: [
|
||||
{url: 'ceramics', text: 'Керамика'},
|
||||
{url: 'batik', text: 'Живопись'},
|
||||
{url: 'easel-composition', text: 'Живопись'},
|
||||
{url: 'cg', text: 'Живопись'},
|
||||
{url: 'scale-modeling', text: 'Живопись'},
|
||||
{url: 'drawing', text: 'Живопись'},
|
||||
{url: 'costume-composition', text: 'Живопись'},
|
||||
{url: 'felting', text: 'Живопись'},
|
||||
{url: 'art-history', text: 'Живопись'},
|
||||
|
||||
{url: 'perishki', text: 'Живопись'},
|
||||
{url: 'matreshki', text: 'Живопись'},
|
||||
{url: 'listochki', text: 'Живопись'},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Коллектив',
|
||||
url: '/collective',
|
||||
color: 'pink',
|
||||
items: [
|
||||
{url: '/teacher/*', text: ''},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Галерея',
|
||||
url: '/gallery',
|
||||
color: 'red',
|
||||
items: [
|
||||
{url: '/album/*', text: ''},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '«Мы соседи»',
|
||||
url: '/neighbors',
|
||||
color: 'yellow',
|
||||
},
|
||||
{
|
||||
text: 'Документы',
|
||||
url: '/documents',
|
||||
color: 'gray',
|
||||
items: [
|
||||
{url: '/document/*', text: ''},
|
||||
]
|
||||
},
|
||||
{
|
||||
url: '/info/',
|
||||
text: 'Сведения об образовательной организации',
|
||||
color: 'orange',
|
||||
items: [
|
||||
{url: 'main', text: 'Основные сведения'},
|
||||
{url: 'structure', text: 'Структура и органы управления образовательной организацией'},
|
||||
{url: 'education', text: 'Образование'},
|
||||
{url: 'standards', text: 'Образовательные стандарты'},
|
||||
//{url: '/collective', text: 'Руководство. Педагогический (научно-педагогический) состав'},
|
||||
{url: 'support', text: 'Материально-техническое обеспечение образовательного процесса'},
|
||||
{url: 'grants', text: 'Стипендии и иные виды материальной поддержки'},
|
||||
{url: 'paid-services', text: 'Платные образовательные услуги'},
|
||||
{url: 'finances', text: 'Финансово-хозяйственная деятельность'},
|
||||
{url: 'vacancy', text: 'Вакантные места для приема'},
|
||||
|
||||
{url: 'tools', text: 'Список рекомендованных материалов'},
|
||||
{url: 'history', text: 'История школы'},
|
||||
|
||||
{url: '*', text: ''},
|
||||
]
|
||||
},
|
||||
{
|
||||
'text': 'Текущий контроль',
|
||||
'url': '/info/audit',
|
||||
'color': 'gray'
|
||||
}
|
||||
]
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
--biggest-font-size: 2.25rem;
|
||||
|
||||
/* MENU */
|
||||
--menu-colored-mark-thickness: 4px;
|
||||
--menu-mark-thickness: 4px;
|
||||
|
||||
/* SIZE */
|
||||
--mobile-width: 31.25em;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
export interface IMenu {
|
||||
name: string
|
||||
href: string
|
||||
active: boolean
|
||||
items?: IMenu[]
|
||||
}
|
||||
|
||||
export interface IDocument {
|
||||
category: string
|
||||
fileName: string
|
||||
|
||||
Reference in new issue
Block a user