From 683d0e8d33939fcdbd5806ca9da3491345467da6 Mon Sep 17 00:00:00 2001 From: Martin Vladic Date: Fri, 8 Nov 2024 11:40:13 +0100 Subject: [PATCH] refactoring --- package-lock.json | 285 +++++++++++++----- package.json | 5 +- .../_stylesheets/project-editor.less | 11 +- packages/eez-studio-ui/generic-dialog.tsx | 6 +- .../eez-studio-ui/properties-electron.tsx | 10 +- packages/eez-studio-ui/properties.tsx | 12 +- packages/eez-studio-ui/tabs.tsx | 250 +++++++-------- packages/eez-studio-ui/tree-table.tsx | 6 +- packages/eez-studio-ui/tree.tsx | 12 +- .../connection/interfaces/serial.ts | 8 +- packages/instrument/window/app.tsx | 69 +++-- .../window/history/list-component.tsx | 4 +- packages/instrument/window/lists/envelope.tsx | 11 +- packages/instrument/window/navigation.tsx | 10 +- packages/instrument/window/note-dialog.tsx | 11 +- .../instrument/window/terminal/terminal.tsx | 22 +- .../project-editor/features/font/Glyphs.tsx | 131 ++++---- .../instrument-commands/importCommandDoc.tsx | 23 +- .../features/scpi/importScpiDoc.tsx | 23 +- .../features/style/StylesTreeNavigation.tsx | 6 +- .../flow/components/actions/index.tsx | 218 ++++++++------ .../components/widgets/dashboard/index.tsx | 194 ++++++------ .../lvgl/LVGLStylesTreeNavigation.tsx | 6 +- .../project/ui/SettingsNavigation.tsx | 26 +- .../ui-components/FileInput.tsx | 36 +-- .../PropertyGrid/ThemedColorInput.tsx | 13 +- .../project-editor/ui-components/Tree.tsx | 181 +++++------ packages/shortcuts/group-dialog.tsx | 6 +- packages/shortcuts/groups.tsx | 11 +- packages/shortcuts/shortcut-dialog.tsx | 11 +- packages/shortcuts/shortcuts.tsx | 11 +- 31 files changed, 887 insertions(+), 741 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8770403..2b52352f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "eezstudio", - "version": "0.19.0", + "version": "0.20.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "eezstudio", - "version": "0.19.0", + "version": "0.20.0", "hasInstallScript": true, "license": "GPL-3.0-only", "dependencies": { @@ -16,6 +16,7 @@ "@emotion/react": "^11.11.0", "@popperjs/core": "^2.9.2", "@types/d3-format": "^3.0.4", + "@uiw/react-color-chrome": "^2.3.2", "adm-zip": "^0.5.9", "app-module-path": "^2.2.0", "archiver": "^5.3.0", @@ -59,10 +60,9 @@ "python-shell": "^3.0.1", "quill": "^1.3.7", "react": "^18.2.0", - "react-color": "^2.19.3", "react-dom": "^18.2.0", "react-select": "^5.8.1", - "react-toastify": "^8.2.0", + "react-toastify": "^10.0.6", "react-virtualized-auto-sizer": "^1.0.7", "react-window": "^1.8.8", "rimraf": "^3.0.2", @@ -105,7 +105,6 @@ "@types/pg": "^8.6.5", "@types/plotly.js-dist-min": "^2.3.4", "@types/react": "^18.2.0", - "@types/react-color": "^3.0.6", "@types/react-dom": "^18.2.0", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", @@ -853,15 +852,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@icons/material": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", - "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", - "license": "MIT", - "peerDependencies": { - "react": "*" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1823,17 +1813,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-color": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.12.tgz", - "integrity": "sha512-pr3uKE3lSvf7GFo1Rn2K3QktiZQFFrSgSGJ/3iMvSOYWt2pPAJ97rVdVfhWxYJZ8prAEXzoP2XX//3qGSQgu7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*", - "@types/reactcss": "*" - } - }, "node_modules/@types/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", @@ -1873,16 +1852,6 @@ "@types/react": "*" } }, - "node_modules/@types/reactcss": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.12.tgz", - "integrity": "sha512-BrXUQ86/wbbFiZv8h/Q1/Q1XOsaHneYmCb/tHe9+M8XBAAUc2EHfdY0DY22ZZjVSaXr5ix7j+zsqO2eGZub8lQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/request": { "version": "2.48.12", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", @@ -2076,6 +2045,195 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/color-convert": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.3.2.tgz", + "integrity": "sha512-Txs0oAcOGhvM15yi7NqDJSws6htpuGx75EblFlZmh4h4AyUYXaeN2HNcOAUt835M3SN1j7rqMC+XERIE4r776Q==", + "license": "MIT", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0" + } + }, + "node_modules/@uiw/react-color-alpha": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.3.2.tgz", + "integrity": "sha512-+yh+KEpNKjxNFFODQrB3Lki2hu6kznoSCngHgptlWBUtAC3e/e7tIiTTedSpCGr7fwIpC0CWrKwxENA3tyY/2Q==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-drag-event-interactive": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-chrome": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.3.2.tgz", + "integrity": "sha512-WvA8dg2y+vgoyy8GFBM3B1+SeJ29ov5OVEei1kACMKxThADPdI4w3RRmhYIMnSeFGVW3bGuBMq6JimjIKZirCQ==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-editable-input-hsla": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2", + "@uiw/react-color-github": "2.3.2", + "@uiw/react-color-hue": "2.3.2", + "@uiw/react-color-saturation": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.3.2.tgz", + "integrity": "sha512-DDl9pCN7hH5Q+OB6LiFGFmkqIQw81EDIEvDi6rr6G9U+k+oqZ9JCBKSZ9qNKSa4MqnuISOkFv0vL3Jh8fSyqcg==", + "license": "MIT", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input-hsla": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.3.2.tgz", + "integrity": "sha512-lLO8K+Zv5L9HKBgM3zYSqeLKisBkpXCxlQmF8iCKYcIgeRilM26qqylskA4n6pVixfSooL0hyL/HwfNmbCIrrg==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-editable-input-rgba": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.3.2.tgz", + "integrity": "sha512-HV0+5zzpaNG5v6EyVgmPfInd9UzYknQI7gdsVmmXKzB13L3RFhiv77r6o+q3IZMEnoDZ3U92uX4FeRaM1NrwYw==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-github": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.3.2.tgz", + "integrity": "sha512-3QxpEOKYXbbV/L1cYsf7nhoOnl19/zWTpRRgda8LOe3SuRhFrFM3ZLa+UJUEXgO1cg9h64gxSKINh2st/FawpQ==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-swatch": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-hue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.3.2.tgz", + "integrity": "sha512-aAveo++GAghw09Ngc8Zzwxhj9mGaJfw8q40fDGFrVNxdrwrAjySIKHzlOSg5kw6WnEp4tUjhkMXDfCZWUhqmPA==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-saturation": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.3.2.tgz", + "integrity": "sha512-aDKMhjylBUb4dH4oTQYz+U4mhpOebbQ2J0Y8y5aX1tfZ3fZuBqnXtWzu7Ffj3ksdKwkQHPddxT5IDTbAChF/og==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2", + "@uiw/react-drag-event-interactive": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-swatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.3.2.tgz", + "integrity": "sha512-AjkEcSdlpxiFm9yull4WDujuHr0tD9/+XkLtcus/MH768zSQbb+rj6cY1nZ8L8FI6LRDGRaVJqFaXm4ZOAaIZw==", + "license": "MIT", + "dependencies": { + "@uiw/color-convert": "2.3.2" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-drag-event-interactive": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.3.2.tgz", + "integrity": "sha512-lG5pJCtqbYBv7Dj0r12PE9q9yg7P2CzlQodw5ZHPY9GCSZVXHJc0g4lGvCbe/4Y8HYqM8aU4CYS8LplpX+mIQw==", + "license": "MIT", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -3893,9 +4051,9 @@ } }, "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { "node": ">=6" @@ -9792,12 +9950,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -10424,12 +10576,6 @@ "node": ">=10" } }, - "node_modules/material-colors": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", - "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==", - "license": "ISC" - }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -12707,24 +12853,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-color": { - "version": "2.19.3", - "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", - "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", - "license": "MIT", - "dependencies": { - "@icons/material": "^0.2.4", - "lodash": "^4.17.15", - "lodash-es": "^4.17.15", - "material-colors": "^1.2.1", - "prop-types": "^15.5.10", - "reactcss": "^1.2.0", - "tinycolor2": "^1.4.1" - }, - "peerDependencies": { - "react": "*" - } - }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -12766,16 +12894,16 @@ } }, "node_modules/react-toastify": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.2.0.tgz", - "integrity": "sha512-Pg2Ju7NngAamarFvLwqrFomJ57u/Ay6i6zfLurt/qPynWkAkOthu6vxfqYpJCyNhHRhR4hu7+bySSeWWJu6PAg==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", + "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==", "license": "MIT", "dependencies": { - "clsx": "^1.1.1" + "clsx": "^2.1.0" }, "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/react-transition-group": { @@ -12827,15 +12955,6 @@ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", "license": "MIT" }, - "node_modules/reactcss": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", - "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", - "license": "MIT", - "dependencies": { - "lodash": "^4.0.1" - } - }, "node_modules/read-binary-file-arch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", diff --git a/package.json b/package.json index ca29fb07..8f88fa3d 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "@types/pg": "^8.6.5", "@types/plotly.js-dist-min": "^2.3.4", "@types/react": "^18.2.0", - "@types/react-color": "^3.0.6", "@types/react-dom": "^18.2.0", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", @@ -123,6 +122,7 @@ "@emotion/react": "^11.11.0", "@popperjs/core": "^2.9.2", "@types/d3-format": "^3.0.4", + "@uiw/react-color-chrome": "^2.3.2", "adm-zip": "^0.5.9", "app-module-path": "^2.2.0", "archiver": "^5.3.0", @@ -166,10 +166,9 @@ "python-shell": "^3.0.1", "quill": "^1.3.7", "react": "^18.2.0", - "react-color": "^2.19.3", "react-dom": "^18.2.0", "react-select": "^5.8.1", - "react-toastify": "^8.2.0", + "react-toastify": "^10.0.6", "react-virtualized-auto-sizer": "^1.0.7", "react-window": "^1.8.8", "rimraf": "^3.0.2", diff --git a/packages/eez-studio-ui/_stylesheets/project-editor.less b/packages/eez-studio-ui/_stylesheets/project-editor.less index d7171b97..22a45945 100644 --- a/packages/eez-studio-ui/_stylesheets/project-editor.less +++ b/packages/eez-studio-ui/_stylesheets/project-editor.less @@ -540,13 +540,12 @@ } .EezStudio_ThemedColorInput_DropdownContent { - font-size: 0.8rem; - - padding: 0; - margin: 0; + background: transparent; + border: none; - border-radius: 4px; - overflow: hidden; + .w-color-chrome { + color: #333; + } } .EezStudio_ObjectReferenceInput_DropdownButton { diff --git a/packages/eez-studio-ui/generic-dialog.tsx b/packages/eez-studio-ui/generic-dialog.tsx index 6756f77e..6558f207 100644 --- a/packages/eez-studio-ui/generic-dialog.tsx +++ b/packages/eez-studio-ui/generic-dialog.tsx @@ -239,7 +239,7 @@ export const GenericDialog = observer( this.errorMessages = undefined; if (this.props.setOnChangeCallback) { - this.props.setOnChangeCallback(this.onChange.bind(this)); + this.props.setOnChangeCallback(this.onChange); } } @@ -320,7 +320,7 @@ export const GenericDialog = observer( return true; } - onChange(fieldProperties: any, value: any) { + onChange = (fieldProperties: any, value: any) => { this.fieldValues[fieldProperties.name] = value; if (this.errorMessages) { @@ -337,7 +337,7 @@ export const GenericDialog = observer( } this.progressType = "none"; - } + }; validate() { let errorMessages: any; diff --git a/packages/eez-studio-ui/properties-electron.tsx b/packages/eez-studio-ui/properties-electron.tsx index c20a16d0..eb3ea9d8 100644 --- a/packages/eez-studio-ui/properties-electron.tsx +++ b/packages/eez-studio-ui/properties-electron.tsx @@ -18,13 +18,7 @@ export const FileInputProperty = observer( }, {} > { - constructor(props: any) { - super(props); - - this.onSelectFile = this.onSelectFile.bind(this); - } - - async onSelectFile(event: any) { + onSelectFile = async (event: any) => { event.preventDefault(); const result = await dialog.showOpenDialog(getCurrentWindow(), { @@ -36,7 +30,7 @@ export const FileInputProperty = observer( if (filePaths && filePaths[0]) { this.props.onChange(filePaths[0]); } - } + }; render() { let id = this.props.id || guid(); diff --git a/packages/eez-studio-ui/properties.tsx b/packages/eez-studio-ui/properties.tsx index d49398f9..d36e5e76 100644 --- a/packages/eez-studio-ui/properties.tsx +++ b/packages/eez-studio-ui/properties.tsx @@ -665,11 +665,9 @@ export const KeybindingProperty = observer( makeObservable(this, { keybinding: computed }); - - this.onKeyDown = this.onKeyDown.bind(this); } - onKeyDown(event: any) { + onKeyDown = (event: any) => { if ( event.nativeEvent.key === "Escape" || event.nativeEvent.key === "Enter" || @@ -715,7 +713,7 @@ export const KeybindingProperty = observer( } this.props.onChange(keybinding.join("+")); - } + }; get keybinding() { return ( @@ -946,7 +944,7 @@ export class AbsoluteFileInputProperty extends React.Component< }, {} > { - async onSelect() { + onSelect = async () => { const result = await dialog.showOpenDialog({ properties: ["openFile"], filters: [{ name: "All Files", extensions: ["*"] }] @@ -955,7 +953,7 @@ export class AbsoluteFileInputProperty extends React.Component< if (result.filePaths && result.filePaths[0]) { this.props.onChange(result.filePaths[0]); } - } + }; render() { return ( @@ -968,7 +966,7 @@ export class AbsoluteFileInputProperty extends React.Component< diff --git a/packages/eez-studio-ui/tabs.tsx b/packages/eez-studio-ui/tabs.tsx index 365c163f..63261c4f 100644 --- a/packages/eez-studio-ui/tabs.tsx +++ b/packages/eez-studio-ui/tabs.tsx @@ -24,40 +24,31 @@ export interface ITab { copyProjectPath?(): void; } -/////////////////////////////// /////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// -export const ItemTypes = { - TAB: "tab" -}; - -interface TabViewProps { - tab: ITab; - index: number; - moveTab?: (dragIndex: number, hoverIndex: number) => void; -} - -export const TabView: React.FC = observer(function TabView({ - tab, - index, - moveTab -}) { - const onMouseUp = React.useCallback( - (e: React.MouseEvent) => { +export const TabView = observer( + class TabView extends React.Component<{ + tab: ITab; + index: number; + moveTab?: (dragIndex: number, hoverIndex: number) => void; + }> { + onMouseUp = (e: React.MouseEvent) => { + const { tab } = this.props; if (e.button === 1) { if (tab.close) { tab.close(); } } - }, - [tab] - ); + }; + + onMouseDown = () => { + const { tab } = this.props; + tab.makeActive(); + }; - const onMouseDown = React.useCallback(() => { - tab.makeActive(); - }, [tab]); + onContextMenu = (event: React.MouseEvent) => { + const { tab } = this.props; - const onContextMenu = React.useCallback( - (event: React.MouseEvent) => { event.preventDefault(); const menu = new Menu(); @@ -92,116 +83,125 @@ export const TabView: React.FC = observer(function TabView({ if (menu.items.length > 0) { menu.popup({}); } - }, - [tab] - ); + }; + + onClose = (e: any) => { + const { tab } = this.props; - const onClose = React.useCallback( - (e: any) => { e.stopPropagation(); if (tab.close) { tab.close(); } - }, - [tab] - ); - - let className = classNames("EezStudio_Tab", { - active: tab.active, - permanent: tab.permanent - }); - - let closeIcon: JSX.Element | undefined; - if (tab.close) { - closeIcon = ( - - close - - ); - } + }; - let icon; - if (typeof tab.icon == "string") { - icon = ; - } else { - icon = tab.icon; - } + render() { + const { tab } = this.props; - let title; - if (typeof tab.title === "string") { - title = ( - <> - {icon} - - {tab.title} - - - ); - } else { - title = ( - <> - {icon} - {tab.title} - - ); - } + let className = classNames("EezStudio_Tab", { + active: tab.active, + permanent: tab.permanent + }); - const opacity = 1; - - return ( -
{ - ev.dataTransfer.setData( - "application/eez-studio-tab", - index.toString() + let closeIcon: JSX.Element | undefined; + if (tab.close) { + closeIcon = ( + + close + ); - ev.dataTransfer.effectAllowed = "move"; - }} - onDragOver={ev => { - ev.preventDefault(); - ev.stopPropagation(); - - if ( - ev.dataTransfer.types.indexOf( - "application/eez-studio-tab" - ) == -1 || - index == 0 - ) { - ev.dataTransfer.dropEffect = "none"; - return; - } + } - ev.dataTransfer.dropEffect = "move"; - }} - onDrop={ev => { - ev.preventDefault(); - const dragIndex = parseInt( - ev.dataTransfer.getData("application/eez-studio-tab") + let icon; + if (typeof tab.icon == "string") { + icon = ; + } else { + icon = tab.icon; + } + + let title; + if (typeof tab.title === "string") { + title = ( + <> + {icon} + + {tab.title} + + ); - if (moveTab) { - moveTab(dragIndex, index); - } - }} - > -
- {title} - {tab.loading && } - {closeIcon} -
-
- ); -}); + } else { + title = ( + <> + {icon} + {tab.title} + + ); + } + + const opacity = 1; + + return ( +
{ + ev.dataTransfer.setData( + "application/eez-studio-tab", + this.props.index.toString() + ); + ev.dataTransfer.effectAllowed = "move"; + }} + onDragOver={ev => { + ev.preventDefault(); + ev.stopPropagation(); + + if ( + ev.dataTransfer.types.indexOf( + "application/eez-studio-tab" + ) == -1 || + this.props.index == 0 + ) { + ev.dataTransfer.dropEffect = "none"; + return; + } + + ev.dataTransfer.dropEffect = "move"; + }} + onDrop={ev => { + ev.preventDefault(); + const dragIndex = parseInt( + ev.dataTransfer.getData( + "application/eez-studio-tab" + ) + ); + if (this.props.moveTab) { + this.props.moveTab(dragIndex, this.props.index); + } + }} + > +
+ {title} + {tab.loading && ( + + )} + {closeIcon} +
+
+ ); + } + } +); //////////////////////////////////////////////////////////////////////////////// diff --git a/packages/eez-studio-ui/tree-table.tsx b/packages/eez-studio-ui/tree-table.tsx index 168e5e60..35d1826c 100644 --- a/packages/eez-studio-ui/tree-table.tsx +++ b/packages/eez-studio-ui/tree-table.tsx @@ -57,12 +57,12 @@ const TreeTableRow = observer( this.props.node.expanded.set(!this.props.node.expanded.get()); }); - onClick(e: React.MouseEvent) { + onClick = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.props.selectNode(this.props.node); - } + }; render() { let childrenRows: JSX.Element[] = []; @@ -145,7 +145,7 @@ const TreeTableRow = observer( { event.preventDefault(); event.stopPropagation(); this.props.selectNode(this.props.node); this.props.toggleExpanded(this.props.level, this.props.node); - } + }; - onClick(e: React.MouseEvent) { + onClick = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.props.selectNode(this.props.node); - } + }; render() { let childrenRows: JSX.Element[] = []; @@ -121,7 +121,7 @@ export const TreeRow = observer( triangle = ( {triangle} {label} diff --git a/packages/instrument/connection/interfaces/serial.ts b/packages/instrument/connection/interfaces/serial.ts index 4d36c85d..e0e1b4cd 100644 --- a/packages/instrument/connection/interfaces/serial.ts +++ b/packages/instrument/connection/interfaces/serial.ts @@ -14,9 +14,7 @@ export class SerialInterface implements CommunicationInterface { connectedCalled = false; data: string | undefined; - constructor(private host: CommunicationInterfaceHost) { - this.sendNextChunkCallback = this.sendNextChunkCallback.bind(this); - } + constructor(private host: CommunicationInterfaceHost) {} connect() { try { @@ -97,7 +95,7 @@ export class SerialInterface implements CommunicationInterface { this.host.disconnected(); } - sendNextChunkCallback() { + sendNextChunkCallback = () => { if (this.port && this.data) { let nextChunk; if (this.data.length <= CONF_CHUNK_SIZE) { @@ -114,7 +112,7 @@ export class SerialInterface implements CommunicationInterface { this.port.drain(this.sendNextChunkCallback); } } - } + }; write(data: string) { if (this.port) { diff --git a/packages/instrument/window/app.tsx b/packages/instrument/window/app.tsx index e7a6a78b..3cbe80f5 100644 --- a/packages/instrument/window/app.tsx +++ b/packages/instrument/window/app.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { computed, makeObservable } from "mobx"; +import { action, computed, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import classNames from "classnames"; @@ -26,34 +26,45 @@ import { settingsController } from "home/settings"; //////////////////////////////////////////////////////////////////////////////// -export function EditInstrumentLabelDialog({ - instrument, - size -}: { - instrument: InstrumentObject; - size?: "small" | "medium" | "large"; -}) { - const [label, setLabel] = React.useState(instrument.label || ""); - - return ( - { - instrument.setLabel(label.trim()); - return true; - }} - size={size} - > - - - - - ); -} +export const EditInstrumentLabelDialog = observer( + class EditInstrumentLabelDialog extends React.Component<{ + instrument: InstrumentObject; + size?: "small" | "medium" | "large"; + }> { + label: string; + + constructor(props: any) { + super(props); + + this.label = this.props.instrument.label || ""; + + makeObservable(this, { + label: observable + }); + } + + render() { + return ( + { + this.props.instrument.setLabel(this.label.trim()); + return true; + }} + size={this.props.size} + > + + (this.label = value))} + /> + + + ); + } + } +); //////////////////////////////////////////////////////////////////////////////// diff --git a/packages/instrument/window/history/list-component.tsx b/packages/instrument/window/history/list-component.tsx index 5c7a4d7a..10e70f69 100644 --- a/packages/instrument/window/history/list-component.tsx +++ b/packages/instrument/window/history/list-component.tsx @@ -394,7 +394,7 @@ export class HistoryListComponentClass extends React.Component<{ let c = 0; - const scrollIntoView = (() => { + const scrollIntoView = () => { const element = $(this.div).find( `.EezStudio_HistoryItem_${historyItem.id}` )[0]; @@ -406,7 +406,7 @@ export class HistoryListComponentClass extends React.Component<{ } else { this.autoReloadEnabled = true; } - }).bind(this); + }; scrollIntoView(); } diff --git a/packages/instrument/window/lists/envelope.tsx b/packages/instrument/window/lists/envelope.tsx index 3e37169c..41f906ed 100644 --- a/packages/instrument/window/lists/envelope.tsx +++ b/packages/instrument/window/lists/envelope.tsx @@ -299,9 +299,6 @@ const EditEnvelopeValue = observer( onTimeChange: action, onValueChange: action }); - - this.onTimeChange = this.onTimeChange.bind(this); - this.onValueChange = this.onValueChange.bind(this); } time = @@ -342,7 +339,7 @@ const EditEnvelopeValue = observer( return undefined; } - onTimeChange(event: React.ChangeEvent) { + onTimeChange = (event: React.ChangeEvent) => { this.time = event.target.value; let time = this.props.timeUnit.parseValue(this.time); if (typeof time === "number") { @@ -362,9 +359,9 @@ const EditEnvelopeValue = observer( this.timeError = "Invalid value"; this.props.onTimeChange(this.props.time!); } - } + }; - onValueChange(event: React.ChangeEvent) { + onValueChange = (event: React.ChangeEvent) => { this.value = event.target.value; let value = this.props.valueUnit.parseValue(this.value); if (typeof value === "number") { @@ -387,7 +384,7 @@ const EditEnvelopeValue = observer( this.valueError = "Invalid value"; this.props.onValueChange(this.props.value); } - } + }; render() { return ( diff --git a/packages/instrument/window/navigation.tsx b/packages/instrument/window/navigation.tsx index b3c737e6..1f1b7063 100644 --- a/packages/instrument/window/navigation.tsx +++ b/packages/instrument/window/navigation.tsx @@ -24,16 +24,10 @@ export const NavigationItem = observer( }, {} > { - constructor(props: any) { - super(props); - - this.handleClick = this.handleClick.bind(this); - } - - handleClick(event: React.MouseEvent) { + handleClick = (event: React.MouseEvent) => { event.preventDefault(); this.props.selectItem(this.props.item); - } + }; render() { let className = classNames( diff --git a/packages/instrument/window/note-dialog.tsx b/packages/instrument/window/note-dialog.tsx index ab6c1112..9c760a02 100644 --- a/packages/instrument/window/note-dialog.tsx +++ b/packages/instrument/window/note-dialog.tsx @@ -10,22 +10,19 @@ class NoteDialog extends React.Component<{ constructor(props: any) { super(props); - this.handleChange = this.handleChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.note = this.props.note || ""; } note: string; - handleChange(value: string) { + handleChange = (value: string) => { this.note = value; - } + }; - handleSubmit() { + handleSubmit = () => { this.props.callback(this.note); return true; - } + }; render() { return ( diff --git a/packages/instrument/window/terminal/terminal.tsx b/packages/instrument/window/terminal/terminal.tsx index 7b6252bc..27e89441 100644 --- a/packages/instrument/window/terminal/terminal.tsx +++ b/packages/instrument/window/terminal/terminal.tsx @@ -49,12 +49,6 @@ const Input = observer( console.error(error); } } - - this.handleHelpClick = this.handleHelpClick.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleKeyDown = this.handleKeyDown.bind(this); - this.handleSendCommandClick = - this.handleSendCommandClick.bind(this); } commandsHistory: string[] = []; @@ -73,13 +67,13 @@ const Input = observer( } } - handleHelpClick() { + handleHelpClick = () => { this.props.appStore.toggleHelpVisible(); - } + }; - handleChange(event: any) { + handleChange = (event: any) => { this.props.terminalState.command = event.target.value; - } + }; sendCommand() { if (this.props.terminalState.command) { @@ -146,7 +140,7 @@ const Input = observer( } } - handleKeyDown(event: any) { + handleKeyDown = (event: any) => { if (event.key === "Enter") { event.preventDefault(); this.sendCommand(); @@ -160,11 +154,11 @@ const Input = observer( this.props.terminalState.command = this.findNextCommand() || ""; this.moveCursorToTheEnd = true; } - } + }; - handleSendCommandClick() { + handleSendCommandClick = () => { this.sendCommand(); - } + }; render() { return ( diff --git a/packages/project-editor/features/font/Glyphs.tsx b/packages/project-editor/features/font/Glyphs.tsx index 07253014..8834e296 100644 --- a/packages/project-editor/features/font/Glyphs.tsx +++ b/packages/project-editor/features/font/Glyphs.tsx @@ -220,77 +220,88 @@ export const Glyphs = observer( //////////////////////////////////////////////////////////////////////////////// export const GlyphComponent = observer( - ({ - glyph, - isSelected, - onSelect, - onDoubleClick - }: { + class GlyphComponent extends React.Component<{ glyph: Glyph; isSelected: boolean; onSelect: () => void; onDoubleClick: () => void; - }) => { - const refDiv = React.useRef(null); - - const canvas = document.createElement("canvas"); - canvas.width = (glyph.glyphBitmap && glyph.glyphBitmap.width) || 1; - canvas.height = glyph.font.height || 1; - let ctx = canvas.getContext("2d")!; - - if (settingsController.isDarkTheme) { - setColor("white"); - setBackColor("black"); - } else { - setColor("black"); - setBackColor("white"); - } + }> { + refDiv = React.createRef(); + + setCanvas() { + console.log("setCanvas"); + + const { glyph } = this.props; + + const canvas = document.createElement("canvas"); + canvas.width = (glyph.glyphBitmap && glyph.glyphBitmap.width) || 1; + canvas.height = glyph.font.height || 1; + let ctx = canvas.getContext("2d")!; + + if (settingsController.isDarkTheme) { + setColor("white"); + setBackColor("black"); + } else { + setColor("black"); + setBackColor("white"); + } - drawGlyph(ctx, -glyph.x, 0, glyph.encoding, glyph.font); + drawGlyph(ctx, -glyph.x, 0, glyph.encoding, glyph.font); - React.useEffect(() => { - if (refDiv.current) { - if (refDiv.current.children[0]) { - refDiv.current.replaceChild( + if (this.refDiv.current) { + if (this.refDiv.current.children[0]) { + this.refDiv.current.replaceChild( canvas, - refDiv.current.children[0] + this.refDiv.current.children[0] ); } else { - refDiv.current.appendChild(canvas); + this.refDiv.current.appendChild(canvas); } } - }); - - return ( -
  • -
    -
    -
    - {getLabel(glyph)} + } + + componentDidMount() { + this.setCanvas(); + } + + componentDidUpdate() { + this.setCanvas(); + } + + render() { + const { glyph, isSelected, onSelect, onDoubleClick } = this.props; + + return ( +
  • +
    +
    +
    + {getLabel(glyph)} +
    - -
  • - ); + + ); + } } ); diff --git a/packages/project-editor/features/instrument-commands/importCommandDoc.tsx b/packages/project-editor/features/instrument-commands/importCommandDoc.tsx index 109c5698..01871a45 100644 --- a/packages/project-editor/features/instrument-commands/importCommandDoc.tsx +++ b/packages/project-editor/features/instrument-commands/importCommandDoc.tsx @@ -395,7 +395,7 @@ export const ImportCommandsDocDialog = observer( onOkCalled = false; - onOk(event: any) { + onOk = (event: any) => { event.preventDefault(); if (this.onOkCalled) { @@ -448,11 +448,11 @@ export const ImportCommandsDocDialog = observer( this.context.undoManager.setCombineCommands(false); this.context.backgroundCheckEnabled = true; - } + }; - onCancel() { + onCancel = () => { this.modal.hide(); - } + }; handleTabClick(activeTab: Section, event: any) { event.preventDefault(); @@ -545,7 +545,7 @@ export const ImportCommandsDocDialog = observer( key="close" type="button" className="btn btn-primary" - onClick={this.onCancel.bind(this)} + onClick={this.onCancel} > Close @@ -689,10 +689,7 @@ export const ImportCommandsDocDialog = observer( }); content = ( -
    +
      {tabs}
    {tables}
    @@ -703,7 +700,7 @@ export const ImportCommandsDocDialog = observer( key="ok" type="button" className="btn btn-default" - onClick={this.onCancel.bind(this)} + onClick={this.onCancel} > Cancel , @@ -711,7 +708,7 @@ export const ImportCommandsDocDialog = observer( key="cancel" type="button" className="btn btn-primary" - onClick={this.onOk.bind(this)} + onClick={this.onOk} disabled={!this.hasSelectedChanges} > OK @@ -727,7 +724,7 @@ export const ImportCommandsDocDialog = observer( key="close" type="button" className="btn btn-primary" - onClick={this.onCancel.bind(this)} + onClick={this.onCancel} > Close @@ -758,7 +755,7 @@ export const ImportCommandsDocDialog = observer( diff --git a/packages/project-editor/features/scpi/importScpiDoc.tsx b/packages/project-editor/features/scpi/importScpiDoc.tsx index 45c16982..767fcd8d 100644 --- a/packages/project-editor/features/scpi/importScpiDoc.tsx +++ b/packages/project-editor/features/scpi/importScpiDoc.tsx @@ -1084,7 +1084,7 @@ export const ImportScpiDocDialog = observer( onOkCalled = false; - onOk(event: any) { + onOk = (event: any) => { event.preventDefault(); if (this.onOkCalled) { @@ -1224,11 +1224,11 @@ export const ImportScpiDocDialog = observer( this.context.undoManager.setCombineCommands(false); this.context.backgroundCheckEnabled = true; - } + }; - onCancel() { + onCancel = () => { this.modal.hide(); - } + }; handleTabClick(activeTab: Section, event: any) { event.preventDefault(); @@ -1324,7 +1324,7 @@ export const ImportScpiDocDialog = observer( key="close" type="button" className="btn btn-primary" - onClick={this.onCancel.bind(this)} + onClick={this.onCancel} > Close @@ -1577,10 +1577,7 @@ export const ImportScpiDocDialog = observer( }); content = ( -
    +
      {tabs}
    {tables}
    @@ -1591,7 +1588,7 @@ export const ImportScpiDocDialog = observer( key="ok" type="button" className="btn btn-default" - onClick={this.onCancel.bind(this)} + onClick={this.onCancel} > Cancel , @@ -1599,7 +1596,7 @@ export const ImportScpiDocDialog = observer( key="cancel" type="button" className="btn btn-primary" - onClick={this.onOk.bind(this)} + onClick={this.onOk} disabled={!this.hasSelectedChanges} > OK @@ -1615,7 +1612,7 @@ export const ImportScpiDocDialog = observer( key="close" type="button" className="btn btn-primary" - onClick={this.onCancel.bind(this)} + onClick={this.onCancel} > Close @@ -1646,7 +1643,7 @@ export const ImportScpiDocDialog = observer( diff --git a/packages/project-editor/features/style/StylesTreeNavigation.tsx b/packages/project-editor/features/style/StylesTreeNavigation.tsx index b98e7f36..5f0cd302 100644 --- a/packages/project-editor/features/style/StylesTreeNavigation.tsx +++ b/packages/project-editor/features/style/StylesTreeNavigation.tsx @@ -319,12 +319,12 @@ export const StylesTreeNavigation = observer( this.treeAdapter.deleteSelection(); } } - onFocus() { + onFocus = () => { const navigationStore = this.context.navigationStore; if (isPartOfNavigation(this.props.navigationObject)) { navigationStore.setSelectedPanel(this); } - } + }; onSearchChange(event: any) { this.searchText = ($(event.target).val() as string).trim(); @@ -394,7 +394,7 @@ export const StylesTreeNavigation = observer( void; - }) => { - const inputId = React.useMemo(() => guid(), []); - const editorId = React.useMemo(() => guid(), []); + }> { + inputId = guid(); + editorId = guid(); - React.useEffect(() => { - const trixEditor = document.getElementById(editorId) as HTMLElement; + trixEditor: any; - if (value != trixEditor.innerHTML) { - (trixEditor as any).editor.loadHTML(value); + onChange = () => { + const { component, flowContext } = this.props; + + const geometry = calcComponentGeometry( + component, + this.trixEditor.closest( + ".EezStudio_ComponentEnclosure" + )! as HTMLElement, + flowContext + ); + + runInAction(() => { + component.geometry = geometry; + }); + }; + + onFocus = () => { + const trixToolbar = + this.trixEditor.parentElement?.querySelector("trix-toolbar"); + if (trixToolbar instanceof HTMLElement) { + trixToolbar.style.visibility = "visible"; } - const onChange = () => { - const geometry = calcComponentGeometry( - component, - trixEditor.closest( - ".EezStudio_ComponentEnclosure" - )! as HTMLElement, - flowContext - ); + if (this.trixEditor.innerHTML != this.props.value) { + this.props.setValue(this.trixEditor.innerHTML); + } + }; - runInAction(() => { - component.geometry = geometry; - }); - }; - const onFocus = () => { - const trixToolbar = - trixEditor.parentElement?.querySelector("trix-toolbar"); - if (trixToolbar instanceof HTMLElement) { - trixToolbar.style.visibility = "visible"; + onBlur = () => { + const trixToolbar = + this.trixEditor.parentElement?.querySelector("trix-toolbar"); + if (trixToolbar instanceof HTMLElement) { + if (!document.activeElement?.classList.contains("trix-input")) { + trixToolbar.style.visibility = ""; } + } - if (trixEditor.innerHTML != value) { - setValue(trixEditor.innerHTML); - } - }; - const onBlur = () => { - const trixToolbar = - trixEditor.parentElement?.querySelector("trix-toolbar"); - if (trixToolbar instanceof HTMLElement) { - if ( - !document.activeElement?.classList.contains( - "trix-input" - ) - ) { - trixToolbar.style.visibility = ""; - } - } + if (this.trixEditor.innerHTML != this.props.value) { + this.props.setValue(this.trixEditor.innerHTML); + } + }; - if (trixEditor.innerHTML != value) { - setValue(trixEditor.innerHTML); - } - }; - const onAttachmentAdd = (event: any) => { - const reader = new FileReader(); - reader.addEventListener( - "load", - function () { - event.attachment.setAttributes({ - url: reader.result - }); + onAttachmentAdd = (event: any) => { + const reader = new FileReader(); - (trixEditor as any).editor.loadHTML( - trixEditor.innerHTML - ); - }, - false - ); - reader.readAsDataURL(event.attachment.file); - }; + reader.addEventListener( + "load", + () => { + event.attachment.setAttributes({ + url: reader.result + }); - trixEditor.addEventListener("trix-change", onChange, false); - trixEditor.addEventListener("trix-focus", onFocus, false); - trixEditor.addEventListener("trix-blur", onBlur, false); - trixEditor.addEventListener( - "trix-attachment-add", - onAttachmentAdd, + (this.trixEditor as any).editor.loadHTML( + this.trixEditor.innerHTML + ); + }, false ); - return () => { - trixEditor.removeEventListener("trix-change", onChange, false); - trixEditor.removeEventListener("trix-focus", onFocus, false); - trixEditor.removeEventListener("trix-blur", onBlur, false); - trixEditor.removeEventListener( + reader.readAsDataURL(event.attachment.file); + }; + + setup() { + if (this.trixEditor) { + this.trixEditor.removeEventListener( + "trix-change", + this.onChange, + false + ); + this.trixEditor.removeEventListener( + "trix-focus", + this.onFocus, + false + ); + this.trixEditor.removeEventListener( + "trix-blur", + this.onBlur, + false + ); + this.trixEditor.removeEventListener( "trix-attachment-add", - onAttachmentAdd, + this.onAttachmentAdd, false ); - }; - }, [value]); + } - var attributes: { [key: string]: string } = { - id: editorId, - input: inputId - }; + this.trixEditor = document.getElementById( + this.editorId + ) as HTMLElement; - return ( -
    - {React.createElement("trix-editor", attributes)} - -
    - ); + if (this.props.value != this.trixEditor.innerHTML) { + (this.trixEditor as any).editor.loadHTML(this.props.value); + } + + this.trixEditor.addEventListener( + "trix-change", + this.onChange, + false + ); + this.trixEditor.addEventListener("trix-focus", this.onFocus, false); + this.trixEditor.addEventListener("trix-blur", this.onBlur, false); + this.trixEditor.addEventListener( + "trix-attachment-add", + this.onAttachmentAdd, + false + ); + } + + componentDidMount(): void { + this.setup(); + } + + componentDidUpdate() { + this.setup(); + } + + render() { + var attributes: { [key: string]: string } = { + id: this.editorId, + input: this.inputId + }; + + return ( +
    + {React.createElement("trix-editor", attributes)} + +
    + ); + } } ); diff --git a/packages/project-editor/flow/components/widgets/dashboard/index.tsx b/packages/project-editor/flow/components/widgets/dashboard/index.tsx index 137ae665..1b985891 100644 --- a/packages/project-editor/flow/components/widgets/dashboard/index.tsx +++ b/packages/project-editor/flow/components/widgets/dashboard/index.tsx @@ -299,33 +299,94 @@ registerClass("RectangleDashboardWidget", RectangleDashboardWidget); //////////////////////////////////////////////////////////////////////////////// -function TextInputWidgetInput({ - value, - flowContext, - textInputWidget, - readOnly, - placeholder, - password, - iterators -}: { - value: string; - flowContext: IFlowContext; - textInputWidget: TextInputWidget; - readOnly: boolean; - placeholder: string; - password: boolean; - iterators: number[]; -}) { - const ref = React.useRef(null); - const [cursor, setCursor] = React.useState(null); - - React.useEffect(() => { - const input = ref.current; - if (input) input.setSelectionRange(cursor, cursor); - }, [ref, cursor, value]); - - const handleKeyDown = (event: React.KeyboardEvent) => { - if (event.key === "Enter") { +const TextInputWidgetInput = observer( + class TextInputWidgetInput extends React.Component<{ + value: string; + flowContext: IFlowContext; + textInputWidget: TextInputWidget; + readOnly: boolean; + placeholder: string; + password: boolean; + iterators: number[]; + }> { + ref = React.createRef(); + cursor: number | null = null; + + constructor(props: any) { + super(props); + + makeObservable(this, { + cursor: observable + }); + } + + setSelectionRange() { + const input = this.ref.current; + if (input) input.setSelectionRange(this.cursor, this.cursor); + } + + componentDidMount() { + this.setSelectionRange(); + } + + componentDidUpdate() { + this.setSelectionRange(); + } + + handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter") { + const flowState = this.props.flowContext.flowState as FlowState; + if (flowState && flowState.runtime) { + flowState.runtime.executeWidgetAction( + this.props.flowContext, + this.props.textInputWidget, + "ON_CHANGE", + makeTextInputChangeEventValue( + this.props.flowContext, + this.props.value + ), + `struct:${TEXT_INPUT_CHANGE_EVENT_STRUCT_NAME}` + ); + } + } + }; + + onChange = (event: React.ChangeEvent) => { + const { flowContext, textInputWidget, iterators } = this.props; + + const flowState = flowContext.flowState as FlowState; + if (flowState) { + runInAction(() => { + this.cursor = event.target.selectionStart; + }); + + const value = event.target.value; + + if (this.props.textInputWidget.data) { + assignProperty( + flowState, + textInputWidget, + "data", + value, + iterators + ); + } + + if (flowState.runtime) { + flowState.runtime.executeWidgetAction( + flowContext, + textInputWidget, + "ON_INPUT", + makeTextInputChangeEventValue(flowContext, value), + `struct:${TEXT_INPUT_CHANGE_EVENT_STRUCT_NAME}` + ); + } + } + }; + + onBlur = () => { + const { flowContext, textInputWidget, value } = this.props; + const flowState = flowContext.flowState as FlowState; if (flowState && flowState.runtime) { flowState.runtime.executeWidgetAction( @@ -336,65 +397,28 @@ function TextInputWidgetInput({ `struct:${TEXT_INPUT_CHANGE_EVENT_STRUCT_NAME}` ); } - } - }; - - return ( - <> - { - const flowState = flowContext.flowState as FlowState; - if (flowState) { - setCursor(event.target.selectionStart); - - const value = event.target.value; - - if (textInputWidget.data) { - assignProperty( - flowState, - textInputWidget, - "data", - value, - iterators - ); - } + }; - if (flowState.runtime) { - flowState.runtime.executeWidgetAction( - flowContext, - textInputWidget, - "ON_INPUT", - makeTextInputChangeEventValue( - flowContext, - value - ), - `struct:${TEXT_INPUT_CHANGE_EVENT_STRUCT_NAME}` - ); - } - } - }} - onBlur={() => { - const flowState = flowContext.flowState as FlowState; - if (flowState && flowState.runtime) { - flowState.runtime.executeWidgetAction( - flowContext, - textInputWidget, - "ON_CHANGE", - makeTextInputChangeEventValue(flowContext, value), - `struct:${TEXT_INPUT_CHANGE_EVENT_STRUCT_NAME}` - ); - } - }} - onKeyDown={handleKeyDown} - readOnly={readOnly} - > - - ); -} + render() { + const { value, readOnly, placeholder, password } = this.props; + + return ( + <> + + + ); + } + } +); export class TextInputWidget extends Widget { static classInfo = makeDerivedClassInfo(Widget.classInfo, { diff --git a/packages/project-editor/lvgl/LVGLStylesTreeNavigation.tsx b/packages/project-editor/lvgl/LVGLStylesTreeNavigation.tsx index 1c032d2d..3b69dea5 100644 --- a/packages/project-editor/lvgl/LVGLStylesTreeNavigation.tsx +++ b/packages/project-editor/lvgl/LVGLStylesTreeNavigation.tsx @@ -312,12 +312,12 @@ export const LVGLStylesTreeNavigation = observer( this.treeAdapter.deleteSelection(); } } - onFocus() { + onFocus = () => { const navigationStore = this.context.navigationStore; if (isPartOfNavigation(this.props.navigationObject)) { navigationStore.setSelectedPanel(this); } - } + }; onSearchChange(event: any) { this.searchText = ($(event.target).val() as string).trim(); @@ -372,7 +372,7 @@ export const LVGLStylesTreeNavigation = observer( ; - onAdd() { + onAdd = () => { let newFeatureObject = createObject( this.context, this.props.projectFeature.create(), @@ -68,9 +68,9 @@ const ProjectFeature = observer( this.context.updateObject(this.context.project, changes); this.context.project.enableTabs(); - } + }; - onRemove() { + onRemove = () => { confirm( "Are you sure you want to remove this feature?", undefined, @@ -95,7 +95,7 @@ const ProjectFeature = observer( } } ); - } + }; render() { let button: JSX.Element | undefined; @@ -145,7 +145,7 @@ const ProjectFeature = observer( button = ( @@ -60,7 +60,7 @@ export class RelativeFileInput extends FieldComponent { @@ -76,11 +76,11 @@ export class AbsoluteFileInput extends FieldComponent { static contextType = ProjectContext; declare context: React.ContextType; - onClear() { + onClear = () => { this.props.onChange(undefined); - } + }; - async onSelect() { + onSelect = async () => { const result = await dialog.showOpenDialog({ properties: ["openFile"], filters: this.props.fieldProperties.options.filters @@ -89,7 +89,7 @@ export class AbsoluteFileInput extends FieldComponent { if (result.filePaths && result.filePaths[0]) { this.props.onChange(result.filePaths[0]); } - } + }; render() { let clearButton: JSX.Element | undefined; @@ -99,7 +99,7 @@ export class AbsoluteFileInput extends FieldComponent { @@ -121,7 +121,7 @@ export class AbsoluteFileInput extends FieldComponent { @@ -137,11 +137,11 @@ export class AbsoluteFileSaveInput extends FieldComponent { static contextType = ProjectContext; declare context: React.ContextType; - onClear() { + onClear = () => { this.props.onChange(undefined); - } + }; - async onSelect() { + onSelect = async () => { const result = await dialog.showSaveDialog({ properties: ["showOverwriteConfirmation"], filters: this.props.fieldProperties.options.filters @@ -150,7 +150,7 @@ export class AbsoluteFileSaveInput extends FieldComponent { if (result.filePath) { this.props.onChange(result.filePath); } - } + }; render() { let clearButton: JSX.Element | undefined; @@ -160,7 +160,7 @@ export class AbsoluteFileSaveInput extends FieldComponent { @@ -182,7 +182,7 @@ export class AbsoluteFileSaveInput extends FieldComponent { diff --git a/packages/project-editor/ui-components/PropertyGrid/ThemedColorInput.tsx b/packages/project-editor/ui-components/PropertyGrid/ThemedColorInput.tsx index 97a1bb07..0d0bbf0a 100644 --- a/packages/project-editor/ui-components/PropertyGrid/ThemedColorInput.tsx +++ b/packages/project-editor/ui-components/PropertyGrid/ThemedColorInput.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactDOM from "react-dom"; import { observer } from "mobx-react"; -import { SketchPicker } from "react-color"; +import Chrome from "@uiw/react-color-chrome"; import { isDark, isLight, isValid } from "eez-studio-shared/color"; @@ -178,22 +178,17 @@ export const ThemedColorInput = observer( const portal = ReactDOM.createPortal(
    - this.onChangeColor(color.hex, false)} - onChangeComplete={color => { - this.onChangeColor(color.hex, true); - }} />
    , document.body diff --git a/packages/project-editor/ui-components/Tree.tsx b/packages/project-editor/ui-components/Tree.tsx index ee4b9567..3d7a52fe 100644 --- a/packages/project-editor/ui-components/Tree.tsx +++ b/packages/project-editor/ui-components/Tree.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useEffect } from "react"; +import React from "react"; import { observable, computed, action, makeObservable } from "mobx"; import { observer } from "mobx-react"; import classNames from "classnames"; @@ -48,22 +48,7 @@ const DropMark = observer( //////////////////////////////////////////////////////////////////////////////// const TreeRow = observer( - ({ - treeAdapter, - item, - level, - draggable, - onDragStart, - onDrag, - onDragEnd, - onClick, - onMouseUp, - onDoubleClick, - collapsable, - onToggleCollapse, - onEditItem, - renderItem - }: { + class TreeRow extends React.Component<{ treeAdapter: TreeAdapter; item: TreeObjectAdapter; level: number; @@ -78,94 +63,110 @@ const TreeRow = observer( onToggleCollapse: (event: any) => void; onEditItem?: (itemId: string) => void; renderItem?: (itemId: string) => React.ReactNode; - }) => { - const ref = useRef(null); + }> { + ref = React.createRef(); - let ensureVisibleTimeout: any; - - useEffect(() => { - if (ensureVisibleTimeout) { - clearTimeout(ensureVisibleTimeout); - } + ensureVisibleTimeout: any; - ensureVisibleTimeout = setTimeout(() => { - ensureVisibleTimeout = undefined; + componentDidMount() { + this.ensureVisibleTimeout = setTimeout(() => { + this.ensureVisibleTimeout = undefined; if ( !( - treeAdapter.draggableAdapter && - treeAdapter.draggableAdapter.isDragging + this.props.treeAdapter.draggableAdapter && + this.props.treeAdapter.draggableAdapter.isDragging ) ) { - if (hasClass(ref.current, "selected")) { - ref.current!.scrollIntoView({ block: "center" }); + if (hasClass(this.ref.current, "selected")) { + this.ref.current!.scrollIntoView({ block: "center" }); } } }, 100); - }, []); - - let className = classNames("tree-row", { - selected: treeAdapter.isSelected(item), - "drag-source": - treeAdapter.draggableAdapter && - treeAdapter.draggableAdapter.isDragSource(item) - }); - - let triangle: JSX.Element | undefined; - if (collapsable) { - triangle = ( - - ); } - return ( -
    - {triangle} - - {renderItem ? ( - renderItem(treeAdapter.getItemId(item)) - ) : ( - - {treeAdapter.itemToString(item)} - - )} - - {onEditItem && ( + componentWillUnmount(): void { + if (this.ensureVisibleTimeout) { + clearTimeout(this.ensureVisibleTimeout); + this.ensureVisibleTimeout = undefined; + } + } + + render() { + const { + treeAdapter, + item, + level, + collapsable, + onToggleCollapse, + onEditItem + } = this.props; + + let className = classNames("tree-row", { + selected: treeAdapter.isSelected(item), + "drag-source": + treeAdapter.draggableAdapter && + treeAdapter.draggableAdapter.isDragSource(item) + }); + + let triangle: JSX.Element | undefined; + if (collapsable) { + triangle = ( onEditItem(treeAdapter.getItemId(item))} + className="tree-row-triangle" + onClick={onToggleCollapse} /> - )} -
    - ); + ); + } + + return ( +
    + {triangle} + + {this.props.renderItem ? ( + this.props.renderItem(treeAdapter.getItemId(item)) + ) : ( + + {treeAdapter.itemToString(item)} + + )} + + {onEditItem && ( + + onEditItem(treeAdapter.getItemId(item)) + } + /> + )} +
    + ); + } } ); diff --git a/packages/shortcuts/group-dialog.tsx b/packages/shortcuts/group-dialog.tsx index ae8fb67d..7ba893d7 100644 --- a/packages/shortcuts/group-dialog.tsx +++ b/packages/shortcuts/group-dialog.tsx @@ -21,8 +21,6 @@ export const GroupDialog = observer( group: observable }); - this.handleSubmit = this.handleSubmit.bind(this); - this.group = { ...this.props.group }; } @@ -46,13 +44,13 @@ export const GroupDialog = observer( ] }); - async handleSubmit() { + handleSubmit = async () => { if (!(await this.validator.checkValidity(this.group))) { return false; } this.props.callback(this.group); return true; - } + }; render() { return ( diff --git a/packages/shortcuts/groups.tsx b/packages/shortcuts/groups.tsx index 8172f04c..5c2776ad 100644 --- a/packages/shortcuts/groups.tsx +++ b/packages/shortcuts/groups.tsx @@ -41,12 +41,9 @@ export const GroupsToolbarButtons = observer( makeObservable(this, { showShortcuts: action }); - - this.addGroup = this.addGroup.bind(this); - this.showShortcuts = this.showShortcuts.bind(this); } - addGroup() { + addGroup = () => { showDialog( ); - } + }; - showShortcuts() { + showShortcuts = () => { this.props.shortcutsOrGroups.set(true); - } + }; render() { return [ diff --git a/packages/shortcuts/shortcut-dialog.tsx b/packages/shortcuts/shortcut-dialog.tsx index e15fee4f..abddd9e2 100644 --- a/packages/shortcuts/shortcut-dialog.tsx +++ b/packages/shortcuts/shortcut-dialog.tsx @@ -67,9 +67,6 @@ export const ShortcutDialog = observer( codeEditorMode: computed }); - this.handleSubmit = this.handleSubmit.bind(this); - this.resetToDefault = this.resetToDefault.bind(this); - this.shortcut = objectClone(props.shortcut); } @@ -173,7 +170,7 @@ export const ShortcutDialog = observer( return undefined; } - async handleSubmit() { + handleSubmit = async () => { this.wasValidated = true; if (!(await this.validator.checkValidity(this.shortcut))) { return false; @@ -187,7 +184,7 @@ export const ShortcutDialog = observer( this.props.callback(this.shortcut); return true; - } + }; async revalidate() { if (this.wasValidated) { @@ -272,7 +269,7 @@ export const ShortcutDialog = observer( ); } - resetToDefault(event: any) { + resetToDefault = (event: any) => { event.preventDefault(); if (this.originalShortcut) { @@ -287,7 +284,7 @@ export const ShortcutDialog = observer( this.shortcut.toolbarButtonColor = this.originalShortcut.toolbarButtonColor; } - } + }; get codeEditorMode() { if ( diff --git a/packages/shortcuts/shortcuts.tsx b/packages/shortcuts/shortcuts.tsx index 91eee6b5..675a5005 100644 --- a/packages/shortcuts/shortcuts.tsx +++ b/packages/shortcuts/shortcuts.tsx @@ -77,9 +77,6 @@ export const ShortcutsToolbarButtons = observer( showGroups: action }); - this.addShortcut = this.addShortcut.bind(this); - this.showGroups = this.showGroups.bind(this); - showExtensionShortcuts.set( window.localStorage.getItem( "Shortcuts_showExtensionShortcuts" @@ -89,7 +86,7 @@ export const ShortcutsToolbarButtons = observer( ); } - addShortcut() { + addShortcut = () => { showShortcutDialog( this.props.shortcutsStore, this.props.groupsStore, @@ -122,11 +119,11 @@ export const ShortcutsToolbarButtons = observer( }, 10); } ); - } + }; - showGroups() { + showGroups = () => { this.props.shortcutsOrGroups!.set(false); - } + }; render() { let buttons = [];