import React from "react" import { Layout, Menu, Avatar } from "antd" import classnames from "classnames" import config from "config" import { Icons, createIconRender } from "components/Icons" import { sidebarKeys as defaultSidebarItems } from "schemas/defaultSettings" import sidebarItems from "schemas/routes.json" import { Translation } from "react-i18next" import { SidebarEditor } from "./components" import "./index.less" const { Sider } = Layout const onClickHandlers = { settings: (event) => { window.app.openSettings() }, } export default class Sidebar extends React.Component { constructor(props) { super(props) this.controller = window.app["SidebarController"] = { toggleVisibility: this.toggleVisibility, toggleEdit: this.toggleEditMode, toggleElevation: this.toggleElevation, attachElement: this.attachElement, isVisible: () => this.state.visible, isEditMode: () => this.state.visible, isCollapsed: () => this.state.collapsed, } this.state = { editMode: false, visible: false, loading: true, collapsed: window.app.settings.get("collapseOnLooseFocus") ?? false, pathResolve: {}, menus: {}, extraItems: { bottom: [], top: [], }, elevated: false, additionalElements: [], } window.app.eventBus.on("edit_sidebar", () => this.toggleEditMode()) window.app.eventBus.on("settingChanged.sidebar_collapse", (value) => { this.toggleCollapse(value) }) // handle sidedrawer open/close window.app.eventBus.on("sidedrawer.hasDrawers", () => { this.toggleElevation(true) }) window.app.eventBus.on("sidedrawer.noDrawers", () => { this.toggleElevation(false) }) } collapseDebounce = null componentDidMount = async () => { await this.loadSidebarItems() setTimeout(() => { this.controller.toggleVisibility(true) }, 100) } getStoragedKeys = () => { return window.app.settings.get("sidebarKeys") } attachElement = (element) => { this.setState({ additionalElements: [...this.state.additionalElements, element], }) } appendItem = (item = {}) => { const { position } = item if (typeof position === "undefined" && typeof this.state.extraItems[position] === "undefined") { console.error("Invalid position") return false } const state = this.state.extraItems state[position].push(item) this.setState({ extraItems: state }) } loadSidebarItems = () => { const items = {} const itemsMap = [] // parse all items from schema sidebarItems.forEach((item, index) => { items[item.id] = { ...item, index, content: ( <> {createIconRender(item.icon)} {item.title} ), } }) // filter undefined to avoid error let keys = (this.getStoragedKeys() ?? defaultSidebarItems).filter((key) => { if (typeof items[key] !== "undefined") { return true } }) // short items keys.forEach((id, index) => { const item = items[id] if (item.locked) { if (item.index !== index) { keys = keys.move(index, item.index) //update index window.app.settings.set("sidebarKeys", keys) } } }) // set items from scoped keys keys.forEach((key, index) => { const item = items[key] try { // avoid if item is duplicated if (itemsMap.includes(item)) { return false } let valid = true if (typeof item.requireState === "object") { const { key, value } = item.requireState //* TODO: check global state } // end validation if (!valid) { return false } if (typeof item.path !== "undefined") { let resolvers = this.state.pathResolve ?? {} resolvers[item.id] = item.path this.setState({ pathResolve: resolvers }) } itemsMap.push(item) } catch (error) { return console.log(error) } }) // update states this.setState({ items, menus: itemsMap, loading: false }) } renderMenuItems(items) { const handleRenderIcon = (icon) => { if (typeof icon === "undefined") { return null } return createIconRender(icon) } return items.map((item) => { if (Array.isArray(item.children)) { return ( {t => t(item.title)} } {...item.props} > {this.renderMenuItems(item.children)} ) } return ( {t => t(item.title ?? item.id)} ) }) } handleClick = (e) => { if (e.item.props.overrideEvent) { return app.eventBus.emit(e.item.props.overrideEvent, e.item.props.overrideEventProps) } if (typeof e.key === "undefined") { window.app.eventBus.emit("invalidSidebarKey", e) return false } if (typeof onClickHandlers[e.key] === "function") { return onClickHandlers[e.key](e) } if (typeof this.state.pathResolve[e.key] !== "undefined") { return window.app.setLocation(`/${this.state.pathResolve[e.key]}`, 150) } return window.app.setLocation(`/${e.key}`, 150) } toggleEditMode = (to) => { if (typeof to === "undefined") { to = !this.state.editMode } if (to) { window.app.eventBus.emit("clearAllOverlays") } else { if (this.itemsMap !== this.getStoragedKeys()) { this.loadSidebarItems() } } this.setState({ editMode: to, collapsed: false }) } toggleCollapse = (to) => { if (!this.state.editMode) { this.setState({ collapsed: to ?? !this.state.collapsed }) } } toggleVisibility = (to) => { this.setState({ visible: to ?? !this.state.visible }) } toggleElevation = (to) => { this.setState({ elevated: to ?? !this.state.elevated }) } onMouseEnter = () => { if (window.app.settings.is("collapseOnLooseFocus", false)) { return false } clearTimeout(this.collapseDebounce) this.collapseDebounce = null if (this.state.collapsed) { this.toggleCollapse(false) } } handleMouseLeave = () => { if (window.app.settings.is("collapseOnLooseFocus", false)) { return false } if (!this.state.collapsed) { this.collapseDebounce = setTimeout(() => { this.toggleCollapse(true) }, window.app.settings.get("autoCollapseDelay") ?? 500) } } renderExtraItems = (position) => { return this.state.extraItems[position].map((item = {}) => { if (typeof item.icon !== "undefined") { if (typeof item.props !== "object") { item.props = Object() } item.props["icon"] = createIconRender(item.icon) } return {item.children} }) } render() { if (this.state.loading) return null const { user } = this.props return ( this.props.onCollapse()} className={ classnames( "sidebar", { ["edit_mode"]: this.state.editMode, ["hidden"]: !this.state.visible, ["elevated"]: this.state.elevated } ) } >
{this.state.editMode && (
)} {!this.state.editMode && (
{this.renderMenuItems(this.state.menus)} {this.renderExtraItems("top")}
)} {!this.state.editMode &&
{this.state.additionalElements}
} {!this.state.editMode && (
} overrideEvent="app.openSearcher" > {(t) => t("Search")} } overrideEvent="app.openCreator" > {(t) => t("Create")} }> {t => t("Notifications")} }> {t => t("Settings")} { user &&
} { !user && }> {t => t("Login")} } {this.renderExtraItems("bottom")}
)}
) } }