fix: widget initialData load. feat: added more widgets
This commit is contained in:
18
README.md
18
README.md
@@ -3,16 +3,16 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://twitter.com/share?url=https://github.com/PaulleDemon/tkbuilder&text=Check out PyUIBuilder tool">
|
||||
<img src="./assets/share/1.png" height="30" />
|
||||
<img src="./assets/share/1.png" height="35" />
|
||||
</a>
|
||||
<a href="https://www.reddit.com/submit?url=https://github.com/PaulleDemon/tkbuilder&title=Check out PyUIBuilder tool">
|
||||
<img src="./assets/share/4.png" height="30" />
|
||||
<img src="./assets/share/4.png" height="35" />
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/shareArticle?mini=true&url=https://github.com/PaulleDemon/tkbuilder&title=check out PyUIBuilder tool">
|
||||
<img src="./assets/share/2.png" height="30" />
|
||||
<img src="./assets/share/2.png" height="35" />
|
||||
</a>
|
||||
<a href="https://youtube.com/">
|
||||
<img src="./assets/share/3.png" height="30" />
|
||||
<img src="./assets/share/3.png" height="35" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -61,7 +61,7 @@ The discount's will be available for limited time only on pre-orders.
|
||||
| Type | Free | Premium - Hobbyist / Per user | Premium - Commercial / Per user |
|
||||
|-------------------------------------------------------------------|-------------------|----------------------------------------------------------|------------------------------------------------------------|
|
||||
| **Support open-source development** | 👍️ | 😎 | 🚀 |
|
||||
| **Priority support** - (priorities your feature requests, issues) | community support | ✅ | ✅ |
|
||||
| **Priority support** - (prioritize your feature requests, issues) | community support | ✅ | ✅ |
|
||||
| **Lifetime license** (one-time purchase) | 👍️ | ✅ | ✅ |
|
||||
| **Early access** to upcoming features | ❌ | ✅ | ✅ |
|
||||
| **Downloadable Electron App** (upcoming) | ❌ | ✅ | ✅ |
|
||||
@@ -72,7 +72,7 @@ The discount's will be available for limited time only on pre-orders.
|
||||
| **Commercial Use** | ✅ | ❌ | ✅ |
|
||||
| **Support for PyQt/PySide frameworks** (upcoming) | ❌ | ❌ | ✅ |
|
||||
| **More upcoming features and support** | ❓️ | ✅ | ✅ |
|
||||
| **Price** | - | ~~$129~~ $29 (save 77.52% for limited time on pre-order) | ~~180~~ $49 (Save 72.78% for a limited time on pre-orders) |
|
||||
| **Price** | - | ~~$129~~ **$29** (save 77.52% for limited time on pre-order) | ~~180~~ **$49** (Save 72.78% for a limited time on pre-orders) |
|
||||
| Pre-order now! | | [Get license]() | [Get license]() |
|
||||
|
||||
## Newsletter
|
||||
@@ -105,6 +105,12 @@ To keep up with the latest developments considering starting ⭐️ this repo
|
||||
|
||||
* Support for 3rd party UI libraries. Many GUI builders don't come with support for 3rd party libraries.
|
||||
|
||||
4. **Why doesn't the theme of the GUI builder match the theme of Tkinter?**
|
||||
|
||||
**A.** Tkinter is a OS-dependent library, so it would render differently on different OS. Having a common UI the the GUI builder makes it simpler for development.
|
||||
|
||||
If you want a live preview before generating the code you can get a premium license and you'll be notified when that feature releases.
|
||||
|
||||
## License Information
|
||||
|
||||
To support development of this project, license differ depending on the usecase.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from "react"
|
||||
|
||||
import { DndContext } from '@dnd-kit/core'
|
||||
// import { DndContext } from '@dnd-kit/core'
|
||||
|
||||
import { CloseOutlined, DeleteOutlined, EditOutlined, FullscreenOutlined, ReloadOutlined } from "@ant-design/icons"
|
||||
import { DeleteOutlined, EditOutlined, ReloadOutlined } from "@ant-design/icons"
|
||||
import { Button, Tooltip, Dropdown } from "antd"
|
||||
|
||||
import Droppable from "../components/utils/droppableDnd"
|
||||
// import Droppable from "../components/utils/droppableDnd"
|
||||
import Widget from "./widgets/base"
|
||||
import Cursor from "./constants/cursor"
|
||||
|
||||
@@ -14,15 +14,12 @@ 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"
|
||||
import { ReactComponent as DotsBackground } from "../assets/background/dots.svg"
|
||||
|
||||
import DroppableWrapper from "../components/draggable/droppable"
|
||||
import { ActiveWidgetContext, ActiveWidgetProvider, withActiveWidget } from "./activeWidgetContext"
|
||||
import { DragWidgetProvider } from "./widgets/draggableWidgetContext"
|
||||
|
||||
import { PosType } from "./constants/layouts"
|
||||
import WidgetContainer from "./constants/containers"
|
||||
import { isSubClassOfWidget } from "../utils/widget"
|
||||
@@ -629,7 +626,6 @@ class Canvas extends React.Component {
|
||||
*/
|
||||
handleAddWidgetChild = ({ parentWidgetId, dragElementID, swap = false }) => {
|
||||
|
||||
// TODO: creation of the child widget if its not created
|
||||
// widgets data structure { id, widgetType: widgetComponentType, children: [], parent: "" }
|
||||
const dropWidgetObj = this.findWidgetFromListById(parentWidgetId)
|
||||
// Find the dragged widget object
|
||||
@@ -846,7 +842,6 @@ class Canvas extends React.Component {
|
||||
throw new Error("WidgetClass has to be passed for widgets dropped from sidebar")
|
||||
}
|
||||
|
||||
// TODO: handle drop 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)
|
||||
@@ -878,8 +873,6 @@ class Canvas extends React.Component {
|
||||
|
||||
// remove child from current position
|
||||
|
||||
// console.log("pre updated widgets: ", updatedWidgets)
|
||||
|
||||
const updatedChildWidget = {
|
||||
...childWidgetObj,
|
||||
parent: "",
|
||||
|
||||
@@ -6,6 +6,8 @@ const Tools = {
|
||||
COLOR_PICKER: "color_picker",
|
||||
EVENT_HANDLER: "event_handler", // shows a event handler with all the possible function in the dropdown
|
||||
CHECK_BUTTON: "check_button",
|
||||
INPUT_LIST: "input_list",
|
||||
INPUT_RADIO_LIST: "input_radio_list",
|
||||
|
||||
LAYOUT_MANAGER: "layout_manager"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { capitalize } from "../utils/common"
|
||||
import Tools from "./constants/tools.js"
|
||||
import { useActiveWidget } from "./activeWidgetContext.js"
|
||||
import { Layouts } from "./constants/layouts.js"
|
||||
import { DynamicRadioInputList } from "../components/inputs.js"
|
||||
|
||||
|
||||
// FIXME: Maximum recursion error
|
||||
@@ -188,12 +189,20 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
|
||||
{val.tool === Tools.CHECK_BUTTON && (
|
||||
<Checkbox
|
||||
value={val.value}
|
||||
checked={val.value}
|
||||
defaultChecked={val.value}
|
||||
onChange={(e) => handleChange(e.target.checked, val.onChange)}
|
||||
>{val.label}</Checkbox>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.INPUT_RADIO_LIST && (
|
||||
<DynamicRadioInputList
|
||||
defaultInputs={val.value.inputs}
|
||||
defaultSelected={val.value.selectedRadio}
|
||||
onChange={({inputs, selectedRadio}) => handleChange({inputs, selectedRadio}, val.onChange)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
val.tool === Tools.LAYOUT_MANAGER && (
|
||||
renderLayoutManager(val)
|
||||
@@ -207,7 +216,7 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
// 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-row tw-flex-wrap tw-content-start tw-gap-4"
|
||||
: "tw-flex tw-flex-col tw-gap-2"
|
||||
|
||||
return (
|
||||
@@ -227,7 +236,7 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
<div
|
||||
className={`tw-absolute tw-top-20 tw-right-5 tw-bg-white ${toolbarOpen ? "tw-w-[280px]" : "tw-w-0"
|
||||
} tw-px-4 tw-p-2 tw-h-[600px] tw-rounded-md tw-z-[1000] tw-shadow-lg
|
||||
tw-transition-transform tw-duration-75
|
||||
tw-transition-transform tw-duration-75 tw-overflow-x-hidden
|
||||
tw-flex tw-flex-col tw-gap-2 tw-overflow-y-auto`}
|
||||
>
|
||||
<h3 className="tw-text-xl tw-text-center">
|
||||
|
||||
@@ -569,18 +569,39 @@ class Widget extends React.Component {
|
||||
|
||||
if (Object.keys(data).length === 0) return // no data to load
|
||||
|
||||
for (let [key, value] of Object.entries(data.attrs | {}))
|
||||
this.setAttrValue(key, value)
|
||||
data = {...data} // create a shallow copy
|
||||
|
||||
delete data.attrs
|
||||
const {attrs, ...restData} = data
|
||||
|
||||
/**
|
||||
* const obj = { a: 1, b: 2, c: 3 }
|
||||
* const { b, ...newObj } = obj
|
||||
* console.log(newObj) // { a: 1, c: 3 }
|
||||
*/
|
||||
// for (let [key, value] of Object.entries(attrs | {}))
|
||||
// this.setAttrValue(key, value)
|
||||
|
||||
this.setState(data)
|
||||
// delete data.attrs
|
||||
|
||||
this.setState(restData, () => {
|
||||
// UPdates attrs
|
||||
let newAttrs = { ...this.state.attrs }
|
||||
|
||||
// Iterate over each path in the updates object
|
||||
Object.entries(attrs).forEach(([path, value]) => {
|
||||
const keys = path.split('.')
|
||||
const lastKey = keys.pop()
|
||||
|
||||
// Traverse the nested object within attrs
|
||||
let nestedObject = newAttrs
|
||||
|
||||
keys.forEach(key => {
|
||||
nestedObject[key] = { ...nestedObject[key] } // Ensure immutability for each nested level
|
||||
nestedObject = nestedObject[key]
|
||||
})
|
||||
|
||||
// Set the value at the last key
|
||||
nestedObject[lastKey].value = value
|
||||
})
|
||||
|
||||
this.updateState({ attrs: newAttrs })
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -698,17 +719,6 @@ class Widget extends React.Component {
|
||||
})
|
||||
|
||||
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (this.droppableTags && this.droppableTags !== null && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
|
||||
if (!allowDrop) {
|
||||
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
|
||||
@@ -731,6 +741,17 @@ class Widget extends React.Component {
|
||||
// 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)
|
||||
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (this.droppableTags && this.droppableTags !== null && (Object.keys(this.droppableTags).length === 0 ||
|
||||
(this.droppableTags.include?.length > 0 && this.droppableTags.include?.includes(dragEleType)) ||
|
||||
(this.droppableTags.exclude?.length > 0 && !this.droppableTags.exclude?.includes(dragEleType))
|
||||
))
|
||||
|
||||
if (!allowDrop && !swapArea) {
|
||||
// only if both swap and drop is not allowed return, if swap is allowed continue
|
||||
return
|
||||
}
|
||||
// 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)
|
||||
|
||||
124
src/components/inputs.js
Normal file
124
src/components/inputs.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { Input, Button, Space, Radio } from "antd"
|
||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
|
||||
|
||||
|
||||
export const DynamicInputList = () => {
|
||||
const [inputs, setInputs] = useState([""]) // Initialize with one input
|
||||
|
||||
const addInput = () => {
|
||||
setInputs([...inputs, ""])
|
||||
}
|
||||
|
||||
const removeInput = (index) => {
|
||||
setInputs(inputs.filter((_, i) => i !== index))
|
||||
}
|
||||
|
||||
const handleInputChange = (value, index) => {
|
||||
const newInputs = [...inputs]
|
||||
newInputs[index] = value
|
||||
setInputs(newInputs)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{inputs.map((input, index) => (
|
||||
<Space key={index} style={{ display: "flex", marginBottom: 8 }} align="baseline">
|
||||
<Input
|
||||
value={input}
|
||||
onChange={(e) => handleInputChange(e.target.value, index)}
|
||||
placeholder={`Input ${index + 1}`}
|
||||
/>
|
||||
{index !== 0 && ( // Do not show delete button for the first input
|
||||
<MinusCircleOutlined onClick={() => removeInput(index)} />
|
||||
)}
|
||||
</Space>
|
||||
))}
|
||||
|
||||
<Button type="dashed" onClick={addInput} icon={<PlusOutlined />}>
|
||||
Add Input
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export const DynamicRadioInputList = React.memo(({defaultInputs=[""], defaultSelected=null, onChange}) => {
|
||||
const [inputs, setInputs] = useState([""]) // Initialize with one input
|
||||
const [selectedRadio, setSelectedRadio] = useState(null) // Tracks selected radio button
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
setInputs(defaultInputs)
|
||||
|
||||
}, [defaultInputs])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
setSelectedRadio(defaultSelected)
|
||||
}, [defaultSelected])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if(onChange){
|
||||
onChange({inputs, selectedRadio})
|
||||
}
|
||||
|
||||
}, [selectedRadio, inputs])
|
||||
|
||||
// Add a new input
|
||||
const addInput = () => {
|
||||
setInputs([...inputs, ""])
|
||||
}
|
||||
|
||||
// Remove an input by index, but keep the first one
|
||||
const removeInput = (index) => {
|
||||
const newInputs = inputs.filter((_, i) => i !== index)
|
||||
setInputs(newInputs)
|
||||
|
||||
// Adjust selected radio if necessary
|
||||
if (selectedRadio >= newInputs.length) {
|
||||
setSelectedRadio(newInputs.length - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Update input value
|
||||
const handleInputChange = (value, index) => {
|
||||
const newInputs = [...inputs]
|
||||
newInputs[index] = value
|
||||
setInputs(newInputs)
|
||||
}
|
||||
|
||||
// Handle radio button selection
|
||||
const handleRadioChange = (e) => {
|
||||
setSelectedRadio(e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Radio.Group onChange={handleRadioChange} value={selectedRadio}>
|
||||
{inputs.map((input, index) => (
|
||||
<Space key={index} style={{ display: "flex", marginBottom: 8 }} align="baseline">
|
||||
<Radio value={index} defaultChecked={ index === selectedRadio}/>
|
||||
<Input
|
||||
value={input}
|
||||
onChange={(e) => handleInputChange(e.target.value, index)}
|
||||
placeholder={`Input ${index + 1}`}
|
||||
/>
|
||||
|
||||
{index !== 0 && ( // Do not show delete button for the first input
|
||||
<div>
|
||||
<MinusCircleOutlined className="tw-text-xl tw-text-red-500"
|
||||
onClick={() => removeInput(index)} />
|
||||
</div>
|
||||
)}
|
||||
</Space>
|
||||
))}
|
||||
</Radio.Group>
|
||||
|
||||
<Button type="dashed" onClick={addInput} icon={<PlusOutlined />}>
|
||||
Add Input
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
@@ -2,12 +2,13 @@
|
||||
import Widget from "../../canvas/widgets/base"
|
||||
|
||||
import ButtonWidget from "./assets/widgets/button.png"
|
||||
import { CheckBox } from "./widgets/ checkButton"
|
||||
import { CheckBox, RadioButton } from "./widgets/ checkButton"
|
||||
import Button from "./widgets/button"
|
||||
import Frame from "./widgets/frame"
|
||||
import { Input, Text } from "./widgets/input"
|
||||
import Label from "./widgets/label"
|
||||
import MainWindow from "./widgets/mainWindow"
|
||||
import Slider from "./widgets/slider"
|
||||
import TopLevel from "./widgets/toplevel"
|
||||
|
||||
|
||||
@@ -60,6 +61,19 @@ const TkinterSidebar = [
|
||||
link: "https://github.com",
|
||||
widgetClass: CheckBox
|
||||
},
|
||||
{
|
||||
name: "Radio button",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: RadioButton
|
||||
},
|
||||
{
|
||||
name: "Scale",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Slider
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ export class CheckBox extends Widget{
|
||||
defaultChecked: {
|
||||
label: "Checked",
|
||||
tool: Tools.CHECK_BUTTON, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "text", maxLength: 100},
|
||||
value: true,
|
||||
onChange: (value) => this.setAttrValue("defaultChecked", value)
|
||||
}
|
||||
@@ -103,4 +102,113 @@ export class CheckBox extends Widget{
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class RadioButton extends Widget{
|
||||
|
||||
static widgetType = "radio_button"
|
||||
// FIXME: the radio buttons are not visible because of the default heigh provided
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null // disables drops
|
||||
|
||||
// const {layout, ...newAttrs} = this.state.attrs // Removes the layout attribute
|
||||
|
||||
let newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
newAttrs = removeKeyFromObject("styling.backgroundColor", newAttrs)
|
||||
|
||||
this.minSize = {width: 50, height: 30}
|
||||
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 'fit' },
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
styling: {
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetStyling("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
radios: {
|
||||
label: "Radio Group",
|
||||
tool: Tools.INPUT_RADIO_LIST,
|
||||
value: {inputs: ["default"], selectedRadio: -1},
|
||||
onChange: ({inputs, selectedRadio}) => {
|
||||
this.setAttrValue("radios", {inputs, selectedRadio})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
// this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetName("Checkbox")
|
||||
this.setWidgetStyling("backgroundColor", "#fff0")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
const attrs = this.state.attrs
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
checkLabel: attrs.checkLabel,
|
||||
size: toolBarAttrs.size,
|
||||
...attrs,
|
||||
})
|
||||
}
|
||||
|
||||
renderContent(){
|
||||
|
||||
const {inputs, selectedRadio} = this.getAttrValue("radios")
|
||||
|
||||
return (
|
||||
<div className="tw-flex tw-p-1 tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden"
|
||||
style={this.state.widgetStyling}
|
||||
>
|
||||
|
||||
{
|
||||
inputs.map((value, index) => {
|
||||
return (
|
||||
<div key={index} className="tw-flex tw-gap-2 tw-w-full tw-h-full tw-place-items-center ">
|
||||
<div className="tw-border-solid tw-border-[#D9D9D9] tw-border-2
|
||||
tw-min-w-[20px] tw-min-h-[20px] tw-w-[20px] tw-h-[20px]
|
||||
tw-text-blue-600 tw-flex tw-items-center tw-justify-center
|
||||
tw-rounded-full tw-overflow-hidden tw-p-1">
|
||||
|
||||
{
|
||||
selectedRadio === index &&
|
||||
<div className="tw-rounded-full tw-bg-blue-600 tw-w-full tw-h-full">
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<span className="tw-text-base" style={{color: this.state.widgetStyling.foregroundColor}}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class Button extends Widget{
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
buttonLabel: this.state.attrs.buttonLabel,
|
||||
size: toolBarAttrs.widgetName,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export class Input extends Widget{
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.widgetName,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
|
||||
100
src/frameworks/tkinter/widgets/slider.js
Normal file
100
src/frameworks/tkinter/widgets/slider.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
|
||||
class Slider extends Widget{
|
||||
|
||||
static widgetType = "scale"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null // disables drops
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 40 },
|
||||
attrs: {
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...newAttrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
value: "#000",
|
||||
onChange: (value) => {
|
||||
this.setWidgetStyling("color", value)
|
||||
this.setAttrValue("styling.foregroundColor", value)
|
||||
}
|
||||
}
|
||||
},
|
||||
scale: {
|
||||
label: "Scale",
|
||||
display: "horizontal",
|
||||
min: {
|
||||
label: "Min",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "min" },
|
||||
value: 0,
|
||||
onChange: (value) => this.setAttrValue("scale.min", value)
|
||||
},
|
||||
max: {
|
||||
label: "Max",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max"},
|
||||
value: 100,
|
||||
onChange: (value) => this.setAttrValue("scale.max", value)
|
||||
},
|
||||
step: {
|
||||
label: "Step",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "max", stringMode: true, step: "0.1"},
|
||||
value: 1,
|
||||
onChange: (value) => this.setAttrValue("scale.step", value)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetName("Scale")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
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-p-2 tw-w-full tw-h-full tw-flex tw-place-items-center" style={this.state.widgetStyling}>
|
||||
<div className="tw-text-sm tw-text-gray-300">
|
||||
{this.getAttrValue("placeHolder")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Slider
|
||||
Reference in New Issue
Block a user