added plugins, updated image paths
@@ -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
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
10
src/App.js
@@ -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",
|
||||
|
||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
src/frameworks/tkinter/assets/widgets/plugins/clock.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
src/frameworks/tkinter/assets/widgets/plugins/map.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
src/frameworks/tkinter/assets/widgets/plugins/tables.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/frameworks/tkinter/assets/widgets/plugins/video.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
105
src/frameworks/tkinter/plugins/analogTimepicker.js
Normal 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
|
||||
BIN
src/frameworks/tkinter/plugins/assets/map.png
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
src/frameworks/tkinter/plugins/assets/video.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
79
src/frameworks/tkinter/plugins/mapview.js
Normal 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
|
||||
139
src/frameworks/tkinter/plugins/pandasTable.js
Normal 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'}
|
||||
*/
|
||||
70
src/frameworks/tkinter/plugins/videoPlayer.js
Normal 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
|
||||
42
src/frameworks/tkinter/sidebarPlugins.js
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
90
src/sidebar/pluginsContainer.js
Normal 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
|
||||