enhancement: toolbar now has accordion/collapsible
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { memo, useEffect, useState } from "react"
|
||||
|
||||
import { Checkbox, ColorPicker, Input, InputNumber, Select } from "antd"
|
||||
import {
|
||||
Checkbox, ColorPicker, Input,
|
||||
InputNumber, Select, Collapse
|
||||
} from "antd"
|
||||
|
||||
import { capitalize } from "../utils/common"
|
||||
import Tools from "./constants/tools.js"
|
||||
@@ -128,7 +131,83 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
}
|
||||
|
||||
|
||||
const renderWidgets = (obj, parentKey = "") => {
|
||||
const renderTool = (keyName, val) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{val.tool === Tools.INPUT && (
|
||||
<Input
|
||||
{...val.toolProps}
|
||||
value={val.value}
|
||||
onChange={(e) => handleChange(e.target.value, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.NUMBER_INPUT && (
|
||||
<InputNumber
|
||||
{...val.toolProps}
|
||||
value={val.value || 0}
|
||||
size="small"
|
||||
onChange={(value) => handleChange(value, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.COLOR_PICKER && (
|
||||
<ColorPicker
|
||||
// defaultValue={val.value || "#fff"}
|
||||
value={val.value || "#fff"}
|
||||
disabledAlpha
|
||||
arrow={false}
|
||||
size="middle"
|
||||
showText
|
||||
format="hex"
|
||||
placement="bottomRight"
|
||||
className="tw-w-fit !tw-min-w-[110px]"
|
||||
onChange={(value) => handleChange(value.toHexString(), val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.SELECT_DROPDOWN && (
|
||||
<Select
|
||||
options={val.options}
|
||||
showSearch
|
||||
value={val.value || ""}
|
||||
placeholder={`${val.label}`}
|
||||
onChange={(value) => handleChange(value, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.CHECK_BUTTON && (
|
||||
<Checkbox
|
||||
checked={val.value}
|
||||
defaultChecked={val.value}
|
||||
onChange={(e) => handleChange(e.target.checked, val.onChange)}
|
||||
>{val.label}</Checkbox>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.INPUT_RADIO_LIST && (
|
||||
<DynamicRadioInputList
|
||||
defaultInputs={val.value.inputs}
|
||||
defaultSelected={val.value.selectedRadio}
|
||||
onChange={({ inputs, selectedRadio }) => handleChange({ inputs, selectedRadio }, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
val.tool === Tools.LAYOUT_MANAGER && (
|
||||
renderLayoutManager(val)
|
||||
)
|
||||
}
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
const renderToolbar = (obj, parentKey = "", toolCount=0) => {
|
||||
const keys = []
|
||||
|
||||
return Object.entries(obj).map(([key, val], i) => {
|
||||
const keyName = parentKey ? `${parentKey}.${key}` : key
|
||||
|
||||
@@ -136,81 +215,34 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
const isFirstLevel = parentKey === ""
|
||||
|
||||
const outerLabelClass = isFirstLevel
|
||||
? "tw-text-base tw-text-blue-700 tw-font-medium"
|
||||
: "tw-text-base"
|
||||
? "tw-text-sm tw-text-black tw-font-medium"
|
||||
: "tw-text-sm"
|
||||
|
||||
// Render tool widgets
|
||||
if (typeof val === "object" && val.tool) {
|
||||
return (
|
||||
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
|
||||
<div className={`${isFirstLevel ? outerLabelClass : "tw-text-sm"}`}>{val.label}</div>
|
||||
|
||||
if (isFirstLevel && keys.length < 3) keys.push(keyName)
|
||||
|
||||
if (isFirstLevel){
|
||||
|
||||
{val.tool === Tools.INPUT && (
|
||||
<Input
|
||||
{...val.toolProps}
|
||||
value={val.value}
|
||||
onChange={(e) => handleChange(e.target.value, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
return (
|
||||
<Collapse key={keyName} ghost defaultActiveKey={keys}>
|
||||
<Collapse.Panel header={val.label} key={keyName}>
|
||||
{renderTool(keyName, val)}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
)
|
||||
|
||||
{val.tool === Tools.NUMBER_INPUT && (
|
||||
<InputNumber
|
||||
{...val.toolProps}
|
||||
value={val.value || 0}
|
||||
size="small"
|
||||
onChange={(value) => handleChange(value, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
|
||||
{val.tool === Tools.COLOR_PICKER && (
|
||||
<ColorPicker
|
||||
// defaultValue={val.value || "#fff"}
|
||||
value={val.value || "#fff"}
|
||||
disabledAlpha
|
||||
arrow={false}
|
||||
size="middle"
|
||||
showText
|
||||
format="hex"
|
||||
placement="bottomRight"
|
||||
className="tw-w-fit !tw-min-w-[110px]"
|
||||
onChange={(value) => handleChange(value.toHexString(), val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.SELECT_DROPDOWN && (
|
||||
<Select
|
||||
options={val.options}
|
||||
showSearch
|
||||
value={val.value || ""}
|
||||
placeholder={`${val.label}`}
|
||||
onChange={(value) => handleChange(value, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.CHECK_BUTTON && (
|
||||
<Checkbox
|
||||
checked={val.value}
|
||||
defaultChecked={val.value}
|
||||
onChange={(e) => handleChange(e.target.checked, val.onChange)}
|
||||
>{val.label}</Checkbox>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.INPUT_RADIO_LIST && (
|
||||
<DynamicRadioInputList
|
||||
defaultInputs={val.value.inputs}
|
||||
defaultSelected={val.value.selectedRadio}
|
||||
onChange={({inputs, selectedRadio}) => handleChange({inputs, selectedRadio}, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
val.tool === Tools.LAYOUT_MANAGER && (
|
||||
renderLayoutManager(val)
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
);
|
||||
else
|
||||
return (
|
||||
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
|
||||
<div className={`${isFirstLevel ? outerLabelClass : "tw-text-sm"}`}>{val.label}</div>
|
||||
{renderTool(keyName, val)}
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
// Handle nested objects and horizontal display for inner elements
|
||||
@@ -219,13 +251,29 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
? "tw-flex tw-flex-row tw-flex-wrap tw-content-start tw-gap-4"
|
||||
: "tw-flex tw-flex-col tw-gap-2"
|
||||
|
||||
return (
|
||||
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
|
||||
{/* Outer label highlighted in blue for first-level */}
|
||||
<div className={outerLabelClass}>{val.label}</div>
|
||||
<div className={`${containerClass} tw-px-2`}>{renderWidgets(val, keyName)}</div>
|
||||
</div>
|
||||
)
|
||||
if (isFirstLevel && keys.length < 3) keys.push(keyName)
|
||||
|
||||
if (isFirstLevel){
|
||||
return (
|
||||
<Collapse key={keyName} ghost defaultActiveKey={keys}>
|
||||
<Collapse.Panel header={val.label} key={keyName}>
|
||||
<div className={`${containerClass} tw-px-2`}>
|
||||
{renderToolbar(val, keyName, toolCount+1)}
|
||||
</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
|
||||
{/* Outer label highlighted in blue for first-level */}
|
||||
<div className={outerLabelClass}>{val.label}</div>
|
||||
<div className={`${containerClass} tw-px-2`}>
|
||||
{renderToolbar(val, keyName, toolCount+1)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -235,15 +283,17 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
return (
|
||||
<div
|
||||
className={`tw-absolute tw-top-20 tw-right-5 tw-bg-white ${toolbarOpen ? "tw-w-[280px]" : "tw-w-0"
|
||||
} tw-px-4 tw-p-2 tw-h-[600px] tw-rounded-md tw-z-[1000] tw-shadow-lg
|
||||
} tw-px-3 tw-p-2 tw-h-[600px] tw-rounded-md tw-z-[1000] tw-shadow-lg
|
||||
tw-transition-transform tw-duration-75 tw-overflow-x-hidden
|
||||
tw-flex tw-flex-col tw-gap-2 tw-overflow-y-auto`}
|
||||
>
|
||||
<h3 className="tw-text-xl tw-text-center">
|
||||
<h3 className="tw-text-lg tw-text-center">
|
||||
{capitalize(`${widgetType || ""}`).replace(/_/g, " ")}
|
||||
</h3>
|
||||
|
||||
<div className="tw-flex tw-flex-col tw-gap-4">{renderWidgets(toolbarAttrs || {})}</div>
|
||||
<div className="tw-flex tw-flex-col tw-gap-2">
|
||||
{renderToolbar(toolbarAttrs || {})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user