fix unmount

This commit is contained in:
SrGooglo 2025-02-05 02:39:10 +00:00
parent dacca7021c
commit ac93563a5e

View File

@ -9,261 +9,287 @@ import NavMenu from "./components/NavMenu"
import "./index.less" import "./index.less"
export class Tab extends React.Component { export class Tab extends React.Component {
state = { state = {
error: null error: null,
} }
// handle on error // handle on error
componentDidCatch(err) { componentDidCatch(err) {
this.setState({ error: err }) this.setState({ error: err })
} }
render() { render() {
if (this.state.error) { if (this.state.error) {
return <antd.Result return (
status="error" <antd.Result
title="Error" status="error"
subTitle={this.state.error} title="Error"
/> subTitle={this.state.error}
} />
)
}
return <> return <>{this.props.children}</>
{this.props.children} }
</>
}
} }
export const Panel = (props) => { export const Panel = (props) => {
return <div return (
{...props.props ?? {}} <div
className={classnames( {...(props.props ?? {})}
"panel", className={classnames("panel", props.align, props.className)}
props.align, >
props.className {props.children}
)} </div>
> )
{props.children}
</div>
} }
export class PagePanelWithNavMenu extends React.Component { export class PagePanelWithNavMenu extends React.Component {
state = { state = {
activeTab: new URLSearchParams(window.location.search).get("type") ?? this.props.defaultTab ?? this.props.tabs[0].key, activeTab:
renders: [], new URLSearchParams(window.location.search).get("type") ??
} this.props.defaultTab ??
this.props.tabs[0].key,
renders: [],
}
primaryPanelRef = React.createRef() primaryPanelRef = React.createRef()
interface = { interface = {
attachComponent: (id, component, options) => { attachComponent: (id, component, options) => {
const renders = this.state.renders const renders = this.state.renders
renders.push({ renders.push({
id: id, id: id,
component: component, component: component,
options: options, options: options,
ref: React.createRef() ref: React.createRef(),
}) })
this.setState({ this.setState({
renders: renders, renders: renders,
}) })
}, },
detachComponent: (id) => { detachComponent: (id) => {
const renders = this.state.renders const renders = this.state.renders
const index = renders.findIndex((render) => render.id === id) const index = renders.findIndex((render) => render.id === id)
renders.splice(index, 1) renders.splice(index, 1)
this.setState({ this.setState({
renders: renders, renders: renders,
}) })
} },
} }
componentDidMount() { componentDidMount() {
app.layout.page_panels = this.interface app.layout.page_panels = this.interface
if (app.isMobile) { if (app.isMobile) {
app.layout.top_bar.shouldUseTopBarSpacer(true) app.layout.top_bar.shouldUseTopBarSpacer(true)
app.layout.toggleCenteredContent(false) app.layout.toggleCenteredContent(false)
} }
app.layout.toggleCenteredContent(true) app.layout.toggleCenteredContent(true)
} }
componentWillUnmount() { componentWillUnmount() {
delete app.layout.page_panels delete app.layout.page_panels
if (!app.isMobile) { if (!app.isMobile) {
app.layout.header.render(null) if (app.layout.header) {
} else { app.layout.header.render(null)
app.layout.top_bar.renderDefault() }
} } else {
} app.layout.top_bar.renderDefault()
}
}
renderActiveTab() { renderActiveTab() {
if (!Array.isArray(this.props.tabs)) { if (!Array.isArray(this.props.tabs)) {
console.error("PagePanelWithNavMenu: tabs must be an array") console.error("PagePanelWithNavMenu: tabs must be an array")
return <></> return <></>
} }
if (this.props.tabs.length === 0) { if (this.props.tabs.length === 0) {
return <></> return <></>
} }
// slip the active tab by splitting on "." // slip the active tab by splitting on "."
if (!this.state.activeTab) { if (!this.state.activeTab) {
console.error("PagePanelWithNavMenu: activeTab is not defined") console.error("PagePanelWithNavMenu: activeTab is not defined")
return <></> return <></>
} }
let tab = null let tab = null
const activeTabDirectory = this.state.activeTab.split(".") const activeTabDirectory = this.state.activeTab.split(".")
activeTabDirectory.forEach((key, index) => { activeTabDirectory.forEach((key, index) => {
if (!tab) { if (!tab) {
tab = this.props.tabs.find((children) => children.key === key) tab = this.props.tabs.find((children) => children.key === key)
} else { } else {
if (!tab.children) { if (!tab.children) {
console.error("PagePanelWithNavMenu: tab.children is not defined") console.error(
"PagePanelWithNavMenu: tab.children is not defined",
)
return tab = null return (tab = null)
} }
tab = tab.children.find((children) => children.key === `${activeTabDirectory[index - 1]}.${key}`) tab = tab.children.find(
} (children) =>
}) children.key ===
`${activeTabDirectory[index - 1]}.${key}`,
)
}
})
if (!tab) { if (!tab) {
if (this.props.onNotFound) { if (this.props.onNotFound) {
return this.props.onNotFound() return this.props.onNotFound()
} }
return <antd.Result return (
status="404" <antd.Result
title="404" status="404"
subTitle="Sorry, the tab you visited does not exist." title="404"
/> subTitle="Sorry, the tab you visited does not exist."
} />
)
}
const componentProps = tab.props ?? this.props.tabProps const componentProps = tab.props ?? this.props.tabProps
return React.createElement(tab.component, { return React.createElement(tab.component, {
...componentProps, ...componentProps,
}) })
} }
replaceQueryTypeToCurrentTab = (key) => { replaceQueryTypeToCurrentTab = (key) => {
history.pushState(undefined, "", `?type=${key ?? this.state.activeTab}`) history.pushState(undefined, "", `?type=${key ?? this.state.activeTab}`)
} }
tabChange = async (key) => { tabChange = async (key) => {
if (this.props.beforeTabChange) { if (this.props.beforeTabChange) {
await this.props.beforeTabChange(key) await this.props.beforeTabChange(key)
} }
await this.setState({ activeTab: key }) await this.setState({ activeTab: key })
if (this.props.useSetQueryType) { if (this.props.useSetQueryType) {
this.replaceQueryTypeToCurrentTab(key) this.replaceQueryTypeToCurrentTab(key)
} }
if (this.props.onTabChange) { if (this.props.onTabChange) {
this.props.onTabChange(key) this.props.onTabChange(key)
} }
} }
handleTabChange = async (key) => { handleTabChange = async (key) => {
if (this.state.activeTab === key) return if (this.state.activeTab === key) return
if (this.props.transition) { if (this.props.transition) {
if (document.startViewTransition) { if (document.startViewTransition) {
return document.startViewTransition(() => { return document.startViewTransition(() => {
this.tabChange(key) this.tabChange(key)
}) })
} }
console.warn("PagePanelWithNavMenu: transition is enabled but document.startViewTransition is not compatible with your browser") console.warn(
"PagePanelWithNavMenu: transition is enabled but document.startViewTransition is not compatible with your browser",
)
if (this.primaryPanelRef.current && this.primaryPanelRef.current?.classList) { if (
// set to primary panel fade-opacity-leave class this.primaryPanelRef.current &&
this.primaryPanelRef.current.classList.add("fade-opacity-leave") this.primaryPanelRef.current?.classList
) {
// set to primary panel fade-opacity-leave class
this.primaryPanelRef.current.classList.add("fade-opacity-leave")
// remove fade-opacity-leave class after animation // remove fade-opacity-leave class after animation
setTimeout(() => { setTimeout(() => {
this.primaryPanelRef.current.classList.remove("fade-opacity-leave") this.primaryPanelRef.current.classList.remove(
}, 300) "fade-opacity-leave",
} )
}, 300)
}
await new Promise(resolve => setTimeout(resolve, 200)) await new Promise((resolve) => setTimeout(resolve, 200))
} }
return this.tabChange(key) return this.tabChange(key)
} }
getItems = (items) => { getItems = (items) => {
if (!Array.isArray(items)) { if (!Array.isArray(items)) {
console.error(`[items] is not an (array), received (${typeof items})`) console.error(
return [] `[items] is not an (array), received (${typeof items})`,
} )
return []
}
items = items.map((item) => { items = items.map((item) => {
return { return {
key: item.key, key: item.key,
icon: createIconRender(item.icon), icon: createIconRender(item.icon),
label: item.label, label: item.label,
children: item.children && this.getItems(item.children), children: item.children && this.getItems(item.children),
disabled: item.disabled, disabled: item.disabled,
props: item.props ?? {}, props: item.props ?? {},
} }
}) })
return items return items
} }
render() { render() {
return <> return (
{ <>
app.isMobile && app.layout.top_bar.render(<NavMenu {app.isMobile &&
activeKey={this.state.activeTab} app.layout.top_bar.render(
items={this.getItems(this.props.tabs)} <NavMenu
onClickItem={(key) => this.handleTabChange(key)} activeKey={this.state.activeTab}
/>) items={this.getItems(this.props.tabs)}
} onClickItem={(key) => this.handleTabChange(key)}
/>,
)}
{ {!app.isMobile &&
!app.isMobile && app.layout.header.render(<NavMenu app.layout.header.render(
header={this.props.navMenuHeader} <NavMenu
activeKey={this.state.activeTab} header={this.props.navMenuHeader}
items={this.getItems([...this.props.tabs ?? [], ...this.props.extraItems ?? []])} activeKey={this.state.activeTab}
onClickItem={(key) => this.handleTabChange(key)} items={this.getItems([
renderNames ...(this.props.tabs ?? []),
> ...(this.props.extraItems ?? []),
{ ])}
Array.isArray(this.state.renders) && [ onClickItem={(key) => this.handleTabChange(key)}
this.state.renders.map((render, index) => { renderNames
return React.createElement(render.component, { >
...render.options.props, {Array.isArray(this.state.renders) && [
ref: render.ref this.state.renders.map((render, index) => {
}) return React.createElement(
}) render.component,
] {
} ...render.options.props,
</NavMenu>) ref: render.ref,
} },
)
}),
]}
</NavMenu>,
)}
<div className="pagePanels"> <div className="pagePanels">
<div className="panel" ref={this.primaryPanelRef}> <div className="panel" ref={this.primaryPanelRef}>
{ {this.renderActiveTab()}
this.renderActiveTab() </div>
} </div>
</div> </>
</div> )
</> }
}
} }
export default PagePanelWithNavMenu export default PagePanelWithNavMenu