user25699736
user25699736

Reputation: 1

How to render a React component inside of a Gridstack Widget?

I have the following issue: I am trying to render different React components (Rechart components with dummy data) inside some Gridstack widgets and I couldn't really find a staight forward example for doing that. I tried using React portals but I can't say I am very fond of this solution, since the portals are taken out of the normal component tree. I also tried rendering the components directly inside the content div, but it doesn't work (the widget sits on top of the chart). Is there some other way to render dynamic components inside Gridstack widgets?

PORTAL EXAMPLE

import React from "react"
import "gridstack/dist/gridstack.min.css"
import { GridStack } from "gridstack"
import "./GridStack1.css"
import { Button } from "@mui/material"
import LineChartExample, { IChartData } from "./LineChartExample"
import { createPortal } from "react-dom"
import BarChartExample from "./BarChartExample"

const enum ChartType {
    Line = 0,
    Bar = 1
}

interface IWidgetProps {
    containerId: string
    chartType: ChartType
    chartData: IChartData[]
}

const Widget: React.FunctionComponent<IWidgetProps> = props => {
    const { containerId, chartType, chartData } = props

    const container = document.getElementById(containerId)

    return container
        ? chartType === ChartType.Line
            ? createPortal(<LineChartExample data={chartData} />, container)
            : createPortal(<BarChartExample data={chartData} />, container)
        : null
}

const GridStackExample: React.FunctionComponent = () => {
    const gridRef = React.useRef(null)
    const [grid, setGrid] = React.useState<GridStack>()
    const [widgetPortals, setWidgetPortals] = React.useState<JSX.Element[]>([])

    React.useEffect(() => {
        if (gridRef.current) {
            const newGrid = GridStack.init(
                {
                    float: true,
                    margin: 20,
                    resizable: {
                        handles: "e,se,s,sw,w"
                    }
                },
                gridRef.current
            )
            setGrid(newGrid)
        }

        const newPortals: JSX.Element[] = []

        const items = [
            { id: "item1", w: 3, h: 3 },
            { id: "item2", w: 6, h: 3 },
            { id: "item3", w: 3, h: 3 },
            { id: "item4", w: 6, h: 3 },
            { id: "item5", w: 6, h: 3 },
            { id: "item6", w: 12, h: 4 }
        ]

        items.forEach((item, index) => {
            const el = document.createElement("div")
            el.className = "grid-stack-item"
            el.innerHTML = `<div class="grid-stack-item-content" id="${item.id}"> </div>`

            if (index < 2) {
                newPortals.push(
                    <Widget
                        key={item.id}
                        containerId={item.id}
                        chartData={[
                            { name: "Page A", uv: 400, pv: 2400, amt: 2400 }
                        ]}
                        chartType={ChartType.Line}
                    />
                )
            } else {
                newPortals.push(
                    <Widget
                        key={item.id}
                        containerId={item.id}
                        chartData={[
                            { name: "Page A", uv: 400, pv: 2400, amt: 2400 }
                        ]}
                        chartType={ChartType.Bar}
                    />
                )
            }
            grid?.addWidget(el, { w: item.w, h: item.h })
        })

        setWidgetPortals(newPortals)

        return () => {
            if (grid) {
                grid.destroy()
            }
        }
    }, [grid])


    return (
        <React.Fragment>
            <div className="grid-stack" ref={gridRef}></div>
            {widgetPortals}
        </React.Fragment>
    )
}

export default GridStackExample

EXAMPLE RENDERING THE REACT COMPONENT DIRECTLY INSIDE THE WIDGET'S CONTENT

import React from "react"
import "gridstack/dist/gridstack.min.css"
import { GridStack /* , GridStackWidget  */ } from "gridstack"
import "./GridStack1.css"
import { Button } from "@mui/material"
import LineChartExample, { IChartData } from "./LineChartExample"
import BarChartExample from "./BarChartExample"

const enum ChartType {
    Line = 0,
    Bar = 1
}

interface IWidgetSetting {
    id: string
    chartType: ChartType
    chartData: IChartData[]
}

function mapWidgetIdsToWidgetSettings(ids: string[]): IWidgetSetting[] {
    const settings: IWidgetSetting[] = []
    ids.forEach((id, index) => {
        const setting: IWidgetSetting = {
            id,
            chartData: [{ name: "Page A", uv: 400, pv: 2400, amt: 2400 }],
            chartType: index % 2 === 0 ? ChartType.Bar : ChartType.Line
        }
        settings.push(setting)
    })

    return settings
}

const Widget: React.FunctionComponent<IWidgetSetting> = props => {
    const { id, chartType, chartData } = props

    return (
        <div className="grid-stack-item" data-gs-id={id}>
            <div className="grid-stack-item-content">
                {chartType === ChartType.Line ? (
                    <LineChartExample data={chartData} />
                ) : (
                    <BarChartExample data={chartData} />
                )}
            </div>
        </div>
    )
}

const GridStackExample: React.FunctionComponent = () => {

    const gridRef = React.useRef(null)
    const [grid, setGrid] = React.useState<GridStack>()
    const [widgetSettings, setWidgetSettings] = React.useState<
        IWidgetSetting[]
    >([])

    React.useEffect(() => {

        if (gridRef.current) {
            const newGrid = GridStack.init(
                {
                    float: true
                },
                gridRef.current
            )
            setGrid(newGrid)
        }

        const items = [
            { id: "item1", w: 3, h: 3 },
            { id: "item2", w: 6, h: 3 },
            { id: "item3", w: 3, h: 3 },
            { id: "item4", w: 6, h: 3 },
            { id: "item5", w: 6, h: 3 },
            { id: "item6", w: 12, h: 4 }
        ]

        items.forEach(item => {
            const el = document.createElement("div")
            el.setAttribute("data-gs-id", item.id)
            el.className = "grid-stack-item"
            el.innerHTML = '<div class="grid-stack-item-content"></div>'
            grid?.addWidget(el, { w: item.w, h: item.h })
        })

        const widgetIds = items.map(i => i.id)

        const gridWidgetsSettings = mapWidgetIdsToWidgetSettings(widgetIds)
        setWidgetSettings(gridWidgetsSettings)

        return () => {
            if (grid) {
                grid.destroy()
            }
        }
    }, [grid])

    return (
        <React.Fragment>
            <div className="grid-stack" ref={gridRef}>
                {widgetSettings.map(setting => (
                    <Widget
                        key={setting.id}
                        id={setting.id}
                        chartData={setting.chartData}
                        chartType={setting.chartType}
                    />
                ))}
            </div>
        </React.Fragment>
    )
}

export default GridStackExample

Upvotes: 0

Views: 340

Answers (0)

Related Questions