feat: menubar
This commit is contained in:
parent
3bedccaa3a
commit
aedaed4df6
57
src/client/menubar.jsx
Normal file
57
src/client/menubar.jsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import {compose} from '@avocado/core';
|
||||||
|
import contempo from 'contempo';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const decorate = compose(
|
||||||
|
contempo(require('./menubar.raw.scss')),
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderTree = (tree) => (
|
||||||
|
<ul>
|
||||||
|
{tree.map((branch) => {
|
||||||
|
const {
|
||||||
|
children = [],
|
||||||
|
label,
|
||||||
|
onClick = () => {},
|
||||||
|
onPointerDown = () => {},
|
||||||
|
} = branch;
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<button onClick={onClick} onPointerDown={onPointerDown} type="button">{label}</button>
|
||||||
|
{children.length > 0 && renderTree(children)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
const MenuBar = (props) => {
|
||||||
|
const {icon, tree} = props;
|
||||||
|
const rendered = renderTree(tree.map((branch) => ({
|
||||||
|
...branch,
|
||||||
|
onPointerDown: (event) => {
|
||||||
|
if (window.document.activeElement === event.target) {
|
||||||
|
event.target.blur();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})));
|
||||||
|
return (
|
||||||
|
<div className="menubar">
|
||||||
|
{icon && <span className="menubar__icon">{icon}</span>}
|
||||||
|
{React.cloneElement(rendered, {className: 'menubar__top-list'})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MenuBar.defaultProps = {
|
||||||
|
icon: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
MenuBar.propTypes = {
|
||||||
|
icon: PropTypes.node,
|
||||||
|
tree: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default decorate(MenuBar);
|
81
src/client/menubar.raw.scss
Normal file
81
src/client/menubar.raw.scss
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
:scope {
|
||||||
|
background-color: #444444;
|
||||||
|
display: flex;
|
||||||
|
font-family: var(--title-font-family);
|
||||||
|
position: relative;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
height: 2.5em;
|
||||||
|
&:hover, &:focus-within {
|
||||||
|
background-color: #555555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li button {
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menubar__icon {
|
||||||
|
height: 2.5em;
|
||||||
|
padding: 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
width: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menubar__icon img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menubar__top-list {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menubar__top-list > li {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> ul {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within > ul {
|
||||||
|
display: flex;
|
||||||
|
top: 2.5em;
|
||||||
|
z-index: 1;
|
||||||
|
li > button {
|
||||||
|
min-width: 15em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within ul {
|
||||||
|
background-color: #272727;
|
||||||
|
box-shadow: 0 0 3px black;
|
||||||
|
flex-direction: column;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
> ul ul {
|
||||||
|
display: none;
|
||||||
|
transform: translate(100%, 0%);
|
||||||
|
}
|
||||||
|
|
||||||
|
> ul > li:hover ul, > ul ul > li:hover {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user