nope/resources/ui/layout/toolbar.tsx

291 lines
8.7 KiB
TypeScript
Raw Normal View History

2020-10-29 18:20:42 +00:00
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import 'bootstrap/dist/css/bootstrap.min.css';
2020-09-02 15:00:03 +00:00
import * as React from 'react';
2020-09-07 05:21:53 +00:00
import { Nav, Navbar, NavDropdown } from 'react-bootstrap';
2020-09-07 05:21:53 +00:00
export interface ToolbarProps<D> {
toolbar: IToolbar<D>;
generateData(): D;
position?: 'top' | 'bottom',
sticky?: boolean;
bg?: 'light' | 'dark',
brand?: ILink
}
/**
* Definition of a Toolbar.
*
* @export
* @interface IToolbar
* @template D
*/
export interface IToolbar<D> {
/**
* Items of the toolbar.
*
* @type {(Array<IAction<D> | IMenu<D> | ILink>)}
* @memberof IToolbar
*/
items: Array<IAction<D> | IMenu<D> | ILink>
}
export interface IAction<D> {
type: 'action';
/**
* Action which should be performed if the element
* has been clicked.
*
* @param {D} data
* @memberof IAction
*/
onClick(data: D): void;
/**
* Label of the Menu.
*
* @type {string}
* @memberof IAction
*/
label: string,
/**
* Additional Icon.
*/
icon?: IconDefinition;
2020-09-07 05:21:53 +00:00
}
2020-09-02 15:00:03 +00:00
2020-09-07 05:21:53 +00:00
/**
* A Menu of the Toolbar
*
* @export
* @interface IMenu
* @template D
*/
export interface IMenu<D> {
type: 'menu',
/**
* The Menu ID.
*
* @type {string}
* @memberof IMenu
*/
id: string;
/**
* Label of the Menu
*
* @type {string}
* @memberof IMenu
*/
label: string,
/**
* Items of the Menu.
*
* @type {(Array<IAction<D> | ILink | IDivider>)}
* @memberof IMenu
*/
items: Array<IAction<D> | IMenu<D> | ILink | IDivider>;
}
/**
* A Classical Link.
*
* @export
* @interface ILink
*/
export interface ILink {
/**
* Type of the Element.
*
* @type {'link'}
* @memberof ILink
*/
type: 'link';
/**
* Label
*
* @type {string}
* @memberof ILink
*/
label: string;
/**
2020-10-25 20:14:51 +00:00
* Referene, where the client should be navigated on click
2020-09-07 05:21:53 +00:00
*
* @type {string}
* @memberof ILink
*/
ref: string;
/**
* Additional Icon.
*/
icon?: IconDefinition;
2020-09-07 05:21:53 +00:00
}
/**
* A Divider in a Menu.
*
* @export
* @interface IDivider
*/
export interface IDivider {
/**
* Type Element
*
* @type {'divider'}
* @memberof IDivider
*/
type: 'divider'
2020-09-02 15:00:03 +00:00
}
2020-10-29 18:20:42 +00:00
export interface ToolbarState<D> extends ToolbarProps<D> {
2020-09-02 15:00:03 +00:00
}
2020-10-29 18:20:42 +00:00
class Toolbar<D> extends React.Component<ToolbarProps<D>, ToolbarState<D>> {
private _currentId = 0;
constructor(props){
super(props);
this.state = Object.assign({
2020-10-30 18:30:59 +00:00
bg: 'dark',
2020-10-29 18:20:42 +00:00
position: 'top',
sticky: true
},this.props);
}
_generateId(){
const id = this._currentId;
this._currentId += 1;
return id;
}
2020-09-07 05:21:53 +00:00
_renderMenuEntry(entry: IAction<D> | IMenu<D> | ILink) {
2020-09-07 05:21:53 +00:00
const _this = this;
switch (entry.type) {
2020-09-07 05:21:53 +00:00
case 'action':
if (entry.icon){
return (
2020-10-29 18:20:42 +00:00
<Nav.Link key={this._generateId()} onClick={e => {
entry.onClick(this.state.generateData());
}}><FontAwesomeIcon icon={entry.icon}/> {entry.label}</Nav.Link>
);
}
2020-09-07 05:21:53 +00:00
return (
2020-10-29 18:20:42 +00:00
<Nav.Link key={this._generateId()} onClick={e => {
entry.onClick(this.state.generateData());
}}>{entry.label}</Nav.Link>
2020-09-07 05:21:53 +00:00
);
case 'menu':
return this._renderDropdown(entry);
2020-09-07 05:21:53 +00:00
case 'link':
if (entry.icon){
return (
2020-10-29 18:20:42 +00:00
<Nav.Link key={this._generateId()} href={entry.ref}>
<FontAwesomeIcon icon={entry.icon}/> {entry.label}
</Nav.Link>
);
}
2020-09-07 05:21:53 +00:00
return (
2020-10-29 18:20:42 +00:00
<Nav.Link key={this._generateId()} href={entry.ref}>{entry.label}</Nav.Link>
2020-09-07 05:21:53 +00:00
);
}
}
_renderDropdown(item: IMenu<D>) {
2020-10-29 18:20:42 +00:00
// Issue: Drop-Down wont display the Dropdowns after opening a modal:
// Issue: https://github.com/react-bootstrap/react-bootstrap/issues/5409
// Dirty Fix: onClick={e => e.stopPropagation() in the "NavDropdown"
2020-09-07 05:21:53 +00:00
return (
2020-10-29 18:20:42 +00:00
<NavDropdown key={this._generateId()} id={item.id} title={item.label} onClick={e => e.stopPropagation()}>
2020-09-07 05:21:53 +00:00
{/* Iterate over the deteced Modules. */}
{item.items.map(entry => {
switch (entry.type) {
case 'action':
if (entry.icon){
return (
2020-10-29 18:20:42 +00:00
<NavDropdown.Item key={this._generateId()} onClick={e => {
entry.onClick(this.state.generateData());
}}><FontAwesomeIcon icon={entry.icon} /> {entry.label}</NavDropdown.Item>
);
}
2020-09-07 05:21:53 +00:00
return (
2020-10-29 18:20:42 +00:00
<Nav.Link key={this._generateId()} onClick={e => {
entry.onClick(this.state.generateData());
}}>{entry.label}</Nav.Link>
2020-09-07 05:21:53 +00:00
);
case 'divider':
return (
2020-10-29 18:20:42 +00:00
<NavDropdown.Divider key={this._generateId()} />
2020-09-07 05:21:53 +00:00
);
case 'link':
if (entry.icon){
return (
2020-10-29 18:20:42 +00:00
<NavDropdown.Item key={this._generateId()} href={entry.ref}>
<FontAwesomeIcon icon={entry.icon} /> {entry.label}
2020-10-29 18:20:42 +00:00
</NavDropdown.Item>
);
}
2020-09-07 05:21:53 +00:00
return (
2020-10-29 18:20:42 +00:00
<NavDropdown.Item key={this._generateId()} href={entry.ref}>{entry.label}</NavDropdown.Item>
2020-09-07 05:21:53 +00:00
);
case 'menu':
return this._renderDropdown(entry);
}
})}
</NavDropdown>
);
}
2020-09-02 15:00:03 +00:00
2020-10-25 20:14:51 +00:00
/**
* Main Function to Render the Layout
*/
2020-10-29 18:20:42 +00:00
render() {
this._currentId = 0;
2020-09-07 05:21:53 +00:00
return (
<>
2020-10-29 18:20:42 +00:00
{this.state.sticky ?
2020-10-25 20:14:51 +00:00
// Render a collapsable Navbar containing the defined Menu structure.
2020-10-29 18:20:42 +00:00
<Navbar collapseOnSelect sticky={this.state.position} expand="lg" bg={this.state.bg} variant={this.state.bg}>
{this.state.brand ? <>
2020-10-29 18:20:42 +00:00
<Navbar.Brand href={this.state.brand.ref}>
{this.state.brand.icon ? <FontAwesomeIcon icon = {this.state.brand.icon} />: ''}
{this.state.brand.label}
</Navbar.Brand>
2020-09-07 05:21:53 +00:00
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
</> :
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
}
<Navbar.Collapse id="responsive-navbar-nav">
2020-10-29 18:20:42 +00:00
<Nav>
{this.state.toolbar.items.map(item => this._renderMenuEntry(item))}
</Nav>
2020-09-07 05:21:53 +00:00
</Navbar.Collapse>
</Navbar> :
2020-10-29 18:20:42 +00:00
<Navbar collapseOnSelect fixed={this.state.position} expand="lg" bg={this.state.bg} variant={this.state.bg}>
{this.state.brand ? <>
<Navbar.Brand href={this.state.brand.ref}>
{this.state.brand.icon ? <FontAwesomeIcon icon = {this.state.brand.icon} />: ''}
{this.state.brand.label}
</Navbar.Brand>
2020-09-07 05:21:53 +00:00
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
</> :
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
}
<Navbar.Collapse id="responsive-navbar-nav">
2020-10-29 18:20:42 +00:00
<Nav>
{this.state.toolbar.items.map(item => this._renderMenuEntry(item))}
</Nav>
2020-09-07 05:21:53 +00:00
</Navbar.Collapse>
</Navbar>
}
</>
);
2020-09-02 15:00:03 +00:00
}
}
export default Toolbar;