mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
some improvements on component renders
This commit is contained in:
parent
08ba20b275
commit
1c5c213b96
@ -6,191 +6,194 @@ import { Icons } from "@components/Icons"
|
|||||||
import WindowContext from "./context"
|
import WindowContext from "./context"
|
||||||
|
|
||||||
export default class DefaultWindowRender extends React.Component {
|
export default class DefaultWindowRender extends React.Component {
|
||||||
static contextType = WindowContext
|
static contextType = WindowContext
|
||||||
|
|
||||||
ref = React.createRef()
|
ref = React.createRef()
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
renderError: false,
|
renderError: false,
|
||||||
title: null,
|
title: null,
|
||||||
actions: [],
|
actions: [],
|
||||||
dimensions: {
|
dimensions: {
|
||||||
height: this.props.height ?? 600,
|
height: this.props.height ?? 600,
|
||||||
width: this.props.width ?? 400,
|
width: this.props.width ?? 400,
|
||||||
},
|
},
|
||||||
position: this.props.defaultPosition,
|
position: this.props.defaultPosition,
|
||||||
visible: false,
|
visible: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
this.setDefaultActions()
|
this.setDefaultActions()
|
||||||
|
|
||||||
if (typeof this.props.actions !== "undefined") {
|
if (typeof this.props.actions !== "undefined") {
|
||||||
if (Array.isArray(this.props.actions)) {
|
if (Array.isArray(this.props.actions)) {
|
||||||
const actions = this.state.actions ?? []
|
const actions = this.state.actions ?? []
|
||||||
|
|
||||||
this.props.actions.forEach((action) => {
|
this.props.actions.forEach((action) => {
|
||||||
actions.push(action)
|
actions.push(action)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({ actions })
|
this.setState({ actions })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.position) {
|
if (!this.state.position) {
|
||||||
this.setState({ position: this.getCenterPosition() })
|
this.setState({ position: this.getCenterPosition() })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleVisibility(true)
|
this.toggleVisibility(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch = (error) => {
|
componentDidCatch = (error) => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
renderError: error,
|
renderError: error,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitle = (title) => {
|
updateTitle = (title) => {
|
||||||
this.setState({ title })
|
this.setState({ title })
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDimensions = (dimensions = {
|
updateDimensions = (
|
||||||
width: 0,
|
dimensions = {
|
||||||
height: 0,
|
width: 0,
|
||||||
}) => {
|
height: 0,
|
||||||
this.ref.current?.updateSize({
|
},
|
||||||
width: dimensions.width,
|
) => {
|
||||||
height: dimensions.height,
|
this.ref.current?.updateSize({
|
||||||
})
|
width: dimensions.width,
|
||||||
}
|
height: dimensions.height,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
updatePosition = (position = {
|
updatePosition = (
|
||||||
x: 0,
|
position = {
|
||||||
y: 0,
|
x: 0,
|
||||||
}) => {
|
y: 0,
|
||||||
this.ref.current?.updatePosition({
|
},
|
||||||
x: position.x,
|
) => {
|
||||||
y: position.y,
|
this.ref.current?.updatePosition({
|
||||||
})
|
x: position.x,
|
||||||
}
|
y: position.y,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
toggleVisibility = (to) => {
|
toggleVisibility = (to) => {
|
||||||
this.setState({ visible: to ?? !this.state.visible })
|
this.setState({ visible: to ?? !this.state.visible })
|
||||||
}
|
}
|
||||||
|
|
||||||
getCenterPosition = () => {
|
getCenterPosition = () => {
|
||||||
const dimensions = this.state?.dimensions ?? {}
|
const dimensions = this.state?.dimensions ?? {}
|
||||||
|
|
||||||
const windowHeight = dimensions.height ?? 600
|
const windowHeight = dimensions.height ?? 600
|
||||||
const windowWidth = dimensions.width ?? 400
|
const windowWidth = dimensions.width ?? 400
|
||||||
|
|
||||||
return {
|
const y = window.innerHeight / 2 - windowHeight / 2
|
||||||
x: window.innerWidth / 2 - windowWidth / 2,
|
const x = window.innerWidth / 2 - windowWidth / 2
|
||||||
y: window.innerHeight / 2 - windowHeight / 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultActions = () => {
|
return {
|
||||||
const { actions } = this.state
|
x: x,
|
||||||
|
y: y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actions.push({
|
setDefaultActions = () => {
|
||||||
key: "close",
|
const { actions } = this.state
|
||||||
render: () => <Icons.FiXCircle style={{ margin: 0, padding: 0 }} />,
|
|
||||||
onClick: () => {
|
|
||||||
this.props.close()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
this.setState({ actions })
|
actions.push({
|
||||||
}
|
key: "close",
|
||||||
|
render: () => <Icons.FiXCircle style={{ margin: 0, padding: 0 }} />,
|
||||||
|
onClick: () => {
|
||||||
|
this.props.close()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
renderActions = () => {
|
this.setState({ actions })
|
||||||
const actions = this.state.actions
|
}
|
||||||
|
|
||||||
if (Array.isArray(actions)) {
|
renderActions = () => {
|
||||||
return actions.map((action) => {
|
const actions = this.state.actions
|
||||||
return (
|
|
||||||
<div key={action.key} onClick={action.onClick} {...action.props}>
|
|
||||||
{React.isValidElement(action.render) ? action.render : React.createElement(action.render)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
if (Array.isArray(actions)) {
|
||||||
}
|
return actions.map((action) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={action.key}
|
||||||
|
onClick={action.onClick}
|
||||||
|
{...action.props}
|
||||||
|
>
|
||||||
|
{React.isValidElement(action.render)
|
||||||
|
? action.render
|
||||||
|
: React.createElement(action.render)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
getComponentRender = () => {
|
return null
|
||||||
const ctx = {
|
}
|
||||||
...this.props,
|
|
||||||
updateTitle: this.updateTitle,
|
|
||||||
updateDimensions: this.updateDimensions,
|
|
||||||
updatePosition: this.updatePosition,
|
|
||||||
close: this.props.close,
|
|
||||||
position: this.state.position,
|
|
||||||
dimensions: this.state.dimensions,
|
|
||||||
}
|
|
||||||
|
|
||||||
return <WindowContext.Provider value={ctx}>
|
getComponentRender = () => {
|
||||||
{
|
const ctx = {
|
||||||
React.isValidElement(this.props.children)
|
...this.props,
|
||||||
? React.cloneElement(this.props.children, ctx)
|
updateTitle: this.updateTitle,
|
||||||
: React.createElement(this.props.children, ctx)
|
updateDimensions: this.updateDimensions,
|
||||||
}
|
updatePosition: this.updatePosition,
|
||||||
</WindowContext.Provider>
|
close: this.props.close,
|
||||||
}
|
position: this.state.position,
|
||||||
|
dimensions: this.state.dimensions,
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { position, dimensions, visible } = this.state
|
<WindowContext.Provider value={ctx}>
|
||||||
|
{React.isValidElement(this.props.children)
|
||||||
|
? React.cloneElement(this.props.children, ctx)
|
||||||
|
: React.createElement(this.props.children, ctx)}
|
||||||
|
</WindowContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!visible) {
|
render() {
|
||||||
return null
|
const { position, dimensions, visible } = this.state
|
||||||
}
|
|
||||||
|
|
||||||
return <Rnd
|
if (!visible) {
|
||||||
ref={this.ref}
|
return null
|
||||||
default={{
|
}
|
||||||
...position,
|
|
||||||
...dimensions,
|
|
||||||
}}
|
|
||||||
minWidth={
|
|
||||||
this.props.minWidth
|
|
||||||
}
|
|
||||||
minHeight={
|
|
||||||
this.props.minHeight
|
|
||||||
}
|
|
||||||
enableResizing={
|
|
||||||
this.props.enableResizing ?? true
|
|
||||||
}
|
|
||||||
disableDragging={
|
|
||||||
this.props.disableDragging ?? false
|
|
||||||
}
|
|
||||||
dragHandleClassName={
|
|
||||||
this.props.dragHandleClassName ?? "window_topbar"
|
|
||||||
}
|
|
||||||
bounds={
|
|
||||||
this.props.bounds ?? "#root"
|
|
||||||
}
|
|
||||||
className="window_wrapper"
|
|
||||||
>
|
|
||||||
<div className="window_topbar">
|
|
||||||
<div className="title">{this.state.title}</div>
|
|
||||||
<div className="actions">{this.renderActions()}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="window_body">
|
return (
|
||||||
{
|
<Rnd
|
||||||
this.state.renderError && <div className="render_error">
|
ref={this.ref}
|
||||||
<h1>Render Error</h1>
|
default={{
|
||||||
<code>{this.state.renderError.message}</code>
|
...position,
|
||||||
</div>
|
...dimensions,
|
||||||
}
|
}}
|
||||||
{
|
minWidth={this.props.minWidth}
|
||||||
!this.state.renderError && this.getComponentRender()
|
minHeight={this.props.minHeight}
|
||||||
}
|
enableResizing={this.props.enableResizing ?? true}
|
||||||
</div>
|
disableDragging={this.props.disableDragging ?? false}
|
||||||
</Rnd>
|
dragHandleClassName={
|
||||||
}
|
this.props.dragHandleClassName ?? "window_topbar"
|
||||||
|
}
|
||||||
|
//bounds={this.props.bounds ?? "#root"}
|
||||||
|
className="window_wrapper"
|
||||||
|
>
|
||||||
|
<div className="window_topbar">
|
||||||
|
<div className="title">{this.state.title}</div>
|
||||||
|
<div className="actions">{this.renderActions()}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="window_body">
|
||||||
|
{this.state.renderError && (
|
||||||
|
<div className="render_error">
|
||||||
|
<h1>Render Error</h1>
|
||||||
|
<code>{this.state.renderError.message}</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!this.state.renderError && this.getComponentRender()}
|
||||||
|
</div>
|
||||||
|
</Rnd>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -8,168 +8,180 @@ import DefaultWindow from "./components/defaultWindow"
|
|||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
export default class WindowManager extends Core {
|
export default class WindowManager extends Core {
|
||||||
static namespace = "window_mng"
|
static namespace = "window_mng"
|
||||||
|
|
||||||
static idMount = "windows"
|
static idMount = "windows"
|
||||||
|
|
||||||
root = null
|
root = null
|
||||||
windows = []
|
windows = []
|
||||||
|
|
||||||
public = {
|
public = {
|
||||||
close: this.close.bind(this),
|
close: this.close.bind(this),
|
||||||
render: this.render.bind(this),
|
render: this.render.bind(this),
|
||||||
}
|
}
|
||||||
|
|
||||||
async onInitialize() {
|
async onInitialize() {
|
||||||
this.root = document.createElement("div")
|
this.root = document.createElement("div")
|
||||||
|
|
||||||
this.root.setAttribute("id", this.constructor.idMount)
|
this.root.setAttribute("id", this.constructor.idMount)
|
||||||
|
|
||||||
document.body.append(this.root)
|
document.body.append(this.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWrapperClick = (id, event) => {
|
handleWrapperClick = (id, event) => {
|
||||||
const element = this.root.querySelector(`#${id}`)
|
const element = this.root.querySelector(`#${id}`)
|
||||||
|
|
||||||
if (element) {
|
if (element) {
|
||||||
if (!element.contains(event.target)) {
|
if (!element.contains(event.target)) {
|
||||||
this.close(id)
|
this.close(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new element with the specified id and appends it to the root element.
|
* Creates a new element with the specified id and appends it to the root element.
|
||||||
* If the element already exists and createOrUpdate option is false, it will throw an error.
|
* If the element already exists and createOrUpdate option is false, it will throw an error.
|
||||||
* If the element already exists and createOrUpdate option is true, it will update the element.
|
* If the element already exists and createOrUpdate option is true, it will update the element.
|
||||||
* If useFrame option is true, it wraps the fragment with a DefaultWindow component before rendering.
|
* If useFrame option is true, it wraps the fragment with a DefaultWindow component before rendering.
|
||||||
*
|
*
|
||||||
* @param {string} id - The id of the element to create or update.
|
* @param {string} id - The id of the element to create or update.
|
||||||
* @param {ReactElement} fragment - The React element to render inside the created element.
|
* @param {ReactElement} fragment - The React element to render inside the created element.
|
||||||
* @param {Object} options - The options for creating or updating the element.
|
* @param {Object} options - The options for creating or updating the element.
|
||||||
* @param {boolean} options.useFrame - Specifies whether to wrap the fragment with a DefaultWindow component.
|
* @param {boolean} options.useFrame - Specifies whether to wrap the fragment with a DefaultWindow component.
|
||||||
* @param {boolean} options.createOrUpdate - Specifies whether to create a new element or update an existing one.
|
* @param {boolean} options.createOrUpdate - Specifies whether to create a new element or update an existing one.
|
||||||
* @return {HTMLElement} The created or updated element.
|
* @return {HTMLElement} The created or updated element.
|
||||||
*/
|
*/
|
||||||
async render(
|
async render(
|
||||||
id,
|
id,
|
||||||
fragment,
|
fragment,
|
||||||
{
|
{
|
||||||
useFrame = false,
|
useFrame = false,
|
||||||
onClose = null,
|
onClose = null,
|
||||||
createOrUpdate = false,
|
createOrUpdate = false,
|
||||||
closeOnClickOutside = false,
|
closeOnClickOutside = false,
|
||||||
} = {}
|
} = {},
|
||||||
) {
|
) {
|
||||||
let element = document.createElement("div")
|
let element = document.createElement("div")
|
||||||
let node = null
|
let node = null
|
||||||
let win = null
|
let win = null
|
||||||
|
|
||||||
// check if window already exist
|
// check if window already exist
|
||||||
// if exist, try to automatically generate a new id
|
// if exist, try to automatically generate a new id
|
||||||
if (this.root.querySelector(`#${id}`) && !createOrUpdate) {
|
if (this.root.querySelector(`#${id}`) && !createOrUpdate) {
|
||||||
const newId = `${id}_${Date.now()}`
|
const newId = `${id}_${Date.now()}`
|
||||||
|
|
||||||
this.console.warn(`Window ${id} already exist, overwritting id to ${newId}.\nYou can use {createOrUpdate = true} option to force refresh render of window`)
|
this.console.warn(
|
||||||
|
`Window ${id} already exist, overwritting id to ${newId}.\nYou can use {createOrUpdate = true} option to force refresh render of window`,
|
||||||
|
)
|
||||||
|
|
||||||
id = newId
|
id = newId
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if window already exist, if exist and createOrUpdate is true, update the element
|
// check if window already exist, if exist and createOrUpdate is true, update the element
|
||||||
// if not exist, create a new element
|
// if not exist, create a new element
|
||||||
if (this.root.querySelector(`#${id}`) && createOrUpdate) {
|
if (this.root.querySelector(`#${id}`) && createOrUpdate) {
|
||||||
element = document.getElementById(id)
|
element = document.getElementById(id)
|
||||||
|
|
||||||
win = this.windows.find((_node) => {
|
win = this.windows.find((_node) => {
|
||||||
return _node.id === id
|
return _node.id === id
|
||||||
})
|
})
|
||||||
|
|
||||||
if (win) {
|
if (win) {
|
||||||
node = win.node
|
node = win.node
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
element.setAttribute("id", id)
|
element.setAttribute("id", id)
|
||||||
|
|
||||||
this.root.appendChild(element)
|
this.root.appendChild(element)
|
||||||
|
|
||||||
node = createRoot(element)
|
node = createRoot(element)
|
||||||
|
|
||||||
win = {
|
win = {
|
||||||
id: id,
|
id: id,
|
||||||
node: node,
|
node: node,
|
||||||
onClose: onClose,
|
onClose: onClose,
|
||||||
closeOnClickOutside: closeOnClickOutside,
|
closeOnClickOutside: closeOnClickOutside,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.windows.push(win)
|
this.windows.push(win)
|
||||||
|
|
||||||
// if closeOnClickOutside is true, add click event listener
|
// if closeOnClickOutside is true, add click event listener
|
||||||
if (closeOnClickOutside === true) {
|
if (closeOnClickOutside === true) {
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
(e) => this.handleWrapperClick(id, e),
|
(e) => this.handleWrapperClick(id, e),
|
||||||
{ once: true },
|
{ once: true },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if useFrame is true, wrap the fragment with a DefaultWindow component
|
// if useFrame is true, wrap the fragment with a DefaultWindow component
|
||||||
if (useFrame) {
|
if (useFrame) {
|
||||||
fragment = <DefaultWindow>
|
fragment = <DefaultWindow>{fragment}</DefaultWindow>
|
||||||
{fragment}
|
}
|
||||||
</DefaultWindow>
|
|
||||||
}
|
|
||||||
|
|
||||||
node.render(React.cloneElement(fragment, {
|
if (React.isValidElement(fragment)) {
|
||||||
close: () => {
|
node.render(
|
||||||
this.close(id, onClose)
|
React.cloneElement(fragment, {
|
||||||
}
|
close: () => {
|
||||||
}))
|
this.close(id, onClose)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
node.render(
|
||||||
|
React.createElement(fragment, {
|
||||||
|
close: () => {
|
||||||
|
this.close(id, onClose)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
element,
|
element,
|
||||||
...win,
|
...win,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the window with the given ID.
|
* Closes the window with the given ID.
|
||||||
*
|
*
|
||||||
* @param {string} id - The ID of the window to be closed.
|
* @param {string} id - The ID of the window to be closed.
|
||||||
* @return {boolean} Returns true if the window was successfully closed, false otherwise.
|
* @return {boolean} Returns true if the window was successfully closed, false otherwise.
|
||||||
*/
|
*/
|
||||||
async close(id) {
|
async close(id) {
|
||||||
const element = document.getElementById(id)
|
const element = document.getElementById(id)
|
||||||
|
|
||||||
const win = this.windows.find((node) => {
|
const win = this.windows.find((node) => {
|
||||||
return node.id === id
|
return node.id === id
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!win || !element) {
|
if (!win || !element) {
|
||||||
this.console.error(`Window [${id}] not found`)
|
this.console.error(`Window [${id}] not found`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.console.debug(`Closing window ${id}`, win, element)
|
this.console.debug(`Closing window ${id}`, win, element)
|
||||||
|
|
||||||
// if onClose callback is defined, call it
|
// if onClose callback is defined, call it
|
||||||
if (typeof win.onClose === "function") {
|
if (typeof win.onClose === "function") {
|
||||||
this.console.debug(`Trigging close callback for window ${id}`)
|
this.console.debug(`Trigging close callback for window ${id}`)
|
||||||
await win.onClose()
|
await win.onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the element from the DOM
|
// remove the element from the DOM
|
||||||
this.console.debug(`Removing element from DOM for window ${id}`)
|
this.console.debug(`Removing element from DOM for window ${id}`)
|
||||||
win.node.unmount()
|
win.node.unmount()
|
||||||
this.root.removeChild(element)
|
this.root.removeChild(element)
|
||||||
|
|
||||||
// remove the window from the list
|
// remove the window from the list
|
||||||
this.console.debug(`Removing window from list for window ${id}`)
|
this.console.debug(`Removing window from list for window ${id}`)
|
||||||
this.windows = this.windows.filter((node) => {
|
this.windows = this.windows.filter((node) => {
|
||||||
return node.id !== id
|
return node.id !== id
|
||||||
})
|
})
|
||||||
|
|
||||||
this.console.debug(`Window ${id} closed`)
|
this.console.debug(`Window ${id} closed`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user