added plugins, updated image paths

This commit is contained in:
paul
2024-09-24 11:50:50 +05:30
parent 19ac520f78
commit be17b93b02
34 changed files with 620 additions and 89 deletions

View File

@@ -52,9 +52,9 @@ To learn more/ see upcoming features visit [roadmap](./roadmap.md)
## License
To support open-source and development of this tool and upcoming free open-source tools, consider buying a one-time license.
To support open-source and development of this tool and upcoming free open-source tools and libraries, consider buying a one-time license.
License will give you access to upcoming features, early access and more.
Purchasing License will allow me to focus on this work and give you access to more advance features, early access and more.
The discount's will be available for limited time only on pre-orders.

121
package-lock.json generated
View File

@@ -16,6 +16,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"analogue-time-picker": "^1.0.2",
"antd": "^5.20.0",
"autoprefixer": "^10.4.20",
"dom-to-image-more": "^3.4.5",
@@ -4507,15 +4508,6 @@
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint-scope": {
"version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -5354,6 +5346,11 @@
"ajv": "^8.8.2"
}
},
"node_modules/analogue-time-picker": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/analogue-time-picker/-/analogue-time-picker-1.0.2.tgz",
"integrity": "sha512-YjF5qZWoiEc5MKk4yi0kxb8IGdOb0D1NERKj8BJCDK9TSmmZzdMh9vJH+Lw2apL+7rb544rja0tKKpKoWoU5NQ=="
},
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -6148,9 +6145,9 @@
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
@@ -6160,7 +6157,7 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
@@ -7816,9 +7813,9 @@
}
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"engines": {
"node": ">= 0.8"
}
@@ -8798,36 +8795,36 @@
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@@ -9334,12 +9331,12 @@
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
@@ -13781,9 +13778,12 @@
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
@@ -13807,9 +13807,9 @@
}
},
"node_modules/micromatch": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
@@ -14594,9 +14594,9 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
},
"node_modules/path-type": {
"version": "5.0.0",
@@ -16113,11 +16113,11 @@
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
"side-channel": "^1.0.4"
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@@ -17781,9 +17781,9 @@
}
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@@ -17816,6 +17816,14 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -17900,14 +17908,14 @@
}
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dependencies": {
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
@@ -19746,11 +19754,10 @@
}
},
"node_modules/webpack": {
"version": "5.93.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz",
"integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==",
"version": "5.94.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.12.1",
@@ -19759,7 +19766,7 @@
"acorn-import-attributes": "^1.9.5",
"browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.17.0",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",

View File

@@ -11,6 +11,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"analogue-time-picker": "^1.0.2",
"antd": "^5.20.0",
"autoprefixer": "^10.4.20",
"dom-to-image-more": "^3.4.5",

View File

@@ -32,4 +32,5 @@ Any feature that has 👑 beside it, is meant only for [premium users](./readme.
### 3.0.0
- [ ] Node based System (eg: like blender/ unity node system)
- [ ] Support for PySide / PyQt 👑 (commercial license only)

View File

@@ -14,7 +14,9 @@ import Widget from './canvas/widgets/base'
import { DraggableWidgetCard } from './components/cards'
import { DragProvider } from './components/draggable/draggableContext'
import { ActiveWidgetProvider } from './canvas/activeWidgetContext'
import TkinterSidebar from './frameworks/tkinter/sidebarWidgets'
import TkinterWidgets from './frameworks/tkinter/sidebarWidgets'
import PluginsContainer from './sidebar/pluginsContainer'
import TkinterPluginWidgets from './frameworks/tkinter/sidebarPlugins'
function App() {
@@ -31,7 +33,7 @@ function App() {
const [dropAnimation, setDropAnimation] = useState(null)
const [sidebarWidgets, setSidebarWidgets] = useState(TkinterSidebar || [])
const [sidebarWidgets, setSidebarWidgets] = useState(TkinterWidgets || [])
const [canvasWidgets, setCanvasWidgets] = useState([]) // contains the reference to the widgets inside the canvas
const [activeSidebarWidget, setActiveSidebarWidget] = useState(null) // helps with the dnd overlay
@@ -44,12 +46,12 @@ function App() {
{
name: "Widgets",
icon: <LayoutFilled />,
content: <WidgetsContainer sidebarContent={TkinterSidebar} onWidgetsUpdate={(widgets) => setSidebarWidgets(widgets)}/>
content: <WidgetsContainer sidebarContent={TkinterWidgets} onWidgetsUpdate={(widgets) => setSidebarWidgets(widgets)}/>
},
{
name: "Plugins",
icon: <ProductFilled />,
content: <></>
content: <PluginsContainer sidebarContent={TkinterPluginWidgets}/>
},
{
name: "Uploads",

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,105 @@
import React from "react"
import { timePicker, timePickerInput } from 'analogue-time-picker'
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
class AnalogTimePicker extends Widget{
static widgetType = "analogue_timepicker"
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.timePicker = null
this.timePickerRef = React.createRef()
this.state = {
...this.state,
size: { width: 'fit', height: 'fit' },
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)
}
}
},
buttonLabel: {
label: "Button Label",
tool: Tools.INPUT, // the tool to display, can be either HTML ELement or a constant string
toolProps: {placeholder: "Button label", maxLength: 100},
value: "Button",
onChange: (value) => this.setAttrValue("buttonLabel", value)
}
}
}
}
componentDidMount(){
super.componentDidMount()
this.setWidgetName("Time picker")
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
this.timePicker = timePicker({
element: this.timePickerRef.current,
mode: "12"
})
const timePickerBtns = this.timePickerRef.current.getElementsByClassName("atp-clock-btn")
for (let i = 0; i < timePickerBtns.length; i++) {
timePickerBtns[i].remove()
}
}
componentWillUnmount(){
this.timePicker.dispose()
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
return ({
id: this.__id,
widgetName: toolBarAttrs.widgetName,
buttonLabel: this.state.attrs.buttonLabel,
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-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 tw-pointer-events-none"
style={this.state.widgetStyling} ref={this.timePickerRef}>
</div>
</div>
)
}
}
export default AnalogTimePicker

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -0,0 +1,79 @@
import React from "react"
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import MapImage from "./assets/map.png"
import { MinusOutlined, PlayCircleFilled, PlusOutlined } from "@ant-design/icons"
class MapView extends Widget{
static widgetType = "map_view"
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 400, height: 250 },
}
}
componentDidMount(){
super.componentDidMount()
this.setWidgetName("Map viewer")
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
return ({
id: this.__id,
widgetName: toolBarAttrs.widgetName,
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-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 tw-pointer-events-none"
style={this.state.widgetStyling}>
<div className="tw-relative tw-w-full tw-h-full">
<div className="tw-absolute tw-left-5 tw-top-3 tw-flex tw-flex-col tw-gap-2">
<div className="tw-text-white tw-bg-black tw-text-center
tw-p-[2px]
tw-w-[25px] tw-h-[25px] tw-rounded-md">
<PlusOutlined className="tw-text-xl"/>
</div>
<div className="tw-text-white tw-bg-black tw-text-center
tw-p-1
tw-w-[25px] tw-h-[25px] tw-rounded-md">
<MinusOutlined className="tw-text-xl"/>
</div>
</div>
<img src={MapImage} className="tw-w-full tw-h-full" />
</div>
</div>
</div>
)
}
}
export default MapView

View File

@@ -0,0 +1,139 @@
import React, { useEffect, useRef, useState } from "react"
import Widget from "../../../canvas/widgets/base"
import { removeKeyFromObject } from "../../../utils/common"
import MapImage from "./assets/map.png"
import { MinusOutlined, PlusOutlined } from "@ant-design/icons"
const ResizableTable = ({minRows=5, minCols=5}) => {
const [rows, setRows] = useState(minRows)
const [cols, setCols] = useState(minCols)
const containerRef = useRef(null)
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
const { width, height } = entry.contentRect
// Set number of columns and rows based on widget width and height
const newCols = Math.max(minCols, Math.floor(width / 100)) // each column is 100px wide
const newRows = Math.max(minRows, Math.floor(height / 50)) // each row is 50px high
setCols(newCols)
setRows(newRows)
}
})
if (containerRef.current) {
resizeObserver.observe(containerRef.current); // Start observing the widget
}
return () => {
if (containerRef.current) {
resizeObserver.unobserve(containerRef.current); // Stop observing when component unmounts
}
}
}, [containerRef])
return (
<div ref={containerRef} className="tw-w-full tw-h-full tw-rounded-md tw-border tw-border-solid tw-overflow-hidden">
<table className="tw-w-full tw-h-full">
<tbody className="">
{Array.from({ length: rows }).map((_, rowIndex) => (
<tr key={rowIndex} className="">
{Array.from({ length: cols }).map((_, colIndex) => (
<td key={colIndex} className="tw-border tw-border-solid tw-border-gray-400 tw-p-2">
{/* Row {rowIndex + 1}, Col {colIndex + 1} */}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)
}
class PandasTable extends Widget{
static widgetType = "pandas_table"
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 400, height: 250 },
}
}
componentDidMount(){
super.componentDidMount()
this.setWidgetName("Pandas Table")
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
return ({
id: this.__id,
widgetName: toolBarAttrs.widgetName,
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-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 tw-pointer-events-none"
style={this.state.widgetStyling}>
<ResizableTable />
</div>
</div>
)
}
}
export default PandasTable
/**
* {'align': 'w',
'cellbackgr': '#F4F4F3',
'cellwidth': 80,
'floatprecision': 2,
'thousandseparator': '',
'font': 'Arial',
'fontsize': 12,
'fontstyle': '',
'grid_color': '#ABB1AD',
'linewidth': 1,
'rowheight': 22,
'rowselectedcolor': '#E4DED4',
'textcolor': 'black'}
*/

View File

@@ -0,0 +1,70 @@
import React from "react"
import Widget from "../../../canvas/widgets/base"
import Tools from "../../../canvas/constants/tools"
import { removeKeyFromObject } from "../../../utils/common"
import VideoImage from "./assets/video.jpg"
import { PlayCircleFilled } from "@ant-design/icons"
class VideoPlayer extends Widget{
static widgetType = "video_player"
constructor(props) {
super(props)
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {
...this.state,
size: { width: 'fit', height: 'fit' },
}
}
componentDidMount(){
super.componentDidMount()
this.setWidgetName("Video Player")
this.setAttrValue("styling.backgroundColor", "#E4E2E2")
}
getToolbarAttrs(){
const toolBarAttrs = super.getToolbarAttrs()
return ({
id: this.__id,
widgetName: toolBarAttrs.widgetName,
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-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 tw-pointer-events-none"
style={this.state.widgetStyling}>
<div className="tw-relative tw-w-full tw-h-full">
<div className="tw-absolute tw-text-white tw-left-1/2 tw-top-1/2
tw--translate-x-1/2 tw--translate-y-1/2">
<PlayCircleFilled className="tw-text-4xl"/>
</div>
<img src={VideoImage} className="tw-w-full tw-h-full" />
</div>
</div>
</div>
)
}
}
export default VideoPlayer

View File

@@ -0,0 +1,42 @@
import ClockImage from "./assets/widgets/plugins/clock.png"
import VideoImage from "./assets/widgets/plugins/video.png"
import MapImage from "./assets/widgets/plugins/map.png"
import DataTableImage from "./assets/widgets/plugins/tables.png"
import AnalogTimePicker from "./plugins/analogTimepicker"
import VideoPlayer from "./plugins/videoPlayer"
import MapView from "./plugins/mapview"
import PandasTable from "./plugins/pandasTable"
const TkinterPluginWidgets = [
{
name: "Analog TimePicker",
img: ClockImage,
link: "https://github.com",
widgetClass: AnalogTimePicker
},
{
name: "Video Player",
img: VideoImage,
link: "https://github.com/PaulleDemon/tkVideoPlayer",
widgetClass: VideoPlayer
},
{
name: "Map viewer",
img: MapImage,
link: "https://github.com/TomSchimansky/TkinterMapView",
widgetClass: MapView
},
{
name: "Pandas Table",
img: DataTableImage,
link: "https://github.com/dmnfarrell/pandastable",
widgetClass: PandasTable
},
]
export default TkinterPluginWidgets

View File

@@ -1,31 +1,31 @@
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 TopLevel from "./widgets/toplevel"
import Frame from "./widgets/frame"
import Label from "./widgets/label"
import Button from "./widgets/button"
import OptionMenu from "./widgets/optionMenu"
import Slider from "./widgets/slider"
import TopLevel from "./widgets/toplevel"
import { CheckBox, RadioButton } from "./widgets/ checkButton"
import { Input, Text } from "./widgets/input"
import SpinBox from "./widgets/spinBox"
import MainWindowImage from "./assets/widgets/mainwindow.png"
import TopLevelImage from "./assets/widgets/Toplevel.png"
import FrameImage from "./assets/widgets/frame2.png"
import LabelImage from "./assets/widgets/label.png"
import ButtonImage from "./assets/widgets/button.png"
import InputImage from "./assets/widgets/input.png"
import TextAreaImage from "./assets/widgets/textarea.png"
import SliderImage from "./assets/widgets/slider.png"
import DropDownImage from "./assets/widgets/dropdown.png"
import CheckButtonImage from "./assets/widgets/check.png"
import RadioButtonImage from "./assets/widgets/radio.png"
import SpinBoxImage from "./assets/widgets/spinbox.png"
import MainWindowImage from "./assets/widgets/main/mainwindow.png"
import TopLevelImage from "./assets/widgets/main/Toplevel.png"
import FrameImage from "./assets/widgets/main/frame2.png"
import LabelImage from "./assets/widgets/main/label.png"
import ButtonImage from "./assets/widgets/main/button.png"
import InputImage from "./assets/widgets/main/input.png"
import TextAreaImage from "./assets/widgets/main/textarea.png"
import SliderImage from "./assets/widgets/main/slider.png"
import DropDownImage from "./assets/widgets/main/dropdown.png"
import CheckButtonImage from "./assets/widgets/main/check.png"
import RadioButtonImage from "./assets/widgets/main/radio.png"
import SpinBoxImage from "./assets/widgets/main/spinbox.png"
const TkinterSidebar = [
const TkinterWidgets = [
{
name: "Main window",
img: MainWindowImage,
@@ -102,7 +102,7 @@ const TkinterSidebar = [
]
export default TkinterSidebar
export default TkinterWidgets
/**

View File

@@ -10,12 +10,7 @@ class Button extends Widget{
constructor(props) {
super(props)
// TODO: disable drop
this.droppableTags = {
// TODO: exclude all
exclude: ["image", "video", "media", "main_window", "toplevel"]
}
this.droppableTags = null
const newAttrs = removeKeyFromObject("layout", this.state.attrs)
this.state = {

View File

@@ -0,0 +1,90 @@
import { useEffect, useMemo, useState } from "react"
import { CloseCircleFilled, SearchOutlined } from "@ant-design/icons"
import {SidebarWidgetCard} from "../components/cards"
import ButtonWidget from "../assets/widgets/button.png"
import { filterObjectListStartingWith } from "../utils/filter"
import Widget from "../canvas/widgets/base"
/**
*
* @param {function} onWidgetsUpdate - this is a callback that will be called once the sidebar is populated with widgets
* @returns
*/
function PluginsContainer({sidebarContent, onWidgetsUpdate}){
const [searchValue, setSearchValue] = useState("")
const [widgetData, setWidgetData] = useState(sidebarContent)
useEffect(() => {
setWidgetData(sidebarContent)
// if (onWidgetsUpdate){
// onWidgetsUpdate(widgets)
// }
}, [sidebarContent])
useEffect(() => {
if (searchValue.length > 0){
const searchData = filterObjectListStartingWith(sidebarContent, "name", searchValue)
setWidgetData(searchData)
}else{
setWidgetData(sidebarContent)
}
}, [searchValue])
function onSearch(event){
setSearchValue(event.target.value)
}
return (
<div className="tw-w-full tw-p-2 tw-gap-4 tw-flex tw-flex-col tw-overflow-x-hidden">
<div className="tw-flex tw-gap-2 input tw-place-items-center">
<SearchOutlined />
<input type="text" placeholder="Search" className="tw-outline-none tw-w-full tw-border-none"
id="" onInput={onSearch} value={searchValue}/>
<div className="">
{
searchValue.length > 0 &&
<div className="tw-cursor-pointer tw-text-gray-600" onClick={() => setSearchValue("")}>
<CloseCircleFilled />
</div>
}
</div>
</div>
<div className="tw-flex tw-flex-col tw-gap-2 tw-h-full tw-p-1">
{
widgetData.map((widget, index) => {
return (
<SidebarWidgetCard key={widget.name}
name={widget.name}
img={widget.img}
url={widget.link}
widgetClass={widget.widgetClass}
/>
)
})
}
</div>
</div>
)
}
export default PluginsContainer