import React from "react"
import config from "@config"
import classnames from "classnames"
import { Translation } from "react-i18next"
import { motion, AnimatePresence } from "motion/react"
import { Menu, Avatar, Dropdown, Tag } from "antd"
import Drawer from "@layouts/components/drawer"
import { Icons } from "@components/Icons"
import GenerateMenuItems from "@utils/generateMenuItems"
import TopMenuItems from "@config/sidebar/TopItems"
import BottomMenuItems from "@config/sidebar/BottomItems"
import ItemsClickHandlers from "./itemClickHandlers"
import "./index.less"
const ActionMenuItems = [
{
key: "profile",
label: (
<>
{(t) => t("Profile")}
>
),
},
{
key: "studio",
label: (
<>
{(t) => t("Studio")}
>
),
},
{
key: "addons",
label: (
<>
{(t) => t("Addons")}
>
),
},
{
type: "divider",
},
{
key: "switch_account",
label: (
<>
{(t) => t("Switch account")}
>
),
},
{
key: "logout",
label: (
<>
{(t) => t("Logout")}
>
),
danger: true,
},
]
export default class Sidebar extends React.Component {
state = {
visible: false,
expanded: false,
topItems: GenerateMenuItems(TopMenuItems),
bottomItems: GenerateMenuItems(BottomMenuItems),
selectedMenuItem: null,
navigationRender: null,
}
sidebarRef = React.createRef()
collapseDebounce = null
interface = (window.app.layout.sidebar = {
toggleVisibility: (to) => {
if (to === false) {
this.interface.toggleExpanded(false, {
instant: true,
})
}
this.setState({ visible: to ?? !this.state.visible })
},
toggleExpanded: async (
to,
{ instant = false, isDropdown = false } = {},
) => {
to = to ?? !this.state.expanded
if (this.collapseDebounce) {
clearTimeout(this.collapseDebounce)
this.collapseDebounce = null
}
if (
(to === false) & (this.state.dropdownOpen === true) &&
isDropdown === true
) {
// FIXME: This is a walkaround for a bug in antd, causing when dropdown set to close, item click event is not fired
// The desing defines when sidebar should be collapsed, dropdown should be closed, but in this case, gonna to keep it open untils dropdown is closed
//this.setState({ dropdownOpen: false })
return false
}
if (to === false) {
if (instant === false) {
await new Promise((resolve) =>
setTimeout(
resolve,
window.app.cores.settings.get(
"sidebar.collapse_delay_time",
) ?? 500,
),
)
}
}
this.setState({ expanded: to })
app.eventBus.emit("sidebar.expanded", to)
},
isVisible: () => this.state.visible,
isExpanded: () => this.state.expanded,
renderNavigationBar: (component, options) => {
this.setState({
navigationRender: {
component,
options,
},
})
},
updateMenuItemProps: this.updateBottomItemProps,
addMenuItem: this.addMenuItem,
removeMenuItem: this.removeMenuItem,
})
events = {
"router.navigate": (path) => {
this.calculateSelectedMenuItem(path)
},
}
componentDidMount = async () => {
this.calculateSelectedMenuItem(window.location.pathname)
for (const [event, handler] of Object.entries(this.events)) {
app.eventBus.on(event, handler)
}
setTimeout(() => {
this.interface.toggleVisibility(true)
}, 10)
}
componentWillUnmount = () => {
for (const [event, handler] of Object.entries(this.events)) {
app.eventBus.off(event, handler)
}
delete app.layout.sidebar
}
calculateSelectedMenuItem = (path) => {
const items = [...this.state.topItems, ...this.state.bottomItems]
this.setState({
selectedMenuItem: items.find((item) =>
String(item.path).includes(path),
),
})
}
addMenuItem = (group, item) => {
group = this.getMenuItemGroupStateKey(group)
if (!group) {
throw new Error("Invalid group")
}
const newItems = [...this.state[group], item]
this.setState({
[group]: newItems,
})
return newItems
}
removeMenuItem = (group, id) => {
group = this.getMenuItemGroupStateKey(group)
if (!group) {
throw new Error("Invalid group")
}
const newItems = this.state[group].filter((item) => item.id !== id)
this.setState({
[group]: newItems,
})
return newItems
}
updateBottomItemProps = (group, id, newProps) => {
group = this.getMenuItemGroupStateKey(group)
if (!group) {
throw new Error("Invalid group")
}
let updatedValue = this.state[group]
updatedValue = updatedValue.map((item) => {
if (item.id === id) {
item.props = {
...item.props,
...newProps,
}
}
})
this.setState({
[group]: updatedValue,
})
return updatedValue
}
getMenuItemGroupStateKey = (group) => {
switch (group) {
case "top": {
return "topItems"
}
case "bottom": {
return "bottomItems"
}
default: {
return null
}
}
}
injectUserItems(items = []) {
if (app.userData) {
items.push({
key: "account",
ignore_click: "true",
className: "user_avatar",
label: (
),
})
} else {
items.push({
key: "login",
label: {(t) => t("Login")},
icon: ,
})
}
return items
}
handleClick = (e) => {
if (e.item.props.ignore_click === "true") {
return
}
if (e.item.props.override_event) {
return app.eventBus.emit(
e.item.props.override_event,
e.item.props.override_event_props,
)
}
if (typeof e.key === "undefined") {
app.eventBus.emit("invalidSidebarKey", e)
return false
}
if (typeof ItemsClickHandlers[e.key] === "function") {
return ItemsClickHandlers[e.key](e)
}
app.cores.sfx.play("sidebar.switch_tab")
let item = [...this.state.topItems, ...this.state.bottomItems].find(
(item) => item.id === e.key,
)
return app.location.push(`/${item.path ?? e.key}`, 150)
}
onMouseEnter = () => {
if (!this.state.visible || app.layout.drawer.isMaskVisible()) {
return false
}
return this.interface.toggleExpanded(true)
}
handleMouseLeave = () => {
if (!this.state.visible) {
return false
}
return this.interface.toggleExpanded(false)
}
onDropdownOpenChange = (to) => {
// this is another walkaround for a bug in antd, causing when dropdown set to close, item click event is not fired
if (!to && this.state.expanded) {
this.interface.toggleExpanded(false, true)
}
this.setState({ dropdownOpen: to })
}
onClickDropdownItem = (item) => {
const handler = ItemsClickHandlers[item.key]
if (typeof handler === "function") {
handler()
}
}
render() {
const selectedKeyId = this.state.selectedMenuItem?.id
return (
{window.__TAURI__ && navigator.platform.includes("Mac") && (
)}
{this.state.visible && (

app.navigation.goMain()}
/>
αlpha
)}
)
}
}