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"
export default class Sidedrawer extends React.Component {
constructor(props) {
super(props)
this.state = {
render: null
}
export const Sidedrawer = (props) => {
const sidedrawerId = props.id ?? props.key
this.SidedrawerController = {
render: this._render,
close: this._close
return <div
key={sidedrawerId}
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 = () => {
this.unlistenEscape()
}
_render = (component) => {
this.listenEscape()
this.setState({ render: component })
drawerIsLocked = (id) => {
return this.state.lockedIds.includes(id)
}
close = () => {
this.unlistenEscape()
this.setState({ render: null })
lockDrawerId = (id) => {
this.setState({
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 = () => {
document.addEventListener("keydown", this.handleKeyPress)
document.addEventListener("keydown", this.handleEscKeyPress)
}
unlistenEscape = () => {
document.removeEventListener("keydown", this.handleKeyPress)
document.removeEventListener("keydown", this.handleEscKeyPress)
}
handleKeyPress = (event) => {
handleEscKeyPress = (event) => {
// avoid handle keypress when is nothing to render
if (!this.state.render) {
if (this.state.drawers.length === 0) {
return false
}
@ -57,27 +211,16 @@ export default class Sidedrawer extends React.Component {
}
if (isEscape) {
// close the last opened drawer
this.close()
}
}
renderComponent = (component) => {
if (!component) {
return null
}
if (React.isValidElement(component)) {
return React.cloneElement(component)
}
return React.createElement(component)
}
render() {
return (
<div ref={this.containerRef} className={classnames("sidedrawer", { hided: !this.state.render })}>
<React.Fragment>{this.renderComponent(this.state.render)}</React.Fragment>
</div>
)
return <div
className="sidedrawers-wrapper"
>
{this.state.drawers}
</div>
}
}
}

View File

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