added key events to canvas
This commit is contained in:
16
README.md
16
README.md
@@ -1 +1,15 @@
|
||||
# TkBuilder
|
||||
# PyUIBuilder - The only Python GUI builder you'll ever need
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
* Framework agnostic - Can outputs code in multiple frameworks.
|
||||
* Easy to use.
|
||||
* Plugins to extend 3rd party UI libraries
|
||||
* Generate Code.
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -13,6 +13,7 @@ import WidgetsContainer from './sidebar/widgetsContainer'
|
||||
import Widget from './canvas/widgets/base'
|
||||
import { DraggableWidgetCard } from './components/cards'
|
||||
import { DragProvider } from './components/draggable/draggableContext'
|
||||
import { ActiveWidgetProvider } from './canvas/activeWidgetContext'
|
||||
|
||||
|
||||
function App() {
|
||||
@@ -141,7 +142,10 @@ function App() {
|
||||
<DragProvider>
|
||||
<div className="tw-w-full tw-h-[94vh] tw-flex">
|
||||
<Sidebar tabs={sidebarTabs}/>
|
||||
|
||||
{/* <ActiveWidgetProvider> */}
|
||||
<Canvas ref={canvasRef} widgets={canvasWidgets} onWidgetAdded={handleWidgetAddedToCanvas}/>
|
||||
{/* </ActiveWidgetProvider> */}
|
||||
</div>
|
||||
|
||||
{/* dragOverlay (dnd-kit) helps move items from one container to another */}
|
||||
|
||||
61
src/canvas/activeWidgetContext.js
Normal file
61
src/canvas/activeWidgetContext.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import React, { createContext, Component, useContext, useState, createRef, useMemo } from 'react'
|
||||
|
||||
|
||||
|
||||
// NOTE: using this context provider causes many re-rendering when the canvas is panned the toolbar updates unnecessarily
|
||||
|
||||
|
||||
// Create the Context
|
||||
export const ActiveWidgetContext = createContext()
|
||||
|
||||
// Create the Provider component
|
||||
export class ActiveWidgetProvider extends Component {
|
||||
state = {
|
||||
activeWidgetId: null,
|
||||
activeWidgetAttrs: {}
|
||||
}
|
||||
|
||||
updateActiveWidget = (widget) => {
|
||||
this.setState({ activeWidgetId: widget })
|
||||
}
|
||||
|
||||
updateToolAttrs = (widgetAttrs) => {
|
||||
this.setState({activeWidgetAttrs: widgetAttrs})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ActiveWidgetContext.Provider
|
||||
value={{ ...this.state, updateActiveWidget: this.updateActiveWidget, updateToolAttrs: this.updateToolAttrs }}
|
||||
>
|
||||
{this.props.children}
|
||||
</ActiveWidgetContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Custom hook for function components
|
||||
export const useActiveWidget = () => {
|
||||
const context = useContext(ActiveWidgetContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useActiveWidget must be used within an ActiveWidgetProvider')
|
||||
}
|
||||
return useMemo(() => context, [context.activeWidgetId, context.activeWidgetAttrs, context.updateToolAttrs, context.updateActiveWidget])
|
||||
}
|
||||
|
||||
// Higher-Order Component for class-based components
|
||||
export const withActiveWidget = (WrappedComponent) => {
|
||||
return class extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ActiveWidgetContext.Consumer>
|
||||
{context => <WrappedComponent {...this.props} {...context} />}
|
||||
</ActiveWidgetContext.Consumer>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { WidgetContext } from './context/widgetContext'
|
||||
|
||||
import DotsBackground from "../assets/background/dots.svg"
|
||||
import DroppableWrapper from "../components/draggable/droppable"
|
||||
import { ActiveWidgetContext, ActiveWidgetProvider, withActiveWidget } from "./activeWidgetContext"
|
||||
|
||||
// const DotsBackground = require("../assets/background/dots.svg")
|
||||
|
||||
@@ -31,6 +32,8 @@ const CanvasModes = {
|
||||
|
||||
class Canvas extends React.Component {
|
||||
|
||||
// static contextType = ActiveWidgetContext
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
@@ -76,6 +79,8 @@ class Canvas extends React.Component {
|
||||
this.mouseDownEvent = this.mouseDownEvent.bind(this)
|
||||
this.mouseMoveEvent = this.mouseMoveEvent.bind(this)
|
||||
this.mouseUpEvent = this.mouseUpEvent.bind(this)
|
||||
this.keyDownEvent = this.keyDownEvent.bind(this)
|
||||
this.wheelZoom = this.wheelZoom.bind(this)
|
||||
|
||||
this.onActiveWidgetUpdate = this.onActiveWidgetUpdate.bind(this)
|
||||
|
||||
@@ -107,15 +112,42 @@ class Canvas extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
this.canvasContainerRef.current.removeEventListener("mousedown", this.mouseDownEvent)
|
||||
this.canvasContainerRef.current.removeEventListener("mouseup", this.mouseUpEvent)
|
||||
this.canvasContainerRef.current.removeEventListener("mousemove", this.mouseMoveEvent)
|
||||
this.canvasContainerRef.current.removeEventListener("wheel", this.wheelZoom)
|
||||
|
||||
this.canvasContainerRef.current.removeEventListener("keydown", this.keyDownEvent)
|
||||
|
||||
|
||||
// NOTE: this will clear the canvas
|
||||
this.clearCanvas()
|
||||
}
|
||||
|
||||
/**
|
||||
initEvents(){
|
||||
|
||||
this.canvasContainerRef.current.addEventListener("mousedown", this.mouseDownEvent)
|
||||
this.canvasContainerRef.current.addEventListener("mouseup", this.mouseUpEvent)
|
||||
this.canvasContainerRef.current.addEventListener("mousemove", this.mouseMoveEvent)
|
||||
this.canvasContainerRef.current.addEventListener("wheel", this.wheelZoom)
|
||||
|
||||
|
||||
this.canvasContainerRef.current.addEventListener("keydown", this.keyDownEvent, true)
|
||||
// window.addEventListener("keydown", this.keyDownEvent, true)
|
||||
|
||||
|
||||
}
|
||||
|
||||
applyTransform(){
|
||||
const { currentTranslate, zoom } = this.state
|
||||
this.canvasRef.current.style.transform = `translate(${currentTranslate.x}px, ${currentTranslate.y}px) scale(${zoom})`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {import("./widgets/base").Widget[]}
|
||||
*/
|
||||
getWidgets(){
|
||||
getWidgets(){
|
||||
|
||||
return this.state.widgets
|
||||
}
|
||||
@@ -130,22 +162,6 @@ class Canvas extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
initEvents(){
|
||||
|
||||
this.canvasContainerRef.current.addEventListener("mousedown", this.mouseDownEvent)
|
||||
this.canvasContainerRef.current.addEventListener("mouseup", this.mouseUpEvent)
|
||||
this.canvasContainerRef.current.addEventListener("mousemove", this.mouseMoveEvent)
|
||||
|
||||
this.canvasContainerRef.current.addEventListener('wheel', (event) => {
|
||||
this.wheelZoom(event)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
applyTransform(){
|
||||
const { currentTranslate, zoom } = this.state
|
||||
this.canvasRef.current.style.transform = `translate(${currentTranslate.x}px, ${currentTranslate.y}px) scale(${zoom})`
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the widget that contains the target
|
||||
@@ -155,6 +171,7 @@ class Canvas extends React.Component {
|
||||
getWidgetFromTarget(target){
|
||||
|
||||
for (let [key, ref] of Object.entries(this.widgetRefs)){
|
||||
// console.log("ref: ", ref)
|
||||
if (ref.current.getElement().contains(target)){
|
||||
return ref.current
|
||||
}
|
||||
@@ -162,6 +179,21 @@ class Canvas extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
keyDownEvent(event){
|
||||
|
||||
if (event.key === "Delete"){
|
||||
this.deleteSelectedWidgets()
|
||||
}
|
||||
|
||||
if (event.key === "+"){
|
||||
this.setZoom(this.state.zoom + 0.1)
|
||||
}
|
||||
|
||||
if (event.key === "-"){
|
||||
this.setZoom(this.state.zoom - 0.1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mouseDownEvent(event){
|
||||
|
||||
@@ -190,6 +222,10 @@ class Canvas extends React.Component {
|
||||
selectedWidgets: [selectedWidget],
|
||||
toolbarAttrs: selectedWidget.getToolbarAttrs()
|
||||
})
|
||||
|
||||
this.context.updateActiveWidget(selectedWidget.__id)
|
||||
this.context.updateToolAttrs(selectedWidget.getToolbarAttrs())
|
||||
// this.props.updateActiveWidget(selectedWidget)
|
||||
}
|
||||
this.currentMode = CanvasModes.MOVE_WIDGET
|
||||
}
|
||||
@@ -302,18 +338,6 @@ class Canvas extends React.Component {
|
||||
return this.state.currentTranslate
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a position relative to canvas container,
|
||||
* returns the position relative to the canvas
|
||||
*/
|
||||
getRelativePositionToCanvas(x, y){
|
||||
|
||||
const canvasRect = this.canvasRef.current.getBoundingClientRect()
|
||||
let zoom = this.state.zoom
|
||||
|
||||
return {x: (canvasRect.left - x ), y: (canvasRect.top - y)}
|
||||
}
|
||||
|
||||
/**
|
||||
* fits the canvas size to fit the widgets bounding box
|
||||
*/
|
||||
@@ -341,32 +365,30 @@ class Canvas extends React.Component {
|
||||
this.canvasContainerRef.current.style.cursor = cursor
|
||||
}
|
||||
|
||||
setZoom(zoom, pos={x:0, y:0}){
|
||||
setZoom(zoom, pos){
|
||||
|
||||
// if (zoom < 0.5 || zoom > 2){
|
||||
// return
|
||||
// }
|
||||
|
||||
const { currentTranslate } = this.state
|
||||
|
||||
let newTranslate = currentTranslate
|
||||
|
||||
if (pos){
|
||||
// Calculate the new translation to zoom into the mouse position
|
||||
const offsetX = pos.x - (this.canvasContainerRef.current.clientWidth / 2 + currentTranslate.x)
|
||||
const offsetY = pos.y - (this.canvasContainerRef.current.clientHeight / 2 + currentTranslate.y)
|
||||
|
||||
// Calculate the new translation to zoom into the mouse position
|
||||
const offsetX = pos.x - (this.canvasContainerRef.current.clientWidth / 2 + currentTranslate.x)
|
||||
const offsetY = pos.y - (this.canvasContainerRef.current.clientHeight / 2 + currentTranslate.y)
|
||||
|
||||
const newTranslateX = currentTranslate.x - offsetX * (zoom - this.state.zoom)
|
||||
const newTranslateY = currentTranslate.y - offsetY * (zoom - this.state.zoom)
|
||||
|
||||
this.setState({
|
||||
zoom: Math.max(0.5, Math.min(zoom, 1.5)), // clamp between 0.5 and 1.5
|
||||
currentTranslate: {
|
||||
const newTranslateX = currentTranslate.x - offsetX * (zoom - this.state.zoom)
|
||||
const newTranslateY = currentTranslate.y - offsetY * (zoom - this.state.zoom)
|
||||
newTranslate = {
|
||||
x: newTranslateX,
|
||||
y: newTranslateY
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
zoom: Math.max(0.5, Math.min(zoom, 1.5)), // clamp between 0.5 and 1.5
|
||||
currentTranslate: newTranslate
|
||||
}, this.applyTransform)
|
||||
|
||||
// this.canvasRef.current.style.width = `${100/zoom}%`
|
||||
// this.canvasRef.current.style.height = `${100/zoom}%`
|
||||
|
||||
}
|
||||
|
||||
@@ -390,6 +412,9 @@ class Canvas extends React.Component {
|
||||
widget.current?.deSelect()
|
||||
})
|
||||
|
||||
this.context?.updateActiveWidget("")
|
||||
this.context.updateToolAttrs({})
|
||||
|
||||
this.setState({
|
||||
selectedWidgets: [],
|
||||
toolbarAttrs: null,
|
||||
@@ -433,7 +458,6 @@ class Canvas extends React.Component {
|
||||
|
||||
// Store the ref in the instance variable
|
||||
this.widgetRefs[id] = widgetRef
|
||||
// console.log("widget ref: ", this.widgetRefs)
|
||||
|
||||
const widgets = [...this.state.widgets, { id, widgetType: widgetComponentType }] // don't add the widget refs in the state
|
||||
// Update the state to include the new widget's type and ID
|
||||
@@ -452,6 +476,10 @@ class Canvas extends React.Component {
|
||||
return {id, widgetRef}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete's the selected widgets from the canvas
|
||||
* @param {null|Widget} widgets - optional widgets that can be deleted along the selected widgets
|
||||
*/
|
||||
deleteSelectedWidgets(widgets=[]){
|
||||
|
||||
|
||||
@@ -519,7 +547,9 @@ class Canvas extends React.Component {
|
||||
if (this.state.selectedWidgets.length === 0 || widgetId !== this.state.selectedWidgets[0].__id)
|
||||
return
|
||||
|
||||
// console.log("updating...")
|
||||
console.log("updating...", this.state.toolbarAttrs, this.state.selectedWidgets.at(0).getToolbarAttrs())
|
||||
|
||||
// console.log("attrs: ", this.state.selectedWidgets.at(0).getToolbarAttrs())
|
||||
|
||||
this.setState({
|
||||
toolbarAttrs: this.state.selectedWidgets.at(0).getToolbarAttrs()
|
||||
@@ -575,36 +605,39 @@ class Canvas extends React.Component {
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/* <ActiveWidgetProvider> */}
|
||||
<DroppableWrapper id="canvas-droppable"
|
||||
className="tw-w-full tw-h-full" onDrop={this.handleDropEvent}>
|
||||
{/* <Dropdown trigger={['contextMenu']} mouseLeaveDelay={0} menu={{items: this.state.contextMenuItems, }}> */}
|
||||
<div className="dots-bg tw-w-full tw-h-full tw-flex tw-relative tw-bg-[#f2f2f2] tw-overflow-hidden"
|
||||
ref={this.canvasContainerRef}
|
||||
style={{
|
||||
transition: " transform 0.3s ease-in-out",
|
||||
backgroundImage: `url('${DotsBackground}')`,
|
||||
backgroundSize: 'cover', // Ensure proper sizing if needed
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
{/* Canvas */}
|
||||
<div data-canvas className="tw-w-full tw-h-full tw-absolute tw-top-0 tw-select-none
|
||||
tw-bg-green-300"
|
||||
ref={this.canvasRef}>
|
||||
<div className="tw-relative tw-w-full tw-h-full">
|
||||
{
|
||||
this.state.widgets.map(this.renderWidget)
|
||||
}
|
||||
</div>
|
||||
<Dropdown trigger={['contextMenu']} mouseLeaveDelay={0} menu={{items: this.state.contextMenuItems, }}>
|
||||
<div className="dots-bg tw-w-full tw-h-full tw-outline-none tw-flex tw-relative tw-bg-[#f2f2f2] tw-overflow-hidden"
|
||||
ref={this.canvasContainerRef}
|
||||
style={{
|
||||
transition: " transform 0.3s ease-in-out",
|
||||
backgroundImage: `url('${DotsBackground}')`,
|
||||
backgroundSize: 'cover', // Ensure proper sizing if needed
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
tabIndex={0} // allow focus
|
||||
>
|
||||
{/* Canvas */}
|
||||
<div data-canvas className="tw-w-full tw-h-full tw-absolute tw-top-0 tw-select-none
|
||||
tw-bg-green-300"
|
||||
ref={this.canvasRef}>
|
||||
<div className="tw-relative tw-w-full tw-h-full">
|
||||
{
|
||||
this.state.widgets.map(this.renderWidget)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{/* </Dropdown> */}
|
||||
</div>
|
||||
</Dropdown>
|
||||
</DroppableWrapper>
|
||||
|
||||
<CanvasToolBar isOpen={this.state.toolbarOpen}
|
||||
widgetType={this.state.selectedWidgets?.at(0)?.getWidgetType() || ""}
|
||||
attrs={this.state.toolbarAttrs}
|
||||
/>
|
||||
{/* </ActiveWidgetProvider> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ColorPicker, Input, InputNumber, Select } from "antd"
|
||||
|
||||
import { capitalize } from "../utils/common"
|
||||
import Tools from "./constants/tools.js"
|
||||
import { useActiveWidget } from "./activeWidgetContext.js"
|
||||
|
||||
|
||||
// FIXME: Maximum recursion error
|
||||
@@ -16,6 +17,9 @@ import Tools from "./constants/tools.js"
|
||||
*/
|
||||
const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
|
||||
// const { activeWidgetAttrs } = useActiveWidget()
|
||||
|
||||
// console.log("active widget context: ", activeWidgetAttrs)
|
||||
const [toolbarOpen, setToolbarOpen] = useState(isOpen)
|
||||
const [toolbarAttrs, setToolbarAttrs] = useState(attrs)
|
||||
|
||||
@@ -24,11 +28,19 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
}, [isOpen])
|
||||
|
||||
useEffect(() => {
|
||||
console.log("active widget: ", attrs)
|
||||
setToolbarAttrs(attrs)
|
||||
}, [attrs])
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
// console.log("active widget: ", activeWidgetAttrs)
|
||||
// setToolbarAttrs(activeWidgetAttrs || {})
|
||||
|
||||
// }, [activeWidgetAttrs])
|
||||
|
||||
const handleChange = (value, callback) => {
|
||||
console.log("changed...")
|
||||
if (callback) {
|
||||
callback(value)
|
||||
}
|
||||
@@ -42,14 +54,14 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
const isFirstLevel = parentKey === ""
|
||||
|
||||
const outerLabelClass = isFirstLevel
|
||||
? "tw-text-lg tw-text-blue-700 tw-font-medium"
|
||||
: "tw-text-lg"
|
||||
? "tw-text-base tw-text-blue-700 tw-font-medium"
|
||||
: "tw-text-base"
|
||||
|
||||
// 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-base"}`}>{val.label}</div>
|
||||
<div className={`${isFirstLevel ? outerLabelClass : "tw-text-sm"}`}>{val.label}</div>
|
||||
|
||||
{val.tool === Tools.INPUT && (
|
||||
<Input
|
||||
|
||||
@@ -6,6 +6,10 @@ import Layouts from "../constants/layouts"
|
||||
import Cursor from "../constants/cursor"
|
||||
import { toSnakeCase } from "../utils/utils"
|
||||
import EditableDiv from "../../components/editableDiv"
|
||||
import DraggableWrapper from "../../components/draggable/draggable"
|
||||
import DroppableWrapper from "../../components/draggable/droppable"
|
||||
|
||||
import { ActiveWidgetContext } from "../activeWidgetContext"
|
||||
|
||||
|
||||
/**
|
||||
@@ -15,6 +19,8 @@ class Widget extends React.Component {
|
||||
|
||||
static widgetType = "widget"
|
||||
|
||||
// static contextType = ActiveWidgetContext
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
@@ -75,7 +81,10 @@ class Widget extends React.Component {
|
||||
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)
|
||||
this.setAttrValue("styling.backgroundColor", value)
|
||||
}
|
||||
},
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
@@ -145,13 +154,33 @@ class Widget extends React.Component {
|
||||
this.canvas.addEventListener("mouseup", this.stopResizing)
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
// if (prevState !== this.state) {
|
||||
// // State has changed
|
||||
// console.log('State has been updated')
|
||||
// } else {
|
||||
// // State has not changed
|
||||
// console.log('State has not changed')
|
||||
// }
|
||||
}
|
||||
|
||||
updateState = (newState, callback) => {
|
||||
|
||||
// FIXME: maximum recursion error when updating size
|
||||
this.setState(newState, () => {
|
||||
|
||||
const { onWidgetUpdate } = this.props
|
||||
if (onWidgetUpdate) {
|
||||
onWidgetUpdate(this.__id)
|
||||
}
|
||||
|
||||
// const { activeWidgetId, updateToolAttrs } = this.context
|
||||
|
||||
// if (activeWidgetId === this.__id)
|
||||
// updateToolAttrs(this.getToolbarAttrs())
|
||||
|
||||
if (callback) callback()
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,7 +203,7 @@ class Widget extends React.Component {
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
size: {
|
||||
label: "Sizing",
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
@@ -228,16 +257,6 @@ class Widget extends React.Component {
|
||||
mousePress(event) {
|
||||
// event.preventDefault()
|
||||
if (!this._disableSelection) {
|
||||
|
||||
// const widgetSelected = new CustomEvent("selection:created", {
|
||||
// detail: {
|
||||
// event,
|
||||
// id: this.__id,
|
||||
// element: this
|
||||
// },
|
||||
// // bubbles: true // Allow the event to bubble up the DOM tree
|
||||
// })
|
||||
// this.canvas.dispatchEvent(widgetSelected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,13 +283,14 @@ class Widget extends React.Component {
|
||||
// don't change position when resizing the widget
|
||||
return
|
||||
}
|
||||
// this.setState({
|
||||
// pos: { x, y }
|
||||
// })
|
||||
|
||||
this.updateState({
|
||||
this.setState({
|
||||
pos: { x, y }
|
||||
})
|
||||
|
||||
// this.updateState({
|
||||
// pos: { x, y }
|
||||
// })
|
||||
}
|
||||
|
||||
setParent(parentId) {
|
||||
@@ -303,38 +323,6 @@ class Widget extends React.Component {
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
// Return scaled width and height
|
||||
return {
|
||||
width: rect.width / scaleX,
|
||||
height: rect.height / scaleY
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getWidgetFunctions() {
|
||||
return this.functions
|
||||
}
|
||||
@@ -400,7 +388,7 @@ class Widget extends React.Component {
|
||||
* @param {string} value - Value of the style
|
||||
* @param {function():void} [callback] - optional callback, thats called after setting the internal state
|
||||
*/
|
||||
setWidgetStyling(key, value, callback) {
|
||||
setWidgetStyling(key, value) {
|
||||
|
||||
const widgetStyle = {
|
||||
...this.state.widgetStyling,
|
||||
@@ -409,9 +397,6 @@ class Widget extends React.Component {
|
||||
|
||||
this.setState({
|
||||
widgetStyling: widgetStyle
|
||||
}, () => {
|
||||
if (callback)
|
||||
callback(widgetStyle)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -420,28 +405,15 @@ class Widget extends React.Component {
|
||||
*
|
||||
* @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) {
|
||||
setWidgetSize(width, height) {
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -530,9 +502,6 @@ class Widget extends React.Component {
|
||||
*/
|
||||
render() {
|
||||
|
||||
const widgetStyle = this.state.widgetStyling
|
||||
|
||||
|
||||
let outerStyle = {
|
||||
cursor: this.cursor,
|
||||
zIndex: this.state.zIndex,
|
||||
@@ -552,51 +521,50 @@ 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'}`}>
|
||||
|
||||
{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]
|
||||
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) => this.startResizing("nw", e)}
|
||||
/>
|
||||
<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) => this.startResizing("ne", e)}
|
||||
/>
|
||||
<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) => this.startResizing("sw", e)}
|
||||
/>
|
||||
<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) => this.startResizing("se", e)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<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) => this.startResizing("nw", e)}
|
||||
/>
|
||||
<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) => this.startResizing("ne", e)}
|
||||
/>
|
||||
<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) => this.startResizing("sw", e)}
|
||||
/>
|
||||
<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) => this.startResizing("se", e)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ export function DraggableWidgetCard({ name, img, url, innerRef}){
|
||||
// <Draggable className="tw-cursor-pointer" id={name}>
|
||||
<DraggableWrapper dragElementType={"widget"} className="tw-cursor-pointer tw-w-fit tw-h-fit">
|
||||
|
||||
<div ref={innerRef} className="tw-select-none tw-pointer-events-none tw-h-[240px] tw-w-[280px] tw-flex tw-flex-col
|
||||
<div ref={innerRef} className="tw-select-none tw-h-[240px] tw-w-[280px] tw-flex tw-flex-col
|
||||
tw-rounded-md tw-overflow-hidden
|
||||
tw-gap-2 tw-text-gray-600 tw-bg-[#ffffff44] tw-border-solid tw-border-[1px]
|
||||
tw-border-[#888] ">
|
||||
<div className="tw-h-[200px] tw-w-full tw-overflow-hidden">
|
||||
<div className="tw-h-[200px] tw-pointer-events-none tw-w-full tw-overflow-hidden">
|
||||
<img src={img} alt={name} className="tw-object-contain tw-h-full tw-w-full tw-select-none" />
|
||||
</div>
|
||||
<span className="tw-text-xl tw-text-center">{name}</span>
|
||||
|
||||
Reference in New Issue
Block a user