added swappable layout
This commit is contained in:
@@ -5,14 +5,14 @@ Any feature that has 👑 beside it, is meant only for [premium users](./readme.
|
||||
### 1.0.0
|
||||
- [x] Create the initial version for UI builder
|
||||
|
||||
### 1.1.0
|
||||
- [ ] Allow swappable layout - (swappy/react-dnd-kit)
|
||||
### 1.2.0
|
||||
- [ ] UI fixes and enhancement
|
||||
- [ ] Add text editor to help with event handlers
|
||||
- [ ] Add text editor to support event handlers
|
||||
- [ ] Rewrite DND for better feedback - (swappy/react-dnd-kit/ GSAP draggable)
|
||||
- [ ] Duplicate widgets
|
||||
|
||||
### 1.5.0
|
||||
- [ ] Add canvas support (try fabricjs)
|
||||
- [ ] Add canvas support tools (lines, rect etc) (try fabricjs)
|
||||
- [ ] Initial version for Electron App 👑
|
||||
- [ ] Save files locally 👑
|
||||
- [ ] Load UI files 👑
|
||||
@@ -21,6 +21,7 @@ Any feature that has 👑 beside it, is meant only for [premium users](./readme.
|
||||
|
||||
### 2.0.0
|
||||
- [ ] Support for more third party plugins
|
||||
- [ ] Support more templates
|
||||
- [ ] Support for Kivy
|
||||
- [ ] Sharable Templates
|
||||
- [ ] Dark theme 👑
|
||||
|
||||
@@ -368,7 +368,7 @@ class Canvas extends React.Component {
|
||||
this.setState({ widgetResizing: "" })
|
||||
}
|
||||
|
||||
for (let [key, widget] of Object.entries(this.widgetRefs)){
|
||||
for (let [key, widget] of Object.entries(this.widgetRefs)) {
|
||||
// since the mouseUp event is not triggered inside the widget once its outside,
|
||||
// we'll need a global mouse up event to re-enable drag
|
||||
widget.current.enableDrag()
|
||||
@@ -643,46 +643,76 @@ 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, create = false) => {
|
||||
handleAddWidgetChild = ({ parentWidgetId, dragElementID, create = false, swap = false }) => {
|
||||
|
||||
// TODO: creation of the child widget if its not created
|
||||
// widgets data structure { id, widgetType: widgetComponentType, children: [], parent: "" }
|
||||
const parentWidgetObj = this.findWidgetFromListById(parentWidgetId)
|
||||
let childWidgetObj = this.findWidgetFromListById(dragElementID)
|
||||
const dropWidgetObj = this.findWidgetFromListById(parentWidgetId)
|
||||
// Find the dragged widget object
|
||||
let dragWidgetObj = this.findWidgetFromListById(dragElementID)
|
||||
|
||||
if (parentWidgetObj && childWidgetObj) {
|
||||
if (dropWidgetObj && dragWidgetObj) {
|
||||
const dragWidget = this.widgetRefs[dragWidgetObj.id]
|
||||
const dragData = dragWidget.current.serialize()
|
||||
|
||||
const childWidget = this.widgetRefs[childWidgetObj.id]
|
||||
const childData = childWidget.current.serialize()
|
||||
|
||||
// Update the child widget's properties (position type, zIndex, etc.)
|
||||
const updatedChildWidget = {
|
||||
...childWidgetObj,
|
||||
parent: parentWidgetId,
|
||||
initialData: {
|
||||
...childData,
|
||||
positionType: PosType.NONE,
|
||||
zIndex: 0,
|
||||
widgetContainer: WidgetContainer.WIDGET
|
||||
if (swap) {
|
||||
// If swapping, we need to find the common parent
|
||||
const grandParentWidgetObj = this.findWidgetFromListById(dropWidgetObj.parent)
|
||||
console.log("parent widget: ", grandParentWidgetObj, dropWidgetObj, this.state.widgets)
|
||||
if (grandParentWidgetObj) {
|
||||
// Find the indices of the dragged and drop widgets in the grandparent's children array
|
||||
const dragIndex = grandParentWidgetObj.children.findIndex(child => child.id === dragElementID)
|
||||
const dropIndex = grandParentWidgetObj.children.findIndex(child => child.id === parentWidgetId)
|
||||
|
||||
if (dragIndex !== -1 && dropIndex !== -1) {
|
||||
// Swap their positions
|
||||
let childrenCopy = [...grandParentWidgetObj.children]
|
||||
const temp = childrenCopy[dragIndex]
|
||||
childrenCopy[dragIndex] = childrenCopy[dropIndex]
|
||||
childrenCopy[dropIndex] = temp
|
||||
|
||||
// Update the grandparent with the swapped children
|
||||
const updatedGrandParentWidget = {
|
||||
...grandParentWidgetObj,
|
||||
children: childrenCopy
|
||||
}
|
||||
|
||||
// Update the state with the new widget hierarchy
|
||||
this.setState((prevState) => ({
|
||||
widgets: this.updateWidgetRecursively(prevState.widgets, updatedGrandParentWidget)
|
||||
}))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Non-swap mode: Add the dragged widget as a child of the drop widget
|
||||
let updatedWidgets = this.removeWidgetFromCurrentList(dragElementID)
|
||||
|
||||
const updatedDragWidget = {
|
||||
...dragWidgetObj,
|
||||
parent: dropWidgetObj.id, // Keep the parent reference
|
||||
initialData: {
|
||||
...dragData,
|
||||
positionType: PosType.NONE,
|
||||
zIndex: 0,
|
||||
widgetContainer: WidgetContainer.WIDGET
|
||||
}
|
||||
}
|
||||
|
||||
const updatedDropWidget = {
|
||||
...dropWidgetObj,
|
||||
children: [...dropWidgetObj.children, updatedDragWidget]
|
||||
}
|
||||
|
||||
|
||||
// Recursively update the widget structure
|
||||
updatedWidgets = this.updateWidgetRecursively(updatedWidgets, updatedDropWidget, updatedDragWidget)
|
||||
|
||||
// Update the state with the new widget hierarchy
|
||||
this.setState({
|
||||
widgets: updatedWidgets
|
||||
})
|
||||
}
|
||||
|
||||
// Remove the child from its previous location
|
||||
let updatedWidgets = this.removeWidgetFromCurrentList(dragElementID)
|
||||
|
||||
// Add the child widget to the new parent's children
|
||||
const updatedParentWidget = {
|
||||
...parentWidgetObj,
|
||||
children: [...parentWidgetObj.children, updatedChildWidget]
|
||||
}
|
||||
|
||||
// Recursively update the widget structure with the new parent and child
|
||||
updatedWidgets = this.updateWidgetRecursively(updatedWidgets, updatedParentWidget, updatedChildWidget)
|
||||
|
||||
// Update the state with the new widget hierarchy
|
||||
this.setState({
|
||||
widgets: updatedWidgets
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ActiveWidgetContext } from "../activeWidgetContext"
|
||||
import { DragWidgetProvider } from "./draggableWidgetContext"
|
||||
import WidgetDraggable from "./widgetDragDrop"
|
||||
import WidgetContainer from "../constants/containers"
|
||||
import { DragContext } from "../../components/draggable/draggableContext"
|
||||
|
||||
|
||||
|
||||
@@ -48,14 +49,15 @@ class Widget extends React.Component {
|
||||
|
||||
this.icon = "" // antd icon name representing this widget
|
||||
|
||||
this.elementRef = React.createRef()
|
||||
this.elementRef = React.createRef() // this is the outer ref for draggable area
|
||||
this.swappableAreaRef = React.createRef() // helps identify if the users intent is to swap or drop inside the widget
|
||||
this.innerAreaRef = React.createRef() // this is the inner area where swap is prevented and only drop is accepted
|
||||
|
||||
this.functions = {
|
||||
"load": { "args1": "number", "args2": "string" }
|
||||
}
|
||||
|
||||
|
||||
this.layout = Layouts.FLEX
|
||||
this.droppableTags = ["widget"] // This indicates if the draggable can be dropped on this widget
|
||||
this.boundingRect = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -68,6 +70,8 @@ class Widget extends React.Component {
|
||||
selected: false,
|
||||
widgetName: widgetName || 'widget', // this will later be converted to variable name
|
||||
enableRename: false, // will open the widgets editable div for renaming
|
||||
|
||||
isDragging: false, // tells if the widget is currently being dragged
|
||||
dragEnabled: true,
|
||||
|
||||
widgetContainer: WidgetContainer.CANVAS, // what is the parent of the widget
|
||||
@@ -532,22 +536,6 @@ class Widget extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleDrop = (event, dragElement) => {
|
||||
console.log("dragging event: ", event, dragElement)
|
||||
|
||||
const container = dragElement.getAttribute("data-container")
|
||||
// TODO: check if the drop is allowed
|
||||
if (container === "canvas"){
|
||||
|
||||
this.props.onAddChildWidget(this.__id, dragElement.getAttribute("data-widget-id"))
|
||||
|
||||
}else if (container === "sidebar"){
|
||||
|
||||
this.props.onAddChildWidget(this.__id, null, true) // if dragged from the sidebar create the widget first
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -591,7 +579,190 @@ class Widget extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
// FIXME: children outside the bounding box
|
||||
/**
|
||||
*
|
||||
* @depreciated - This function is depreciated in favour of handleDropEvent()
|
||||
*/
|
||||
handleDrop = (event, dragElement) => {
|
||||
// THIS function is depreciated in favour of handleDropEvent()
|
||||
console.log("dragging event: ", event, dragElement)
|
||||
const container = dragElement.getAttribute("data-container")
|
||||
// TODO: check if the drop is allowed
|
||||
if (container === "canvas"){
|
||||
|
||||
this.props.onAddChildWidget(this.__id, dragElement.getAttribute("data-widget-id"))
|
||||
|
||||
}else if (container === "sidebar"){
|
||||
|
||||
this.props.onAddChildWidget(this.__id, null, true) // if dragged from the sidebar create the widget first
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleDragStart = (e, callback) => {
|
||||
e.stopPropagation()
|
||||
this.setState({isDragging: true})
|
||||
|
||||
callback(this.elementRef?.current || null)
|
||||
|
||||
console.log("Drag start: ", this.elementRef)
|
||||
|
||||
// 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
|
||||
dragImage.style.position = 'absolute'
|
||||
dragImage.style.top = '-9999px' // Move it out of view
|
||||
|
||||
document.body.appendChild(dragImage)
|
||||
const rect = this.elementRef?.current.getBoundingClientRect()
|
||||
const offsetX = e.clientX - rect.left
|
||||
const offsetY = e.clientY - rect.top
|
||||
|
||||
// Set the custom drag image with correct offset to avoid snapping to the top-left corner
|
||||
e.dataTransfer.setDragImage(dragImage, offsetX, offsetY)
|
||||
|
||||
// Remove the custom drag image after some time to avoid leaving it in the DOM
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(dragImage)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
handleDragEnter = (e, draggedElement, setOverElement) => {
|
||||
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
console.log("Drag entering...", dragEleType, draggedElement, this.droppableTags)
|
||||
// FIXME: the outer widget shouldn't be swallowed by inner widget
|
||||
if (draggedElement === this.elementRef.current){
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
}
|
||||
|
||||
setOverElement(e.currentTarget) // provide context to the provider
|
||||
|
||||
let showDrop = {
|
||||
allow: true,
|
||||
show: true
|
||||
}
|
||||
|
||||
if (this.droppableTags.length === 0 || this.droppableTags.includes(dragEleType)) {
|
||||
showDrop = {
|
||||
allow: true,
|
||||
show: true
|
||||
}
|
||||
|
||||
} else {
|
||||
showDrop = {
|
||||
allow: false,
|
||||
show: true
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
showDroppableStyle: showDrop
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
handleDragOver = (e, draggedElement) => {
|
||||
if (draggedElement === this.elementRef.current){
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
}
|
||||
|
||||
// console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
if (this.droppableTags.length === 0 || this.droppableTags.includes(dragEleType)) {
|
||||
e.preventDefault() // NOTE: this is necessary to allow drop to take place
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleDropEvent = (e, draggedElement) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
// FIXME: sometimes the elements shoDroppableStyle is not gone, when dropping on the same widget
|
||||
this.setState({
|
||||
showDroppableStyle: {
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
}, () => {
|
||||
console.log("droppable cleared: ", this.elementRef.current, this.state.showDroppableStyle)
|
||||
})
|
||||
|
||||
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
if (this.droppableTags.length > 0 && !this.droppableTags.includes(dragEleType)) {
|
||||
return // prevent drop if the draggable element doesn't match
|
||||
}
|
||||
|
||||
if (draggedElement === this.elementRef.current){
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
}
|
||||
|
||||
let currentElement = e.currentTarget
|
||||
|
||||
while (currentElement) {
|
||||
if (currentElement === draggedElement) {
|
||||
console.log("Dropped into a descendant element, ignoring drop")
|
||||
return // Exit early to prevent the drop
|
||||
}
|
||||
currentElement = currentElement.parentElement // Traverse up to check ancestors
|
||||
}
|
||||
|
||||
const container = draggedElement.getAttribute("data-container")
|
||||
|
||||
const thisContainer = this.elementRef.current.getAttribute("data-container")
|
||||
// console.log("Dropped as swappable: ", e.target, this.swappableAreaRef.current.contains(e.target))
|
||||
// If swaparea is true, then it swaps instead of adding it as a child, also make sure that the parent widget(this widget) is on the widget and not on the canvas
|
||||
const swapArea = (this.swappableAreaRef.current.contains(e.target) && !this.innerAreaRef.current.contains(e.target) && thisContainer === WidgetContainer.WIDGET)
|
||||
|
||||
// TODO: check if the drop is allowed
|
||||
if ([WidgetContainer.CANVAS, WidgetContainer.WIDGET].includes(container)){
|
||||
// console.log("Dropped on meee: ", swapArea, this.swappableAreaRef.current.contains(e.target), thisContainer)
|
||||
|
||||
this.props.onAddChildWidget({parentWidgetId: this.__id,
|
||||
dragElementID: draggedElement.getAttribute("data-widget-id"),
|
||||
swap: swapArea || false
|
||||
})
|
||||
|
||||
}else if (container === WidgetContainer.SIDEBAR){
|
||||
|
||||
// console.log("Dropped on Sidebar: ", this.__id)
|
||||
this.props.onAddChildWidget({parentWidgetId: this.__id, create: true}) // if dragged from the sidebar create the widget first
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
handleDragLeave = (e, draggedElement) => {
|
||||
|
||||
// console.log("Left: ", e.currentTarget, e.relatedTarget, draggedElement)
|
||||
|
||||
if (!e.currentTarget.contains(draggedElement)) {
|
||||
this.setState({
|
||||
showDroppableStyle: {
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
handleDragEnd = (callback) => {
|
||||
callback()
|
||||
this.setState({isDragging: false})
|
||||
}
|
||||
|
||||
|
||||
// FIXME: children outside the bounding box, add tw-overflow-hidden
|
||||
renderContent() {
|
||||
// throw new NotImplementedError("render method has to be implemented")
|
||||
return (
|
||||
@@ -616,125 +787,165 @@ class Widget extends React.Component {
|
||||
left: `${this.state.pos.x}px`,
|
||||
width: `${this.state.size.width}px`,
|
||||
height: `${this.state.size.height}px`,
|
||||
opacity: this.state.isDragging ? 0.3 : 1
|
||||
}
|
||||
|
||||
// console.log("selected: ", this.state.dragEnabled)
|
||||
return (
|
||||
<WidgetDraggable widgetRef={this.elementRef}
|
||||
enableDrag={this.state.dragEnabled}
|
||||
onDrop={this.handleDrop}
|
||||
onDragEnter={({dragElement, showDrop}) => {
|
||||
this.setState({
|
||||
showDroppableStyle: showDrop
|
||||
})
|
||||
}
|
||||
}
|
||||
onDragLeave={ () => {
|
||||
this.setState({
|
||||
showDroppableStyle: {
|
||||
allow: false,
|
||||
show: false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
<div data-widget-id={this.__id}
|
||||
ref={this.elementRef}
|
||||
className="tw-absolute tw-shadow-xl tw-w-fit tw-h-fit"
|
||||
style={outerStyle}
|
||||
data-draggable-type={this.getWidgetType()} // helps with droppable
|
||||
data-container={this.state.widgetContainer} // indicates how the canvas should handle dragging, one is sidebar other is canvas
|
||||
>
|
||||
<DragContext.Consumer>
|
||||
|
||||
<div className="tw-relative tw-w-full tw-h-full tw-top-0 tw-left-0"
|
||||
>
|
||||
{this.renderContent()}
|
||||
{
|
||||
({draggedElement, onDragStart, onDragEnd, setOverElement}) => (
|
||||
|
||||
{
|
||||
// show drop style on drag hover
|
||||
this.state.showDroppableStyle.show &&
|
||||
<div className={`${this.state.showDroppableStyle.allow ? "tw-border-blue-600" : "tw-border-red-600"}
|
||||
tw-absolute tw-top-[-5px] tw-left-[-5px] tw-w-full tw-h-full tw-z-[2]
|
||||
tw-border-2 tw-border-dashed tw-rounded-lg tw-pointer-events-none
|
||||
<div data-widget-id={this.__id}
|
||||
ref={this.elementRef}
|
||||
className="tw-shadow-xl tw-w-fit tw-h-fit"
|
||||
style={outerStyle}
|
||||
data-draggable-type={this.getWidgetType()} // helps with droppable
|
||||
data-container={this.state.widgetContainer} // indicates how the canvas should handle dragging, one is sidebar other is canvas
|
||||
|
||||
draggable={this.state.dragEnabled}
|
||||
|
||||
onDragOver={(e) => this.handleDragOver(e, draggedElement)}
|
||||
onDrop={(e) => this.handleDropEvent(e, draggedElement)}
|
||||
|
||||
`}
|
||||
style={
|
||||
{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}
|
||||
}
|
||||
>
|
||||
onDragEnter={(e) => this.handleDragEnter(e, draggedElement, setOverElement)}
|
||||
onDragLeave={(e) => this.handleDragLeave(e, draggedElement)}
|
||||
|
||||
onDragStart={(e) => this.handleDragStart(e, onDragStart)}
|
||||
onDragEnd={(e) => this.handleDragEnd(onDragEnd)}
|
||||
>
|
||||
{/* FIXME: Swappable when the parent layout is flex/grid and gap is more, this trick won't work, add bg color to check */}
|
||||
{/* FIXME: Swappable, when the parent layout is gap is 0, it doesn't work well */}
|
||||
<div className="tw-relative tw-w-full tw-h-full tw-top-0 tw-left-0"
|
||||
>
|
||||
<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-scale-[1.1] tw-opacity-1 tw-z-[-1] `}
|
||||
style={{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}}
|
||||
ref={this.swappableAreaRef}
|
||||
|
||||
>
|
||||
{/* helps with swappable: if the mouse is in this area while hovering/dropping, then swap */}
|
||||
</div>
|
||||
<div className="tw-relative tw-w-full tw-h-full" ref={this.innerAreaRef}>
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
}
|
||||
|
||||
<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'}`}>
|
||||
{
|
||||
// show drop style on drag hover
|
||||
this.state.showDroppableStyle.show &&
|
||||
<div className={`${this.state.showDroppableStyle.allow ? "tw-border-blue-600" : "tw-border-red-600"}
|
||||
tw-absolute tw-top-[-5px] tw-left-[-5px] tw-w-full tw-h-full tw-z-[2]
|
||||
tw-border-2 tw-border-dashed tw-rounded-lg tw-pointer-events-none
|
||||
|
||||
<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"
|
||||
/>
|
||||
`}
|
||||
style={{
|
||||
width: "calc(100% + 10px)",
|
||||
height: "calc(100% + 10px)",
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className={`tw-absolute 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)",
|
||||
}}
|
||||
>
|
||||
|
||||
<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()
|
||||
this.props.onWidgetResizing("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()
|
||||
this.props.onWidgetResizing("ne")
|
||||
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()
|
||||
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()
|
||||
this.props.onWidgetResizing("se")
|
||||
this.setState({dragEnabled: false})
|
||||
}}
|
||||
onMouseUp={() => this.setState({dragEnabled: true})}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<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()
|
||||
this.props.onWidgetResizing("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()
|
||||
this.props.onWidgetResizing("ne")
|
||||
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()
|
||||
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()
|
||||
this.props.onWidgetResizing("se")
|
||||
this.setState({dragEnabled: false})
|
||||
}}
|
||||
onMouseUp={() => this.setState({dragEnabled: true})}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</DragContext.Consumer>
|
||||
// <WidgetDraggable widgetRef={this.elementRef}
|
||||
// enableDrag={this.state.dragEnabled}
|
||||
// onDrop={this.handleDrop}
|
||||
// onDragEnter={({dragElement, showDrop}) => {
|
||||
// this.setState({
|
||||
// showDroppableStyle: showDrop
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// onDragLeave={ () => {
|
||||
// this.setState({
|
||||
// showDroppableStyle: {
|
||||
// allow: false,
|
||||
// show: false
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</WidgetDraggable>
|
||||
// >
|
||||
// </WidgetDraggable>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useDragContext } from "../../components/draggable/draggableContext"
|
||||
*
|
||||
*/
|
||||
const WidgetDraggable = memo(({ widgetRef, enableDrag=true, dragElementType="widget",
|
||||
onDragEnter, onDragLeave, onDrop,
|
||||
onDragEnter, onDragLeave, onDrop, style={},
|
||||
droppableTags = ["widget"], ...props }) => {
|
||||
|
||||
// FIXME: It's not possible to move the widget ~10px because, its considered as self drop, so fix it
|
||||
@@ -112,11 +112,20 @@ const WidgetDraggable = memo(({ widgetRef, enableDrag=true, dragElementType="wid
|
||||
e.stopPropagation()
|
||||
console.log("Dropped: ", draggedElement, props.children)
|
||||
|
||||
if (draggedElement === widgetRef.current){
|
||||
// prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
return
|
||||
setShowDroppable({
|
||||
allow: false,
|
||||
show: false
|
||||
})
|
||||
|
||||
if (onDrop) {
|
||||
onDrop(e, draggedElement)
|
||||
}
|
||||
|
||||
// if (draggedElement === widgetRef.current){
|
||||
// // prevent drop on itself, since the widget is invisible when dragging, if dropped on itself, it may consume itself
|
||||
// return
|
||||
// }
|
||||
|
||||
let currentElement = e.currentTarget
|
||||
while (currentElement) {
|
||||
if (currentElement === draggedElement) {
|
||||
@@ -126,14 +135,6 @@ const WidgetDraggable = memo(({ widgetRef, enableDrag=true, dragElementType="wid
|
||||
currentElement = currentElement.parentElement // Traverse up to check ancestors
|
||||
}
|
||||
|
||||
setShowDroppable({
|
||||
allow: false,
|
||||
show: false
|
||||
})
|
||||
|
||||
if (onDrop) {
|
||||
onDrop(e, draggedElement)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,8 +156,9 @@ const WidgetDraggable = memo(({ widgetRef, enableDrag=true, dragElementType="wid
|
||||
setIsDragging(false)
|
||||
}
|
||||
|
||||
// TODO: FIXME, currently the draggable div doesn't move with the child, instead only child div moves, simulating childrens movement, add color and check
|
||||
return (
|
||||
<div className={`${props.className || ""} tw-w-fit tw-h-fit tw-bg-blue`}
|
||||
<div className={`${props.className || ""}`}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDropEvent}
|
||||
onDragEnter={handleDragEnter}
|
||||
@@ -164,10 +166,9 @@ const WidgetDraggable = memo(({ widgetRef, enableDrag=true, dragElementType="wid
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
draggable={enableDrag}
|
||||
style={{ opacity: isDragging ? 0.3 : 1}} // hide the initial position when dragging
|
||||
style={{ opacity: isDragging ? 0.3 : 1, ...style}} // hide the initial position when dragging
|
||||
>
|
||||
{props.children}
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import React, { createContext, useContext, useState } from 'react'
|
||||
|
||||
const DragContext = createContext()
|
||||
export const DragContext = createContext()
|
||||
|
||||
export const useDragContext = () => useContext(DragContext)
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ function Premium({ children, className = "" }) {
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-x-circle-fill tw-text-red-600 tw-text-base"></i>
|
||||
<span>Access to UI builder exe for local development</span>
|
||||
<span>Downloadable UI builder exe for local development</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-x-circle-fill tw-text-red-600 tw-text-base"></i>
|
||||
@@ -132,7 +132,7 @@ function Premium({ children, className = "" }) {
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Access to UI builder exe for local development</span>
|
||||
<span>Downloadable UI builder exe for local development</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
@@ -207,7 +207,7 @@ function Premium({ children, className = "" }) {
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
<span>Access to UI builder exe for local development</span>
|
||||
<span>Downloadable UI builder exe for local development</span>
|
||||
</li>
|
||||
<li className="tw-flex tw-place-items-center tw-gap-2">
|
||||
<i className="bi bi-check-circle-fill tw-text-green-600 tw-text-base"></i>
|
||||
|
||||
Reference in New Issue
Block a user