rewrite new Sidedrawer controller

This commit is contained in:
srgooglo 2022-06-01 03:11:23 +02:00
parent 5ba73d13eb
commit 954d231da4
2 changed files with 210 additions and 51 deletions

View File

@ -3,48 +3,202 @@ import classnames from "classnames"
import "./index.less" import "./index.less"
export default class Sidedrawer extends React.Component { export const Sidedrawer = (props) => {
constructor(props) { const sidedrawerId = props.id ?? props.key
super(props)
this.state = {
render: null
}
this.SidedrawerController = { return <div
render: this._render, key={sidedrawerId}
close: this._close id={sidedrawerId}
className={
classnames("sidedrawer", {
"hided": !props.defaultVisible,
})
} }
>
{
React.createElement(props.children, {
...props.props,
close: props.close,
})
}
</div>
}
window.app["SidedrawerController"] = this.SidedrawerController export default class SidedrawerController extends React.Component {
state = {
drawers: [],
lockedIds: [],
} }
containerRef = React.createRef() constructor(props) {
super(props)
this.controller = window.app["SidedrawerController"] = {
open: this.open,
close: this.close,
closeAll: this.closeAll,
hasDrawers: this.state.drawers.length > 0,
}
}
componentDidMount = () => {
this.listenEscape()
}
componentDidUpdate() {
this.controller.hasDrawers = this.state.drawers.length > 0
if (this.controller.hasDrawers) {
window.app.eventBus.emit("sidedrawer.hasDrawers")
} else {
window.app.eventBus.emit("sidedrawer.noDrawers")
}
}
componentWillUnmount = () => { componentWillUnmount = () => {
this.unlistenEscape() this.unlistenEscape()
} }
_render = (component) => { drawerIsLocked = (id) => {
this.listenEscape() return this.state.lockedIds.includes(id)
this.setState({ render: component })
} }
close = () => { lockDrawerId = (id) => {
this.unlistenEscape() this.setState({
this.setState({ render: null }) lockedIds: [...this.state.lockedIds, id],
})
}
unlockDrawer = (id) => {
this.setState({
lockedIds: this.state.lockedIds.filter(lockedId => lockedId !== id),
})
}
open = async (id, component, options = {}) => {
if (typeof id !== "string") {
options = component
component = id
id = component.key ?? component.id ?? Math.random().toString(36).substr(2, 9)
}
let drawers = this.state.drawers
// check if id is already in use
// but only if its allowed to be used multiple times
const existentDrawer = drawers.find((drawer) => drawer.props.id === id)
if (existentDrawer) {
if (!existentDrawer.props.allowMultiples) {
console.warn(`Sidedrawer with id "${id}" already exists.`)
return false
}
// fix id appending the corresponding array index at the end of the id
// ex ["A", "B", "C"] => ["A", "B", "C", "A-1"]
// to prevent id collisions
let index = 0
let newId = id
while (drawers.find(drawer => drawer.props.id === newId)) {
index++
newId = id + "-" + index
}
id = newId
}
drawers.push(React.createElement(
Sidedrawer,
{
key: id,
id: id,
allowMultiples: options.allowMultiples ?? false,
...options.props,
close: this.close,
escClosable: options.escClosable ?? true,
defaultVisible: false,
selfLock: () => {
this.lockDrawerId(id)
},
selfUnlock: () => {
this.unlockDrawer(id)
}
},
component
))
if (options.lock) {
this.lockDrawerId(id)
}
await this.setState({ drawers })
setTimeout(() => {
this.toggleDrawerVisibility(id, true)
}, 10)
window.app.eventBus.emit("sidedrawer.open")
}
toggleDrawerVisibility = (id, to) => {
const drawer = document.getElementById(id)
const drawerClasses = drawer.classList
if (to) {
drawerClasses.remove("hided")
} else {
drawerClasses.add("hided")
}
}
close = (id) => {
// if an id is provided filter by key
// else close the last opened drawer
let drawers = this.state.drawers
let drawerId = id ?? drawers[drawers.length - 1].props.id
// check if id is locked
if (this.drawerIsLocked(id)) {
console.warn(`Sidedrawer with id "${id}" is locked.`)
return false
}
// check if id exists
const drawer = drawers.find(drawer => drawer.props.id === drawerId)
if (!drawer) {
console.warn(`Sidedrawer with id "${id}" does not exist.`)
return false
}
// emit event
window.app.eventBus.emit("sidedrawer.close")
// toogleVisibility off
this.toggleDrawerVisibility(drawerId, false)
// await drawer transition
setTimeout(() => {
// remove drawer
drawers = drawers.filter(drawer => drawer.props.id !== drawerId)
this.setState({ drawers })
}, 500)
} }
listenEscape = () => { listenEscape = () => {
document.addEventListener("keydown", this.handleKeyPress) document.addEventListener("keydown", this.handleEscKeyPress)
} }
unlistenEscape = () => { unlistenEscape = () => {
document.removeEventListener("keydown", this.handleKeyPress) document.removeEventListener("keydown", this.handleEscKeyPress)
} }
handleKeyPress = (event) => { handleEscKeyPress = (event) => {
// avoid handle keypress when is nothing to render // avoid handle keypress when is nothing to render
if (!this.state.render) { if (this.state.drawers.length === 0) {
return false return false
} }
@ -57,27 +211,16 @@ export default class Sidedrawer extends React.Component {
} }
if (isEscape) { if (isEscape) {
// close the last opened drawer
this.close() this.close()
} }
} }
renderComponent = (component) => {
if (!component) {
return null
}
if (React.isValidElement(component)) {
return React.cloneElement(component)
}
return React.createElement(component)
}
render() { render() {
return ( return <div
<div ref={this.containerRef} className={classnames("sidedrawer", { hided: !this.state.render })}> className="sidedrawers-wrapper"
<React.Fragment>{this.renderComponent(this.state.render)}</React.Fragment> >
</div> {this.state.drawers}
) </div>
} }
} }

View File

@ -1,21 +1,37 @@
@import "theme/vars.less"; @import "theme/vars.less";
.sidedrawer { .sidedrawers-wrapper {
width: 30vw; // by default display: flex;
flex-direction: row;
background-color: var(--sidedrawer-background-color); .sidedrawer {
border-radius: @app_sidebar_borderRadius 0 0 @app_sidebar_borderRadius; position: relative;
word-break: break-all; width: 30vw; // by default
height: 100vh;
transition: all 150ms ease-out; min-width: 700px;
padding: 20px; z-index: 20;
overflow-x: hidden;
overflow-y: overlay;
&.hided { background-color: var(--sidedrawer-background-color);
width: 0; border-radius: 0 @app_sidebar_borderRadius @app_sidebar_borderRadius 0;
padding: 0;
padding: 20px;
padding-left: 70px;
transform: translate(-50px, 0);
overflow-x: hidden;
overflow-y: overlay;
word-break: break-all;
transition: all 250ms ease-in-out;
&.hided {
width: 0;
min-width: 0;
padding: 0;
}
} }
} }