From 954d231da4868415c88b71a1e767d8fb46c24920 Mon Sep 17 00:00:00 2001 From: srgooglo Date: Wed, 1 Jun 2022 03:11:23 +0200 Subject: [PATCH] rewrite new `Sidedrawer` controller --- packages/app/src/layout/sidedrawer/index.jsx | 221 ++++++++++++++---- packages/app/src/layout/sidedrawer/index.less | 40 +++- 2 files changed, 210 insertions(+), 51 deletions(-) diff --git a/packages/app/src/layout/sidedrawer/index.jsx b/packages/app/src/layout/sidedrawer/index.jsx index 6de1e851..5e2a0732 100644 --- a/packages/app/src/layout/sidedrawer/index.jsx +++ b/packages/app/src/layout/sidedrawer/index.jsx @@ -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
+ { + React.createElement(props.children, { + ...props.props, + close: props.close, + }) + } +
+} - 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 ( -
- {this.renderComponent(this.state.render)} -
- ) + return
+ {this.state.drawers} +
} -} +} \ No newline at end of file diff --git a/packages/app/src/layout/sidedrawer/index.less b/packages/app/src/layout/sidedrawer/index.less index 7d7aec2a..bc0e70a4 100644 --- a/packages/app/src/layout/sidedrawer/index.less +++ b/packages/app/src/layout/sidedrawer/index.less @@ -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; + } } } \ No newline at end of file