fixed toolbar

This commit is contained in:
paul
2024-09-15 12:08:29 +05:30
parent 4030debcfb
commit 70b7b18864
4 changed files with 381 additions and 257 deletions

View File

@@ -14,6 +14,7 @@ import CanvasToolBar from "./toolbar"
import { UID } from "../utils/uid"
import { removeDuplicateObjects } from "../utils/common"
import { WidgetContext } from './context/widgetContext'
// import {ReactComponent as DotsBackground} from "../assets/background/dots.svg"
import DotsBackground from "../assets/background/dots.svg"
@@ -58,10 +59,12 @@ class Canvas extends React.Component {
isPanning: false,
currentTranslate: { x: 0, y: 0 },
canvasSize: { width: 500, height: 500 },
contextMenuItems: [],
toolbarOpen: true,
selectedWidgets: []
selectedWidgets: [],
toolbarOpen: true,
toolbarAttrs: null
}
this._onWidgetListUpdated = onWidgetListUpdated // a function callback when the widget is added to the canvas
@@ -72,7 +75,9 @@ class Canvas extends React.Component {
this.mouseDownEvent = this.mouseDownEvent.bind(this)
this.mouseMoveEvent = this.mouseMoveEvent.bind(this)
this.mouseUpEvent = this.mouseUpEvent.bind(this)
this.onActiveWidgetUpdate = this.onActiveWidgetUpdate.bind(this)
this.getWidgets = this.getWidgets.bind(this)
this.getActiveObjects = this.getActiveObjects.bind(this)
this.getWidgetFromTarget = this.getWidgetFromTarget.bind(this)
@@ -169,6 +174,8 @@ class Canvas extends React.Component {
const selectedLength = this.state.selectedWidgets.length
console.log("selected widget: ", selectedWidget)
if (selectedLength === 0 || (selectedLength === 1 && selectedWidget.__id !== this.state.selectedWidgets[0].__id)){
this.state.selectedWidgets[0]?.deSelect() // deselect the previous widget before adding the new one
this.state.selectedWidgets[0]?.setZIndex(0)
@@ -177,10 +184,9 @@ class Canvas extends React.Component {
selectedWidget.select()
this.setState({
selectedWidgets: [selectedWidget]
selectedWidgets: [selectedWidget],
toolbarAttrs: selectedWidget.getToolbarAttrs()
})
console.log("selected")
}
this.currentMode = CanvasModes.MOVE_WIDGET
}
@@ -384,7 +390,9 @@ class Canvas extends React.Component {
})
this.setState({
selectedWidgets: []
selectedWidgets: [],
toolbarAttrs: {},
// toolbarOpen:
})
}
@@ -482,10 +490,8 @@ class Canvas extends React.Component {
// }
this.widgetRefs = {}
this.setState(() => ({
this.setState({
widgets: []
}), () => {
})
if (this._onWidgetListUpdated)
@@ -507,12 +513,25 @@ class Canvas extends React.Component {
this._onWidgetListUpdated(widgets)
}
onActiveWidgetUpdate(widgetId){
if (this.state.selectedWidgets.length === 0 || widgetId !== this.state.selectedWidgets[0].__id)
return
this.setState({
toolbarAttrs: this.state.selectedWidgets.at(0).getToolbarAttrs()
})
}
renderWidget(widget){
const { id, widgetType: ComponentType } = widget
// console.log("widet: ", this.widgetRefs, id)
return <ComponentType key={id} id={id} ref={this.widgetRefs[id]}
canvasRef={this.canvasContainerRef} />
canvasRef={this.canvasContainerRef}
onWidgetUpdate={this.onActiveWidgetUpdate}
/>
}
render() {
@@ -556,8 +575,8 @@ class Canvas extends React.Component {
</Droppable>
<CanvasToolBar isOpen={this.state.toolbarOpen}
activeWidget={this.state.selectedWidgets[0]}
setActiveWidget={this.setSelectedWidget}
widgetType={this.state.selectedWidgets?.at(0)?.getWidgetType() || ""}
attrs={this.state.toolbarAttrs}
/>
</div>
)

View File

@@ -0,0 +1,35 @@
import React, { createContext, Component } from 'react'
const WidgetContext = createContext()
// NOTE: Don't use context provider
class WidgetProvider extends Component {
state = {
activeWidget: null, // Keeps track of the active widget's data
widgetMethods: null, // Function to update active widget's state
}
setActiveWidget = (widgetData, widgetMethods) => {
this.setState({
activeWidget: widgetData,
widgetMethods: widgetMethods, // Store the update function of the active widget
})
}
render() {
return (
<WidgetContext.Provider
value={{
activeWidget: this.state.activeWidget,
setActiveWidget: this.setActiveWidget,
widgetMethods: this.state.widgetMethods, // Expose the update function
}}
>
{this.props.children}
</WidgetContext.Provider>
)
}
}
export { WidgetContext, WidgetProvider }

View File

@@ -6,71 +6,88 @@ import { capitalize } from "../utils/common"
import Tools from "./constants/tools.js"
// FIXME: Maximum recursion error
/**
*
* @param {boolean} isOpen
* @param {import("./widgets/base.js").Widget} activeWidget
* @param {React.Dispatch<React.SetStateAction<import("./widgets/base.js").Widget>>} setActiveWidget
* @returns
* @param {string} widgetType
* @param {object} attrs - widget attributes
*/
function CanvasToolBar({isOpen, activeWidget, setActiveWidget}){
function CanvasToolBar({ isOpen, widgetType, attrs = {} }) {
const [toolbarOpen, setToolbarOpen] = useState(isOpen)
const [toolbarAttrs, setToolbarAttrs] = useState(attrs)
useEffect(() => {
setToolbarOpen(isOpen)
}, [isOpen])
const handleWidgetNameChange = (e) => {
activeWidget?.setWidgetName(e.target.value) // Update widget's internal state
const updatedWidget = { ...activeWidget } // Create a shallow copy of the widget
setActiveWidget(updatedWidget) // Update the state with the modified widget
}
useEffect(() => {
setToolbarAttrs(attrs)
}, [attrs])
const handleChange = (attrPath, value, callback) => {
// console.log("Value: ", attrPath, value)
activeWidget?.setAttrValue(attrPath, value) // Update widget's internal state
const updatedWidget = { ...activeWidget }
if (callback){
// const handleTextInputChange = (e) => {
// activeWidget?.setWidgetName(e.target.value) // Update widget's internal state
// const updatedWidget = { ...activeWidget } // Create a shallow copy of the widget
// 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 handleChange = (value, callback) => {
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
const keyName = parentKey ? `${parentKey}.${key}` : key
// Highlight outer labels in blue for first-level keys
const isFirstLevel = parentKey === ""
const outerLabelClass = isFirstLevel
? "tw-text-lg tw-text-blue-700 tw-font-medium"
: "tw-text-lg"
// Render tool widgets
if (typeof val === "object" && val.tool) {
// Render widgets based on the tool type
return (
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
{
parentKey ?
<div className={`tw-text-sm tw-font-medium `}>{val.label}</div>
:
<div className="tw-text-lg tw-text-blue-700">{capitalize(key)}</div>
}
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
<div className={`${isFirstLevel ? outerLabelClass : "tw-text-base"}`}>{val.label}</div>
{
val.tool === Tools.NUMBER_INPUT && (
<InputNumber
defaultValue={val.value || 0}
size="small"
onChange={(value) => handleChange(keyName, value, val.onChange)}
{val.tool === Tools.INPUT && (
<Input
{...val.toolProps}
value={val.value}
onChange={(e) => handleChange(e.target.value, val.onChange)}
/>
)}
)}
{
val.tool === Tools.COLOR_PICKER && (
<ColorPicker
{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"}
disabledAlpha
arrow={false}
@@ -78,65 +95,56 @@ function CanvasToolBar({isOpen, activeWidget, setActiveWidget}){
showText
format="hex"
placement="bottomRight"
className="tw-w-fit !tw-min-w-[100px]"
onChange={(value) => handleChange(keyName, value.toHexString(), val.onChange)}
className="tw-w-fit !tw-min-w-[110px]"
onChange={(value) => handleChange(value.toHexString(), val.onChange)}
/>
)}
{
val.tool === Tools.SELECT_DROPDOWN && (
<Select
)}
{val.tool === Tools.SELECT_DROPDOWN && (
<Select
options={val.options}
showSearch
value={val.value || ""}
placeholder={`${val.label}`}
onChange={(value) => handleChange(keyName, value, val.onChange)}
onChange={(value) => handleChange(value, val.onChange)}
/>
)
}
)}
</div>
);
}
{/* Add more widget types here as needed */}
</div>
)
}
// If the value is another nested object, recursively call renderWidgets
// Handle nested objects and horizontal display for inner elements
if (typeof val === "object") {
const containerClass = val.display === "horizontal"
? "tw-flex tw-flex-row tw-gap-4"
: "tw-flex tw-flex-col tw-gap-2"
return (
<div key={keyName} className="tw-flex tw-flex-col tw-gap-2">
<div className="tw-text-lg tw-text-blue-700">{capitalize(key)}</div>
{renderWidgets(val, keyName)}
</div>
<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>
)
}
return null // Skip rendering for non-object types
return null
})
}
return (
<div className={`tw-absolute tw-top-20 tw-right-5 tw-bg-white ${toolbarOpen ? "tw-w-[320px]": "tw-w-0"}
tw-px-4 tw-p-2 tw-h-[600px] tw-rounded-md tw-z-20 tw-shadow-lg
tw-transition-transform tw-duration-75
tw-flex tw-flex-col tw-overflow-y-auto
`}
>
<h3 className="tw-text-xl tw-text-center">
{capitalize(`${activeWidget?.getWidgetType() || ""}`)}
<div
className={`tw-absolute tw-top-20 tw-right-5 tw-bg-white ${toolbarOpen ? "tw-w-[320px]" : "tw-w-0"
} tw-px-4 tw-p-2 tw-h-[600px] tw-rounded-md tw-z-20 tw-shadow-lg
tw-transition-transform tw-duration-75
tw-flex tw-flex-col tw-gap-2 tw-overflow-y-auto`}
>
<h3 className="tw-text-xl tw-text-center">
{capitalize(`${widgetType || ""}`)}
</h3>
<div>
<Input placeholder="widget name"
value={activeWidget?.getWidgetName() || ""}
onChange={handleWidgetNameChange}
/>
</div>
<hr />
<div className="tw-flex tw-flex-col tw-gap-4">
{renderWidgets(activeWidget?.state?.attrs || {})}
</div>
<div className="tw-flex tw-flex-col tw-gap-4">{renderWidgets(toolbarAttrs || {})}</div>
</div>
)

View File

@@ -8,18 +8,17 @@ import { toSnakeCase } from "../utils/utils"
import EditableDiv from "../../components/editableDiv"
/**
* Base class to be extended
*/
class Widget extends React.Component{
class Widget extends React.Component {
static widgetType = "widget"
constructor(props){
constructor(props) {
super(props)
const {id, widgetName, canvasRef} = props
const { id, widgetName, canvasRef } = props
// console.log("Id: ", id)
// this id has to be unique inside the canvas, it will be set automatically and should never be changed
this.__id = id
@@ -32,37 +31,17 @@ class Widget extends React.Component{
this._parent = "" // id of the parent widget, default empty string
this._children = [] // id's of all the child widgets
this.minSize = {width: 50, height: 50} // disable resizing below this number
this.maxSize = {width: 500, height: 500} // disable resizing above this number
this.minSize = { width: 50, height: 50 } // disable resizing below this number
this.maxSize = { width: 500, height: 500 } // disable resizing above this number
this.cursor = Cursor.POINTER
this.icon = "" // antd icon name representing this widget
this.elementRef = React.createRef()
this.attrs = {
styling: {
backgroundColor: {
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
value: ""
},
foregroundColor: {
tool: Tools.COLOR_PICKER,
value: ""
},
},
layout: "show", // enables layout use "hide" to hide layout dropdown, takes the layout from this.layout
events: {
event1: {
tool: Tools.EVENT_HANDLER,
value: ""
}
}
}
this.functions = {
"load": {"args1": "number", "args2": "string"}
"load": { "args1": "number", "args2": "string" }
}
@@ -72,62 +51,49 @@ class Widget extends React.Component{
y: 0,
height: 100,
width: 100
}
}
this.state = {
zIndex: 0,
selected: false,
widgetName: widgetName || 'widget', // this will later be converted to variable name
widgetName: widgetName || 'widget', // this will later be converted to variable name
enableRename: false, // will open the widgets editable div for renaming
resizing: false,
resizeCorner: "",
pos: {x: 0, y: 0}, // used for outer styling
size: {width: 100, height: 100}, // used for outer styling
pos: { x: 0, y: 0 },
size: { width: 100, height: 100 },
position: "absolute",
widgetStyling: {
// use for widget's inner styling
},
attrs: {
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)
onChange: (value) => this.setWidgetStyling("backgroundColor", value)
},
foregroundColor: {
label: "Foreground Color",
tool: Tools.COLOR_PICKER,
value: "",
},
label: "Styling"
},
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"},
{ 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)
},
onChange: (value) => this.setWidgetStyling("backgroundColor", value)
},
events: {
event1: {
@@ -140,72 +106,128 @@ class Widget extends React.Component{
this.mousePress = this.mousePress.bind(this)
this.getElement = this.getElement.bind(this)
this.getBoundingRect = this.getBoundingRect.bind(this)
this.getPos = this.getPos.bind(this)
this.getSize = this.getSize.bind(this)
this.getWidgetName = this.getWidgetName.bind(this)
this.getWidgetType = this.getWidgetType.bind(this)
this.getBoundingRect = this.getBoundingRect.bind(this)
this.getToolbarAttrs = this.getToolbarAttrs.bind(this)
// this.openRenaming = this.openRenaming.bind(this)
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)
this.setAttrValue = this.setAttrValue.bind(this)
this.setWidgetName = this.setWidgetName.bind(this)
this.setWidgetStyling = this.setWidgetStyling.bind(this)
this.startResizing = this.startResizing.bind(this)
this.handleResize = this.handleResize.bind(this)
this.stopResizing = this.stopResizing.bind(this)
}
}
componentDidMount(){
componentDidMount() {
this.elementRef.current?.addEventListener("click", this.mousePress)
this.canvas.addEventListener("mousemove", this.handleResize)
this.canvas.addEventListener("mouseup", this.stopResizing)
}
componentWillUnmount(){
componentWillUnmount() {
this.elementRef.current?.removeEventListener("click", this.mousePress)
this.canvas.addEventListener("mousemove", this.handleResize)
this.canvas.addEventListener("mouseup", this.stopResizing)
}
// TODO: add context menu items such as delete, add etc
contextMenu(){
updateState = (newState, callback) => {
this.setState(newState, () => {
const { onWidgetUpdate } = this.props
if (onWidgetUpdate) {
onWidgetUpdate(this.__id)
}
if (callback) callback()
})
}
getVariableName(){
_getWidgetMethods = () => {
return {
rename: this.setWidgetName,
resize: this.setWidgetSize,
setWidgetAttrs: this.setAttrValue,
}
}
getToolbarAttrs(){
return ({
widgetName: {
label: "Widget Name",
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
toolProps: {placeholder: "Widget name", maxLength: 40},
value: this.state.widgetName,
onChange: (value) => this.setWidgetName(value)
},
size: {
label: "Sizing",
display: "horizontal",
width: {
label: "Width",
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
toolProps: {placeholder: "width", max: this.maxSize.width, min: this.minSize.width},
value: this.state.size.width || 100,
onChange: (value) => this.setWidgetSize(value, null)
},
height: {
label: "Height",
tool: Tools.NUMBER_INPUT,
toolProps: {placeholder: "width", max: this.maxSize.height, min: this.minSize.height},
value: this.state.size.height || 100,
onChange: (value) => this.setWidgetSize(null, value)
},
},
...this.state.attrs,
})
}
// TODO: add context menu items such as delete, add etc
contextMenu() {
}
getVariableName() {
return toSnakeCase(this.state.widgetName)
}
getWidgetName(){
getWidgetName() {
return this.state.widgetName
}
getWidgetType(){
getWidgetType() {
return this.constructor.widgetType
}
getAttributes(){
getAttributes() {
return this.state.attrs
}
/**
* removes the element/widget
*/
remove(){
this.elementRef.current.remove()
remove() {
this.canvas.removeWidget(this.__id)
}
mousePress(event){
mousePress(event) {
// event.preventDefault()
if (!this._disableSelection){
if (!this._disableSelection) {
// const widgetSelected = new CustomEvent("selection:created", {
// detail: {
@@ -219,117 +241,109 @@ class Widget extends React.Component{
}
}
select(){
select() {
this.setState({
selected: true
})
}
deSelect(){
deSelect() {
this.setState({
selected: false
})
}
isSelected(){
isSelected() {
return this.state.selected
}
setPos(x, y){
setPos(x, y) {
if (this.state.resizing){
if (this.state.resizing) {
// don't change position when resizing the widget
return
}
this.setState({
pos: {x, y}
// this.setState({
// pos: { x, y }
// })
this.updateState({
pos: { x, y }
})
// this.setState((prev) => ({
// // pos: {x: x, y: y}
// widgetStyling: {
// ...prev.widgetStyling,
// left: x,
// top: y,
// }
// }))
}
setParent(parentId){
setParent(parentId) {
this._parent = parentId
}
addChild(childId){
addChild(childId) {
this._children.push(childId)
}
removeChild(childId){
this._children = this._children.filter(function(item) {
return item !== childId
})
removeChild(childId) {
this._children = this._children.filter(function (item) {
return item !== childId
})
}
getPos(){
getPos() {
return this.state.pos
}
getProps(){
getProps() {
return this.attrs
}
getBoundingRect(){
getBoundingRect() {
return this.elementRef.current?.getBoundingClientRect()
}
getSize(){
// const boundingRect = this.getBoundingRect()
return {width: this.state.size.width, height: this.state.size.height}
getSize() {
return this.state.size
}
getScaleAwareDimensions() {
// Get the bounding rectangle
const rect = this.elementRef.current.getBoundingClientRect()
// Get the computed style of the element
const style = window.getComputedStyle(this.elementRef.current)
// Get the transform matrix
const transform = style.transform
// Extract scale factors from the matrix
let scaleX = 1
let scaleY = 1
if (transform && transform !== 'none') {
// For 2D transforms (a, c, b, d)
const matrix = transform.match(/^matrix\(([^,]+),[^,]+,([^,]+),[^,]+,[^,]+,[^,]+\)$/);
if (matrix) {
scaleX = parseFloat(matrix[1])
scaleY = parseFloat(matrix[2])
}
// For 2D transforms (a, c, b, d)
const matrix = transform.match(/^matrix\(([^,]+),[^,]+,([^,]+),[^,]+,[^,]+,[^,]+\)$/);
if (matrix) {
scaleX = parseFloat(matrix[1])
scaleY = parseFloat(matrix[2])
}
}
// Return scaled width and height
return {
width: rect.width / scaleX,
height: rect.height / scaleY
width: rect.width / scaleX,
height: rect.height / scaleY
}
}
}
getWidgetFunctions(){
getWidgetFunctions() {
return this.functions
}
getId(){
getId() {
return this.__id
}
getElement(){
getElement() {
return this.elementRef.current
}
@@ -338,22 +352,22 @@ class Widget extends React.Component{
* @param {string} path - path to the key, eg: styling.backgroundColor
* @param {any} value
*/
setAttrValue(path, 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 }
})
}
@@ -363,28 +377,72 @@ class Widget extends React.Component{
this.setState({ resizing: true, resizeCorner: corner })
}
setZIndex(zIndex){
setZIndex(zIndex) {
this.setState({
zIndex: zIndex
})
}
setWidgetName(name){
setWidgetName(name) {
this.setState((prev) => ({
widgetName: name.length > 0 ? name : prev.widgetName
}))
// this.setState((prev) => ({
// widgetName: name.length > 0 ? name : prev.widgetName
// }))
this.updateState({
widgetName: name.length > 0 ? name : this.state.widgetName
})
}
setWidgetStyling(key, value){
this.setState((prev) => ({
widgetStyling: {
...prev.widgetStyling,
[key]: value
}
}))
/**
*
* @param {string} key - The string in react Style format
* @param {string} value - Value of the style
* @param {function():void} [callback] - optional callback, thats called after setting the internal state
*/
setWidgetStyling(key, value, callback) {
const widgetStyle = {
...this.state.widgetStyling,
[key]: value
}
this.setState({
widgetStyling: widgetStyle
}, () => {
if (callback)
callback(widgetStyle)
})
}
/**
*
* @param {number|null} width
* @param {number|null} height
* @param {function():void} [callback] - optional callback, thats called after setting the internal state
*/
setWidgetSize(width, height, callback) {
const newSize = {
width: Math.max(this.minSize.width, Math.min(width || this.state.size.width, this.maxSize.width)),
height: Math.max(this.minSize.height, Math.min(height || this.state.size.height, this.maxSize.height)),
}
this.setState({
size: newSize
}, () => {
if (callback) {
callback(newSize)
}
})
this.updateState({
size: newSize
}, () => {
if (callback)
callback(newSize)
})
}
handleResize(event) {
@@ -396,9 +454,9 @@ class Widget extends React.Component{
let newSize = { ...size }
let newPos = { ...pos }
const {width: minWidth, height: minHeight} = this.minSize
const {width: maxWidth, height: maxHeight} = this.maxSize
const { width: minWidth, height: minHeight } = this.minSize
const { width: maxWidth, height: maxHeight } = this.maxSize
// console.log("resizing: ", deltaX, deltaY, event)
switch (resizeCorner) {
@@ -426,7 +484,11 @@ class Widget extends React.Component{
break
}
this.setState({ size: newSize, pos: newPos })
// this.setState({ size: newSize, pos: newPos })
this.updateState({
size: newSize,
pos: newPos
})
}
stopResizing() {
@@ -435,20 +497,20 @@ class Widget extends React.Component{
}
}
openRenaming(){
openRenaming() {
this.setState({
selected: true,
enableRename: true
})
}
closeRenaming(){
closeRenaming() {
this.setState({
enableRename: false
})
}
renderContent(){
renderContent() {
// throw new NotImplementedError("render method has to be implemented")
return (
<div className="tw-w-full tw-h-full tw-rounded-md tw-bg-red-500" style={this.state.widgetStyling}>
@@ -461,8 +523,8 @@ class Widget extends React.Component{
* This is an internal methods don't override
* @returns {HTMLElement}
*/
render(){
render() {
const widgetStyle = this.state.widgetStyling
@@ -470,8 +532,8 @@ class Widget extends React.Component{
cursor: this.cursor,
zIndex: this.state.zIndex,
position: "absolute", // don't change this if it has to be movable on the canvas
top: `${this.state.pos.y}px`,
left: `${this.state.pos.x}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`,
}
@@ -485,23 +547,23 @@ class Widget extends React.Component{
// console.log("selected: ", this.state.selected)
return (
<div data-id={this.__id} ref={this.elementRef} className="tw-absolute tw-shadow-xl tw-w-fit tw-h-fit"
style={outerStyle}
>
<div data-id={this.__id} ref={this.elementRef} className="tw-absolute tw-shadow-xl tw-w-fit tw-h-fit"
style={outerStyle}
>
{this.renderContent()}
<div className={`tw-absolute tw-bg-transparent tw-scale-[1.1] tw-opacity-100
tw-w-full tw-h-full tw-top-0
${this.state.selected ? 'tw-border-2 tw-border-solid tw-border-blue-500' : 'tw-hidden'}`}>
<div className="tw-relative tw-w-full tw-h-full">
<EditableDiv value={this.state.widgetName} onChange={this.setWidgetName}
maxLength={40}
openEdit={this.state.enableRename}
className="tw-text-sm tw-w-fit tw-max-w-[160px] tw-text-clip tw-min-w-[150px]
maxLength={40}
openEdit={this.state.enableRename}
className="tw-text-sm tw-w-fit tw-max-w-[160px] tw-text-clip tw-min-w-[150px]
tw-overflow-hidden tw-absolute tw--top-6 tw-h-6"
/>
/>
<div
className="tw-w-2 tw-h-2 tw-absolute tw--left-1 tw--top-1 tw-bg-blue-500"
@@ -526,7 +588,7 @@ class Widget extends React.Component{
</div>
</div>
</div>
@@ -534,7 +596,7 @@ class Widget extends React.Component{
}
}
}
export default Widget