mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
231 lines
6.4 KiB
JavaScript
Executable File
231 lines
6.4 KiB
JavaScript
Executable File
import React from "react"
|
|
import classnames from "classnames"
|
|
import * as antd from "antd"
|
|
|
|
import { createIconRender } from "components/Icons"
|
|
|
|
import NavMenu from "./components/NavMenu"
|
|
|
|
import "./index.less"
|
|
|
|
export const Panel = (props) => {
|
|
return <div
|
|
{...props.props ?? {}}
|
|
className={classnames(
|
|
"panel",
|
|
props.align,
|
|
props.className
|
|
)}
|
|
>
|
|
{props.children}
|
|
</div>
|
|
}
|
|
|
|
export class PagePanelWithNavMenu extends React.Component {
|
|
state = {
|
|
activeTab: this.props.defaultTab ?? new URLSearchParams(window.location.search).get("type") ?? this.props.tabs[0].key,
|
|
renders: [],
|
|
}
|
|
|
|
primaryPanelRef = React.createRef()
|
|
|
|
interface = {
|
|
attachComponent: (id, component, options) => {
|
|
const renders = this.state.renders
|
|
|
|
renders.push({
|
|
id: id,
|
|
component: component,
|
|
options: options,
|
|
ref: React.createRef()
|
|
})
|
|
|
|
this.setState({
|
|
renders: renders,
|
|
})
|
|
},
|
|
detachComponent: (id) => {
|
|
const renders = this.state.renders
|
|
|
|
const index = renders.findIndex((render) => render.id === id)
|
|
|
|
renders.splice(index, 1)
|
|
|
|
this.setState({
|
|
renders: renders,
|
|
})
|
|
}
|
|
}
|
|
|
|
componentDidMount() {
|
|
app.layout.page_panels = this.interface
|
|
|
|
if (app.isMobile) {
|
|
app.layout.top_bar.shouldUseTopBarSpacer(false)
|
|
}
|
|
|
|
app.layout.toggleCenteredContent(true)
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
delete app.layout.page_panels
|
|
|
|
if (app.isMobile) {
|
|
app.layout.top_bar.shouldUseTopBarSpacer(true)
|
|
} else {
|
|
app.layout.header.render(null)
|
|
}
|
|
}
|
|
|
|
renderActiveTab() {
|
|
if (!Array.isArray(this.props.tabs)) {
|
|
console.error("PagePanelWithNavMenu: tabs must be an array")
|
|
return <></>
|
|
}
|
|
|
|
// slip the active tab by splitting on "."
|
|
if (!this.state.activeTab) {
|
|
console.error("PagePanelWithNavMenu: activeTab is not defined")
|
|
return <></>
|
|
}
|
|
|
|
let tab = null
|
|
|
|
const activeTabDirectory = this.state.activeTab.split(".")
|
|
|
|
activeTabDirectory.forEach((key, index) => {
|
|
if (!tab) {
|
|
tab = this.props.tabs.find((children) => children.key === key)
|
|
} else {
|
|
if (!tab.children) {
|
|
console.error("PagePanelWithNavMenu: tab.children is not defined")
|
|
|
|
return tab = null
|
|
}
|
|
|
|
tab = tab.children.find((children) => children.key === `${activeTabDirectory[index - 1]}.${key}`)
|
|
}
|
|
})
|
|
|
|
if (!tab) {
|
|
if (this.props.onNotFound) {
|
|
return this.props.onNotFound()
|
|
}
|
|
|
|
return <antd.Result
|
|
status="404"
|
|
title="404"
|
|
subTitle="Sorry, the tab you visited does not exist."
|
|
/>
|
|
}
|
|
|
|
const componentProps = tab.props ?? this.props.tabProps
|
|
|
|
return React.createElement(tab.component, {
|
|
...componentProps,
|
|
ref: this.primaryPanelRef,
|
|
})
|
|
}
|
|
|
|
replaceQueryTypeToCurrentTab = () => {
|
|
app.history.replace(`${window.location.pathname}?type=${this.state.activeTab}`)
|
|
}
|
|
|
|
tabChange = async (key) => {
|
|
if (this.props.beforeTabChange) {
|
|
await this.props.beforeTabChange(key)
|
|
}
|
|
|
|
await this.setState({ activeTab: key })
|
|
|
|
if (this.props.useSetQueryType) {
|
|
this.replaceQueryTypeToCurrentTab()
|
|
}
|
|
|
|
if (this.props.onTabChange) {
|
|
this.props.onTabChange(key)
|
|
}
|
|
}
|
|
|
|
handleTabChange = async (key) => {
|
|
if (this.state.activeTab === key) return
|
|
|
|
if (this.props.transition) {
|
|
if (document.startViewTransition) {
|
|
return document.startViewTransition(() => {
|
|
this.tabChange(key)
|
|
})
|
|
}
|
|
|
|
console.warn("PagePanelWithNavMenu: transition is enabled but document.startViewTransition is not compatible with your browser")
|
|
|
|
// set to primary panel fade-opacity-leave class
|
|
this.primaryPanelRef.current.classList.add("fade-opacity-leave")
|
|
|
|
// remove fade-opacity-leave class after animation
|
|
setTimeout(() => {
|
|
this.primaryPanelRef.current.classList.remove("fade-opacity-leave")
|
|
}, 300)
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 200))
|
|
}
|
|
|
|
return this.tabChange(key)
|
|
}
|
|
|
|
getItems = (items) => {
|
|
if (!Array.isArray(items)) {
|
|
console.error(`[items] is not an (array), received (${typeof items})`)
|
|
return []
|
|
}
|
|
|
|
return items.map((item) => {
|
|
return {
|
|
key: item.key,
|
|
icon: createIconRender(item.icon),
|
|
label: item.label,
|
|
children: item.children && this.getItems(item.children),
|
|
disabled: item.disabled,
|
|
}
|
|
})
|
|
}
|
|
|
|
render() {
|
|
return <>
|
|
{
|
|
app.isMobile && app.layout.top_bar.render(<NavMenu
|
|
activeKey={this.state.activeTab}
|
|
items={this.getItems(this.props.tabs)}
|
|
onClickItem={(key) => this.handleTabChange(key)}
|
|
/>)
|
|
}
|
|
|
|
{
|
|
!app.isMobile && app.layout.header.render(<NavMenu
|
|
header={this.props.navMenuHeader}
|
|
activeKey={this.state.activeTab}
|
|
items={this.getItems(this.props.tabs)}
|
|
onClickItem={(key) => this.handleTabChange(key)}
|
|
renderNames
|
|
>
|
|
{
|
|
Array.isArray(this.state.renders) && [
|
|
this.state.renders.map((render, index) => {
|
|
return React.createElement(render.component, {
|
|
...render.options.props,
|
|
ref: render.ref
|
|
})
|
|
})
|
|
]
|
|
}
|
|
</NavMenu>)
|
|
}
|
|
|
|
{
|
|
this.renderActiveTab()
|
|
}
|
|
</>
|
|
}
|
|
}
|
|
|
|
export default PagePanelWithNavMenu |