feat: added checkbox and other widgets for tkinter
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,3 +1,5 @@
|
||||
{
|
||||
"prettier.configPath": ".vscode/.prettierrc",
|
||||
"markdown.extension.toc.updateOnSave": false
|
||||
|
||||
}
|
||||
21
README.md
21
README.md
@@ -1,5 +1,6 @@
|
||||
# PyUIBuilder - The only Python GUI builder you'll ever need
|
||||
|
||||
|
||||
<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" />
|
||||
@@ -18,6 +19,20 @@
|
||||
Build Python GUI's with the ease of Canva
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Roadmap](#roadmap)
|
||||
- [License](#license)
|
||||
- [Newsletter](#newsletter)
|
||||
- [FAQ](#faq)
|
||||
- [License Information](#license-information)
|
||||
- [Webbased Editor](#webbased-editor)
|
||||
- [Electron App - Hobbyist License](#electron-app---hobbyist-license)
|
||||
- [Electron App - Commercial License](#electron-app---commercial-license)
|
||||
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
* Framework agnostic - Can outputs code in multiple frameworks.
|
||||
@@ -37,7 +52,7 @@ To learn more/ see upcoming features visit [roadmap](./roadmap.md)
|
||||
|
||||
## License
|
||||
|
||||
To support open-source and development of this tool, consider buying a one-time license.
|
||||
To support open-source and development of this tool and upcoming free open-source tools, consider buying a one-time license.
|
||||
|
||||
License will give you access to upcoming features, early access and more.
|
||||
|
||||
@@ -66,6 +81,10 @@ Join the free newsletter to know about upcoming updates, learn how I built this
|
||||
|
||||
[Join free newsletter](https://paulfreeman.substack.com/subscribe?utm_source=Github-Pybuilder)
|
||||
|
||||
#### Keep yourself updated
|
||||
|
||||
To keep up with the latest developments considering starting ⭐️ this repo
|
||||
|
||||
## FAQ
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
## Road map for PyUIBuilder
|
||||
|
||||
Any feature that has 👑 beside it, is meant only for [premium users](./readme.md#license--support)
|
||||
Any feature that has 👑 beside it, is meant only for [premium users](./readme.md#license)
|
||||
|
||||
### 1.0.0
|
||||
- [x] Create the initial version for UI builder
|
||||
|
||||
### 1.2.0
|
||||
- [ ] UI fixes and enhancement
|
||||
- [ ] Documentation
|
||||
- [ ] Tree view for elements on the canvas
|
||||
- [ ] Add text editor to support event handlers
|
||||
- [ ] Support more widgets
|
||||
|
||||
@@ -26,6 +26,7 @@ import { DragWidgetProvider } from "./widgets/draggableWidgetContext"
|
||||
import { PosType } from "./constants/layouts"
|
||||
import WidgetContainer from "./constants/containers"
|
||||
import { isSubClassOfWidget } from "../utils/widget"
|
||||
import { ButtonModal } from "../components/modals"
|
||||
|
||||
// const DotsBackground = require("../assets/background/dots.svg")
|
||||
|
||||
@@ -115,8 +116,6 @@ class Canvas extends React.Component {
|
||||
componentDidMount() {
|
||||
this.initEvents()
|
||||
|
||||
this.createWidget(Widget)
|
||||
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -636,7 +635,7 @@ class Canvas extends React.Component {
|
||||
// Find the dragged widget object
|
||||
let dragWidgetObj = this.findWidgetFromListById(dragElementID)
|
||||
|
||||
console.log("Drag widget obj: ", dragWidgetObj)
|
||||
// console.log("Drag widget obj: ", dragWidgetObj)
|
||||
|
||||
if (dropWidgetObj && dragWidgetObj) {
|
||||
const dragWidget = this.widgetRefs[dragWidgetObj.id]
|
||||
@@ -713,7 +712,7 @@ class Canvas extends React.Component {
|
||||
throw new Error("widgetComponentType must be a subclass of Widget class")
|
||||
}
|
||||
|
||||
console.log("componete: ", widgetComponentType)
|
||||
// console.log("componete: ", widgetComponentType)
|
||||
|
||||
const widgetRef = React.createRef()
|
||||
|
||||
@@ -759,6 +758,11 @@ class Canvas extends React.Component {
|
||||
|
||||
let activeWidgets = removeDuplicateObjects([...widgets, this.state.selectedWidget], "__id")
|
||||
|
||||
this.setState({
|
||||
toolbarAttrs: null,
|
||||
selectedWidget: null
|
||||
})
|
||||
|
||||
const widgetIds = activeWidgets.map(widget => widget.__id)
|
||||
|
||||
for (let widgetId of widgetIds) {
|
||||
@@ -963,9 +967,17 @@ class Canvas extends React.Component {
|
||||
<Tooltip title="Reset viewport">
|
||||
<Button icon={<ReloadOutlined />} onClick={this.resetTransforms} />
|
||||
</Tooltip>
|
||||
<Tooltip title="Clear canvas">
|
||||
<Button danger icon={<DeleteOutlined />} onClick={this.clearCanvas} />
|
||||
</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"
|
||||
>
|
||||
<Tooltip title="Clear canvas">
|
||||
<Button danger icon={<DeleteOutlined />} />
|
||||
</Tooltip>
|
||||
</ButtonModal>
|
||||
</div>
|
||||
|
||||
{/* <ActiveWidgetProvider> */}
|
||||
|
||||
@@ -5,6 +5,7 @@ const Tools = {
|
||||
SELECT_DROPDOWN: "select_dropdown",
|
||||
COLOR_PICKER: "color_picker",
|
||||
EVENT_HANDLER: "event_handler", // shows a event handler with all the possible function in the dropdown
|
||||
CHECK_BUTTON: "check_button",
|
||||
|
||||
LAYOUT_MANAGER: "layout_manager"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useState } from "react"
|
||||
|
||||
import { ColorPicker, Input, InputNumber, Select } from "antd"
|
||||
import { Checkbox, ColorPicker, Input, InputNumber, Select } from "antd"
|
||||
|
||||
import { capitalize } from "../utils/common"
|
||||
import Tools from "./constants/tools.js"
|
||||
@@ -186,6 +186,14 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{val.tool === Tools.CHECK_BUTTON && (
|
||||
<Checkbox
|
||||
value={val.value}
|
||||
defaultChecked={val.value}
|
||||
onChange={(e) => handleChange(e.target.checked, val.onChange)}
|
||||
>{val.label}</Checkbox>
|
||||
)}
|
||||
|
||||
{
|
||||
val.tool === Tools.LAYOUT_MANAGER && (
|
||||
renderLayoutManager(val)
|
||||
@@ -223,7 +231,7 @@ const CanvasToolBar = memo(({ isOpen, widgetType, attrs = {} }) => {
|
||||
tw-flex tw-flex-col tw-gap-2 tw-overflow-y-auto`}
|
||||
>
|
||||
<h3 className="tw-text-xl tw-text-center">
|
||||
{capitalize(`${widgetType || ""}`)}
|
||||
{capitalize(`${widgetType || ""}`).replace(/_/g, " ")}
|
||||
</h3>
|
||||
|
||||
<div className="tw-flex tw-flex-col tw-gap-4">{renderWidgets(toolbarAttrs || {})}</div>
|
||||
|
||||
@@ -55,7 +55,9 @@ class Widget extends React.Component {
|
||||
"load": { "args1": "number", "args2": "string" }
|
||||
}
|
||||
|
||||
this.droppableTags = {} // This indicates if the draggable can be dropped on this widget
|
||||
// This indicates if the draggable can be dropped on this widget, set this to null to disable drops
|
||||
this.droppableTags = {}
|
||||
|
||||
this.boundingRect = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -168,9 +170,14 @@ class Widget extends React.Component {
|
||||
componentDidMount() {
|
||||
|
||||
// FIXME: initial layout is not set properly
|
||||
console.log("prior layout: ", this.state.attrs.layout.value)
|
||||
this.setLayout(this.state.attrs.layout.value)
|
||||
this.setWidgetStyling('backgroundColor', this.state.attrs.styling?.backgroundColor.value || "#fff")
|
||||
|
||||
if (this.state.attrs.layout){
|
||||
this.setLayout(this.state.attrs.layout.value)
|
||||
console.log("prior layout: ", this.state.attrs.layout.value)
|
||||
}
|
||||
|
||||
if (this.state.attrs.styling.backgroundColor)
|
||||
this.setWidgetStyling('backgroundColor', this.state.attrs.styling?.backgroundColor.value || "#fff")
|
||||
|
||||
this.load(this.props.initialData || {}) // load the initial data
|
||||
|
||||
@@ -577,26 +584,6 @@ class Widget extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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()
|
||||
@@ -653,7 +640,7 @@ class Widget extends React.Component {
|
||||
show: true
|
||||
}
|
||||
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
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))
|
||||
))
|
||||
@@ -686,7 +673,7 @@ class Widget extends React.Component {
|
||||
// console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
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))
|
||||
))
|
||||
@@ -713,7 +700,7 @@ class Widget extends React.Component {
|
||||
|
||||
const dragEleType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (this.droppableTags && (Object.keys(this.droppableTags).length === 0 ||
|
||||
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))
|
||||
))
|
||||
@@ -816,7 +803,7 @@ class Widget extends React.Component {
|
||||
* This is an internal methods don't override
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
render() {
|
||||
render() {
|
||||
|
||||
let outerStyle = {
|
||||
cursor: this.cursor,
|
||||
@@ -962,27 +949,6 @@ class Widget extends React.Component {
|
||||
}
|
||||
|
||||
</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
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// >
|
||||
// </WidgetDraggable>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function SidebarWidgetCard({ name, img, url, widgetClass, innerRef}){
|
||||
return (
|
||||
// <Draggable className="tw-cursor-pointer" id={name}>
|
||||
<DraggableWrapper data-container={"sidebar"}
|
||||
dragElementType={"widget"}
|
||||
dragElementType={widgetClass.widgetType}
|
||||
dragWidgetClass={widgetClass}
|
||||
className="tw-cursor-pointer tw-w-fit tw-h-fit">
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useDragContext } from "./draggableContext"
|
||||
|
||||
|
||||
/**
|
||||
* @param {{include: [], exclude: []} || {}} - droppableTags - if empty object, allows everything to be dropped, define include to allow only included widgets, define exclude to exclude
|
||||
* @param {{include: [], exclude: []} || {} || null} - droppableTags - if empty object, allows everything to be dropped, if null nothing can be dropped, define include to allow only included widgets, define exclude to exclude
|
||||
*/
|
||||
const DroppableWrapper = memo(({onDrop, droppableTags={}, ...props}) => {
|
||||
|
||||
@@ -20,11 +20,10 @@ const DroppableWrapper = memo(({onDrop, droppableTags={}, ...props}) => {
|
||||
|
||||
const dragElementType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
console.log("Current target: ", droppableTags, Object.keys(droppableTags))
|
||||
|
||||
setOverElement(e.currentTarget)
|
||||
|
||||
const allowDrop = (droppableTags && (Object.keys(droppableTags).length === 0 ||
|
||||
const allowDrop = (droppableTags && droppableTags !== null && (Object.keys(droppableTags).length === 0 ||
|
||||
(droppableTags.include?.length > 0 && droppableTags.include?.includes(dragElementType)) ||
|
||||
(droppableTags.exclude?.length > 0 && !droppableTags.exclude?.includes(dragElementType))
|
||||
))
|
||||
@@ -46,7 +45,7 @@ const DroppableWrapper = memo(({onDrop, droppableTags={}, ...props}) => {
|
||||
// console.log("Drag over: ", e.dataTransfer.getData("text/plain"), e.dataTransfer)
|
||||
const dragElementType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
const allowDrop = (droppableTags && (Object.keys(droppableTags).length === 0 ||
|
||||
const allowDrop = (droppableTags && droppableTags !== null && (Object.keys(droppableTags).length === 0 ||
|
||||
(droppableTags.include?.length > 0 && droppableTags.include?.includes(dragElementType)) ||
|
||||
(droppableTags.exclude?.length > 0 && !droppableTags.exclude?.includes(dragElementType))
|
||||
))
|
||||
@@ -67,7 +66,7 @@ const DroppableWrapper = memo(({onDrop, droppableTags={}, ...props}) => {
|
||||
const dragElementType = draggedElement.getAttribute("data-draggable-type")
|
||||
|
||||
|
||||
const allowDrop = (droppableTags && (Object.keys(droppableTags).length === 0 ||
|
||||
const allowDrop = (droppableTags && droppableTags !== null && (Object.keys(droppableTags).length === 0 ||
|
||||
(droppableTags.include?.length > 0 && droppableTags.include?.includes(dragElementType)) ||
|
||||
(droppableTags.exclude?.length > 0 && !droppableTags.exclude?.includes(dragElementType))
|
||||
))
|
||||
|
||||
51
src/components/modals.js
Normal file
51
src/components/modals.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useState } from "react"
|
||||
|
||||
import { Modal } from "antd"
|
||||
|
||||
/**
|
||||
* opens modal when clicked, acts as a wrapper
|
||||
* @param {string} - message
|
||||
* @param {string} - title
|
||||
* @param {string} - okText
|
||||
* @param {"default"|"danger"|"primary"} - okButtonType
|
||||
* @returns
|
||||
*/
|
||||
export const ButtonModal = ({ message, title, okText="OK", onOk, onCancel, okButtonType="default", children}) => {
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
|
||||
const showModal = () => {
|
||||
setIsModalOpen(true)
|
||||
}
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalOpen(false)
|
||||
console.log("Ok pressed")
|
||||
if (onOk){
|
||||
onOk()
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = (e) => {
|
||||
e.stopPropagation()
|
||||
|
||||
setIsModalOpen(false)
|
||||
console.log("cancel pressed")
|
||||
|
||||
if (onCancel){
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div onClick={showModal}>
|
||||
{children}
|
||||
<Modal title={title} open={isModalOpen} onClose={handleCancel}
|
||||
okText={okText}
|
||||
onOk={handleOk} okType={okButtonType} onCancel={handleCancel}>
|
||||
<p>{message}</p>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import Widget from "../../canvas/widgets/base"
|
||||
|
||||
import ButtonWidget from "./assets/widgets/button.png"
|
||||
import { CheckBox } from "./widgets/ checkButton"
|
||||
import Button from "./widgets/button"
|
||||
import Frame from "./widgets/frame"
|
||||
import Input from "./widgets/input"
|
||||
import { Input, Text } from "./widgets/input"
|
||||
import Label from "./widgets/label"
|
||||
import MainWindow from "./widgets/mainWindow"
|
||||
import TopLevel from "./widgets/toplevel"
|
||||
@@ -42,12 +43,50 @@ const TkinterSidebar = [
|
||||
widgetClass: Button
|
||||
},
|
||||
{
|
||||
name: "Input",
|
||||
name: "Entry",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Input
|
||||
},
|
||||
{
|
||||
name: "Text",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: Text
|
||||
},
|
||||
{
|
||||
name: "CheckBox",
|
||||
img: ButtonWidget,
|
||||
link: "https://github.com",
|
||||
widgetClass: CheckBox
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
export default TkinterSidebar
|
||||
export default TkinterSidebar
|
||||
|
||||
|
||||
/**
|
||||
* widgets = {
|
||||
"Tk": set(),
|
||||
"Label": set(),
|
||||
"Button": set(),
|
||||
"Entry": set(),
|
||||
"CheckButton": set(),
|
||||
"RadioButton": set(),
|
||||
"Scale": set(),
|
||||
"ListBox": set(),
|
||||
"Frame": set(),
|
||||
"LabelFrame": set(),
|
||||
"PanedWindow": set(),
|
||||
"SpinBox": set(),
|
||||
"OptionMenu": set(),
|
||||
"Canvas": set(),
|
||||
"TopLevel": set(),
|
||||
"Message": set(),
|
||||
"Menu": set(),
|
||||
"MenuButton": set(),
|
||||
"ScrollBar": set(),
|
||||
"Text": set()
|
||||
}
|
||||
*/
|
||||
106
src/frameworks/tkinter/widgets/ checkButton.js
Normal file
106
src/frameworks/tkinter/widgets/ checkButton.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { Checkbox } from "antd"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
import { CheckOutlined, CheckSquareFilled } from "@ant-design/icons"
|
||||
|
||||
|
||||
export class CheckBox extends Widget{
|
||||
|
||||
static widgetType = "check_button"
|
||||
// TODO: remove layouts
|
||||
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: 30 },
|
||||
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)
|
||||
}
|
||||
}
|
||||
},
|
||||
checkLabel: {
|
||||
label: "Check Label",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "Button label", maxLength: 100},
|
||||
value: "Checkbox",
|
||||
onChange: (value) => this.setAttrValue("checkLabel", value)
|
||||
},
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(){
|
||||
return (
|
||||
<div className="tw-flex tw-p-1 tw-w-full tw-h-full tw-rounded-md tw-overflow-hidden"
|
||||
style={this.state.widgetStyling}
|
||||
>
|
||||
|
||||
<div className="tw-flex tw-gap-2 tw-w-full tw-h-full tw-place-items-center tw-place-content-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-md tw-overflow-hidden">
|
||||
{
|
||||
this.getAttrValue("defaultChecked") === true &&
|
||||
<CheckSquareFilled className="tw-text-[20px]" />
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
{this.getAttrValue("checkLabel")}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
|
||||
class Button extends Widget{
|
||||
@@ -15,14 +16,15 @@ class Button extends Widget{
|
||||
// TODO: exclude all
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 80, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
...newAttrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
@@ -47,38 +49,20 @@ class Button extends Widget{
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setWidgetName("button")
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
buttonLabel: this.state.attrs.buttonLabel,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
size: toolBarAttrs.widgetName,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
@@ -87,7 +71,8 @@ class Button extends Widget{
|
||||
|
||||
renderContent(){
|
||||
return (
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md tw-border tw-border-solid tw-overflow-hidden">
|
||||
<div className="tw-w-flex tw-flex-col tw-w-full tw-h-full tw-rounded-md
|
||||
tw-border tw-border-solid tw-border-gray-400 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 className="tw-text-sm" style={{color: this.getAttrValue("styling.foregroundColor")}}>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
|
||||
class Input extends Widget{
|
||||
export class Input extends Widget{
|
||||
|
||||
static widgetType = "input"
|
||||
// TODO: override the widgetName value
|
||||
static widgetType = "entry"
|
||||
// TODO: remove layouts
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = {
|
||||
// TODO: exclude all
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
this.droppableTags = null // disables drops
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
...newAttrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
@@ -46,37 +46,93 @@ class Input extends Widget{
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetName("Entry")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
size: toolBarAttrs.widgetName,
|
||||
|
||||
...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 class Text extends Widget{
|
||||
|
||||
static widgetType = "Text"
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.droppableTags = null
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 120, height: 80 },
|
||||
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)
|
||||
}
|
||||
}
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
placeHolder: {
|
||||
label: "PlaceHolder",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: {placeholder: "text", maxLength: 100},
|
||||
value: "placeholder text",
|
||||
onChange: (value) => this.setAttrValue("placeHolder", value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetName("text")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
placeHolder: this.state.attrs.placeHolder,
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
@@ -96,6 +152,3 @@ class Input extends Widget{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Input
|
||||
@@ -1,5 +1,6 @@
|
||||
import Widget from "../../../canvas/widgets/base"
|
||||
import Tools from "../../../canvas/constants/tools"
|
||||
import { removeKeyFromObject } from "../../../utils/common"
|
||||
|
||||
|
||||
class Label extends Widget{
|
||||
@@ -14,13 +15,16 @@ class Label extends Widget{
|
||||
exclude: ["image", "video", "media", "main_window", "toplevel"]
|
||||
}
|
||||
|
||||
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
|
||||
|
||||
|
||||
this.state = {
|
||||
...this.state,
|
||||
size: { width: 80, height: 40 },
|
||||
attrs: {
|
||||
...this.state.attrs,
|
||||
...newAttrs,
|
||||
styling: {
|
||||
...this.state.attrs.styling,
|
||||
...newAttrs.styling,
|
||||
foregroundColor: {
|
||||
label: "Foreground Color",
|
||||
tool: Tools.COLOR_PICKER, // the tool to display, can be either HTML ELement or a constant string
|
||||
@@ -45,38 +49,18 @@ class Label extends Widget{
|
||||
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
this.setAttrValue("styling.backgroundColor", "#fff")
|
||||
this.setWidgetStyling("backgroundColor", "#fff0")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
labelWidget: this.state.attrs.labelWidget,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
|
||||
@@ -33,37 +33,17 @@ class MainWindow extends Widget{
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
this.setWidgetName("main")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
title: this.state.attrs.title,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
|
||||
@@ -34,37 +34,16 @@ class TopLevel extends Widget{
|
||||
componentDidMount(){
|
||||
super.componentDidMount()
|
||||
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
|
||||
this.setWidgetName("toplevel")
|
||||
}
|
||||
|
||||
getToolbarAttrs(){
|
||||
const toolBarAttrs = super.getToolbarAttrs()
|
||||
|
||||
return ({
|
||||
id: this.__id,
|
||||
widgetName: {
|
||||
label: "Widget Name",
|
||||
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "Widget name", maxLength: 40 },
|
||||
value: this.state.widgetName,
|
||||
onChange: (value) => this.setWidgetName(value)
|
||||
},
|
||||
widgetName: toolBarAttrs.widgetName,
|
||||
title: this.state.attrs.title,
|
||||
size: {
|
||||
label: "Size",
|
||||
display: "horizontal",
|
||||
width: {
|
||||
label: "Width",
|
||||
tool: Tools.NUMBER_INPUT, // the tool to display, can be either HTML ELement or a constant string
|
||||
toolProps: { placeholder: "width", max: this.maxSize.width, min: this.minSize.width },
|
||||
value: this.state.size.width || 100,
|
||||
onChange: (value) => this.setWidgetSize(value, null)
|
||||
},
|
||||
height: {
|
||||
label: "Height",
|
||||
tool: Tools.NUMBER_INPUT,
|
||||
toolProps: { placeholder: "height", max: this.maxSize.height, min: this.minSize.height },
|
||||
value: this.state.size.height || 100,
|
||||
onChange: (value) => this.setWidgetSize(null, value)
|
||||
},
|
||||
},
|
||||
size: toolBarAttrs.size,
|
||||
|
||||
...this.state.attrs,
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
|
||||
export function removeDuplicateObjects(array, key) {
|
||||
const seen = new Set()
|
||||
|
||||
return array.filter(item => {
|
||||
if (!seen.has(item[key])) {
|
||||
seen.add(item[key])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
const seen = new Set()
|
||||
|
||||
return array.filter(item => {
|
||||
if (!seen.has(item[key])) {
|
||||
seen.add(item[key])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,5 +20,34 @@ export function removeDuplicateObjects(array, key) {
|
||||
* @returns
|
||||
*/
|
||||
export function capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given the key as a path, removes the widget attribute at the given path
|
||||
* @param {string} path - path to the key, eg: styling.backgroundColor
|
||||
* @param {{}} _object - object with key and value
|
||||
*/
|
||||
export function removeKeyFromObject(path, _object) {
|
||||
const keys = path.split('.')
|
||||
const lastKey = keys.pop()
|
||||
|
||||
// Traverse the state and find the nested object up to the second last key
|
||||
let newAttrs = { ..._object }
|
||||
let nestedObject = newAttrs
|
||||
|
||||
for (const key of keys) {
|
||||
if (nestedObject[key] !== undefined) {
|
||||
nestedObject[key] = { ...nestedObject[key] } // Ensure immutability
|
||||
nestedObject = nestedObject[key]
|
||||
} else {
|
||||
return // Key doesn't exist, so nothing to remove
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the attribute
|
||||
delete nestedObject[lastKey]
|
||||
|
||||
return newAttrs
|
||||
}
|
||||
Reference in New Issue
Block a user