fixed place layout manager. Place/absolute position now working inside the widgets

This commit is contained in:
paul
2024-09-24 21:49:26 +05:30
parent 22e0a0543d
commit 00277205f5
6 changed files with 412 additions and 152 deletions

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@
/.pnp
.pnp.js
python-tests/
# testing
/coverage

View File

@@ -23,10 +23,11 @@ import { ReactComponent as DotsBackground } from "../assets/background/dots.svg"
import DroppableWrapper from "../components/draggable/droppable"
import { PosType } from "./constants/layouts"
import { Layouts, PosType } from "./constants/layouts"
import WidgetContainer from "./constants/containers"
import { isSubClassOfWidget } from "../utils/widget"
import { ButtonModal } from "../components/modals"
import ResizeWidgetContainer from "./resizeContainer"
// const DotsBackground = require("../assets/background/dots.svg")
@@ -65,6 +66,7 @@ class Canvas extends React.Component {
this.widgetRefs = {} // stores the actual refs to the widgets inside the canvas {id: ref, id2, ref2...}
this.state = {
isWidgetDragging: false,
widgetResizing: "", // set this to "nw", "sw" etc based on the side when widgets resizing handles are selected
widgets: [], // stores the mapping to widgetRefs, stores id and WidgetType, later used for rendering [{id: , widgetType: WidgetClass, children: [], parent: "", initialData: {}}]
zoom: 1,
@@ -181,8 +183,8 @@ class Canvas extends React.Component {
let innerWidget = null
for (let [key, ref] of Object.entries(this.widgetRefs)) {
if (ref.current === target){
if (ref.current === target) {
innerWidget = ref.current
break
}
@@ -235,11 +237,13 @@ class Canvas extends React.Component {
this.state.selectedWidget?.setZIndex(0)
selectedWidget.setZIndex(1000)
selectedWidget.select()
// console.log("selected widget", selectedWidget, this.state.selectedWidget)
this.setState({
selectedWidget: selectedWidget,
toolbarAttrs: selectedWidget.getToolbarAttrs()
})
// if (!this.state.selectedWidget || (selectedWidget.getId() !== this.state.selectedWidget?.getId())) {
// this.state.selectedWidget?.deSelect() // deselect the previous widget before adding the new one
// this.state.selectedWidget?.setZIndex(0)
@@ -301,16 +305,16 @@ class Canvas extends React.Component {
{
key: "snap",
label: (<div onClick={() => {
domtoimage.toPng(selectedWidget.getElement(), {
width: selectedWidget.getElement().offsetWidth * 2, // Multiply element's width by 2
height: selectedWidget.getElement().offsetHeight * 2 // Multiply element's height by 2
}).then((dataUrl) => {
saveAs(dataUrl, 'widget.png')
}).catch((error) => {
console.error('Error capturing widget as PNg:', error)
})
}}>
<FileImageOutlined /> Save as Image</div>),
domtoimage.toPng(selectedWidget.getElement(), {
width: selectedWidget.getElement().offsetWidth * 2, // Multiply element's width by 2
height: selectedWidget.getElement().offsetHeight * 2 // Multiply element's height by 2
}).then((dataUrl) => {
saveAs(dataUrl, 'widget.png')
}).catch((error) => {
console.error('Error capturing widget as PNg:', error)
})
}}>
<FileImageOutlined /> Save as Image</div>),
icons: <FileImageOutlined />,
}
]
@@ -638,6 +642,114 @@ class Canvas extends React.Component {
})
}
/**
* Handles drop event to canvas from the sidebar and on canvas widget movement
* @param {DragEvent} e
*/
handleDropEvent = (e, draggedElement, widgetClass = null) => {
e.preventDefault()
this.setState({ isWidgetDragging: false })
if (!draggedElement || !draggedElement.getAttribute("data-drag-start-within")) {
// if the drag is starting from outside (eg: file drop) or if drag doesn't exist
return
}
const container = draggedElement.getAttribute("data-container")
const canvasRect = this.canvasRef.current.getBoundingClientRect()
const draggedElementRect = draggedElement.getBoundingClientRect()
const elementWidth = draggedElementRect.width
const elementHeight = draggedElementRect.height
const { clientX, clientY } = e
let finalPosition = {
x: (clientX - canvasRect.left) / this.state.zoom,
y: (clientY - canvasRect.top) / this.state.zoom,
}
if (container === WidgetContainer.SIDEBAR) {
if (!widgetClass) {
throw new Error("WidgetClass has to be passed for widgets dropped from sidebar")
}
// if the widget is being dropped from the sidebar, use the info to create the widget first
this.createWidget(widgetClass, ({ id, widgetRef }) => {
widgetRef.current.setPos(finalPosition.x, finalPosition.y)
})
} else if ([WidgetContainer.CANVAS, WidgetContainer.WIDGET].includes(container)) {
// snaps to center
finalPosition = {
x: (clientX - canvasRect.left) / this.state.zoom - (elementWidth / 2) / this.state.zoom,
y: (clientY - canvasRect.top) / this.state.zoom - (elementHeight / 2) / this.state.zoom,
}
let widgetId = draggedElement.getAttribute("data-widget-id")
const widgetObj = this.getWidgetById(widgetId)
// console.log("WidgetObj: ", widgetObj)
if (container === WidgetContainer.CANVAS) {
widgetObj.current.setPos(finalPosition.x, finalPosition.y)
} else if (container === WidgetContainer.WIDGET) {
// if the widget was inside another widget move it outside
let childWidgetObj = this.findWidgetFromListById(widgetObj.current.getId())
let parentWidgetObj = this.findWidgetFromListById(childWidgetObj.parent)
const childData = widgetObj.current.serialize() // save the data and pass it the updated child object
// remove child from current position
const updatedChildWidget = {
...childWidgetObj,
parent: "",
initialData: {
...childData,
pos: { x: finalPosition.x, y: finalPosition.y },
positionType: PosType.ABSOLUTE, // makes sure that after dropping the position is set to absolute value
zIndex: 0,
widgetContainer: WidgetContainer.CANVAS
}
}
let updatedWidgets = this.removeWidgetFromCurrentList(widgetObj.current.getId())
// Create a new copy of the parent widget with the child added
const updatedParentWidget = {
...parentWidgetObj,
// children: parentWidgetObj.children.filter(child => child.id !== childWidgetObj.id)
}
updatedWidgets = updatedWidgets.map(widget => {
if (widget.id === parentWidgetObj.id) {
return updatedParentWidget // Update the parent widget with the child removed
} else {
return widget // Leave other widgets unchanged
}
})
this.setState({
widgets: [...updatedWidgets, updatedChildWidget]
})
}
}
}
/**
* Adds the child into the children attribute inside the this.widgets list of objects
* // widgets data structure { id, widgetType: widgetComponentType, children: [], parent: "" }
@@ -645,8 +757,9 @@ class Canvas extends React.Component {
* @param {object} dragElement
* @param {boolean} create - if create is set to true the widget will be created before adding to the child tree
*/
handleAddWidgetChild = ({ parentWidgetId, dragElementID, swap = false }) => {
handleAddWidgetChild = ({event, parentWidgetId, dragElementID, swap = false }) => {
console.log("event: ", event)
// widgets data structure { id, widgetType: widgetComponentType, children: [], parent: "" }
const dropWidgetObj = this.findWidgetFromListById(parentWidgetId)
// Find the dragged widget object
@@ -658,6 +771,18 @@ class Canvas extends React.Component {
const dragWidget = this.widgetRefs[dragWidgetObj.id]
const dragData = dragWidget.current.serialize()
const parentWidget = this.widgetRefs[parentWidgetId].current
const parentRect = parentWidget.getBoundingRect()
const canvasRect = this.canvasRef.current.getBoundingClientRect()
const { clientX, clientY } = event
let finalPosition = {
x: (clientX - parentRect.left) / this.state.zoom,
y: (clientY - parentRect.top) / this.state.zoom,
}
if (swap) {
// If swapping, we need to find the common parent
const grandParentWidgetObj = this.findWidgetFromListById(dropWidgetObj.parent)
@@ -690,17 +815,25 @@ class Canvas extends React.Component {
// Non-swap mode: Add the dragged widget as a child of the drop widget
let updatedWidgets = this.removeWidgetFromCurrentList(dragElementID)
const parentLayout = parentWidget.getLayout()?.layout
console.log("parent layout: ", parentLayout, parentWidget.getLayout(), parentWidget)
dragWidget.current.setPos(finalPosition.x, finalPosition.y)
const updatedDragWidget = {
...dragWidgetObj,
parent: dropWidgetObj.id, // Keep the parent reference
initialData: {
...dragData,
positionType: PosType.NONE,
positionType: parentLayout === Layouts.PLACE ? PosType.ABSOLUTE : PosType.NONE,
zIndex: 0,
pos: {x: finalPosition.x, y: finalPosition.y},
widgetContainer: WidgetContainer.WIDGET
}
}
console.log("updated widget: ", updatedDragWidget)
const updatedDropWidget = {
...dropWidgetObj,
children: [...dropWidgetObj.children, updatedDragWidget]
@@ -725,7 +858,7 @@ class Canvas extends React.Component {
*/
createWidget(widgetComponentType, callback) {
if (!isSubClassOfWidget(widgetComponentType)){
if (!isSubClassOfWidget(widgetComponentType)) {
throw new Error("widgetComponentType must be a subclass of Widget class")
}
@@ -804,6 +937,28 @@ class Canvas extends React.Component {
this._onWidgetListUpdated([])
}
getWidgetByIdFromWidgetList = (widgetId) => {
function recursiveFind(objects) {
for (const obj of objects) {
// Check if the current object has the matching ID
if (obj.id === widgetId) {
return obj // Return the object if found
}
// Recursively check children if they exist
if (obj.children && obj.children.length > 0) {
const found = recursiveFind(obj.children)
if (found) {
return found // Return the found object from children
}
}
}
return null // Return null if not found
}
return recursiveFind(this.state.widgets)
}
removeWidget(widgetId) {
@@ -833,114 +988,23 @@ class Canvas extends React.Component {
}
/**
* Handles drop event to canvas from the sidebar and on canvas widget movement
* @param {DragEvent} e
* informs the child about the parent layout
*/
handleDropEvent = (e, draggedElement, widgetClass=null) => {
updateChildLayouts = ({parentId, parentLayout}) => {
const parent = this.getWidgetByIdFromWidgetList(parentId)
e.preventDefault()
if (!parent) return
if (!draggedElement || !draggedElement.getAttribute("data-drag-start-within")){
// if the drag is starting from outside (eg: file drop) or if drag doesn't exist
return
}
const container = draggedElement.getAttribute("data-container")
const canvasRect = this.canvasRef.current.getBoundingClientRect()
const draggedElementRect = draggedElement.getBoundingClientRect()
const elementWidth = draggedElementRect.width
const elementHeight = draggedElementRect.height
const { clientX, clientY } = e
let finalPosition = {
x: (clientX - canvasRect.left) / this.state.zoom,
y: (clientY - canvasRect.top) / this.state.zoom,
}
if (container === WidgetContainer.SIDEBAR) {
if (!widgetClass){
throw new Error("WidgetClass has to be passed for widgets dropped from sidebar")
}
// if the widget is being dropped from the sidebar, use the info to create the widget first
this.createWidget(widgetClass, ({ id, widgetRef }) => {
widgetRef.current.setPos(finalPosition.x, finalPosition.y)
})
} else if ([WidgetContainer.CANVAS, WidgetContainer.WIDGET].includes(container)) {
// snaps to center
finalPosition = {
x: (clientX - canvasRect.left) / this.state.zoom - (elementWidth / 2) / this.state.zoom,
y: (clientY - canvasRect.top) / this.state.zoom - (elementHeight / 2) / this.state.zoom,
}
let widgetId = draggedElement.getAttribute("data-widget-id")
const widgetObj = this.getWidgetById(widgetId)
// console.log("WidgetObj: ", widgetObj)
if (container === WidgetContainer.CANVAS) {
widgetObj.current.setPos(finalPosition.x, finalPosition.y)
} else if (container === WidgetContainer.WIDGET) {
// if the widget was inside another widget move it outside
let childWidgetObj = this.findWidgetFromListById(widgetObj.current.getId())
let parentWidgetObj = this.findWidgetFromListById(childWidgetObj.parent)
const childData = widgetObj.current.serialize() // save the data and pass it the updated child object
// remove child from current position
const updatedChildWidget = {
...childWidgetObj,
parent: "",
initialData: {
...childData,
pos: { x: finalPosition.x, y: finalPosition.y },
positionType: PosType.ABSOLUTE, // makes sure that after dropping the position is set to absolute value
zIndex: 0,
widgetContainer: WidgetContainer.CANVAS
}
}
let updatedWidgets = this.removeWidgetFromCurrentList(widgetObj.current.getId())
// Create a new copy of the parent widget with the child added
const updatedParentWidget = {
...parentWidgetObj,
// children: parentWidgetObj.children.filter(child => child.id !== childWidgetObj.id)
}
updatedWidgets = updatedWidgets.map(widget => {
if (widget.id === parentWidgetObj.id) {
return updatedParentWidget // Update the parent widget with the child removed
} else {
return widget // Leave other widgets unchanged
}
})
this.setState({
widgets: [...updatedWidgets, updatedChildWidget]
})
}
for (let child of parent.children){
this.widgetRefs[child.id].current.setParentLayout(parentLayout)
}
}
renderWidget = (widget) => {
const { id, widgetType: ComponentType, children = [], parent, initialData = {} } = widget
@@ -959,6 +1023,7 @@ class Canvas extends React.Component {
return (
<ComponentType
key={id}
id={id}
@@ -969,6 +1034,9 @@ class Canvas extends React.Component {
onAddChildWidget={this.handleAddWidgetChild}
onCreateWidgetRequest={this.createWidget} // create widget when dropped from sidebar
onWidgetResizing={(resizeSide) => this.setState({ widgetResizing: resizeSide })}
// onWidgetDragStart={() => this.setState({isWidgetDragging: true})}
// onWidgetDragEnd={() => this.setState({isWidgetDragging: false})}
onLayoutUpdate={this.updateChildLayouts}
>
{/* Render children inside the parent with layout applied */}
{renderChildren(children)}
@@ -977,6 +1045,7 @@ class Canvas extends React.Component {
}
render() {
return (
<div className="tw-relative tw-flex tw-w-full tw-h-full tw-max-h-[100vh]">
@@ -986,13 +1055,13 @@ class Canvas extends React.Component {
<Tooltip title="Reset viewport">
<Button icon={<ReloadOutlined />} onClick={this.resetTransforms} />
</Tooltip>
<ButtonModal
message={"Are you sure you want to clear the canvas? This cannot be undone."}
title={"Clear canvas"}
onOk={this.clearCanvas}
okText="Yes"
okButtonType="danger"
>
<ButtonModal
message={"Are you sure you want to clear the canvas? This cannot be undone."}
title={"Clear canvas"}
onOk={this.clearCanvas}
okText="Yes"
okButtonType="danger"
>
<Tooltip title="Clear canvas">
<Button danger icon={<DeleteOutlined />} />
</Tooltip>
@@ -1001,7 +1070,7 @@ class Canvas extends React.Component {
{/* <ActiveWidgetProvider> */}
<DroppableWrapper id="canvas-droppable"
droppableTags={{exclude: ["image", "video"]}}
droppableTags={{ exclude: ["image", "video"] }}
className="tw-w-full tw-h-full"
onDrop={this.handleDropEvent}>
{/* <DragWidgetProvider> */}
@@ -1024,13 +1093,17 @@ class Canvas extends React.Component {
}}
/>
{/* Canvas */}
<div data-canvas className="tw-w-full tw-h-full tw-absolute tw-top-0 tw-select-none
"
<div data-canvas className="tw-w-full tw-h-full tw-absolute tw-top-0 tw-select-none"
ref={this.canvasRef}>
<div className="tw-relative tw-w-full tw-h-full">
{
this.state.widgets.map(this.renderWidget)
}
{/* { this.state.selectedWidget &&
<ResizeWidgetContainer selectedWidget={this.state.selectedWidget}/>
} */}
</div>
</div>
</div>

View File

@@ -0,0 +1,102 @@
import Cursor from "./constants/cursor"
import Widget from "./widgets/base"
import { useEffect, useState } from "react"
// FIXME: when using this if the widhet has invisible swappable area, this won't work
/**
*
* @param {Widget} - selectedWidget
* @returns
*/
const ResizeWidgetContainer = ({selectedWidget, onResize}) => {
const [pos, setPos] = useState({x: 0, y: 0})
const [size, setSize] = useState({width: 0, height: 0})
useEffect(() => {
if (selectedWidget){
setPos(selectedWidget.getPos())
setSize(selectedWidget.getSize())
}
console.log("selected widget resizable: ", selectedWidget)
}, [selectedWidget, selectedWidget?.getPos(), selectedWidget?.getSize()])
return (
<div className={`tw-absolute tw-bg-transparent tw-top-[-20px] tw-left-[-20px] tw-opacity-100
tw-w-full tw-h-full tw-z-[-1] tw-border-2 tw-border-solid tw-border-blue-500`}
style={{
top: `${pos.y - 40}px`,
left: `${pos.x - 20}px`,
width: `${size.width + 40}px`,
height: `${size.height + 40}px`,
}}
>
<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]
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"
style={{ cursor: Cursor.NW_RESIZE }}
onMouseDown={(e) => {
e.stopPropagation()
e.preventDefault()
onResize("nw")
// this.setState({ dragEnabled: false })
}}
// onMouseUp={() => this.setState({ dragEnabled: true })}
/>
<div
className="tw-w-2 tw-h-2 tw-absolute tw--right-1 tw--top-1 tw-bg-blue-500"
style={{ cursor: Cursor.SW_RESIZE }}
onMouseDown={(e) => {
e.stopPropagation()
e.preventDefault()
onResize("nw")
// this.setState({ dragEnabled: false })
}}
// onMouseUp={() => this.setState({ dragEnabled: true })}
/>
<div
className="tw-w-2 tw-h-2 tw-absolute tw--left-1 tw--bottom-1 tw-bg-blue-500"
style={{ cursor: Cursor.SW_RESIZE }}
onMouseDown={(e) => {
e.stopPropagation()
e.preventDefault()
onResize("nw")
// this.props.onWidgetResizing("sw")
// this.setState({ dragEnabled: false })
}}
onMouseUp={() => this.setState({ dragEnabled: true })}
/>
<div
className="tw-w-2 tw-h-2 tw-absolute tw--right-1 tw--bottom-1 tw-bg-blue-500"
style={{ cursor: Cursor.SE_RESIZE }}
onMouseDown={(e) => {
e.stopPropagation()
e.preventDefault()
onResize("nw")
// this.props.onWidgetResizing("se")
// this.setState({ dragEnabled: false })
}}
// onMouseUp={() => this.setState({ dragEnabled: true })}
/>
</div>
</div>
)
}
export default ResizeWidgetContainer

View File

@@ -25,6 +25,9 @@ class Widget extends React.Component {
static widgetType = "widget"
static requirements = [] // requirements for the widgets (libraries) eg: tkvideoplayer, tktimepicker
static requiredImports = [] // import statements
// static contextType = ActiveWidgetContext
constructor(props) {
@@ -71,6 +74,8 @@ class Widget extends React.Component {
widgetName: widgetName || 'widget', // this will later be converted to variable name
enableRename: false, // will open the widgets editable div for renaming
parentLayout: null, // depending on the parents layout the child will behave
isDragging: false, // tells if the widget is currently being dragged
dragEnabled: true,
@@ -128,6 +133,7 @@ class Widget extends React.Component {
],
},
onChange: (value) => {
console.log("changed: ", value)
// this.setAttrValue("layout", value)
this.setLayout(value)
}
@@ -267,6 +273,18 @@ class Widget extends React.Component {
return this.constructor.widgetType
}
getRequirements = () => {
return this.constructor.requirements
}
getImports = () => {
return this.constructor.requiredImports
}
getCode = () => {
throw new NotImplementedError("Get Code must be implemented by the subclass")
}
getAttributes() {
return this.state.attrs
}
@@ -344,19 +362,6 @@ class Widget extends React.Component {
return this.elementRef.current
}
getLayoutStyleForWidget = () => {
switch (this.state.attrs.layout) {
case 'grid':
return { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '10px' }
case 'flex':
return { display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }
case 'absolute':
return { position: 'absolute', left: "0", top: "0" } // Custom positioning
default:
return {}
}
}
/**
* Given the key as a path, sets the value for the widget attribute
@@ -454,24 +459,62 @@ class Widget extends React.Component {
})
}
/**
*
* @param {Layouts} layout
*/
setParentLayout = (layout) => {
let updates = {
parentLayout: layout,
}
if (layout === Layouts.FLEX || layout === Layouts.GRID){
updates = {
...updates,
positionType: PosType.NONE
}
}else if (layout === Layouts.PLACE){
updates = {
...updates,
positionType: PosType.ABSOLUTE
}
}
console.log("Parent layout updated: ", updates)
this.setState(updates)
}
getLayout = () => {
return this.state?.attrs?.layout?.value || Layouts.FLEX
}
setLayout(value) {
// FIXME: when the parent layout is place, the child widgets should have position absolute
const { layout, direction, grid = { rows: 1, cols: 1 }, gap = 10 } = value
console.log("layout value: ", value)
const widgetStyle = {
...this.state.widgetStyling,
display: layout,
display: layout !== Layouts.PLACE ? layout : "block",
flexDirection: direction,
gap: `${gap}px`,
flexWrap: "wrap"
// TODO: add grid rows and cols
}
this.setAttrValue("layout", value)
this.updateState({
widgetStyling: widgetStyle
})
this.setAttrValue("layout", value)
this.props.onLayoutUpdate({parentId: this.__id, parentLayout: layout})// inform children about the layout update
}
/**
@@ -480,7 +523,7 @@ class Widget extends React.Component {
* @param {string} value - Value of the style
*/
setWidgetStyling(key, value) {
const widgetStyle = {
...this.state.widgetStyling,
[key]: value
@@ -555,6 +598,7 @@ class Widget extends React.Component {
size: this.state.size,
widgetContainer: this.state.widgetContainer,
widgetStyling: this.state.widgetStyling,
parentLayout: this.state.parentLayout,
positionType: this.state.positionType,
attrs: this.serializeAttrsValues() // makes sure that functions are not serialized
})
@@ -571,14 +615,40 @@ class Widget extends React.Component {
data = {...data} // create a shallow copy
const {attrs, ...restData} = data
const {attrs, parentLayout, ...restData} = data
// for (let [key, value] of Object.entries(attrs | {}))
// this.setAttrValue(key, value)
// delete data.attrs
this.setState(restData, () => {
let layoutUpdates = {
parentLayout: parentLayout
}
// FIXME: Need to load the data properly
if (parentLayout === Layouts.FLEX || parentLayout === Layouts.GRID){
layoutUpdates = {
...layoutUpdates,
positionType: PosType.NONE
}
}else if (parentLayout === Layouts.PLACE){
layoutUpdates = {
...layoutUpdates,
positionType: PosType.ABSOLUTE
}
}
console.log("loaded layout: ", layoutUpdates)
const newData = {
...restData,
layoutUpdates
}
console.log("loaded layout2: ", newData)
this.setState(newData, () => {
// UPdates attrs
let newAttrs = { ...this.state.attrs }
@@ -611,6 +681,8 @@ class Widget extends React.Component {
callback(this.elementRef?.current || null)
// this.props.onWidgetDragStart(this.elementRef?.current)
// Create custom drag image with full opacity, this will ensure the image isn't taken from part of the canvas
const dragImage = this.elementRef?.current.cloneNode(true)
dragImage.style.opacity = '1' // Ensure full opacity
@@ -775,6 +847,7 @@ class Widget extends React.Component {
// console.log("Dropped on meee: ", swapArea, this.swappableAreaRef.current.contains(e.target), thisContainer)
this.props.onAddChildWidget({
event: e,
parentWidgetId: this.__id,
dragElementID: draggedElement.getAttribute("data-widget-id"),
swap: swapArea || false
@@ -784,7 +857,11 @@ class Widget extends React.Component {
// console.log("Dropped on Sidebar: ", this.__id)
this.props.onCreateWidgetRequest(widgetClass, ({ id, widgetRef }) => {
this.props.onAddChildWidget({ parentWidgetId: this.__id, dragElementID: id }) // if dragged from the sidebar create the widget first
this.props.onAddChildWidget({
event: e,
parentWidgetId: this.__id,
dragElementID: id
}) // if dragged from the sidebar create the widget first
})
}
@@ -811,6 +888,8 @@ class Widget extends React.Component {
callback()
this.setState({ isDragging: false })
this.enablePointerEvents()
// this.props.onWidgetDragEnd(this.elementRef?.current)
}
disablePointerEvents = () => {
@@ -854,6 +933,9 @@ class Widget extends React.Component {
height: `${this.state.size.height}px`,
opacity: this.state.isDragging ? 0.3 : 1,
}
// const boundingRect = this.getBoundingRect
// FIXME: if the parent container has tw-overflow-none, then the resizable indicator are also hidden
return (
@@ -887,14 +969,14 @@ class Widget extends React.Component {
>
<div className={`tw-absolute tw-top-[-5px] tw-left-[-5px]
tw-border-1 tw-opacity-0 tw-border-solid tw-border-black
tw-w-full tw-h-full
tw-w-full tw-h-full tw-bg-red-400
tw-scale-[1.1] tw-opacity-1 tw-z-[-1] `}
style={{
width: "calc(100% + 10px)",
height: "calc(100% + 10px)",
}}
ref={this.swappableAreaRef}
// swapable area
>
{/* helps with swappable: if the mouse is in this area while hovering/dropping, then swap */}
</div>
@@ -916,13 +998,14 @@ class Widget extends React.Component {
>
</div>
}
<div className={`tw-absolute tw-bg-transparent tw-top-[-10px] tw-left-[-10px] tw-opacity-100
tw-w-full tw-h-full tw-z-[-1]
{/* FIXME: the resize handles get clipped in parent container */}
<div className={`tw-absolute tw-z-[-1] tw-bg-transparent tw-top-[-10px] tw-left-[-10px] tw-opacity-100
tw-w-full tw-h-full
${this.state.selected ? 'tw-border-2 tw-border-solid tw-border-blue-500' : 'tw-hidden'}`}
style={{
width: "calc(100% + 20px)",
height: "calc(100% + 20px)",
zIndex: -1,
}}
>

View File

@@ -26,7 +26,7 @@ class Frame extends Widget{
renderContent(){
return (
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden">
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-relative tw-rounded-md tw-overflow-hidden">
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" style={this.state.widgetStyling}>
{this.props.children}
</div>

View File

@@ -65,7 +65,7 @@ class MainWindow extends Widget{
</div>
</div>
</div>
<div className="tw-p-2 tw-w-full tw-h-full tw-content-start" style={this.state.widgetStyling}>
<div className="tw-p-2 tw-w-full tw-relative tw-h-full tw-overflow-hidden tw-content-start" style={this.state.widgetStyling}>
{this.props.children}
</div>
</div>