{/* Canvas */}
{
diff --git a/src/canvas/constants/tools.js b/src/canvas/constants/tools.js
index f10073a..13f1b63 100644
--- a/src/canvas/constants/tools.js
+++ b/src/canvas/constants/tools.js
@@ -1,6 +1,8 @@
const Tools = {
INPUT: "input",
+ NUMBER_INPUT: "number_input",
+ SELECT_DROPDOWN: "select_dropdown",
COLOR_PICKER: "color_picker",
EVENT_HANDLER: "event_handler", // shows a event handler with all the possible function in the dropdown
}
diff --git a/src/canvas/toolbar.js b/src/canvas/toolbar.js
index 61e5154..5d84f61 100644
--- a/src/canvas/toolbar.js
+++ b/src/canvas/toolbar.js
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react"
-import { ColorPicker, Input } from "antd"
+import { ColorPicker, Input, InputNumber, Select } from "antd"
import { capitalize } from "../utils/common"
import Tools from "./constants/tools.js"
@@ -23,20 +23,106 @@ function CanvasToolBar({isOpen, activeWidget, setActiveWidget}){
const handleWidgetNameChange = (e) => {
+ activeWidget?.setWidgetName(e.target.value) // Update widget's internal state
const updatedWidget = { ...activeWidget } // Create a shallow copy of the widget
- updatedWidget?.setWidgetName(e.target.value) // Update widget's internal state
setActiveWidget(updatedWidget) // Update the state with the modified widget
}
+ const handleChange = (attrPath, value, callback) => {
+ // console.log("Value: ", attrPath, value)
+ activeWidget?.setAttrValue(attrPath, value) // Update widget's internal state
+ const updatedWidget = { ...activeWidget }
+
+ if (callback){
+ callback(value)
+ }
+
+ setActiveWidget(updatedWidget)
+ }
+
+
+ const renderWidgets = (obj, parentKey = "") => {
+ return Object.entries(obj).map(([key, val], i) => {
+ // console.log("parent key: ", parentKey)
+ // Build a unique identifier for keys that handle nested structures
+ const keyName = parentKey ? `${parentKey}.${key}` : key
+
+ // Check if the current value is an object and has a "tool" property
+ if (typeof val === "object" && val.tool) {
+ // Render widgets based on the tool type
+ return (
+
+ {
+ parentKey ?
+
{val.label}
+ :
+
{capitalize(key)}
+ }
+
+{
+ val.tool === Tools.NUMBER_INPUT && (
+
handleChange(keyName, value, val.onChange)}
+ />
+ )}
+
+ {
+ val.tool === Tools.COLOR_PICKER && (
+ handleChange(keyName, value.toHexString(), val.onChange)}
+ />
+ )}
+
+ {
+ val.tool === Tools.SELECT_DROPDOWN && (
+
+ )
+ }
+
+ // If the value is another nested object, recursively call renderWidgets
+ if (typeof val === "object") {
+ return (
+
+
{capitalize(key)}
+ {renderWidgets(val, keyName)}
+
+ )
+ }
+
+ return null // Skip rendering for non-object types
+ })
+ }
+
return (
-
+
{capitalize(`${activeWidget?.getWidgetType() || ""}`)}
@@ -48,25 +134,7 @@ function CanvasToolBar({isOpen, activeWidget, setActiveWidget}){
- {
- Object.entries(activeWidget?.state?.attrs || {}).map(([key, value], i) => {
- console.log("valyes: ")
- return (
-
-
{key}
- {
- value?.backgroundColor?.tool === Tools.COLOR_PICKER &&
-
- }
-
- )
-
- })
- }
+ {renderWidgets(activeWidget?.state?.attrs || {})}
diff --git a/src/canvas/widgets/base.js b/src/canvas/widgets/base.js
index f98c12a..9f1771e 100644
--- a/src/canvas/widgets/base.js
+++ b/src/canvas/widgets/base.js
@@ -82,27 +82,53 @@ class Widget extends React.Component{
resizing: false,
resizeCorner: "",
+ pos: {x: 0, y: 0}, // used for outer styling
+ size: {width: 100, height: 100}, // used for outer styling
+ position: "absolute",
+
widgetStyling: {
- position: "absolute",
- left: 0,
- top: 0,
- width: 100,
- height: 100
+ // use for widget's inner styling
},
attrs: {
styling: {
backgroundColor: {
+ label: "Background Color",
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: "",
onChange: (value) => this.setWidgetStyling("backgroundColor", value)
},
foregroundColor: {
+ label: "Foreground Color",
tool: Tools.COLOR_PICKER,
- value: ""
+ value: "",
+ },
+ },
+ layout: {
+ label: "Layout",
+ tool: Tools.SELECT_DROPDOWN, // the tool to display, can be either HTML ELement or a constant string
+ value: "flex",
+ options: [
+ {value: "flex", label: "Flex"},
+ {value: "grid", label: "Grid"},
+ {value: "place", label: "Place"},
+ ],
+ onChange: (value) => this.setWidgetStyling("backgroundColor", value)
+ },
+ size: {
+ width: {
+ label: "Width",
+ tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
+ value: 100,
+ // onChange: (value) => this.setS("backgroundColor", value)
+ },
+ height: {
+ label: "Height",
+ tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
+ value: 100,
+ // onChange: (value) => this.setS("backgroundColor", value)
},
},
- layout: "show", // enables layout use "hide" to hide layout dropdown, takes the layout from this.layout
events: {
event1: {
tool: Tools.EVENT_HANDLER,
@@ -122,6 +148,7 @@ class Widget extends React.Component{
this.isSelected = this.isSelected.bind(this)
this.setWidgetName = this.setWidgetName.bind(this)
+ this.setAttrValue = this.setAttrValue.bind(this)
this.getPos = this.getPos.bind(this)
this.setPos = this.setPos.bind(this)
@@ -215,17 +242,19 @@ class Widget extends React.Component{
// don't change position when resizing the widget
return
}
+ this.setState({
+ pos: {x, y}
+ })
- this.setState((prev) => ({
- // pos: {x: x, y: y}
- widgetStyling: {
- ...prev.widgetStyling,
- left: x,
- top: y,
- }
- }))
+ // this.setState((prev) => ({
+ // // pos: {x: x, y: y}
+ // widgetStyling: {
+ // ...prev.widgetStyling,
+ // left: x,
+ // top: y,
+ // }
+ // }))
- // console.log("POs: ", x, y)
}
setParent(parentId){
@@ -243,7 +272,7 @@ class Widget extends React.Component{
}
getPos(){
- return {x: this.state.widgetStyling.left, y: this.state.widgetStyling.top}
+ return this.state.pos
}
getProps(){
@@ -257,7 +286,7 @@ class Widget extends React.Component{
getSize(){
// const boundingRect = this.getBoundingRect()
- return {width: this.state.widgetStyling.width, height: this.state.widgetStyling.height}
+ return {width: this.state.size.width, height: this.state.size.height}
}
getScaleAwareDimensions() {
@@ -304,6 +333,31 @@ class Widget extends React.Component{
return this.elementRef.current
}
+ /**
+ * Given the key as a path, sets the value for the widget attribute
+ * @param {string} path - path to the key, eg: styling.backgroundColor
+ * @param {any} value
+ */
+ setAttrValue(path, value){
+ this.setState((prevState) => {
+ // Split the path to access the nested property (e.g., "styling.backgroundColor")
+ const keys = path.split('.')
+ const lastKey = keys.pop()
+
+ // Traverse the state and update the nested value immutably
+ let newAttrs = { ...prevState.attrs }
+ let nestedObject = newAttrs
+
+ keys.forEach(key => {
+ nestedObject[key] = { ...nestedObject[key] } // Ensure immutability
+ nestedObject = nestedObject[key]
+ })
+ nestedObject[lastKey].value = value
+
+ return { attrs: newAttrs }
+ })
+ }
+
startResizing(corner, event) {
event.stopPropagation()
this.setState({ resizing: true, resizeCorner: corner })
@@ -315,36 +369,54 @@ class Widget extends React.Component{
})
}
+ setWidgetName(name){
+
+ this.setState((prev) => ({
+ widgetName: name.length > 0 ? name : prev.widgetName
+ }))
+ }
+
+ setWidgetStyling(key, value){
+
+ this.setState((prev) => ({
+ widgetStyling: {
+ ...prev.widgetStyling,
+ [key]: value
+ }
+ }))
+
+ }
+
handleResize(event) {
if (!this.state.resizing) return
- const { resizeCorner, widgetStyling } = this.state
+ const { resizeCorner, size, pos } = this.state
const deltaX = event.movementX
const deltaY = event.movementY
- let newSize = { width: widgetStyling.width, height: widgetStyling.height }
- let newPos = { x: widgetStyling.left, y: widgetStyling.top }
+ let newSize = { ...size }
+ let newPos = { ...pos }
const {width: minWidth, height: minHeight} = this.minSize
const {width: maxWidth, height: maxHeight} = this.maxSize
- console.log("resizing: ", deltaX, deltaY, event)
+ // console.log("resizing: ", deltaX, deltaY, event)
switch (resizeCorner) {
case "nw":
newSize.width = Math.max(minWidth, Math.min(maxWidth, newSize.width - deltaX))
newSize.height = Math.max(minHeight, Math.min(maxHeight, newSize.height - deltaY))
- newPos.x += (newSize.width !== widgetStyling.width) ? deltaX : 0
- newPos.y += (newSize.height !== widgetStyling.height) ? deltaY : 0
+ newPos.x += (newSize.width !== size.width) ? deltaX : 0
+ newPos.y += (newSize.height !== size.height) ? deltaY : 0
break
case "ne":
newSize.width = Math.max(minWidth, Math.min(maxWidth, newSize.width + deltaX))
newSize.height = Math.max(minHeight, Math.min(maxHeight, newSize.height - deltaY))
- newPos.y += (newSize.height !== widgetStyling.height) ? deltaY : 0
+ newPos.y += (newSize.height !== size.height) ? deltaY : 0
break
case "sw":
newSize.width = Math.max(minWidth, Math.min(maxWidth, newSize.width - deltaX))
newSize.height = Math.max(minHeight, Math.min(maxHeight, newSize.height + deltaY))
- newPos.x += (newSize.width !== widgetStyling.width) ? deltaX : 0
+ newPos.x += (newSize.width !== size.width) ? deltaX : 0
break
case "se":
newSize.width = Math.max(minWidth, Math.min(maxWidth, newSize.width + deltaX))
@@ -354,16 +426,7 @@ class Widget extends React.Component{
break
}
- // this.setState({ size: newSize, pos: newPos })
- this.setState((prev) => ({
- widgetStyling: {
- ...prev.widgetStyling,
- left: newPos.x,
- top: newPos.y,
- width: newSize.width,
- height: newSize.height,
- }
- }))
+ this.setState({ size: newSize, pos: newPos })
}
stopResizing() {
@@ -385,28 +448,10 @@ class Widget extends React.Component{
})
}
- setWidgetName(name){
-
- this.setState((prev) => ({
- widgetName: name.length > 0 ? name : prev.widgetName
- }))
- }
-
- setWidgetStyling(key, value){
-
- this.setState((prev) => ({
- widgetStyling: {
- ...prev.widgetStyling,
- [key]: value
- }
- }))
-
- }
-
renderContent(){
// throw new NotImplementedError("render method has to be implemented")
return (
-
+
)
@@ -421,15 +466,14 @@ class Widget extends React.Component{
const widgetStyle = this.state.widgetStyling
- let style = {
- ...widgetStyle,
+ let outerStyle = {
cursor: this.cursor,
zIndex: this.state.zIndex,
position: "absolute", // don't change this if it has to be movable on the canvas
- top: `${widgetStyle.top}px`,
- left: `${widgetStyle.left}px`,
- width: `${widgetStyle.width}px`,
- height: `${widgetStyle.height}px`,
+ top: `${this.state.pos.y}px`,
+ left: `${this.state.pos.x}px`,
+ width: `${this.state.size.width}px`,
+ height: `${this.state.size.height}px`,
}
let selectionStyle = {
@@ -442,8 +486,8 @@ class Widget extends React.Component{
// console.log("selected: ", this.state.selected)
return (
-
{this.renderContent()}
diff --git a/src/styles/index.css b/src/styles/index.css
index 1843b03..961219b 100644
--- a/src/styles/index.css
+++ b/src/styles/index.css
@@ -11,6 +11,11 @@ body {
-moz-osx-font-smoothing: grayscale;
}
+.dots-bg{
+ background-image: url("../assets/background/dots.svg");
+ background-repeat: no-repeat;
+ background-size: cover;
+}
.input{
border: 2px solid #e3e5e8;
@@ -22,4 +27,4 @@ body {
.input:active, .input:focus, .input:focus-within{
border-color: #60a5fa;
-}
\ No newline at end of file
+}