From 65a594a304a752115a0e1e6c46200e251a0061dc Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 19 Jun 2024 16:22:09 +0200 Subject: [PATCH 001/138] moving towards react18 first steps --- viewer/components/ItemList.jsx | 217 +++++++++++++--------------- viewer/components/OverlayDialog.jsx | 2 - viewer/gui/MainPage.jsx | 2 - viewer/package.json | 74 ++++------ viewer/util/compare.js | 3 +- 5 files changed, 134 insertions(+), 164 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index e184f51d0..7846252ce 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -10,10 +10,9 @@ * ths onIemClick will directly pass through */ -import React from 'react'; +import React,{useState} from 'react'; import PropTypes from 'prop-types'; import assign from 'object-assign'; -import {SortableContainer, SortableElement} from 'react-sortable-hoc'; const getKey=function(obj){ @@ -23,14 +22,95 @@ const getKey=function(obj){ return rt; }; -class ItemList extends React.Component{ - constructor(props){ - super(props); - this.onSortEnd=this.onSortEnd.bind(this); - this.Content=this.Content.bind(this); - this.ItemWrapper=this.ItemWrapper.bind(this); - this.ItemWrapperNoClick=this.ItemWrapperNoClick.bind(this); - } +const ItemWrapper=(props)=>{ + let {ItemClass,...iprops}=props; + const memoClick=(data)=>{ + if (data && data.stopPropagation) data.stopPropagation(); + if (data && data.preventDefault) data.preventDefault(); + if (props.reverse){ + let len=props.itemList?props.itemList.length:0; + props.onItemClick(assign({},iprops,{index:len-iprops.index}),data); + } + else { + props.onItemClick(iprops, data); + } + }; + return +} +const ItemWrapperNoClick=(props)=>{ + let {ItemClass,...iprops}=props; + return +} + +const Content=(props)=>{ + let idx = 0; + let existingKeys={}; + return ( +
{if (props.listRef) props.listRef(el)}} + onClick={(ev)=>{ + if (props.onClick){ + ev.stopPropagation(); + props.onClick(ev); + } + }} + > + {props.allitems.map(function (entry) { + let itemProps ={...entry}; + let key = getKey(entry); + //we allow for multiple items with the same name + //we try at most 20 times to get a unique key by appending _idx + let tries=20; + while (tries > 0 && (! key || existingKeys[key])){ + key+="_"+idx; + tries--; + } + itemProps.index=props.reverse?props.allitems.length-idx:idx; + itemProps.key = key; + existingKeys[key]=true; + if (props.selectedIndex !== undefined){ + if (idx == props.selectedIndex) { + itemProps.selected = true; + } + else { + itemProps.selected = false; + } + } + let ItemClass; + if (props.itemCreator) { + ItemClass = props.itemCreator(entry); + if (!ItemClass) return null; + } + else { + ItemClass = props.itemClass; + } + if (props.dragdrop) { + //ItemClass=SortableElement(ItemClass); + } + let ItemWrapperEl; + if (!itemProps.onClick && props.onItemClick) { + ItemWrapperEl=ItemWrapper; + } + else{ + ItemWrapperEl=ItemWrapperNoClick; + } + idx++; + + return + })} +
+ ); +}; + +const ItemList=(props)=>{ + const itemList=props.itemList; + /* onSortEnd(data){ let len=this.props.itemList?this.props.itemList.length:0; if (this.props.reverse) { @@ -41,108 +121,19 @@ class ItemList extends React.Component{ } } - - ItemWrapper(props){ - let self=this; - let {ItemClass,...iprops}=props; - const memoClick=React.useCallback((data)=>{ - if (data && data.stopPropagation) data.stopPropagation(); - if (data && data.preventDefault) data.preventDefault(); - if (self.props.reverse){ - let len=self.props.itemList?self.props.itemList.length:0; - self.props.onItemClick(assign({},iprops,{index:len-iprops.index}),data); - } - else { - self.props.onItemClick(iprops, data); - } - },[iprops]); - return - } - ItemWrapperNoClick(props){ - let {ItemClass,...iprops}=props; - return - } - Content(props) { - let self=this; - let idx = 0; - let existingKeys={}; - return ( -
{if (props.listRef) props.listRef(el)}} - onClick={(ev)=>{ - if (self.props.onClick){ - ev.stopPropagation(); - self.props.onClick(ev); - } - }} - > - {props.allitems.map(function (entry) { - let itemProps = assign({}, entry); - let key = getKey(entry); - //we allow for multiple items with the same name - //we try at most 20 times to get a unique key by appending _idx - let tries=20; - while (tries > 0 && (! key || existingKeys[key])){ - key+="_"+idx; - tries--; - } - itemProps.index=self.props.reverse?props.allitems.length-idx:idx; - itemProps.key = key; - existingKeys[key]=true; - if (self.props.selectedIndex !== undefined){ - if (idx == self.props.selectedIndex) { - itemProps.selected = true; - } - else { - itemProps.selected = false; - } - } - let ItemClass; - if (self.props.itemCreator) { - ItemClass = self.props.itemCreator(entry); - if (!ItemClass) return null; - } - else { - ItemClass = self.props.itemClass; - } - if (self.props.dragdrop) { - ItemClass=SortableElement(ItemClass); - } - let ItemWrapper; - if (!itemProps.onClick && self.props.onItemClick) { - ItemWrapper=self.ItemWrapper; - } - else{ - ItemWrapper=self.ItemWrapperNoClick; - } - idx++; - - return - })} -
- ); - }; - render() { - let allitems = this.props.itemList || []; - if (this.props.hideOnEmpty && allitems.length < 1) return null; - let self = this; + */ + if (props.hideOnEmpty && itemList.length < 1) return null; let className = "listContainer"; - if (this.props.scrollable) className+=" scrollable"; - if (this.props.className) className += " " + this.props.className; - if (this.props.horizontal) className += " horizontal"; - let style=this.props.style||{}; - if (this.props.fontSize){ - style.fontSize=this.props.fontSize; + if (props.scrollable) className+=" scrollable"; + if (props.className) className += " " + props.className; + if (props.horizontal) className += " horizontal"; + let style=props.style||{}; + if (props.fontSize){ + style.fontSize=props.fontSize; } let dragProps={}; - let Content=this.Content; + /*let Content=this.Content; if (this.props.dragdrop){ Content= SortableContainer(Content); dragProps.axis=self.props.horizontal?"x":"y"; @@ -151,19 +142,19 @@ class ItemList extends React.Component{ dragProps.helperClass="sortableHelper"; } - if (this.props.scrollable) { + */ + if (props.scrollable) { return ( -
{if (self.props.listRef) self.props.listRef(el)}}> - +
{if (props.listRef) props.listRef(el)}}> +
); } else { return( - + ); } - } } diff --git a/viewer/components/OverlayDialog.jsx b/viewer/components/OverlayDialog.jsx index 9c79a8953..5246ed2cf 100644 --- a/viewer/components/OverlayDialog.jsx +++ b/viewer/components/OverlayDialog.jsx @@ -8,7 +8,6 @@ */ import React from 'react'; -import PropTypes from 'prop-types'; import assign from 'object-assign'; import DialogDisplay from './OverlayDialogDisplay.jsx'; import Dynamic from '../hoc/Dynamic.jsx'; @@ -17,7 +16,6 @@ import globalStore from '../util/globalstore.jsx'; import ItemList from '../components/ItemList.jsx'; import keys from '../util/keys.jsx'; import DB from './DialogButton.jsx'; -import shallowcompare from '../util/compare.js'; let id=1; const notifyClosed=()=>{ diff --git a/viewer/gui/MainPage.jsx b/viewer/gui/MainPage.jsx index 4935f3d52..8246ae6d0 100644 --- a/viewer/gui/MainPage.jsx +++ b/viewer/gui/MainPage.jsx @@ -24,8 +24,6 @@ import RemoteChannelDialog from "../components/RemoteChannelDialog"; import {RecursiveCompare} from '../util/compare'; import LocalStorage from '../util/localStorageManager'; import splitsupport from "../util/splitsupport"; -import LeaveHandler from '../util/leavehandler'; - const getImgSrc=function(color){ diff --git a/viewer/package.json b/viewer/package.json index 453c833f8..eb5293bfa 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -21,69 +21,53 @@ "author": "Andreas Vogel", "license": "MIT", "devDependencies": { - "@babel/core": "^7.17.4", - "@babel/preset-env": "^7.16.11", - "@babel/preset-react": "^7.16.7", - "babel-loader": "^8.2.3", + "@babel/core": "^7.24.7", + "@babel/preset-env": "^7.24.7", + "@babel/preset-react": "^7.24.7", + "babel-loader": "^9.1.3", "babel-plugin-prismjs": "^2.1.0", - "babelify": "^10.0.0", - "copy-webpack-plugin": "^10.2.4", - "cross-env": "^7.0.3", - "css-loader": "^6.6.0", - "css-selector-tokenizer": "^0.8.0", - "cssnano": "^5.0.17", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", "file-loader": "^6.2.0", - "fs": "0.0.2", "generate-file-webpack-plugin": "^1.0.1", - "html-webpack-plugin": "^5.5.0", - "less": "^4.1.2", - "less-loader": "^10.2.0", - "loader-utils": "^3.2.0", - "lodash.camelcase": "^4.3.0", - "mini-css-extract-plugin": "^2.5.3", + "html-webpack-plugin": "^5.6.0", + "less": "^4.2.0", + "less-loader": "^12.2.0", + "mini-css-extract-plugin": "^2.9.0", "nlf": "^2.1.1", - "object-assign": "^4.1.1", - "postcss": "^8.4.6", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", "prop-types": "^15.8.1", - "qr.js": "0.0.0", - "qrcode.react": "^1.0.1", - "react-addons-shallow-compare": "^15.6.3", - "react-addons-update": "^15.6.3", - "react-dom": "^17.0", - "shallow-equals": "^1.0.0", - "style-loader": "^3.3.1", - "terser-webpack-plugin": "^5.3.1", - "val-loader": "^4.0.0", - "webpack": "^5.69.0", - "webpack-cli": "^4.9.2" + "qrcode.react": "^3.1.0", + "react-dom": "^18.3.1", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.10", + "val-loader": "^6.0.0", + "webpack": "^5.92.0", + "webpack-cli": "^5.1.4" }, "dependencies": { - "@braintree/browser-detection": "^1.12.1", + "@braintree/browser-detection": "^2.0.0", + "@dnd-kit/core": "^6.1.0", "@openlayers/pepjs": "^0.5.4", "PubSub": "^4.0.0", "canvas-gauges": "git+https://github.com/wellenvogel/canvas-gauges#av20220223", "clone-deep": "^4.0.1", "codeflask": "git+https://github.com/wellenvogel/CodeFlask.git#20220109", - "core-js": "^2.6.12", + "core-js": "^3.37.1", "create-react-class": "^15.7.0", + "eslint": "^9.5.0", + "eslint-plugin-react": "^7.34.3", "fullscreen-polyfill": "^1.0.4", "geodesy": "^2.4.0", - "lodash.merge": "^4.6.2", "object-assign": "^4.1.1", "ol": "6.12.0", - "prismjs": "^1.26.0", - "react": "^17.0", - "react-html-parser": "git+https://github.com/wellenvogel/react-html-parser.git#av-20220217-2", - "react-measure": "^2.5.2", - "react-sortable-hoc": "^2.0.0", - "regenerator-runtime": "^0.13.9", - "shallow-equal": "^1.2.1", - "tinycolor2": "^1.4.2", + "prismjs": "^1.29.0", + "react": "^18.3.1", + "react-html-parser": "git+https://github.com/wellenvogel/react-html-parser.git#av-20240619", + "regenerator-runtime": "^0.14.1", + "shallow-equal": "^3.1.0", + "tinycolor2": "^1.6.0", "whatwg-fetch-timeout": "^2.0.2-timeout", + "xml-utils": "^1.10.1", "xml-writer": "^1.7.0" } } diff --git a/viewer/util/compare.js b/viewer/util/compare.js index 7a0dc922d..3e1e0eb62 100644 --- a/viewer/util/compare.js +++ b/viewer/util/compare.js @@ -1,8 +1,7 @@ /** * Created by andreas on 25.09.17. */ -import equalsObjects from 'shallow-equal/objects'; -import equalsArrays from 'shallow-equal/arrays'; +import {shallowEqualObjects as equalsObjects, shallowEqualArrays as equalsArrays} from 'shallow-equal'; let Compare=function(oldData, newData){ if (oldData === undefined && newData === undefined) return true; From d74d3be7e97b1c6d77323e7b914743041410e9e1 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 20 Jun 2024 16:12:07 +0200 Subject: [PATCH 002/138] add eslint --- viewer/package.json | 10 ++++++++-- viewer/util/api.js | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/viewer/package.json b/viewer/package.json index eb5293bfa..fa7f513fa 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -24,12 +24,19 @@ "@babel/core": "^7.24.7", "@babel/preset-env": "^7.24.7", "@babel/preset-react": "^7.24.7", + "@eslint/compat": "^1.1.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.5.0", "babel-loader": "^9.1.3", "babel-plugin-prismjs": "^2.1.0", "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", + "eslint": "^9.5.0", + "eslint-plugin-react": "^7.34.3", + "eslint-plugin-react-hooks": "^4.6.2", "file-loader": "^6.2.0", "generate-file-webpack-plugin": "^1.0.1", + "globals": "^15.6.0", "html-webpack-plugin": "^5.6.0", "less": "^4.2.0", "less-loader": "^12.2.0", @@ -40,6 +47,7 @@ "react-dom": "^18.3.1", "style-loader": "^4.0.0", "terser-webpack-plugin": "^5.3.10", + "typescript-eslint": "^7.13.1", "val-loader": "^6.0.0", "webpack": "^5.92.0", "webpack-cli": "^5.1.4" @@ -54,8 +62,6 @@ "codeflask": "git+https://github.com/wellenvogel/CodeFlask.git#20220109", "core-js": "^3.37.1", "create-react-class": "^15.7.0", - "eslint": "^9.5.0", - "eslint-plugin-react": "^7.34.3", "fullscreen-polyfill": "^1.0.4", "geodesy": "^2.4.0", "object-assign": "^4.1.1", diff --git a/viewer/util/api.js b/viewer/util/api.js index 619ad2e3b..a869608f5 100644 --- a/viewer/util/api.js +++ b/viewer/util/api.js @@ -9,7 +9,6 @@ import Formatter from './formatter.js'; import Helper from './helper.js'; import Toast from '../components/Toast.jsx'; import featureFormatter from "./featureFormatter"; -import NavCompute from "../nav/navcompute"; import LatLon from "geodesy/latlon-spherical"; import Dms from "geodesy/dms"; From 58548cd8c7dd623eeca237557a82286b99cbb543 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 20 Jun 2024 16:22:58 +0200 Subject: [PATCH 003/138] add type script support --- viewer/eslint.config.mjs | 15 +++++++++++++++ viewer/package.json | 4 ++++ viewer/test/TsTest.tsx | 11 +++++++++++ viewer/tsconfig.json | 9 +++++++++ viewer/webpack.config.js | 3 ++- 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 viewer/eslint.config.mjs create mode 100644 viewer/test/TsTest.tsx create mode 100644 viewer/tsconfig.json diff --git a/viewer/eslint.config.mjs b/viewer/eslint.config.mjs new file mode 100644 index 000000000..2cac24bfd --- /dev/null +++ b/viewer/eslint.config.mjs @@ -0,0 +1,15 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; +import pluginReactConfig from "eslint-plugin-react/configs/recommended.js"; +import { fixupConfigRules } from "@eslint/compat"; + + +export default [ + {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]}, + { languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } }, + {languageOptions: { globals: globals.browser }}, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + ...fixupConfigRules(pluginReactConfig), +]; \ No newline at end of file diff --git a/viewer/package.json b/viewer/package.json index fa7f513fa..5eed5e3d3 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -27,6 +27,8 @@ "@eslint/compat": "^1.1.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.5.0", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", "babel-loader": "^9.1.3", "babel-plugin-prismjs": "^2.1.0", "copy-webpack-plugin": "^12.0.2", @@ -47,6 +49,8 @@ "react-dom": "^18.3.1", "style-loader": "^4.0.0", "terser-webpack-plugin": "^5.3.10", + "ts-loader": "^9.5.1", + "typescript": "^5.4.5", "typescript-eslint": "^7.13.1", "val-loader": "^6.0.0", "webpack": "^5.92.0", diff --git a/viewer/test/TsTest.tsx b/viewer/test/TsTest.tsx new file mode 100644 index 000000000..70c401107 --- /dev/null +++ b/viewer/test/TsTest.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +export interface Test{ + name: string; +} + +export default TestDisplay + +function TestDisplay(props: Test) { + return
{props.name}
; +} \ No newline at end of file diff --git a/viewer/tsconfig.json b/viewer/tsconfig.json new file mode 100644 index 000000000..13515d750 --- /dev/null +++ b/viewer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "sourceMap": true, + "jsx": "react", + "target": "ES2015", + "allowJs": true, + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/viewer/webpack.config.js b/viewer/webpack.config.js index 2d3c3319e..4f37e01d9 100644 --- a/viewer/webpack.config.js +++ b/viewer/webpack.config.js @@ -116,7 +116,7 @@ module.exports = (env, argv) => { clean: cleanOutput }, resolve: { - extensions: ['.jsx', '.scss', '.js', '.json'], + extensions: ['.jsx', '.scss', '.js', '.json','.tsx','.ts'], alias: resolveAlias }, module: { @@ -154,6 +154,7 @@ module.exports = (env, argv) => { } }, + { test: /\.tsx?$|\.ts$/, loader: 'ts-loader' }, { test: /\.css$/, From f1a13f366af05cfa0aaed09c15d8d37635e95540 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 20 Jun 2024 16:28:29 +0200 Subject: [PATCH 004/138] propTypes for main page --- viewer/gui/MainPage.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/viewer/gui/MainPage.jsx b/viewer/gui/MainPage.jsx index 8246ae6d0..382944ace 100644 --- a/viewer/gui/MainPage.jsx +++ b/viewer/gui/MainPage.jsx @@ -429,7 +429,9 @@ class MainPage extends React.Component { } - +MainPage.propTypes= { +...Page.propTypes +} From 3ea7202a1c84539c23dae3e9192baa5177870a94 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 20 Jun 2024 18:58:10 +0200 Subject: [PATCH 005/138] intermediate: start restructure ItemList --- viewer/components/ItemList.jsx | 3 ++- viewer/package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 7846252ce..389777bd1 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -175,7 +175,8 @@ ItemList.propTypes={ dragdrop: PropTypes.bool, horizontal: PropTypes.bool, reverse: PropTypes.bool, //let the index count backwards - onSortEnd: PropTypes.func + onSortEnd: PropTypes.func, + style: PropTypes.object }; export default ItemList; \ No newline at end of file diff --git a/viewer/package.json b/viewer/package.json index 5eed5e3d3..536c04e6f 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -58,7 +58,7 @@ }, "dependencies": { "@braintree/browser-detection": "^2.0.0", - "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", "@openlayers/pepjs": "^0.5.4", "PubSub": "^4.0.0", "canvas-gauges": "git+https://github.com/wellenvogel/canvas-gauges#av20220223", From ee36cc7b24ce02224b1a92c7b775887e958ec966 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 28 Jun 2024 11:36:48 +0200 Subject: [PATCH 006/138] restructure MapEventGuard --- viewer/hoc/MapEventGuard.jsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/viewer/hoc/MapEventGuard.jsx b/viewer/hoc/MapEventGuard.jsx index 7b8fe25a0..cbbc6c857 100644 --- a/viewer/hoc/MapEventGuard.jsx +++ b/viewer/hoc/MapEventGuard.jsx @@ -43,11 +43,7 @@ export default (Component,opt_store)=>{ return React.forwardRef((props,ref)=>{ let {onClick,...forwards}=props; let clickHandler=onClick?function() { - let timeDiff = globalStore.getData(keys.properties.mapClickWorkaroundTime, 300); - if (lastMapClickTime !== undefined) { - let now = (new Date()).getTime(); - if ((now - timeDiff) <= lastMapClickTime) return; - } + if (isMapDeadTime()) return; if (props.onClick) { props.onClick.apply(this, [...arguments]); } @@ -58,4 +54,13 @@ export default (Component,opt_store)=>{ {...forwards} /> }) -}; \ No newline at end of file +}; + +export const isMapDeadTime=()=>{ + let timeDiff = globalStore.getData(keys.properties.mapClickWorkaroundTime, 300); + if (lastMapClickTime !== undefined) { + let now = (new Date()).getTime(); + if ((now - timeDiff) <= lastMapClickTime) return true; + } + return false; +} \ No newline at end of file From 9e82bafabe20a0656b929f0e9900d337e8974fb2 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 3 Jul 2024 16:01:59 +0200 Subject: [PATCH 007/138] add zip build to beta/release --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b8a6a283a..855d2ac6a 100644 --- a/build.gradle +++ b/build.gradle @@ -316,7 +316,7 @@ task release{ println "all release packages have been build" } dependsOn testGit, releaseRpm, releaseDeb,raspiDeb - dependsOn windowsBuild + dependsOn windowsBuild, buildZip if (buildAndroid){ dependsOn assembleReleaseAndroid } @@ -328,7 +328,7 @@ task beta{ println "all beta packages have been build" } dependsOn releaseRpm, releaseDeb,raspiDeb - dependsOn windowsBuild + dependsOn windowsBuild, buildZip if (buildAndroid){ dependsOn assembleBetaAndroid } From dabc0d633dbd88ddb0f1a56f7f130bba3cc5ec98 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 3 Jul 2024 16:02:27 +0200 Subject: [PATCH 008/138] #349: use a fixed version for the pip installer on windows --- windows/postinstall.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/postinstall.ps1 b/windows/postinstall.ps1 index f6016a30f..af5ff0613 100755 --- a/windows/postinstall.ps1 +++ b/windows/postinstall.ps1 @@ -19,7 +19,7 @@ try { installCmd = "python"; pathFile = "python37._pth" } - [PSCustomObject]@{"urlBase" = "https://bootstrap.pypa.io/"; + [PSCustomObject]@{"urlBase" = "https://bootstrap.pypa.io/pip/3.7"; name = "get-pip.py"; exe = "$pythonDir\Scripts\pip.exe"; } From b4816ca98abb6f9b67ee45aea269b11e6f6f1d59 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 3 Jul 2024 16:01:59 +0200 Subject: [PATCH 009/138] add zip build to beta/release --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b8a6a283a..855d2ac6a 100644 --- a/build.gradle +++ b/build.gradle @@ -316,7 +316,7 @@ task release{ println "all release packages have been build" } dependsOn testGit, releaseRpm, releaseDeb,raspiDeb - dependsOn windowsBuild + dependsOn windowsBuild, buildZip if (buildAndroid){ dependsOn assembleReleaseAndroid } @@ -328,7 +328,7 @@ task beta{ println "all beta packages have been build" } dependsOn releaseRpm, releaseDeb,raspiDeb - dependsOn windowsBuild + dependsOn windowsBuild, buildZip if (buildAndroid){ dependsOn assembleBetaAndroid } From d35af7e5bd64abe58512b2bad64aaee9c797d11a Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 3 Jul 2024 16:02:27 +0200 Subject: [PATCH 010/138] #349: use a fixed version for the pip installer on windows --- windows/postinstall.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/postinstall.ps1 b/windows/postinstall.ps1 index f6016a30f..af5ff0613 100755 --- a/windows/postinstall.ps1 +++ b/windows/postinstall.ps1 @@ -19,7 +19,7 @@ try { installCmd = "python"; pathFile = "python37._pth" } - [PSCustomObject]@{"urlBase" = "https://bootstrap.pypa.io/"; + [PSCustomObject]@{"urlBase" = "https://bootstrap.pypa.io/pip/3.7"; name = "get-pip.py"; exe = "$pythonDir\Scripts\pip.exe"; } From 7429a6fdb11f246ebbf3956bec1be9ca31089cd4 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Wed, 3 Jul 2024 18:24:19 +0200 Subject: [PATCH 011/138] try react dnd --- viewer/components/ItemList.jsx | 144 +++++++++++++++++++++------------ viewer/package.json | 4 +- 2 files changed, 97 insertions(+), 51 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 389777bd1..80d9df7dc 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -10,9 +10,10 @@ * ths onIemClick will directly pass through */ -import React,{useState} from 'react'; +import React, {useRef} from 'react'; import PropTypes from 'prop-types'; -import assign from 'object-assign'; +import {DndProvider, useDrag, useDrop} from 'react-dnd'; +import {TouchBackend} from 'react-dnd-touch-backend'; const getKey=function(obj){ @@ -21,30 +22,71 @@ const getKey=function(obj){ rt=obj.name; return rt; }; - -const ItemWrapper=(props)=>{ - let {ItemClass,...iprops}=props; - const memoClick=(data)=>{ - if (data && data.stopPropagation) data.stopPropagation(); - if (data && data.preventDefault) data.preventDefault(); - if (props.reverse){ - let len=props.itemList?props.itemList.length:0; - props.onItemClick(assign({},iprops,{index:len-iprops.index}),data); - } - else { - props.onItemClick(iprops, data); - } - }; - return -} -const ItemWrapperNoClick=(props)=>{ - let {ItemClass,...iprops}=props; - return +const DDType="dd"; +const DDItem=({ItemClass,...props})=>{ + const ref = useRef(null) + const [{ handlerId }, drop] = useDrop({ + accept: DDType, + collect(monitor) { + return { + handlerId: monitor.getHandlerId(), + } + }, + hover(item, monitor) { + if (!ref.current) { + return + } + const dragIndex = item.index + const hoverIndex = index + // Don't replace items with themselves + if (dragIndex === hoverIndex) { + return + } + // Determine rectangle on screen + const hoverBoundingRect = ref.current?.getBoundingClientRect() + // Get vertical middle + const hoverMiddleY = + (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + // Determine mouse position + const clientOffset = monitor.getClientOffset() + // Get pixels to the top + const hoverClientY = clientOffset.y - hoverBoundingRect.top + // Only perform the move when the mouse has crossed half of the items height + // When dragging downwards, only move when the cursor is below 50% + // When dragging upwards, only move when the cursor is above 50% + // Dragging downwards + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return + } + // Dragging upwards + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return + } + // Time to actually perform the action + props.move(dragIndex, hoverIndex) + // Note: we're mutating the monitor item here! + // Generally it's better to avoid mutations, + // but it's good here for the sake of performance + // to avoid expensive index searches. + item.index = hoverIndex + }, + }) + const [{ isDragging }, drag] = useDrag({ + type: DDType, + item: () => { + return { id, index } + }, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + }) + const opacity = isDragging ? 0 : 1 + drag(drop(ref)) + return ( +
+ +
+ ) } const Content=(props)=>{ @@ -90,24 +132,37 @@ const Content=(props)=>{ else { ItemClass = props.itemClass; } - if (props.dragdrop) { - //ItemClass=SortableElement(ItemClass); - } - let ItemWrapperEl; if (!itemProps.onClick && props.onItemClick) { - ItemWrapperEl=ItemWrapper; - } - else{ - ItemWrapperEl=ItemWrapperNoClick; + itemProps.onClick = (ev) => { + if (ev.stopPropagation) ev.stopPropagation(); + if (ev.preventDefault) ev.preventDefault(); + if (props.reverse){ + let len=props.itemList?props.itemList.length:0; + props.onItemClick({...itemProps,index:len-itemProps.index},ev); + } + else{ + props.onItemClick(itemProps, ev); + } + } } idx++; - - return + if (props.dragdrop){ + return { + console.log("move",p1,p2); + }}/> + } + return })}
); }; +const DDContent=({Content,...props})=>{ + return + + +} + const ItemList=(props)=>{ const itemList=props.itemList; /* @@ -131,28 +186,17 @@ const ItemList=(props)=>{ if (props.fontSize){ style.fontSize=props.fontSize; } - - let dragProps={}; - /*let Content=this.Content; - if (this.props.dragdrop){ - Content= SortableContainer(Content); - dragProps.axis=self.props.horizontal?"x":"y"; - dragProps.distance=20; - dragProps.onSortEnd=self.onSortEnd; - dragProps.helperClass="sortableHelper"; - - } - */ + let ContentEl=props.dragdrop?(props)=>:Content; if (props.scrollable) { return (
{if (props.listRef) props.listRef(el)}}> - +
); } else { return( - + ); } diff --git a/viewer/package.json b/viewer/package.json index 536c04e6f..d93e4bfee 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -60,7 +60,6 @@ "@braintree/browser-detection": "^2.0.0", "@dnd-kit/sortable": "^8.0.0", "@openlayers/pepjs": "^0.5.4", - "PubSub": "^4.0.0", "canvas-gauges": "git+https://github.com/wellenvogel/canvas-gauges#av20220223", "clone-deep": "^4.0.1", "codeflask": "git+https://github.com/wellenvogel/CodeFlask.git#20220109", @@ -71,7 +70,10 @@ "object-assign": "^4.1.1", "ol": "6.12.0", "prismjs": "^1.29.0", + "PubSub": "^4.0.0", "react": "^18.3.1", + "react-dnd": "^16.0.1", + "react-dnd-touch-backend": "^16.0.1", "react-html-parser": "git+https://github.com/wellenvogel/react-html-parser.git#av-20240619", "regenerator-runtime": "^0.14.1", "shallow-equal": "^3.1.0", From 260f60c0165dd86c02675ea76e369ebc96306293 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 3 Jul 2024 19:17:38 +0200 Subject: [PATCH 012/138] move to react 18 createRoot --- viewer/avnav_viewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/avnav_viewer.js b/viewer/avnav_viewer.js index 503d77938..10b007314 100644 --- a/viewer/avnav_viewer.js +++ b/viewer/avnav_viewer.js @@ -156,7 +156,7 @@ export default function() { }; const doLateLoads=(loadPlugins)=>{ - ReactDOM.render(,document.getElementById('new_pages')); + ReactDOM.createRoot(document.getElementById('new_pages')).render(); //ios browser sometimes has issues with less... setTimeout(function(){ propertyHandler.incrementSequence(); From 5a3d01bae86d47f117baf735a9f6f0314836b931 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 4 Jul 2024 19:55:10 +0200 Subject: [PATCH 013/138] intermediate: dnd-kit --- viewer/components/DirectWidget.jsx | 5 +- viewer/components/ItemList.jsx | 75 ++++++++++++++++-------------- viewer/package.json | 1 + 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/viewer/components/DirectWidget.jsx b/viewer/components/DirectWidget.jsx index b585e9191..5878896df 100644 --- a/viewer/components/DirectWidget.jsx +++ b/viewer/components/DirectWidget.jsx @@ -44,7 +44,7 @@ class DirectWidget extends React.Component{ let style=props.style||{}; return ( -
+
@@ -71,7 +71,8 @@ DirectWidget.propTypes={ className: PropTypes.string, style: PropTypes.object, default: PropTypes.string, - translateFunction: PropTypes.func + translateFunction: PropTypes.func, + dragRef: PropTypes.func }; DirectWidget.editableParameters={ diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 389777bd1..741ec1c1e 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -10,10 +10,13 @@ * ths onIemClick will directly pass through */ -import React,{useState} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import assign from 'object-assign'; - +import { + DndContext, + closestCenter +} from '@dnd-kit/core'; +import {useSortable} from "@dnd-kit/sortable"; const getKey=function(obj){ let rt=obj.key; @@ -22,29 +25,24 @@ const getKey=function(obj){ return rt; }; -const ItemWrapper=(props)=>{ - let {ItemClass,...iprops}=props; - const memoClick=(data)=>{ - if (data && data.stopPropagation) data.stopPropagation(); - if (data && data.preventDefault) data.preventDefault(); - if (props.reverse){ - let len=props.itemList?props.itemList.length:0; - props.onItemClick(assign({},iprops,{index:len-iprops.index}),data); - } - else { - props.onItemClick(iprops, data); - } +const SortableItem=(props)=>{ + const {ItemClass,id,...fwProps}=props; + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + } = useSortable({id: id}); + + const style = { + transform: "",//CSS.Transform.toString(transform), + transition, }; - return -} -const ItemWrapperNoClick=(props)=>{ - let {ItemClass,...iprops}=props; - return + const nodeRef=(el)=>{ + setNodeRef(el); + } + return } const Content=(props)=>{ @@ -90,19 +88,24 @@ const Content=(props)=>{ else { ItemClass = props.itemClass; } - if (props.dragdrop) { - //ItemClass=SortableElement(ItemClass); - } - let ItemWrapperEl; if (!itemProps.onClick && props.onItemClick) { - ItemWrapperEl=ItemWrapper; - } - else{ - ItemWrapperEl=ItemWrapperNoClick; + itemProps.onClick=(data)=>{ + if (data && data.stopPropagation) data.stopPropagation(); + if (data && data.preventDefault) data.preventDefault(); + if (props.reverse){ + let len=props.itemList?props.itemList.length:0; + props.onItemClick({...itemProps,index:len-iprops.index},data); + } + else { + props.onItemClick(itemProps, data); + } + } } idx++; - - return + if (props.dragdrop) { + return + } + return })}
); @@ -178,5 +181,5 @@ ItemList.propTypes={ onSortEnd: PropTypes.func, style: PropTypes.object }; - +Content.propTypes=ItemList.propTypes; export default ItemList; \ No newline at end of file diff --git a/viewer/package.json b/viewer/package.json index 536c04e6f..e2f7e0a19 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -58,6 +58,7 @@ }, "dependencies": { "@braintree/browser-detection": "^2.0.0", + "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@openlayers/pepjs": "^0.5.4", "PubSub": "^4.0.0", From 5561182b94bf7ed14a38526547bc155d68aa4c8a Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 4 Jul 2024 20:29:15 +0200 Subject: [PATCH 014/138] make status item robust against strange names --- viewer/components/StatusItems.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/components/StatusItems.jsx b/viewer/components/StatusItems.jsx index 84e80b7b3..9c3c75ada 100644 --- a/viewer/components/StatusItems.jsx +++ b/viewer/components/StatusItems.jsx @@ -40,7 +40,7 @@ export const EditIcon=(props)=>{ } export const ChildStatus=(props)=>{ let canEdit=props.canEdit && props.connected; - let sub=props.sub || (props.name && props.name.match(/:#:/)); + let sub=props.sub || (props.name && (typeof(props.name.match) === 'function') && props.name.match(/:#:/)); let name=sub?props.name.replace(/^.*:#:/,''):props.name; let clName="childStatus"; if (sub) clName+=" sub"; From 888ab29630df5f0143ef90e4a81e40323125daef Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 14 Jul 2024 17:57:08 +0200 Subject: [PATCH 015/138] 1st dnd working for direct widget --- viewer/components/DirectWidget.jsx | 67 ++++++------ viewer/components/ItemList.jsx | 169 +++++++++++++---------------- viewer/hoc/Sortable.js | 2 +- viewer/package.json | 1 + 4 files changed, 114 insertions(+), 125 deletions(-) diff --git a/viewer/components/DirectWidget.jsx b/viewer/components/DirectWidget.jsx index 9dcf17fa3..dec8505dd 100644 --- a/viewer/components/DirectWidget.jsx +++ b/viewer/components/DirectWidget.jsx @@ -7,9 +7,39 @@ import PropTypes from 'prop-types'; import Helper from '../util/helper.js'; import Value from './Value.jsx'; import GuiHelper from '../util/GuiHelpers.js'; -import assign from 'object-assign'; import {useAvNavSortable} from "../hoc/Sortable"; +const RenderFunction=(props)=>{ + const sortableProps=useAvNavSortable(props.dragId) + let classes="widget "; + if (props.isAverage) classes+=" average"; + if (props.className) classes+=" "+props.className; + let val; + let vdef=props.default||'0'; + if (props.value !== undefined) { + val=props.formatter?props.formatter(props.value):vdef+""; + } + else{ + if (! isNaN(vdef) && props.formatter) val=props.formatter(vdef); + else val=vdef+""; + } + const style={...props.style,...sortableProps.style}; + return ( +
+
+
+ +
+
+
{props.caption}
+ {props.unit !== undefined? +
{props.unit}
+ :
+ } +
+ ); +} + class DirectWidget extends React.Component{ constructor(props){ super(props); @@ -25,39 +55,12 @@ class DirectWidget extends React.Component{ return props; } else{ - return this.props.translateFunction(assign({},props)); + return this.props.translateFunction({...props}); } } - render(){ - const sortableProps=useAvNavSortable(this.props.dragId) - let classes="widget "; - let props=this.getProps(this.props); - if (props.isAverage) classes+=" average"; - if (props.className) classes+=" "+props.className; - let val; - let vdef=props.default||'0'; - if (props.value !== undefined) { - val=this.props.formatter?this.props.formatter(props.value):vdef+""; - } - else{ - if (! isNaN(vdef) && this.props.formatter) val=this.props.formatter(vdef); - else val=vdef+""; - } - const style={...props.style,...sortableProps.style}; - return ( -
-
-
- -
-
-
{props.caption}
- {this.props.unit !== undefined? -
{props.unit}
- :
- } -
- ); + render() { + let props = this.getProps(this.props); + return ; } }; diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 741ec1c1e..878a03ba0 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -13,10 +13,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - DndContext, - closestCenter + DndContext } from '@dnd-kit/core'; -import {useSortable} from "@dnd-kit/sortable"; +import {SortableContext} from "@dnd-kit/sortable"; const getKey=function(obj){ let rt=obj.key; @@ -25,29 +24,8 @@ const getKey=function(obj){ return rt; }; -const SortableItem=(props)=>{ - const {ItemClass,id,...fwProps}=props; - const { - attributes, - listeners, - setNodeRef, - transform, - transition, - } = useSortable({id: id}); - - const style = { - transform: "",//CSS.Transform.toString(transform), - transition, - }; - const nodeRef=(el)=>{ - setNodeRef(el); - } - return -} const Content=(props)=>{ - let idx = 0; - let existingKeys={}; return (
{ }} > {props.allitems.map(function (entry) { - let itemProps ={...entry}; - let key = getKey(entry); - //we allow for multiple items with the same name - //we try at most 20 times to get a unique key by appending _idx - let tries=20; - while (tries > 0 && (! key || existingKeys[key])){ - key+="_"+idx; - tries--; - } - itemProps.index=props.reverse?props.allitems.length-idx:idx; - itemProps.key = key; - existingKeys[key]=true; - if (props.selectedIndex !== undefined){ - if (idx == props.selectedIndex) { - itemProps.selected = true; - } - else { - itemProps.selected = false; - } - } + const itemProps={...entry}; let ItemClass; if (props.itemCreator) { ItemClass = props.itemCreator(entry); @@ -94,72 +53,98 @@ const Content=(props)=>{ if (data && data.preventDefault) data.preventDefault(); if (props.reverse){ let len=props.itemList?props.itemList.length:0; - props.onItemClick({...itemProps,index:len-iprops.index},data); + props.onItemClick({...itemProps,index:len-itemProps.index},data); } else { props.onItemClick(itemProps, data); } } } - idx++; - if (props.dragdrop) { - return - } - return + return })}
); }; -const ItemList=(props)=>{ - const itemList=props.itemList; - /* - onSortEnd(data){ - let len=this.props.itemList?this.props.itemList.length:0; - if (this.props.reverse) { - if (this.props.onSortEnd) this.props.onSortEnd(len-data.oldIndex,len- data.newIndex); +const ItemList = (props) => { + const itemList = []; + const sortableIds = []; + const existingKeys = {}; + let idx = 0; + const allitems = props.itemList || []; + allitems.forEach((entry) => { + const itemProps = {...entry}; + let key = getKey(entry); + //we allow for multiple items with the same name + //we try at most 20 times to get a unique key by appending _idx + let tries = 20; + while (tries > 0 && (!key || existingKeys[key])) { + key += "_" + idx; + tries--; } - else{ - if (this.props.onSortEnd) this.props.onSortEnd(data.oldIndex, data.newIndex); + itemProps.index = props.reverse ? allitems.length - idx : idx; + itemProps.key = key; + if (props.dragdrop) { + itemProps.dragId = idx+""; + sortableIds.push(itemProps.dragId); } - - } - */ - if (props.hideOnEmpty && itemList.length < 1) return null; - let className = "listContainer"; - if (props.scrollable) className+=" scrollable"; - if (props.className) className += " " + props.className; - if (props.horizontal) className += " horizontal"; - let style=props.style||{}; - if (props.fontSize){ - style.fontSize=props.fontSize; + existingKeys[key] = true; + if (props.selectedIndex !== undefined) { + if (idx == props.selectedIndex) { + itemProps.selected = true; + } else { + itemProps.selected = false; + } } + idx++; + itemList.push(itemProps); - let dragProps={}; - /*let Content=this.Content; - if (this.props.dragdrop){ - Content= SortableContainer(Content); - dragProps.axis=self.props.horizontal?"x":"y"; - dragProps.distance=20; - dragProps.onSortEnd=self.onSortEnd; - dragProps.helperClass="sortableHelper"; + }) + if (props.hideOnEmpty && itemList.length < 1) return null; + let className = "listContainer"; + if (props.scrollable) className += " scrollable"; + if (props.className) className += " " + props.className; + if (props.horizontal) className += " horizontal"; + let style = props.style || {}; + if (props.fontSize) { + style.fontSize = props.fontSize; + } + const handleDragEnd=({active,over})=>{ + if (props.onSortEnd){ + props.onSortEnd(active.id,over.id); } - */ - if (props.scrollable) { - return ( -
{if (props.listRef) props.listRef(el)}}> - -
- ); - } - else { - return( - - ); - } - + } + const SortableContent = + (sprops) => { + const {sortableIds, ...fwProps} = sprops; + if (props.dragdrop) { + return ( + + + + + + ) + } else { + return + } + }; + if (props.scrollable) { + return ( +
{ + if (props.listRef) props.listRef(el) + }}> + +
+ ); + } else { + return ( + + ); + } } ItemList.propTypes={ diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 34d1db445..cc4c678b8 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -40,7 +40,7 @@ export const useAvNavSortable=(id,ref)=>{ }; const setRef=(e)=>{ if (typeof(ref) === 'function') ref(e); - setRef(e); + setNodeRef(e); } return {ref:setRef,style:nstyle,...attributes,...listeners}; } diff --git a/viewer/package.json b/viewer/package.json index 33f25d26c..d2557384b 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -60,6 +60,7 @@ "@braintree/browser-detection": "^2.0.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", "@openlayers/pepjs": "^0.5.4", "canvas-gauges": "git+https://github.com/wellenvogel/canvas-gauges#av20220223", "clone-deep": "^4.0.1", From 85ae52424d043bc8b0e4f48f7c79188d8d7eeaae Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 14 Jul 2024 18:09:08 +0200 Subject: [PATCH 016/138] dnd working for direct widget --- viewer/components/ItemList.jsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 878a03ba0..31334182a 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -13,7 +13,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - DndContext + DndContext, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors } from '@dnd-kit/core'; import {SortableContext} from "@dnd-kit/sortable"; @@ -115,13 +115,27 @@ const ItemList = (props) => { props.onSortEnd(active.id,over.id); } } + const mouseSensor = useSensor(MouseSensor,{ + activationConstraint:{ + distance: 10 + } }); + const touchSensor = useSensor(TouchSensor,{ + activationConstraint: { + distance: 10, + }}); + const keyboardSensor = useSensor(KeyboardSensor); + const sensors = useSensors( + mouseSensor, + touchSensor, + keyboardSensor, + ); const SortableContent = (sprops) => { const {sortableIds, ...fwProps} = sprops; if (props.dragdrop) { return ( - + From b561d38f5cc7752337ccb138a24ae7137f5d745a Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 14 Jul 2024 20:21:10 +0200 Subject: [PATCH 017/138] intermediate: refactor widget to functional components --- viewer/components/ActiveRouteWidget.jsx | 48 +++--- viewer/components/AisTargetWidget.jsx | 121 +++++++-------- viewer/components/AlarmWidget.jsx | 59 ++++---- viewer/components/CenterDisplayWidget.jsx | 104 +++++++------ viewer/components/DateTimeWidget.jsx | 36 ++--- viewer/components/DirectWidget.jsx | 32 +--- viewer/components/EditRouteWidget.jsx | 83 +++++------ viewer/components/EmptyWidget.jsx | 18 ++- viewer/components/EtaWidget.jsx | 32 ++-- viewer/components/RoutePointsWidget.jsx | 105 +++++++------ viewer/components/TimeStatusWidget.jsx | 49 +++---- viewer/components/WindGraphics.jsx | 170 +++++++++++----------- viewer/components/WindWidget.jsx | 148 +++++++++---------- viewer/components/XteWidget.jsx | 160 ++++++++++---------- viewer/components/ZoomWidget.jsx | 42 +++--- viewer/hoc/Dynamic.jsx | 5 + viewer/util/GuiHelpers.js | 17 +++ 17 files changed, 572 insertions(+), 657 deletions(-) diff --git a/viewer/components/ActiveRouteWidget.jsx b/viewer/components/ActiveRouteWidget.jsx index 5ad3e61ca..d1efbd694 100644 --- a/viewer/components/ActiveRouteWidget.jsx +++ b/viewer/components/ActiveRouteWidget.jsx @@ -3,45 +3,34 @@ */ import React from "react"; -import compare from '../util/compare'; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; import Formatter from '../util/formatter.js'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class ActiveRouteWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - shouldComponentUpdate(nextProps,nextState){ - return Helper.compareProperties(this.props,nextProps,ActiveRouteWidget.storeKeys); - } - componentDidUpdate(){ - - } - - render() { - if (!this.props.routeName && ! this.props.isEditing) return null; - let self = this; - let classes = "widget activeRouteWidget " + this.props.className || ""; - if (this.props.isApproaching) classes += " approach "; +const ActiveRouteWidget =(props)=>{ + useKeyEventHandler(props,"widget"); + const ddProps=useAvNavSortable(props.dragId); + if (!props.routeName && ! props.isEditing) return null; + let classes = "widget activeRouteWidget " + props.className || ""; + if (props.isApproaching) classes += " approach "; + const style={...props.style,...ddProps.style}; return ( -
+
RTE
-
{this.props.routeName}
+
{props.routeName}
- {Formatter.formatDistance(this.props.remain)} + {Formatter.formatDistance(props.remain)} nm
-
{Formatter.formatTime(this.props.eta)}
- { this.props.isApproaching ? +
{Formatter.formatTime(props.eta)}
+ { props.isApproaching ?
{Formatter.formatDirection(this.props.nextCourse)} + className="routeNextCourse">{Formatter.formatDirection(props.nextCourse)} °
:
@@ -51,7 +40,6 @@ class ActiveRouteWidget extends React.Component{ ); } -} ActiveRouteWidget.propTypes={ //formatter: React.PropTypes.func, @@ -62,7 +50,11 @@ ActiveRouteWidget.propTypes={ routeName: PropTypes.string, eta: PropTypes.objectOf(Date), remain: PropTypes.number, - nextCourse: PropTypes.number + nextCourse: PropTypes.number, + dragId: PropTypes.string, + isEditing: PropTypes.bool, + isApproaching: PropTypes.bool, + style: PropTypes.object }; ActiveRouteWidget.storeKeys={ diff --git a/viewer/components/AisTargetWidget.jsx b/viewer/components/AisTargetWidget.jsx index c8f20db36..52ff7dd17 100644 --- a/viewer/components/AisTargetWidget.jsx +++ b/viewer/components/AisTargetWidget.jsx @@ -4,101 +4,92 @@ import React from "react"; import PropTypes from 'prop-types'; -import compare from '../util/compare'; import keys from '../util/keys.jsx'; -import Formatter from '../util/formatter.js'; import PropertyHandler from '../util/propertyhandler.js'; import AisFormatter from '../nav/aisformatter.jsx'; -import assign from 'object-assign'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class AisTargetWidget extends React.Component{ - constructor(props){ - super(props); - this.click=this.click.bind(this); - GuiHelper.nameKeyEventHandler(this,"widget",this.click); - } - shouldComponentUpdate(nextProps,nextState){ - return ! compare(this.props.current,nextProps.current); +const AisTargetWidget = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + const click = (ev) => { + if (ev.stopPropagation) ev.stopPropagation(); + props.onClick({...props, mmsi: props.current ? props.current.mmsi : undefined}); } - componentDidUpdate(){ + let current = props.current || {}; + let classes = "widget aisTargetWidget " + props.className || ""; + let small = (props.mode === "horizontal"); + let aisProperties = {}; + let color = undefined; + if (current.mmsi && current.mmsi !== "") { + aisProperties.warning = current.warning || false; + aisProperties.nearest = current.nearest || false; + aisProperties.tracking = (current.mmsi === props.trackedMmsi); + color = PropertyHandler.getAisColor(aisProperties); } - render(){ - let current=this.props.current||{}; - let self=this; - let classes="widget aisTargetWidget "+this.props.className||""; - let small = (this.props.mode === "horizontal" ); - let aisProperties={}; - let color=undefined; - if (current.mmsi && current.mmsi !== "") { - aisProperties.warning = current.warning || false; - aisProperties.nearest = current.nearest || false; - aisProperties.tracking = (current.mmsi === this.props.trackedMmsi); - color=PropertyHandler.getAisColor(aisProperties); - } - let front=AisFormatter.format('passFront',current); - if (current.mmsi !== undefined || this.props.mode === "gps" || this.props.isEditing) { - let style=assign({},this.props.style,{backgroundColor:color}); - return ( + let front = AisFormatter.format('passFront', current); + if (current.mmsi !== undefined || props.mode === "gps" || props.isEditing) { + const style = {...props.style, ...ddProps.style, backgroundColor: color}; + return ( -
-
AIS
-
- { !small &&
- D - {AisFormatter.format('distance', current)} - nm -
} - { !small &&
- C - {AisFormatter.format('cpa', current)} - nm -
} -
-
- {current.mmsi !== undefined && +
+
AIS
+
+ {!small &&
+ D + {AisFormatter.format('distance', current)} + nm +
} + {!small &&
+ C + {AisFormatter.format('cpa', current)} + nm +
} +
+
+ {current.mmsi !== undefined &&
T {AisFormatter.format('tcpa', current)} h
- } - {current.mmsi !== undefined && + } + {current.mmsi !== undefined &&
{front}
- } -
+ }
- ); - } - else{ - return null; - } - - } - click(ev){ - if (ev.stopPropagation) ev.stopPropagation(); - this.props.onClick(assign({},this.props,{mmsi:this.props.current?this.props.current.mmsi:undefined})); +
+ ); + } else { + return null; } } -AisTargetWidget.storeKeys={ +AisTargetWidget.storeKeys = { current: keys.nav.ais.nearest, isEditing: keys.gui.global.layoutEditing, trackedMmsi: keys.nav.ais.trackedMmsi }; -AisTargetWidget.propTypes={ +AisTargetWidget.propTypes = { //formatter: React.PropTypes.func, onClick: PropTypes.func, className: PropTypes.string, current: PropTypes.object, - mode: PropTypes.string + mode: PropTypes.string, + dragId: PropTypes.string, + trackedMmsi: PropTypes.string, + isEditing: PropTypes.bool, + style: PropTypes.object }; export default AisTargetWidget; \ No newline at end of file diff --git a/viewer/components/AlarmWidget.jsx b/viewer/components/AlarmWidget.jsx index f66a7eb88..c10df9279 100644 --- a/viewer/components/AlarmWidget.jsx +++ b/viewer/components/AlarmWidget.jsx @@ -5,34 +5,28 @@ import React from "react"; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; -import compare from '../util/compare.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; import AlarmHandler from '../nav/alarmhandler.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class AlarmWidget extends React.Component{ - constructor(props){ - super(props); - this.onClick=this.onClick.bind(this); - let self=this; - GuiHelper.keyEventHandler(this,(component,action)=>{ - if (action == 'stop'){ - if (self.props.onClick) self.props.onClick(); - } - },"alarm",["stop"]) - } - componentDidMount(){ - - } - shouldComponentUpdate(nextProps,nextState){ - return ! AlarmHandler.compareAlarms(nextProps.alarmInfo,this.props.alarmInfo); +//TODO: compare alarm info correctly +const AlarmWidget=(props)=>{ + useKeyEventHandler({name:'stop'},"alarm",()=>{ + if (props.onClick) props.onClick(); + }) + const onClick=(ev)=>{ + if (props.onClick){ + props.onClick(ev); + } + ev.stopPropagation(); } - render(){ - if (this.props.disabled) return null; - let classes="widget alarmWidget "+this.props.className||""; + const ddProps=useAvNavSortable(props.dragId); + if (props.disabled) return null; + let classes="widget alarmWidget "+props.className||""; let alarmText=undefined; - if (this.props.alarmInfo){ - let list=AlarmHandler.sortedActiveAlarms(this.props.alarmInfo) + if (props.alarmInfo){ + let list=AlarmHandler.sortedActiveAlarms(props.alarmInfo) list.forEach((al)=>{ if (alarmText){ alarmText+=","+al.name; @@ -42,14 +36,15 @@ class AlarmWidget extends React.Component{ } }) } + const style={...props.style,...ddProps.style}; if (! alarmText) { - if (! this.props.isEditing || ! this.props.mode) return null; - return
+ if (! props.isEditing || ! props.mode) return null; + return
Alarm
; } return ( -
+
Alarm
{alarmText} @@ -57,21 +52,17 @@ class AlarmWidget extends React.Component{
); } - onClick(ev){ - if (this.props.onClick){ - this.props.onClick(ev); - } - ev.stopPropagation(); - } -} AlarmWidget.propTypes={ className: PropTypes.string, onClick: PropTypes.func, alarmInfo: PropTypes.object, isEditing: PropTypes.bool, - style: PropTypes.object + style: PropTypes.object, + dragId: PropTypes.string, + disabled: PropTypes.bool, + mode: PropTypes.string }; AlarmWidget.storeKeys={ diff --git a/viewer/components/CenterDisplayWidget.jsx b/viewer/components/CenterDisplayWidget.jsx index d0058ddce..c3e1c1629 100644 --- a/viewer/components/CenterDisplayWidget.jsx +++ b/viewer/components/CenterDisplayWidget.jsx @@ -6,33 +6,27 @@ import React from "react"; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; import Formatter from '../util/formatter.js'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; import NavCompute from "../nav/navcompute"; +import {useAvNavSortable} from "../hoc/Sortable"; -class CenterDisplayWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); +const CenterDisplayWidget = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + let classes = "widget centerDisplayWidget " + props.className || ""; + let small = (props.mode == "horizontal"); + let measurePosition = props.measurePosition; + let measureValues; + if (measurePosition) { + measureValues = NavCompute.computeDistance(measurePosition, props.centerPosition, props.measureRhumbLine); } - shouldComponentUpdate(nextProps,nextState) { - return Helper.compareProperties(this.props,nextProps,CenterDisplayWidget.storeKeys); - } - - render() { - let classes = "widget centerDisplayWidget " + this.props.className || ""; - let small = (this.props.mode == "horizontal"); - let measurePosition=this.props.measurePosition; - let measureValues; - if (measurePosition) { - measureValues = NavCompute.computeDistance(measurePosition,this.props.centerPosition,this.props.measureRhumbLine); - } - return ( -
-
Center
- { !small &&
{Formatter.formatLonLats(this.props.centerPosition)}
} - {(measurePosition !== undefined) && + const style = {...props.style, ...ddProps.style}; + return ( +
+
Center
+ {!small &&
{Formatter.formatLonLats(props.centerPosition)}
} + {(measurePosition !== undefined) &&
@@ -47,42 +41,41 @@ class CenterDisplayWidget extends React.Component{ nm
- } -
-
-
- {Formatter.formatDirection(this.props.markerCourse)} - ° -
-
- / -
-
- {Formatter.formatDistance(this.props.markerDistance)} - nm -
+ } +
+
+
+ {Formatter.formatDirection(props.markerCourse)} + °
-
-
-
- {Formatter.formatDirection(this.props.centerCourse)} - ° -
-
- / -
-
- {Formatter.formatDistance(this.props.centerDistance)} - nm - -
+
+ / +
+
+ {Formatter.formatDistance(props.markerDistance)} + nm
- ); - } +
+
+
+ {Formatter.formatDirection(props.centerCourse)} + ° +
+
+ / +
+
+ {Formatter.formatDistance(props.centerDistance)} + nm +
+
+
+ ); } + CenterDisplayWidget.storeKeys={ markerCourse:keys.nav.center.markerCourse, markerDistance:keys.nav.center.markerDistance, @@ -102,6 +95,9 @@ CenterDisplayWidget.propTypes={ centerDistance:PropTypes.number, centerPosition: PropTypes.object, measurePosition: PropTypes.object, - measureRhumbLine: PropTypes.bool + measureRhumbLine: PropTypes.bool, + dragId: PropTypes.string, + style: PropTypes.object, + mode: PropTypes.string }; export default CenterDisplayWidget; \ No newline at end of file diff --git a/viewer/components/DateTimeWidget.jsx b/viewer/components/DateTimeWidget.jsx index 2ca1abe91..8c40d3f1c 100644 --- a/viewer/components/DateTimeWidget.jsx +++ b/viewer/components/DateTimeWidget.jsx @@ -6,30 +6,24 @@ import React from "react"; import PropTypes from "prop-types"; import keys from "../util/keys.jsx"; import Formatter from "../util/formatter.js"; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class DateTimeWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - shouldComponentUpdate(nextProps,nextState) { - return Helper.compareProperties(this.props,nextProps,DateTimeWidget.storeKeys); - } - render(){ - let self=this; - let classes="widget dateTimeWidget "+this.props.className||""; +const DateTimeWidget=(props)=>{ + useKeyEventHandler(props,"widget"); + const ddProps=useAvNavSortable(props.dragId); + let classes="widget dateTimeWidget "+props.className||""; let time="----"; - if (this.props.time){ - time=Formatter.formatTime(this.props.time); + if (props.time){ + time=Formatter.formatTime(props.time); } let date="----"; - if (this.props.time){ - date=Formatter.formatDate(this.props.time); + if (props.time){ + date=Formatter.formatDate(props.time); } + const style={...props.style,...ddProps.style}; return ( -
+
Date
{date}
@@ -39,13 +33,13 @@ class DateTimeWidget extends React.Component{ ); } -}; - DateTimeWidget.propTypes={ onClick: PropTypes.func, className: PropTypes.string, time: PropTypes.objectOf(Date), - gpsValid: PropTypes.bool + gpsValid: PropTypes.bool, + style: PropTypes.object, + dragId: PropTypes.string }; DateTimeWidget.storeKeys={ time: keys.nav.gps.rtime, diff --git a/viewer/components/DirectWidget.jsx b/viewer/components/DirectWidget.jsx index dec8505dd..a2ef4a86a 100644 --- a/viewer/components/DirectWidget.jsx +++ b/viewer/components/DirectWidget.jsx @@ -4,12 +4,13 @@ import React from "react"; import PropTypes from 'prop-types'; -import Helper from '../util/helper.js'; import Value from './Value.jsx'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; import {useAvNavSortable} from "../hoc/Sortable"; -const RenderFunction=(props)=>{ +const DirectWidget=(wprops)=>{ + const props=wprops.translateFunction?wprops.translateFunction(wprops):wprops; + useKeyEventHandler(wprops,"widget"); const sortableProps=useAvNavSortable(props.dragId) let classes="widget "; if (props.isAverage) classes+=" average"; @@ -40,30 +41,6 @@ const RenderFunction=(props)=>{ ); } -class DirectWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - this.getProps=this.getProps.bind(this); - } - shouldComponentUpdate(nextProps,nextState) { - return Helper.compareProperties(this.getProps(this.props), - this.getProps(nextProps),{value:1,isAverage:1}); - } - getProps(props){ - if (! this.props.translateFunction){ - return props; - } - else{ - return this.props.translateFunction({...props}); - } - } - render() { - let props = this.getProps(this.props); - return ; - } -}; - DirectWidget.propTypes={ name: PropTypes.string, unit: PropTypes.string, @@ -78,7 +55,6 @@ DirectWidget.propTypes={ translateFunction: PropTypes.func, dragId: PropTypes.string }; - DirectWidget.editableParameters={ caption:true, unit:true, diff --git a/viewer/components/EditRouteWidget.jsx b/viewer/components/EditRouteWidget.jsx index 588b2f745..4a4ecdc9c 100644 --- a/viewer/components/EditRouteWidget.jsx +++ b/viewer/components/EditRouteWidget.jsx @@ -6,68 +6,61 @@ import React from "react"; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; import Formatter from '../util/formatter.js' -import Helper from '../util/helper.js'; import routeobjects from '../nav/routeobjects.js'; import RouteEdit,{StateHelper} from '../nav/routeeditor.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; const editor=new RouteEdit(RouteEdit.MODES.EDIT); -class EditRouteWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - - shouldComponentUpdate(nextProps,nextState){ - return Helper.compareProperties(this.props,nextProps, EditRouteWidget.storeKeys); - } - render(){ - let [route,notUsed,isActive]=StateHelper.getRouteIndexFlag(this.props); - let classes="widget editRouteWidget "+this.props.className||""; - if (isActive) classes +=" activeRoute "; - if (!route){ - return ( -
-
RTE
-
No Route
-
- ) - } - let rname=route.name; - let numPoints=route.points.length; - let len=route.computeLength(0,this.props.useRhumbLine); - let remain=isActive?this.props.remain:undefined; - let eta=isActive?this.props.eta:undefined; +const EditRouteWidget = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + let [route, notUsed, isActive] = StateHelper.getRouteIndexFlag(props); + let classes = "widget editRouteWidget " + props.className || ""; + if (isActive) classes += " activeRoute "; + if (!route) { return ( -
+
+
RTE
+
No Route
+
+ ) + } + let rname = route.name; + let numPoints = route.points.length; + let len = route.computeLength(0, props.useRhumbLine); + let remain = isActive ? props.remain : undefined; + let eta = isActive ? props.eta : undefined; + const style = {...props.style, ...ddProps.style}; + return ( +
RTE
{rname}
PTS: - {Formatter.formatDecimal(numPoints,3)} + {Formatter.formatDecimal(numPoints, 3)}
DST: {Formatter.formatDistance(len)}
- { this.props.mode !== "horizontal"? -
- RTG: - {Formatter.formatDistance(remain)} -
:null} - { this.props.mode !== "horizontal"? -
- ETA: - {Formatter.formatTime(eta)} -
:null} + {props.mode !== "horizontal" ? +
+ RTG: + {Formatter.formatDistance(remain)} +
: null} + {props.mode !== "horizontal" ? +
+ ETA: + {Formatter.formatTime(eta)} +
: null}
- ); - } - + ); } + EditRouteWidget.propTypes={ onClick:PropTypes.func, className:PropTypes.string, @@ -77,7 +70,9 @@ EditRouteWidget.propTypes={ eta: PropTypes.objectOf(Date), isApproaching: PropTypes.bool, isActive: PropTypes.bool, - useRhumbLine: PropTypes.bool + useRhumbLine: PropTypes.bool, + dragId: PropTypes.string, + style: PropTypes.object }; EditRouteWidget.storeKeys=editor.getStoreKeys({ diff --git a/viewer/components/EmptyWidget.jsx b/viewer/components/EmptyWidget.jsx index ede181c8f..df7d801f6 100644 --- a/viewer/components/EmptyWidget.jsx +++ b/viewer/components/EmptyWidget.jsx @@ -4,22 +4,24 @@ import React from "react"; import PropTypes from 'prop-types'; +import {useAvNavSortable} from "../hoc/Sortable"; -class EmptyWidget extends React.Component{ - render(){ - let classes="widget "+this.props.classes||""; - if (this.props.className) classes+=" "+this.props.className; - let style=this.props.style||{}; +const EmptyWidget =(props)=>{ + const ddProps=useAvNavSortable(props.dragId); + let classes="widget "+props.classes||""; + if (props.className) classes+=" "+props.className; + const style={...props.style,...ddProps.style}; return ( -
+
); } -} - EmptyWidget.propTypes={ onClick: PropTypes.func, + className: PropTypes.string, + dragId: PropTypes.string, + style: PropTypes.object, classes: PropTypes.string }; diff --git a/viewer/components/EtaWidget.jsx b/viewer/components/EtaWidget.jsx index a08794c40..0413e2fa4 100644 --- a/viewer/components/EtaWidget.jsx +++ b/viewer/components/EtaWidget.jsx @@ -7,32 +7,24 @@ import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; import Formatter from '../util/formatter.js'; import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import GuiHelper, {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class EtaWidget extends React.Component{ - - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - - shouldComponentUpdate(nextProps,nextState) { - return Helper.compareProperties(this.props,nextProps,EtaWidget.storeKeys); - } - render(){ - let eta=this.props.eta?Formatter.formatTime(this.props.eta):'--:--:--'; - let classes="widget etaWidget "+this.props.className||""; +const EtaWidget =(props)=>{ + useKeyEventHandler(props,"widget"); + const ddProps=useAvNavSortable(props.dragId); + let eta=props.eta?Formatter.formatTime(props.eta):'--:--:--'; + let classes="widget etaWidget "+props.className||""; + const style={...props.style,...ddProps.style}; return ( -
-
{this.props.caption}
+
+
{props.caption}
{eta}
-
{this.props.wpname}
+
{props.wpname}
); - } - -}; + }; EtaWidget.propTypes={ onClick: PropTypes.func, diff --git a/viewer/components/RoutePointsWidget.jsx b/viewer/components/RoutePointsWidget.jsx index cf3e56e37..bf8f91a7c 100644 --- a/viewer/components/RoutePointsWidget.jsx +++ b/viewer/components/RoutePointsWidget.jsx @@ -2,17 +2,15 @@ * Created by andreas on 23.02.16. */ -import React from "react"; +import React, {useEffect} from "react"; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; -import Formatter from '../util/formatter.js' -import Helper from '../util/helper.js'; import routeobjects from '../nav/routeobjects.js'; import ItemList from './ItemList.jsx'; import WaypointItem from './WayPointItem.jsx'; -import assign from 'object-assign'; import RouteEdit,{StateHelper} from '../nav/routeeditor.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import GuiHelper, {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; const editor=new RouteEdit(RouteEdit.MODES.EDIT); @@ -21,63 +19,64 @@ const RoutePoint=(showLL)=>{ return } }; - -class RoutePointsWidget extends React.Component{ - constructor(props){ - super(props); - this.scrollSelected=this.scrollSelected.bind(this); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - - shouldComponentUpdate(nextProps,nextState){ +//TODO: compare! +const compareFunction=(prev,current)=>{ for (let k in RoutePointsWidget.propTypes){ if (k == 'route') continue; - if (nextProps[k] !== this.props[k]) return true; + if (prev[k] !== current[k]) return true; } - if (!nextProps.route != !this.props.route) return true; - if (!nextProps.route) return false; - return nextProps.route.differsTo(this.props.route); - } - scrollSelected(){ - if (! this.listRef) return; - let el=this.listRef.querySelector('.activeEntry'); + if (!prev.route != !current.route) return true; + if (!current.route) return false; + return current.route.differsTo(prev.route); +} +const RoutePointsWidget = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + let listRef = undefined; + const scrollSelected = () => { + if (!listRef) return; + let el = listRef.querySelector('.activeEntry'); if (el) { - let mode=GuiHelper.scrollInContainer(this.listRef,el); + let mode = GuiHelper.scrollInContainer(listRef, el); if (mode < 1 || mode > 2) return; - el.scrollIntoView(mode==1); + el.scrollIntoView(mode == 1); } } - componentDidMount(){ - this.scrollSelected(); - } - componentDidUpdate(){ - this.scrollSelected(); - } - render(){ - let self=this; - let [route,index,isActive]=StateHelper.getRouteIndexFlag(this.props); - if ((! route || !route.points || route.points.length < 1) && ! this.props.isEditing) return null; - let classes="widget routePointsWidget "+this.props.className||""; - if (isActive) classes +=" activeRoute "; - if (this.props.mode == 'horizontal' && ! this.props.isEditing) return null; //we do not display... - return ( - {return RoutePoint(this.props.showLatLon)}} - scrollable={true} - onItemClick={(item,data)=>{if (self.props.onClick) - self.props.onClick(new routeobjects.RoutePoint(item)) }} - onClick={(ev)=>{if (self.props.isEditing && self.props.onClick){ - self.props.onClick(ev); - }}} - listRef={(element)=>{self.listRef=element}} - /> - ); - } - + useEffect(() => { + scrollSelected(); + }); + let [route, index, isActive] = StateHelper.getRouteIndexFlag(props); + if ((!route || !route.points || route.points.length < 1) && !props.isEditing) return null; + let classes = "widget routePointsWidget " + props.className || ""; + if (isActive) classes += " activeRoute "; + if (props.mode == 'horizontal' && !props.isEditing) return null; //we do not display... + const style = {...props.style, ...ddProps.style}; + return ( +
+ { + return RoutePoint(props.showLatLon) + }} + scrollable={true} + onItemClick={(item, data) => { + if (props.onClick) + props.onClick(new routeobjects.RoutePoint(item)) + }} + onClick={(ev) => { + if (props.isEditing && props.onClick) { + props.onClick(ev); + } + }} + listRef={(element) => { + listRef = element + }} + /> +
+ ); } + RoutePointsWidget.propTypes={ onClick: PropTypes.func, className: PropTypes.string, diff --git a/viewer/components/TimeStatusWidget.jsx b/viewer/components/TimeStatusWidget.jsx index 86766768d..9af4b5d91 100644 --- a/viewer/components/TimeStatusWidget.jsx +++ b/viewer/components/TimeStatusWidget.jsx @@ -7,43 +7,38 @@ import PropTypes from "prop-types"; import keys from "../util/keys.jsx"; import Formatter from "../util/formatter.js"; import globalStore from '../util/globalstore.jsx'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class TimeStatusWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); +const TimeStatusWidget = (props=> { + useKeyEventHandler(props,"widget"); + const ddProps=useAvNavSortable(props.dragId); + let classes="widget timeStatusWidget "+props.className||""; + let imgSrc=globalStore.getData(props.gpsValid? + keys.properties.statusOkImage: + keys.properties.statusErrorImage); + let time="----"; + if (props.time !== undefined){ + time=Formatter.formatTime(props.time); } - shouldComponentUpdate(nextProps,nextState) { - return Helper.compareProperties(this.props,nextProps,TimeStatusWidget.storeKeys); - } - render(){ - let self=this; - let classes="widget timeStatusWidget "+this.props.className||""; - let imgSrc=globalStore.getData(this.props.gpsValid? - keys.properties.statusOkImage: - keys.properties.statusErrorImage); - let time="----"; - if (this.props.time !== undefined){ - time=Formatter.formatTime(this.props.time); - } - return ( -
-
{this.props.caption}
+ const style={...props.style,...ddProps.style}; + return ( +
+
{props.caption}
{time}
- ); - } - -}; + ); +}); TimeStatusWidget.propTypes={ onClick: PropTypes.func, className: PropTypes.string, time: PropTypes.objectOf(Date), - gpsValid: PropTypes.bool + gpsValid: PropTypes.bool, + style: PropTypes.object, + caption: PropTypes.string, + dragId: PropTypes.string }; TimeStatusWidget.storeKeys={ time: keys.nav.gps.rtime, diff --git a/viewer/components/WindGraphics.jsx b/viewer/components/WindGraphics.jsx index 01886c817..8482e6d1a 100644 --- a/viewer/components/WindGraphics.jsx +++ b/viewer/components/WindGraphics.jsx @@ -7,9 +7,9 @@ import PropTypes from 'prop-types'; import Formatter from '../util/formatter'; import keys from '../util/keys.jsx'; import navcompute from '../nav/navcompute.js'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; -import WindWidget, {getWindData} from "./WindWidget"; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {getWindData} from "./WindWidget"; +import {useAvNavSortable} from "../hoc/Sortable"; const normalColors={ green: 'rgba(5, 128, 30, 0.57)', @@ -27,76 +27,33 @@ const nightColors={ pointer: 'rgba(252, 11, 11, 0.6)', text: 'rgba(252, 11, 11, 0.6)' }; -class WindGraphics extends React.Component{ - constructor(props){ - super(props); - this.canvasRef=this.canvasRef.bind(this); - this.drawWind=this.drawWind.bind(this); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - shouldComponentUpdate(nextProps,nextState){ - return Helper.compareProperties(this.props,nextProps,WindGraphics.storeKeys); - } - getValues(){ - return getWindData(this.props); - } - render(){ - let self = this; - let classes = "widget windGraphics " + this.props.classes || ""+ " "+this.props.className||""; - let style = this.props.style || {}; - setTimeout(self.drawWind,0); - let current=this.getValues(); - let windSpeed=""; - let showKnots=this.props.showKnots; - try{ - windSpeed=parseFloat(current.windSpeed); - if (showKnots){ - windSpeed=windSpeed*3600/navcompute.NM; - } - if (windSpeed < 10) windSpeed=Formatter.formatDecimal(windSpeed,1,2); - else windSpeed=Formatter.formatDecimal(windSpeed,3,0); - }catch(e){} - return ( -
- -
Wind
-
{showKnots?"kn":"m/s"}
-
{windSpeed}
-
{current.suffix}
-
- - ); - - } - canvasRef(item){ - let self=this; - this.canvas=item; - setTimeout(self.drawWind,0); - } - drawWind(){ - let current=this.getValues(); - let colors=this.props.nightMode?nightColors:normalColors; - let canvas=this.canvas; - if (! canvas) return; - let ctx=canvas.getContext('2d'); +const WindGraphics = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + let canvas = undefined; + const drawWind = () => { + let current = getWindData(props); + let colors = props.nightMode ? nightColors : normalColors; + if (!canvas) return; + let ctx = canvas.getContext('2d'); // Set scale factor for all values - let crect=canvas.getBoundingClientRect(); - let w=crect.width; - let h=crect.height; - canvas.width=w; - canvas.height=h; + let crect = canvas.getBoundingClientRect(); + let w = crect.width; + let h = crect.height; + canvas.width = w; + canvas.height = h; let width = 200; // Control width let height = 200; // Control height - let f1=w/width; - let f2=h/height; - let f=Math.min(f1,f2); - let fontSize=f*height/5; - let mvx=(w-width*f)/2; - let mvy=(h-height*f)/2; - ctx.translate(mvx>0?0.9*mvx:0,mvy>0?mvy:0); //move the drawing to the middle - ctx.scale(f,f); - let scaleAngle=this.props.scaleAngle||50; - scaleAngle=parseFloat(scaleAngle); + let f1 = w / width; + let f2 = h / height; + let f = Math.min(f1, f2); + let fontSize = f * height / 5; + let mvx = (w - width * f) / 2; + let mvy = (h - height * f) / 2; + ctx.translate(mvx > 0 ? 0.9 * mvx : 0, mvy > 0 ? mvy : 0); //move the drawing to the middle + ctx.scale(f, f); + let scaleAngle = props.scaleAngle || 50; + scaleAngle = parseFloat(scaleAngle); // Settings @@ -118,9 +75,9 @@ class WindGraphics extends React.Component{ // Write inner circle in center position ctx.beginPath(); ctx.lineWidth = circle_linewidth; - ctx.arc(width / 2 ,height / 2,radius*0.97,0,2*Math.PI); + ctx.arc(width / 2, height / 2, radius * 0.97, 0, 2 * Math.PI); ctx.stroke(); - let start,end; + let start, end; if (current.suffix === 'A') { // Write left partial circle ctx.beginPath(); @@ -148,27 +105,27 @@ class WindGraphics extends React.Component{ ctx.stroke(); } // Write scale - for (let i = 0; i < 12; i++){ + for (let i = 0; i < 12; i++) { ctx.beginPath(); - ctx.strokeStyle =colors.scale; // dark gray + ctx.strokeStyle = colors.scale; // dark gray ctx.lineWidth = 10; - start = i*30-1; - end = i*30+1; - ctx.arc(width / 2 ,height / 2,radius*0.9,2*Math.PI/360*start,2*Math.PI/360*end); + start = i * 30 - 1; + end = i * 30 + 1; + ctx.arc(width / 2, height / 2, radius * 0.9, 2 * Math.PI / 360 * start, 2 * Math.PI / 360 * end); ctx.stroke(); } // Create text // Move the pointer from 0,0 to center position - ctx.translate(width / 2 ,height / 2); - ctx.font = fontSize+"px Arial"; - if (! this.props.show360 && current.suffix !== 'TD'){ - if (winddirection > 180) winddirection-=360; + ctx.translate(width / 2, height / 2); + ctx.font = fontSize + "px Arial"; + if (!props.show360 && current.suffix !== 'TD') { + if (winddirection > 180) winddirection -= 360; } - let txt=Formatter.formatDirection(winddirection).replace(/ /g,"0"); - let xFactor=-0.8; - if (winddirection < 0) xFactor=-1.0; - ctx.fillStyle=colors.text; - ctx.fillText(txt,xFactor*fontSize,0.4*fontSize); + let txt = Formatter.formatDirection(winddirection).replace(/ /g, "0"); + let xFactor = -0.8; + if (winddirection < 0) xFactor = -1.0; + ctx.fillStyle = colors.text; + ctx.fillText(txt, xFactor * fontSize, 0.4 * fontSize); // Rotate ctx.rotate(angle * Math.PI / 180); // Write pointer @@ -176,10 +133,41 @@ class WindGraphics extends React.Component{ ctx.lineWidth = pointer_linewidth; ctx.lineCap = 'round'; ctx.strokeStyle = colors.pointer; - ctx.moveTo(0,-40); - ctx.lineTo(0,-40-pointer_lenght); + ctx.moveTo(0, -40); + ctx.lineTo(0, -40 - pointer_lenght); ctx.stroke(); } + const canvasRef = (item) => { + canvas = item; + setTimeout(drawWind, 0); + } + + + let classes = "widget windGraphics " + props.classes || "" + " " + props.className || ""; + let style = {...props.style, ...ddProps.style}; + setTimeout(drawWind, 0); + let current = getWindData(props); + let windSpeed = ""; + let showKnots = props.showKnots; + try { + windSpeed = parseFloat(current.windSpeed); + if (showKnots) { + windSpeed = windSpeed * 3600 / navcompute.NM; + } + if (windSpeed < 10) windSpeed = Formatter.formatDecimal(windSpeed, 1, 2); + else windSpeed = Formatter.formatDecimal(windSpeed, 3, 0); + } catch (e) { + } + return ( +
+ +
Wind
+
{showKnots ? "kn" : "m/s"}
+
{windSpeed}
+
{current.suffix}
+
+ + ); } @@ -193,7 +181,11 @@ WindGraphics.propTypes={ showKnots: PropTypes.bool, scaleAngle: PropTypes.number, nightMode: PropTypes.bool, - kind: PropTypes.string //true,apparent,auto + kind: PropTypes.string, //true,apparent,auto, + className: PropTypes.string, + dragId: PropTypes.string, + style: PropTypes.object, + show360: PropTypes.bool }; WindGraphics.storeKeys={ windSpeed: keys.nav.gps.windSpeed, diff --git a/viewer/components/WindWidget.jsx b/viewer/components/WindWidget.jsx index b22bdf618..ed808db33 100644 --- a/viewer/components/WindWidget.jsx +++ b/viewer/components/WindWidget.jsx @@ -6,9 +6,9 @@ import React from "react"; import PropTypes from 'prop-types'; import Formatter from '../util/formatter'; import keys from '../util/keys.jsx'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; import navcompute from '../nav/navcompute.js'; +import {useAvNavSortable} from "../hoc/Sortable"; export const getWindData=(props)=>{ let kind = props.kind; @@ -57,87 +57,78 @@ export const getWindData=(props)=>{ } } -class WindWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - shouldComponentUpdate(nextProps,nextState){ - return Helper.compareProperties(this.props,nextProps,WindWidget.storeKeys); - } - render(){ - let wind=getWindData(this.props); - const names={ - A :{ - speed: 'AWS', - angle: 'AWA' - }, - TD: { - speed: 'TWS', - angle: 'TWD' - }, - TA:{ - speed: 'TWS', - angle: 'TWA' - } +const WindWidget = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + let wind = getWindData(props); + const names = { + A: { + speed: 'AWS', + angle: 'AWA' + }, + TD: { + speed: 'TWS', + angle: 'TWD' + }, + TA: { + speed: 'TWS', + angle: 'TWA' } - let classes = "widget windWidget " +this.props.className||""; - let style = this.props.style || {}; - let windSpeedStr=''; - try{ - windSpeedStr=parseFloat(wind.windSpeed); - if (isNaN(windSpeedStr)){ - windSpeedStr="---" - } - else { - if (this.props.showKnots) { - let nm = navcompute.NM; - windSpeedStr = windSpeedStr * 3600 / nm; - } - if (windSpeedStr < 10) windSpeedStr = Formatter.formatDecimal(windSpeedStr, 2, 1); - else windSpeedStr = Formatter.formatDecimal(windSpeedStr, 3, 0); + } + let classes = "widget windWidget " + props.className || ""; + let style = {...props.style, ...ddProps.style}; + let windSpeedStr = ''; + try { + windSpeedStr = parseFloat(wind.windSpeed); + if (isNaN(windSpeedStr)) { + windSpeedStr = "---" + } else { + if (props.showKnots) { + let nm = navcompute.NM; + windSpeedStr = windSpeedStr * 3600 / nm; } - }catch(e){} - if (! this.props.show360 && wind.suffix !== 'TD'){ - if (wind.windAngle > 180) wind.windAngle-=360; + if (windSpeedStr < 10) windSpeedStr = Formatter.formatDecimal(windSpeedStr, 2, 1); + else windSpeedStr = Formatter.formatDecimal(windSpeedStr, 3, 0); } - return ( -
- {(this.props.mode === 'horizontal') ? - -
{'W'+wind.suffix}
-
- {Formatter.formatDirection(wind.windAngle)} - ° - /{windSpeedStr} - {this.props.showKnots ? "kn" : "m/s"} + } catch (e) { + } + if (!props.show360 && wind.suffix !== 'TD') { + if (wind.windAngle > 180) wind.windAngle -= 360; + } + return ( +
+ {(props.mode === 'horizontal') ? + +
{'W' + wind.suffix}
+
+ {Formatter.formatDirection(wind.windAngle)} + ° + /{windSpeedStr} + {props.showKnots ? "kn" : "m/s"} +
+
+ : + +
+
+
{Formatter.formatDirection(wind.windAngle)}
+
{names[wind.suffix].angle}
+
°
- - : - -
-
-
{Formatter.formatDirection(wind.windAngle)}
-
{names[wind.suffix].angle}
-
°
-
-
-
{windSpeedStr}
-
{names[wind.suffix].speed}
-
{this.props.showKnots ? "kn" : "m/s"}
-
+
+
{windSpeedStr}
+
{names[wind.suffix].speed}
+
{props.showKnots ? "kn" : "m/s"}
- - } -
- - ); - - } - +
+
+ } +
+ ); } + WindWidget.propTypes={ onClick: PropTypes.func, className: PropTypes.string, @@ -146,7 +137,12 @@ WindWidget.propTypes={ windAngleTrue: PropTypes.number, windSpeedTrue: PropTypes.number, enabled: PropTypes.bool, - kind: PropTypes.string //true,apparent,auto + kind: PropTypes.string, //true,apparent,auto, + showKnots: PropTypes.bool, + show360: PropTypes.bool, + mode: PropTypes.string, + dragId: PropTypes.string, + style: PropTypes.object }; WindWidget.storeKeys={ diff --git a/viewer/components/XteWidget.jsx b/viewer/components/XteWidget.jsx index a44e3da1e..154a45797 100644 --- a/viewer/components/XteWidget.jsx +++ b/viewer/components/XteWidget.jsx @@ -4,12 +4,11 @@ import React from "react"; import PropTypes from 'prop-types'; -import PropertyHandler from '../util/propertyhandler.js'; import Formatter from '../util/formatter'; import keys from '../util/keys.jsx'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; import navcompute from '../nav/navcompute.js'; +import {useAvNavSortable} from "../hoc/Sortable"; const normalColors={ all: '#000000' @@ -18,110 +17,97 @@ const nightColors={ all: 'rgba(252, 11, 11, 0.6)' } -class XteWidget extends React.Component{ - - constructor(props){ - super(props); - this.canvasRef=this.canvasRef.bind(this); - this.drawXte=this.drawXte.bind(this); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - shouldComponentUpdate(nextProps,nextState){ - return Helper.compareProperties(this.props,nextProps, XteWidget.storeKeys); - } - render(){ - let self = this; - let classes = "widget xteWidget " +this.props.className||""; - let style = this.props.style || {}; - setTimeout(self.drawXte,0); - return ( -
- -
XTE
-
nm
-
- - ); - - } - canvasRef(item){ - this.canvas=item; - setTimeout(self.drawXte,0); - } - drawXte(){ - let canvas=this.canvas; - if (! canvas) return; - let context=canvas.getContext('2d'); - let xteMax=this.props.xteMax; - let xteText=Formatter.formatDecimal(xteMax,1,1); - let color=canvas.style.color; - if (! color){ - color=this.props.nightMode?nightColors.all:normalColors.all; +const XteWidget = (props) => { + useKeyEventHandler(props, "widget"); + const ddProps = useAvNavSortable(props.dragId); + let canvas = undefined; + const canvasRef = (e) => canvas = e; + const drawXte = () => { + if (!canvas) return; + let context = canvas.getContext('2d'); + let xteMax = props.xteMax; + let xteText = Formatter.formatDecimal(xteMax, 1, 1); + let color = canvas.style.color; + if (!color) { + color = props.nightMode ? nightColors.all : normalColors.all; } - let crect=canvas.getBoundingClientRect(); - let w=crect.width; - let h=crect.height; - canvas.width=w; - canvas.height=h; - context.clearRect(0,0,w,h); + let crect = canvas.getBoundingClientRect(); + let w = crect.width; + let h = crect.height; + canvas.width = w; + canvas.height = h; + context.clearRect(0, 0, w, h); //fix for duplicate canvas in Android 4.x stock browser and webview //https://medium.com/@dhashvir/android-4-1-x-stock-browser-canvas-solution-ffcb939af758 - context.canvas.style.visibility ='hidden'; // Force a change in DOM + context.canvas.style.visibility = 'hidden'; // Force a change in DOM context.canvas.offsetHeight; // Cause a repaint to take play context.canvas.style.visibility = 'inherit'; // Make visible again - context.fillStyle =color; - context.strokeStyle=color; - let textBase=h*0.9; - let textSize=h*0.2; - let left=w*0.1; - let right=w*0.9; - let linebase=h*0.4; - let sideHeight=h*0.3; - let middleHeight=h*0.6; - let shipUpper=h*0.45; - let shipH=h*0.3; - let shipw=w*0.03; - let mText="0"; - context.font="normal "+Math.ceil(textSize)+"px Arial"; - context.textAlign="center"; - context.fillText(xteText,left,textBase); - context.fillText(xteText,right,textBase); - context.fillText("0",0.5*w,textBase); - context.lineWidth=3; + context.fillStyle = color; + context.strokeStyle = color; + let textBase = h * 0.9; + let textSize = h * 0.2; + let left = w * 0.1; + let right = w * 0.9; + let linebase = h * 0.4; + let sideHeight = h * 0.3; + let middleHeight = h * 0.6; + let shipUpper = h * 0.45; + let shipH = h * 0.3; + let shipw = w * 0.03; + context.font = "normal " + Math.ceil(textSize) + "px Arial"; + context.textAlign = "center"; + context.fillText(xteText, left, textBase); + context.fillText(xteText, right, textBase); + context.fillText("0", 0.5 * w, textBase); + context.lineWidth = 3; context.beginPath(); - context.moveTo(left,linebase-0.5*sideHeight); - context.lineTo(left,linebase+0.5*sideHeight); - context.moveTo(left,linebase); - context.lineTo(right,linebase); - context.moveTo(right,linebase-0.5*sideHeight); - context.lineTo(right,linebase+0.5*sideHeight); - context.moveTo(0.5*w,linebase-0.5*middleHeight); - context.lineTo(0.5*w,linebase+0.5*middleHeight); + context.moveTo(left, linebase - 0.5 * sideHeight); + context.lineTo(left, linebase + 0.5 * sideHeight); + context.moveTo(left, linebase); + context.lineTo(right, linebase); + context.moveTo(right, linebase - 0.5 * sideHeight); + context.lineTo(right, linebase + 0.5 * sideHeight); + context.moveTo(0.5 * w, linebase - 0.5 * middleHeight); + context.lineTo(0.5 * w, linebase + 0.5 * middleHeight); context.stroke(); context.closePath(); - let curXte=this.props.markerXte/navcompute.NM; + let curXte = props.markerXte / navcompute.NM; if (curXte === undefined) return; - let xtepos=parseFloat(curXte)/xteMax; - if (xtepos < -1.1) xtepos=-1.1; - if (xtepos > 1.1) xtepos=1.1; - xtepos=xtepos*(right-left)/2+left+(right-left)/2; + let xtepos = parseFloat(curXte) / xteMax; + if (xtepos < -1.1) xtepos = -1.1; + if (xtepos > 1.1) xtepos = 1.1; + xtepos = xtepos * (right - left) / 2 + left + (right - left) / 2; context.beginPath(); - context.moveTo(xtepos,shipUpper); - context.lineTo(xtepos-shipw,shipUpper+shipH); - context.lineTo(xtepos+shipw,shipUpper+shipH); - context.lineTo(xtepos,shipUpper); + context.moveTo(xtepos, shipUpper); + context.lineTo(xtepos - shipw, shipUpper + shipH); + context.lineTo(xtepos + shipw, shipUpper + shipH); + context.lineTo(xtepos, shipUpper); context.fill(); context.closePath(); } + let classes = "widget xteWidget " + props.className || ""; + const style = {...props.style, ...ddProps.style}; + setTimeout(drawXte, 0); + return ( +
+ +
XTE
+
nm
+
+ + ); } + XteWidget.propTypes={ onClick: PropTypes.func, className: PropTypes.string, markerXte: PropTypes.number, - xteMax: PropTypes.number - + xteMax: PropTypes.number, + dragId: PropTypes.string, + style: PropTypes.object, + nightMode: PropTypes.bool }; XteWidget.storeKeys={ diff --git a/viewer/components/ZoomWidget.jsx b/viewer/components/ZoomWidget.jsx index f06224a91..5ca56b880 100644 --- a/viewer/components/ZoomWidget.jsx +++ b/viewer/components/ZoomWidget.jsx @@ -6,42 +6,35 @@ import React from "react"; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; import Formatter from '../util/formatter.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {useAvNavSortable} from "../hoc/Sortable"; -class ZoomWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - shouldComponentUpdate(nextProps,nextState) { - if (this.props.zoom != nextProps.zoom || this.props.requiredZoom != nextProps.requiredZoom) return true; - return false; - } - render(){ +const ZoomWidget =(props)=>{ + useKeyEventHandler(props,"widget"); + const ddProps=useAvNavSortable(props.dragId); let classes="widget zoomWidget "; - if (this.props.className) classes+=" "+this.props.className; - let style=this.props.style||{}; - let val=this.props.default||'--'; - if (this.props.zoom !== undefined) { - val=Formatter.formatDecimalOpt(this.props.zoom, 2, 1); + if (props.className) classes+=" "+props.className; + let val=props.default||'--'; + if (props.zoom !== undefined) { + val=Formatter.formatDecimalOpt(props.zoom, 2, 1); } let rzoom=undefined; - if (this.props.requiredZoom && this.props.requiredZoom != this.props.zoom){ - rzoom=Formatter.formatDecimalOpt(this.props.requiredZoom,2,1); + if (props.requiredZoom && props.requiredZoom != props.zoom){ + rzoom=Formatter.formatDecimalOpt(props.requiredZoom,2,1); } + const style={...props.style,...ddProps.style}; return ( -
+
{val} { (rzoom !== undefined)?
({rzoom})
:'' }
-
{this.props.caption}
+
{props.caption}
); - } -}; + }; ZoomWidget.propTypes={ name: PropTypes.string, @@ -50,7 +43,10 @@ ZoomWidget.propTypes={ classes: PropTypes.string, style: PropTypes.object, zoom: PropTypes.number, - requiredZoom: PropTypes.number + requiredZoom: PropTypes.number, + className: PropTypes.string, + default: PropTypes.any, + dragId: PropTypes.string }; ZoomWidget.storeKeys={ diff --git a/viewer/hoc/Dynamic.jsx b/viewer/hoc/Dynamic.jsx index 0880613be..35ef25f96 100644 --- a/viewer/hoc/Dynamic.jsx +++ b/viewer/hoc/Dynamic.jsx @@ -10,6 +10,7 @@ import globalStore from "../util/globalstore.jsx"; import React from 'react'; import assign from 'object-assign'; +import Helper from "../util/helper"; @@ -95,6 +96,10 @@ export default function(Component,opt_options,opt_store){ let childprops=assign({},forwardProps,this.state); return } + shouldComponentUpdate(nextProps, nextState, nextContext) { + return true; + //return Helper.compareProperties(this.state,nextState,this.getStoreKeys()); + } }; return Dynamic; }; diff --git a/viewer/util/GuiHelpers.js b/viewer/util/GuiHelpers.js index 0b72b318c..26cb454ef 100644 --- a/viewer/util/GuiHelpers.js +++ b/viewer/util/GuiHelpers.js @@ -6,6 +6,7 @@ import assign from 'object-assign'; import shallowcompare from "./compare"; import Requests from "./requests"; import base from "../base"; +import {useEffect} from "react"; @@ -275,6 +276,22 @@ const nameKeyEventHandler=(thisref,component,opt_callback)=>{ },component,thisref.props.name); }; +export const useKeyEventHandler=(props,component,opt_callback)=>{ + return useEffect(()=>{ + if (! props.name || ! (props.onClick|| opt_callback)) return; + const handler=(cbComponent,cbAction)=>{ + if (cbComponent === component && cbAction === props.name){ + if (opt_callback) opt_callback(cbComponent,cbAction); + else props.onClick(); + } + }; + KeyHandler.registerHandler(handler,component,props.name); + return ()=>{ + KeyHandler.deregisterHandler(handler); + } + },[]) +} + //from https://stackoverflow.com/questions/487073/how-to-check-if-element-is-visible-after-scrolling //returns: //0 - no scroll From b7e2468fa7ee126712f8ca2d3d9929a8c4a5f25c Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 15 Jul 2024 10:02:06 +0200 Subject: [PATCH 018/138] eslint 8 --- viewer/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/viewer/package.json b/viewer/package.json index d2557384b..a0ce1b3f5 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -26,14 +26,14 @@ "@babel/preset-react": "^7.24.7", "@eslint/compat": "^1.1.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.5.0", + "@eslint/js": "^8.57.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "babel-loader": "^9.1.3", "babel-plugin-prismjs": "^2.1.0", "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", - "eslint": "^9.5.0", + "eslint": "^8.57.0", "eslint-plugin-react": "^7.34.3", "eslint-plugin-react-hooks": "^4.6.2", "file-loader": "^6.2.0", From d4b89e583a35fb5c07f382b58b1da9498ffa1045 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 1 Aug 2024 17:40:19 +0200 Subject: [PATCH 019/138] intermediate_ migrate widgets to react18 --- viewer/components/DateTimeWidget.jsx | 11 ++- viewer/components/DirectWidget.jsx | 10 +-- viewer/components/EditRouteWidget.jsx | 12 ++- viewer/components/EmptyWidget.jsx | 11 ++- viewer/components/EtaWidget.jsx | 14 ++-- viewer/components/ExternalWidget.jsx | 98 +++++++++++-------------- viewer/components/RoutePointsWidget.jsx | 24 ++---- viewer/components/TimeStatusWidget.jsx | 12 ++- viewer/components/WidgetBase.jsx | 35 +++++++++ viewer/hoc/Dynamic.jsx | 29 +++----- viewer/hoc/Sortable.js | 5 ++ 11 files changed, 131 insertions(+), 130 deletions(-) create mode 100644 viewer/components/WidgetBase.jsx diff --git a/viewer/components/DateTimeWidget.jsx b/viewer/components/DateTimeWidget.jsx index 8c40d3f1c..59146626d 100644 --- a/viewer/components/DateTimeWidget.jsx +++ b/viewer/components/DateTimeWidget.jsx @@ -7,7 +7,8 @@ import PropTypes from "prop-types"; import keys from "../util/keys.jsx"; import Formatter from "../util/formatter.js"; import {useKeyEventHandler} from '../util/GuiHelpers.js'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const DateTimeWidget=(props)=>{ useKeyEventHandler(props,"widget"); @@ -34,12 +35,10 @@ const DateTimeWidget=(props)=>{ } DateTimeWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, + ...SortableProps, + ...WidgetProps, time: PropTypes.objectOf(Date), - gpsValid: PropTypes.bool, - style: PropTypes.object, - dragId: PropTypes.string + gpsValid: PropTypes.bool }; DateTimeWidget.storeKeys={ time: keys.nav.gps.rtime, diff --git a/viewer/components/DirectWidget.jsx b/viewer/components/DirectWidget.jsx index a2ef4a86a..48b09fce1 100644 --- a/viewer/components/DirectWidget.jsx +++ b/viewer/components/DirectWidget.jsx @@ -6,7 +6,8 @@ import React from "react"; import PropTypes from 'prop-types'; import Value from './Value.jsx'; import {useKeyEventHandler} from '../util/GuiHelpers.js'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const DirectWidget=(wprops)=>{ const props=wprops.translateFunction?wprops.translateFunction(wprops):wprops; @@ -44,16 +45,13 @@ const DirectWidget=(wprops)=>{ DirectWidget.propTypes={ name: PropTypes.string, unit: PropTypes.string, - caption: PropTypes.string, + ...SortableProps, + ...WidgetProps, value: PropTypes.any, isAverage: PropTypes.bool, formatter: PropTypes.func.isRequired, - onClick: PropTypes.func, - className: PropTypes.string, - style: PropTypes.object, default: PropTypes.string, translateFunction: PropTypes.func, - dragId: PropTypes.string }; DirectWidget.editableParameters={ caption:true, diff --git a/viewer/components/EditRouteWidget.jsx b/viewer/components/EditRouteWidget.jsx index 4a4ecdc9c..d4e3aa89f 100644 --- a/viewer/components/EditRouteWidget.jsx +++ b/viewer/components/EditRouteWidget.jsx @@ -9,7 +9,8 @@ import Formatter from '../util/formatter.js' import routeobjects from '../nav/routeobjects.js'; import RouteEdit,{StateHelper} from '../nav/routeeditor.js'; import {useKeyEventHandler} from '../util/GuiHelpers.js'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const editor=new RouteEdit(RouteEdit.MODES.EDIT); @@ -62,17 +63,14 @@ const EditRouteWidget = (props) => { EditRouteWidget.propTypes={ - onClick:PropTypes.func, - className:PropTypes.string, - mode: PropTypes.string, //display info side by side if small + ...SortableProps, + ...WidgetProps, route: PropTypes.objectOf(routeobjects.Route), remain: PropTypes.number, eta: PropTypes.objectOf(Date), isApproaching: PropTypes.bool, isActive: PropTypes.bool, - useRhumbLine: PropTypes.bool, - dragId: PropTypes.string, - style: PropTypes.object + useRhumbLine: PropTypes.bool }; EditRouteWidget.storeKeys=editor.getStoreKeys({ diff --git a/viewer/components/EmptyWidget.jsx b/viewer/components/EmptyWidget.jsx index df7d801f6..4ce80b3de 100644 --- a/viewer/components/EmptyWidget.jsx +++ b/viewer/components/EmptyWidget.jsx @@ -4,7 +4,8 @@ import React from "react"; import PropTypes from 'prop-types'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const EmptyWidget =(props)=>{ const ddProps=useAvNavSortable(props.dragId); @@ -18,11 +19,9 @@ const EmptyWidget =(props)=>{ } EmptyWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, - dragId: PropTypes.string, - style: PropTypes.object, - classes: PropTypes.string + ...SortableProps, + ...WidgetProps, + classes: PropTypes.string }; export default EmptyWidget; \ No newline at end of file diff --git a/viewer/components/EtaWidget.jsx b/viewer/components/EtaWidget.jsx index 0413e2fa4..312ec791a 100644 --- a/viewer/components/EtaWidget.jsx +++ b/viewer/components/EtaWidget.jsx @@ -6,9 +6,9 @@ import React from "react"; import PropTypes from 'prop-types'; import keys from '../util/keys.jsx'; import Formatter from '../util/formatter.js'; -import Helper from '../util/helper.js'; -import GuiHelper, {useKeyEventHandler} from '../util/GuiHelpers.js'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const EtaWidget =(props)=>{ @@ -18,7 +18,7 @@ const EtaWidget =(props)=>{ let classes="widget etaWidget "+props.className||""; const style={...props.style,...ddProps.style}; return ( -
+
{props.caption}
{eta}
{props.wpname}
@@ -27,10 +27,8 @@ const EtaWidget =(props)=>{ }; EtaWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, - style: PropTypes.object, - caption: PropTypes.string, + ...SortableProps, + ...WidgetProps, eta: PropTypes.objectOf(Date), wpname: PropTypes.string }; diff --git a/viewer/components/ExternalWidget.jsx b/viewer/components/ExternalWidget.jsx index bbfbbffbe..8199e414f 100644 --- a/viewer/components/ExternalWidget.jsx +++ b/viewer/components/ExternalWidget.jsx @@ -2,14 +2,16 @@ * Created by andreas on 23.02.16. */ -import React from "react"; +import React, {useCallback, useEffect, useMemo, useRef, useState} from "react"; import PropTypes from 'prop-types'; import Helper from '../util/helper.js'; import Value from './Value.jsx'; -import GuiHelper from '../util/GuiHelpers.js'; +import GuiHelper, {useKeyEventHandler} from '../util/GuiHelpers.js'; import ReactHtmlParser,{convertNodeToElement} from 'react-html-parser/dist/react-html-parser.min.js'; import base from '../base.js'; import assign from 'object-assign'; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const REACT_EVENTS=('onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown onKeyPress onKeyUp'+ ' onFocus onBlur onChange onInput onInvalid onReset onSubmit onError onLoad onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit'+ @@ -55,39 +57,46 @@ const transform=(self,node,index)=>{ return convertNodeToElement(node,index,(node,index)=>{transform(self,node,index)}); }; -class ExternalWidget extends React.Component{ - constructor(props){ - super(props); - let self=this; - this.state={updateCount:1}; - this.canvasRef=this.canvasRef.bind(this); - this.renderCanvas=this.renderCanvas.bind(this); - GuiHelper.nameKeyEventHandler(this,"widget"); - this.userData={ - eventHandler:[], - triggerRedraw: ()=>{self.setState({updateCount:self.state.updateCount+1})} - }; - if (typeof(this.props.initFunction) === 'function'){ - this.props.initFunction.call(this.userData,this.userData,this.props); +export const ExternalWidget =(props)=>{ + useKeyEventHandler(props,"widget"); + const {updateCount,setUpdateCount}=useState(1); + const ddProps=useAvNavSortable(props.dragId); + const initialCalled=useRef(false); + const canvasRef=useRef(null); + const getProps=()=>{ + if (props.translateFunction) return props.translateFunction({...props}); + return props; + }; + const userData=useRef( { + eventHandler: [], + triggerRedraw: () => { + setUpdateCount(updateCount+1) } + }); + if (! initialCalled.current && typeof (props.initFunction) === 'function') { + initialCalled.current=true; + props.initFunction.call(userData.current, userData.current, getProps()); } - getProps(){ - if (! this.props.translateFunction){ - return this.props; + useEffect(()=>{ + if (canvasRef.current && props.renderCanvas){ + props.renderCanvas.apply(userData.current,[canvasRef.current,getProps()]); } - else{ - return this.props.translateFunction(assign({},this.props)); + + return ()=>{ + if (initialCalled.current && typeof(props.finalizeFunction) === 'function'){ + props.finalizeFunction.call(userData.current,userData.current,getProps()); + } + initialCalled.current=false; } - } - render(){ - let convertedProps=this.getProps() + }) + + let convertedProps=getProps() let classes="widget externalWidget"; if (convertedProps.className) classes+=" "+convertedProps.className; - let style=convertedProps.style||{}; let innerHtml=null; - if (this.props.renderHtml){ + if (props.renderHtml){ try { - innerHtml = this.props.renderHtml.apply(this.userData,[convertedProps]); + innerHtml = props.renderHtml.apply(userData.current,[convertedProps]); }catch (e){ base.log("External Widget: render error "+e); innerHtml="

render error

"; @@ -97,10 +106,10 @@ class ExternalWidget extends React.Component{ } } let userHtml=(innerHtml!=null)?ReactHtmlParser(innerHtml, - {transform:(node,index)=>{transform(this.userData,node,index);}}):null; + {transform:(node,index)=>{transform(userData.current,node,index);}}):null; return ( -
- {this.props.renderCanvas?:null} +
+ {props.renderCanvas?:null}
{userHtml}
@@ -110,36 +119,13 @@ class ExternalWidget extends React.Component{ :null}
); - } - componentDidUpdate(){ - this.renderCanvas(); - } - componentWillUnmount(){ - if (typeof(this.props.finalizeFunction) === 'function'){ - this.props.finalizeFunction.call(this.userData,this.userData,this.getProps()); - } - } - canvasRef(item){ - this.canvas=item; - setTimeout(this.renderCanvas,0); - } - renderCanvas(){ - if (! this.canvas) return; - try { - this.props.renderCanvas.apply(this.userData,[this.canvas, this.getProps()]); - }catch (e){ - base.log("GaugeRadial: canvas render error "+e); - } - } -}; +} ExternalWidget.propTypes={ + ...SortableProps, + ...WidgetProps, name: PropTypes.string, unit: PropTypes.string, - caption: PropTypes.string, - onClick: PropTypes.func, - className: PropTypes.string, - style: PropTypes.object, default: PropTypes.string, renderHtml: PropTypes.func, renderCanvas: PropTypes.func, diff --git a/viewer/components/RoutePointsWidget.jsx b/viewer/components/RoutePointsWidget.jsx index bf8f91a7c..7018fadf2 100644 --- a/viewer/components/RoutePointsWidget.jsx +++ b/viewer/components/RoutePointsWidget.jsx @@ -10,24 +10,15 @@ import ItemList from './ItemList.jsx'; import WaypointItem from './WayPointItem.jsx'; import RouteEdit,{StateHelper} from '../nav/routeeditor.js'; import GuiHelper, {useKeyEventHandler} from '../util/GuiHelpers.js'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const editor=new RouteEdit(RouteEdit.MODES.EDIT); const RoutePoint=(showLL)=>{ - return function(props){ + return (props)=>{ return } -}; -//TODO: compare! -const compareFunction=(prev,current)=>{ - for (let k in RoutePointsWidget.propTypes){ - if (k == 'route') continue; - if (prev[k] !== current[k]) return true; - } - if (!prev.route != !current.route) return true; - if (!current.route) return false; - return current.route.differsTo(prev.route); } const RoutePointsWidget = (props) => { useKeyEventHandler(props, "widget"); @@ -55,11 +46,11 @@ const RoutePointsWidget = (props) => {
{ + itemCreator={() => { return RoutePoint(props.showLatLon) }} scrollable={true} - onItemClick={(item, data) => { + onItemClick={(item) => { if (props.onClick) props.onClick(new routeobjects.RoutePoint(item)) }} @@ -78,9 +69,8 @@ const RoutePointsWidget = (props) => { RoutePointsWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, - mode: PropTypes.string, //display info side by side if small + ...SortableProps, + ...WidgetProps, route: PropTypes.objectOf(routeobjects.Route), isActive: PropTypes.bool, index: PropTypes.number, diff --git a/viewer/components/TimeStatusWidget.jsx b/viewer/components/TimeStatusWidget.jsx index 9af4b5d91..bba70925b 100644 --- a/viewer/components/TimeStatusWidget.jsx +++ b/viewer/components/TimeStatusWidget.jsx @@ -8,7 +8,8 @@ import keys from "../util/keys.jsx"; import Formatter from "../util/formatter.js"; import globalStore from '../util/globalstore.jsx'; import {useKeyEventHandler} from '../util/GuiHelpers.js'; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const TimeStatusWidget = (props=> { useKeyEventHandler(props,"widget"); @@ -32,13 +33,10 @@ const TimeStatusWidget = (props=> { }); TimeStatusWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, + ...SortableProps, + ...WidgetProps, time: PropTypes.objectOf(Date), - gpsValid: PropTypes.bool, - style: PropTypes.object, - caption: PropTypes.string, - dragId: PropTypes.string + gpsValid: PropTypes.bool }; TimeStatusWidget.storeKeys={ time: keys.nav.gps.rtime, diff --git a/viewer/components/WidgetBase.jsx b/viewer/components/WidgetBase.jsx new file mode 100644 index 000000000..e931e57b5 --- /dev/null +++ b/viewer/components/WidgetBase.jsx @@ -0,0 +1,35 @@ +/** + *############################################################################### + # Copyright (c) 2012-2020 Andreas Vogel andreas@wellenvogel.net + # + # Permission is hereby granted, free of charge, to any person obtaining a + # copy of this software and associated documentation files (the "Software"), + # to deal in the Software without restriction, including without limitation + # the rights to use, copy, modify, merge, publish, distribute, sublicense, + # and/or sell copies of the Software, and to permit persons to whom the + # Software is furnished to do so, subject to the following conditions: + # + # The above copyright notice and this permission notice shall be included + # in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + # DEALINGS IN THE SOFTWARE. + # + ############################################################################### + Widget Base data + */ + +import PropTypes from "prop-types"; + +export const WidgetProps={ + onClick: PropTypes.func, + style: PropTypes.object, + className: PropTypes.string, + mode: PropTypes.string, //display info side by side if small + caption: PropTypes.string +} \ No newline at end of file diff --git a/viewer/hoc/Dynamic.jsx b/viewer/hoc/Dynamic.jsx index 35ef25f96..c188a8142 100644 --- a/viewer/hoc/Dynamic.jsx +++ b/viewer/hoc/Dynamic.jsx @@ -9,9 +9,6 @@ import globalStore from "../util/globalstore.jsx"; import React from 'react'; -import assign from 'object-assign'; -import Helper from "../util/helper"; - export default function(Component,opt_options,opt_store){ @@ -35,28 +32,30 @@ export default function(Component,opt_options,opt_store){ if (! updateFunction && opt_options) updateFunction=opt_options.changeCallback; if (! updateFunction) return; let {storeKeys,uf,changeCallback,...forwardProps}=this.props; - let childprops=assign({},forwardProps,data); + let childprops={...forwardProps,...data}; updateFunction(childprops); } getStoreKeys(){ let storeKeys=this.props.storeKeys; if (opt_options && opt_options.storeKeys) { - storeKeys=assign({},opt_options.storeKeys,storeKeys); + storeKeys={...opt_options.storeKeys,...storeKeys}; } if (!storeKeys) return ; - if (storeKeys instanceof Array) return storeKeys; - if (storeKeys instanceof Object) return Object.values(storeKeys); - return [storeKeys]; + if (! (storeKeys instanceof Object)){ + throw Error("store keys is no object",storeKeys); + } + return storeKeys; } getTranslatedStoreValues(){ - if (! this.getStoreKeys()) return {}; - let values=store.getMultiple(this.props.storeKeys||opt_options.storeKeys); + const keys=this.getStoreKeys(); + if (! keys) return {}; + let values=store.getMultiple(keys); let updateFunction=this.props.updateFunction; if (! updateFunction){ if (opt_options && opt_options.updateFunction) updateFunction=opt_options.updateFunction; } if (updateFunction) { - return updateFunction(values,this.getStoreKeys()); + return updateFunction(values,keys); } return values; } @@ -93,13 +92,9 @@ export default function(Component,opt_options,opt_store){ } render(){ let {storeKeys,updateFunction,changeCallback,...forwardProps}=this.props; - let childprops=assign({},forwardProps,this.state); + let childprops={...forwardProps,...this.state}; return } - shouldComponentUpdate(nextProps, nextState, nextContext) { - return true; - //return Helper.compareProperties(this.state,nextState,this.getStoreKeys()); - } - }; + } return Dynamic; }; diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index cc4c678b8..1e64baa54 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -23,6 +23,7 @@ import React from 'react'; import {useSortable} from '@dnd-kit/sortable'; import {CSS} from '@dnd-kit/utilities'; +import PropTypes from "prop-types"; export const useAvNavSortable=(id,ref)=>{ if (id === undefined) return {}; @@ -44,3 +45,7 @@ export const useAvNavSortable=(id,ref)=>{ } return {ref:setRef,style:nstyle,...attributes,...listeners}; } + +export const SortableProps={ + dragId: PropTypes.string +} From 10007972a12b7a05bbe70210a4bc0634bccfb655 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 7 Aug 2024 15:53:07 +0200 Subject: [PATCH 020/138] intermediate, not working: combined widget sortable --- viewer/components/WidgetFactory.jsx | 79 ++++++++++++++++++----------- viewer/components/WidgetList.js | 1 - 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/viewer/components/WidgetFactory.jsx b/viewer/components/WidgetFactory.jsx index af8a056a9..ffb7bcdb9 100644 --- a/viewer/components/WidgetFactory.jsx +++ b/viewer/components/WidgetFactory.jsx @@ -15,6 +15,8 @@ import {createEditableParameter, EditableParameter} from "./EditableParameters"; import Compare from "../util/compare"; import CloneDeep from 'clone-deep'; import MapWidget from "./MapWidget"; +import {useAvNavSortable} from "../hoc/Sortable"; +import {useKeyEventHandler} from "../util/GuiHelpers"; export const filterByEditables=(editableParameters,values)=>{ let rt={}; @@ -246,8 +248,34 @@ export const getFormatterParameters=(widget)=>{ } } } - - +const Dummy=(dprops)=>{ + console.log("Dummy render",dprops); + return
{ + dprops.onClick(ev); + }} className={"widget"}>XXX:{dprops.cey}
+} +const CombinedWidget=(props)=>{ + + useKeyEventHandler(props,"widget") + let {editableParameters,children,onClick,childProperties,style,dragId,className,...forwardProps}=props; + const ddProps = useAvNavSortable(dragId); + const cl=(ev)=>{ + if (onClick) onClick(ev); + } + const cc=(ev)=>{ + console.log("child click",ev); + } + let cidx = 0; + delete childProperties.style; + className = (className || '') + " widget combinedWidget"; + return
+ {(children||[] ).map((item) => { + let Item = theFactory.createWidget(item, childProperties); + cidx++; + return + })} +
+} class WidgetFactory{ @@ -427,48 +455,38 @@ class WidgetFactory{ } } - return function (props) { - let wprops = assign({}, props, mergedProps); - delete wprops.editableParameters; - let {style,...childProperties}=opt_properties||{}; //filter out style for children - if (mergedProps.children) { - let cidx=0; - let className=(mergedProps.className||'')+" widget combinedWidget"; - return
- {mergedProps.children.map((item)=> { - let Item = self.createWidget(item, childProperties); - cidx++; - return - })} -
+ if (mergedProps.children) { + return (props)=> { + return } - else { + } else { + return function (props) { + let wprops = {...props, ...mergedProps}; let RenderWidget = mergedProps.wclass || DirectWidget; let storeKeys = mergedProps.storeKeys; - if (wprops.className) wprops.className+=" "+wprops.name; - else wprops.className=wprops.name; + if (wprops.className) wprops.className += " " + wprops.name; + else wprops.className = wprops.name; if (!storeKeys) { storeKeys = RenderWidget.storeKeys; } - if (wprops.handleVisible){ - RenderWidget=Visible(RenderWidget); + if (wprops.handleVisible) { + RenderWidget = Visible(RenderWidget); delete wprops.handleVisible; } - if (wprops.nightMode === undefined && (storeKeys === undefined || storeKeys.nightMode === undefined)){ - if (storeKeys === undefined){ - storeKeys={nightMode:keys.properties.nightMode} - } - else{ - storeKeys=assign({nightMode: keys.properties.nightMode},storeKeys) + if (wprops.nightMode === undefined && (storeKeys === undefined || storeKeys.nightMode === undefined)) { + if (storeKeys === undefined) { + storeKeys = {nightMode: keys.properties.nightMode} + } else { + storeKeys = assign({nightMode: keys.properties.nightMode}, storeKeys) } } if (storeKeys) { - RenderWidget = Dynamic(RenderWidget, {storeKeys:storeKeys}); + RenderWidget = Dynamic(RenderWidget, {storeKeys: storeKeys}); } delete wprops.storeKeys; return } - }; + } } getWidget(index){ if (index < 0 || index >= this.widgetDefinitions.length) return undefined; @@ -642,5 +660,6 @@ WidgetFactory.prototype.registerFormatter=function(name,formatterFunction){ Formatter[name]=formatterFunction; } +const theFactory=new WidgetFactory(); -export default new WidgetFactory(); \ No newline at end of file +export default theFactory; \ No newline at end of file diff --git a/viewer/components/WidgetList.js b/viewer/components/WidgetList.js index a22927f4d..e3aa36001 100644 --- a/viewer/components/WidgetList.js +++ b/viewer/components/WidgetList.js @@ -11,7 +11,6 @@ import WindWidget, {getWindData} from './WindWidget'; import XteWidget from './XteWidget'; import EmptyWidget from './EmptyWidget'; import WindGraphics from './WindGraphics'; -import DirectWidget from './DirectWidget.jsx'; import ZoomWidget from './ZoomWidget.jsx'; import keys from '../util/keys.jsx'; import AlarmWidget from './AlarmWidget.jsx'; From 40672939c43b110f446c3e8345de71f14804e4d0 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 7 Aug 2024 17:15:57 +0200 Subject: [PATCH 021/138] intermediate: use own drag and drop handler --- viewer/hoc/Sortable.js | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 1e64baa54..074ab301b 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -25,7 +25,7 @@ import {useSortable} from '@dnd-kit/sortable'; import {CSS} from '@dnd-kit/utilities'; import PropTypes from "prop-types"; -export const useAvNavSortable=(id,ref)=>{ +export const useAvNavSortableO=(id,ref)=>{ if (id === undefined) return {}; const { attributes, @@ -49,3 +49,42 @@ export const useAvNavSortable=(id,ref)=>{ export const SortableProps={ dragId: PropTypes.string } + +export const useAvNavSortable=(id,ref)=>{ + const ATTR='data-dragid'; + if (id === undefined) return {}; + let rt={ + onDragStart:(ev)=>{ + let data={ + rect: ev.currentTarget.getBoundingClientRect(), + id: id, + client: {x:ev.clientX,y:ev.clientY} + }; + data.offset={x:data.client.x-data.rect.left,y:data.client.y-data.rect.top} + ev.dataTransfer.setData("text",JSON.stringify(data)); + }, + onDragOver:(ev)=>{ + let ta=ev.target.getAttribute(ATTR); + if ( ta !== undefined) { + ev.preventDefault(); + } + }, + onDrop: (ev)=>{ + ev.preventDefault(); + let dids=ev.dataTransfer.getData("text"); + let tdata=JSON.parse(dids); + let tid=ev.currentTarget.getAttribute(ATTR); + if (tid === tdata.id) return; + let trect=ev.currentTarget.getBoundingClientRect(); + let toffset={x:ev.clientX-trect.left,y:ev.clientY-trect.top}; + let dragupperleft={x:toffset.x-tdata.offset.x,y:toffset.y-tdata.offset.y} + let mode="after"; + if (dragupperleft.y < 0 ) mode="before"; + console.log("drop from ",tdata.id,"to ",tid,"mode",mode,tdata,trect,dragupperleft); + }, + draggable: true, + droppable: true + } + rt[ATTR]=id; + return rt; +} From b9eb2ee48d6064104e4e0218dbaf3d86a31e47d7 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 7 Aug 2024 17:21:24 +0200 Subject: [PATCH 022/138] use own type for drag and drop --- viewer/hoc/Sortable.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 074ab301b..b638ee8ea 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -52,6 +52,7 @@ export const SortableProps={ export const useAvNavSortable=(id,ref)=>{ const ATTR='data-dragid'; + const TYPE='application-x-avnav-dnd'; if (id === undefined) return {}; let rt={ onDragStart:(ev)=>{ @@ -61,17 +62,20 @@ export const useAvNavSortable=(id,ref)=>{ client: {x:ev.clientX,y:ev.clientY} }; data.offset={x:data.client.x-data.rect.left,y:data.client.y-data.rect.top} - ev.dataTransfer.setData("text",JSON.stringify(data)); + ev.dataTransfer.setData(TYPE,JSON.stringify(data)); + ev.dataTransfer.supp }, onDragOver:(ev)=>{ let ta=ev.target.getAttribute(ATTR); if ( ta !== undefined) { - ev.preventDefault(); + if (ev.dataTransfer.getData(TYPE) !== undefined) { + ev.preventDefault(); + } } }, onDrop: (ev)=>{ ev.preventDefault(); - let dids=ev.dataTransfer.getData("text"); + let dids=ev.dataTransfer.getData(TYPE); let tdata=JSON.parse(dids); let tid=ev.currentTarget.getAttribute(ATTR); if (tid === tdata.id) return; From 491ae2279aea8550194f16e4ac57acefbfc6a16a Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 7 Aug 2024 18:12:43 +0200 Subject: [PATCH 023/138] mainly working: own dnd --- viewer/components/ItemList.jsx | 51 ++++++++++++++-------------------- viewer/hoc/Sortable.js | 40 +++++++++++++++++++------- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 31334182a..42dbf3780 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -16,6 +16,7 @@ import { DndContext, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors } from '@dnd-kit/core'; import {SortableContext} from "@dnd-kit/sortable"; +import {SortContext, SortModes} from "../hoc/Sortable"; const getKey=function(obj){ let rt=obj.key; @@ -65,10 +66,13 @@ const Content=(props)=>{
); }; - +let sid=0; +const getSid=()=>{ + sid++; + return sid; +} const ItemList = (props) => { const itemList = []; - const sortableIds = []; const existingKeys = {}; let idx = 0; const allitems = props.itemList || []; @@ -85,8 +89,7 @@ const ItemList = (props) => { itemProps.index = props.reverse ? allitems.length - idx : idx; itemProps.key = key; if (props.dragdrop) { - itemProps.dragId = idx+""; - sortableIds.push(itemProps.dragId); + itemProps.dragId = idx; } existingKeys[key] = true; if (props.selectedIndex !== undefined) { @@ -110,39 +113,27 @@ const ItemList = (props) => { if (props.fontSize) { style.fontSize = props.fontSize; } - const handleDragEnd=({active,over})=>{ + const handleDragEnd=(active,over,after)=>{ + if (after){ + if (active === (over + 1)) return; + } + else{ + if (active === (over - 1)) return; + } if (props.onSortEnd){ - props.onSortEnd(active.id,over.id); + props.onSortEnd(active,over); } } - const mouseSensor = useSensor(MouseSensor,{ - activationConstraint:{ - distance: 10 - } }); - const touchSensor = useSensor(TouchSensor,{ - activationConstraint: { - distance: 10, - }}); - const keyboardSensor = useSensor(KeyboardSensor); - - const sensors = useSensors( - mouseSensor, - touchSensor, - keyboardSensor, - ); const SortableContent = (sprops) => { - const {sortableIds, ...fwProps} = sprops; if (props.dragdrop) { return ( - - - - - + + + ) } else { - return + return } }; if (props.scrollable) { @@ -150,12 +141,12 @@ const ItemList = (props) => {
{ if (props.listRef) props.listRef(el) }}> - +
); } else { return ( - ); } diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index b638ee8ea..8f3b985b0 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -20,7 +20,7 @@ # DEALINGS IN THE SOFTWARE. */ -import React from 'react'; +import React, {createContext, useContext} from 'react'; import {useSortable} from '@dnd-kit/sortable'; import {CSS} from '@dnd-kit/utilities'; import PropTypes from "prop-types"; @@ -53,23 +53,28 @@ export const SortableProps={ export const useAvNavSortable=(id,ref)=>{ const ATTR='data-dragid'; const TYPE='application-x-avnav-dnd'; - if (id === undefined) return {}; + const context= useContext(SortContext); + if (id === undefined || context.id === undefined) return {}; let rt={ onDragStart:(ev)=>{ let data={ rect: ev.currentTarget.getBoundingClientRect(), id: id, - client: {x:ev.clientX,y:ev.clientY} + client: {x:ev.clientX,y:ev.clientY}, + ctxid: context.id }; data.offset={x:data.client.x-data.rect.left,y:data.client.y-data.rect.top} ev.dataTransfer.setData(TYPE,JSON.stringify(data)); - ev.dataTransfer.supp }, onDragOver:(ev)=>{ let ta=ev.target.getAttribute(ATTR); if ( ta !== undefined) { - if (ev.dataTransfer.getData(TYPE) !== undefined) { - ev.preventDefault(); + const tdatas=ev.dataTransfer.getData(TYPE); + if (tdatas !== undefined) { + //const tdata=JSON.parse(tdatas); + //if (tdata.ctxid === context.id) { + ev.preventDefault(); + //} } } }, @@ -77,14 +82,22 @@ export const useAvNavSortable=(id,ref)=>{ ev.preventDefault(); let dids=ev.dataTransfer.getData(TYPE); let tdata=JSON.parse(dids); - let tid=ev.currentTarget.getAttribute(ATTR); + if (tdata.ctxid !== context.id) return; + let tid=parseInt(ev.currentTarget.getAttribute(ATTR)); if (tid === tdata.id) return; let trect=ev.currentTarget.getBoundingClientRect(); let toffset={x:ev.clientX-trect.left,y:ev.clientY-trect.top}; let dragupperleft={x:toffset.x-tdata.offset.x,y:toffset.y-tdata.offset.y} - let mode="after"; - if (dragupperleft.y < 0 ) mode="before"; - console.log("drop from ",tdata.id,"to ",tid,"mode",mode,tdata,trect,dragupperleft); + let after=true; + if (context.mode === SortModes.vertical){ + if (dragupperleft.y < 0) after=false; + } + else{ + if (dragupperleft.x < 0) after=false; + } + if (context.onDragEnd){ + context.onDragEnd(tdata.id,tid,after); + } }, draggable: true, droppable: true @@ -92,3 +105,10 @@ export const useAvNavSortable=(id,ref)=>{ rt[ATTR]=id; return rt; } + +export const SortModes={ + horizontal: 0, + vertical:1 +} +export const SortContext=createContext({id:undefined,mode:SortModes.vertical,onDragEnd:undefined}); + From f42ff23ae5a8aaf94dabe4d617dce4bbd9150be4 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 7 Aug 2024 18:16:13 +0200 Subject: [PATCH 024/138] remove dnd-kit --- viewer/components/ItemList.jsx | 4 ---- viewer/hoc/Sortable.js | 22 ---------------------- viewer/package.json | 3 --- 3 files changed, 29 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 42dbf3780..6d29e594d 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -12,10 +12,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { - DndContext, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors -} from '@dnd-kit/core'; -import {SortableContext} from "@dnd-kit/sortable"; import {SortContext, SortModes} from "../hoc/Sortable"; const getKey=function(obj){ diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 8f3b985b0..8226bd579 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -21,30 +21,8 @@ */ import React, {createContext, useContext} from 'react'; -import {useSortable} from '@dnd-kit/sortable'; -import {CSS} from '@dnd-kit/utilities'; import PropTypes from "prop-types"; -export const useAvNavSortableO=(id,ref)=>{ - if (id === undefined) return {}; - const { - attributes, - listeners, - setNodeRef, - transform, - transition, - } = useSortable({id:id}); - - const nstyle = { - transform: CSS.Transform.toString(transform), - transition, - }; - const setRef=(e)=>{ - if (typeof(ref) === 'function') ref(e); - setNodeRef(e); - } - return {ref:setRef,style:nstyle,...attributes,...listeners}; -} export const SortableProps={ dragId: PropTypes.string diff --git a/viewer/package.json b/viewer/package.json index a0ce1b3f5..f4646a98b 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -58,9 +58,6 @@ }, "dependencies": { "@braintree/browser-detection": "^2.0.0", - "@dnd-kit/core": "^6.1.0", - "@dnd-kit/sortable": "^8.0.0", - "@dnd-kit/utilities": "^3.2.2", "@openlayers/pepjs": "^0.5.4", "canvas-gauges": "git+https://github.com/wellenvogel/canvas-gauges#av20220223", "clone-deep": "^4.0.1", From 16056a259388575153954caf703a1c73e1008dde Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 8 Aug 2024 17:18:03 +0200 Subject: [PATCH 025/138] intermediate: monitor sortable items refs --- viewer/components/ItemList.jsx | 9 ++-- viewer/hoc/Sortable.js | 89 +++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 10 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 6d29e594d..0c367fc1c 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -65,7 +65,7 @@ const Content=(props)=>{ let sid=0; const getSid=()=>{ sid++; - return sid; + return "itemList"+sid; } const ItemList = (props) => { const itemList = []; @@ -124,9 +124,12 @@ const ItemList = (props) => { (sprops) => { if (props.dragdrop) { return ( - + - + ) } else { return diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 8226bd579..38a9f0be2 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -23,6 +23,64 @@ import React, {createContext, useContext} from 'react'; import PropTypes from "prop-types"; +export const SortModes={ + horizontal: 0, + vertical:1 +} + +class SortHandler{ + constructor() { + this.refs={}; + this.dragging=undefined; + } + ref(id,el){ + if (el) this.refs[id]=el; + else delete this.refs[id]; + } + setDrag(id,el){ + this.dragging={id:id,el:el}; + console.log("start drag",id,el); + } + endDrag(){ + if (this.dragging){ + console.log("end drag", this.dragging); + } + this.dragging=undefined; + } + findPosition(dragUpperLeft,id){ + let bestMatching=undefined; + const d=(rect)=>{ + return { + x: dragUpperLeft.x-rect.left, + y: dragUpperLeft.y-rect.top + } + } + for (let k in this.refs){ + if (k === id) continue; + const el=this.refs[k]; + if (! el){ + continue; + } + const elrect=el.getBoundingClientRect(); + if (! bestMatching){ + bestMatching={el:el,rect:elrect,diff:d(elrect)}; + } + else{ + const cd=d(elrect); + if (Math.abs(cd.y) < Math.abs(bestMatching.diff.y)){ + bestMatching={el:el,rect:elrect,diff:cd} + } + } + } + return bestMatching; + } +} +const SortContextImpl=createContext({ + id:undefined, + mode:SortModes.vertical, + onDragEnd:undefined, + handler:undefined +}); export const SortableProps={ dragId: PropTypes.string @@ -31,7 +89,7 @@ export const SortableProps={ export const useAvNavSortable=(id,ref)=>{ const ATTR='data-dragid'; const TYPE='application-x-avnav-dnd'; - const context= useContext(SortContext); + const context= useContext(SortContextImpl); if (id === undefined || context.id === undefined) return {}; let rt={ onDragStart:(ev)=>{ @@ -43,6 +101,7 @@ export const useAvNavSortable=(id,ref)=>{ }; data.offset={x:data.client.x-data.rect.left,y:data.client.y-data.rect.top} ev.dataTransfer.setData(TYPE,JSON.stringify(data)); + context.handler.setDrag(id,ev.currentTarget); }, onDragOver:(ev)=>{ let ta=ev.target.getAttribute(ATTR); @@ -58,6 +117,7 @@ export const useAvNavSortable=(id,ref)=>{ }, onDrop: (ev)=>{ ev.preventDefault(); + context.handler.endDrag(); let dids=ev.dataTransfer.getData(TYPE); let tdata=JSON.parse(dids); if (tdata.ctxid !== context.id) return; @@ -66,6 +126,11 @@ export const useAvNavSortable=(id,ref)=>{ let trect=ev.currentTarget.getBoundingClientRect(); let toffset={x:ev.clientX-trect.left,y:ev.clientY-trect.top}; let dragupperleft={x:toffset.x-tdata.offset.x,y:toffset.y-tdata.offset.y} + let bestMatching=context.handler.findPosition({ + x:ev.clientX-tdata.offset.x, + y:ev.clientY-tdata.offset.y + },tdata.id); + console.log("best matching",bestMatching); let after=true; if (context.mode === SortModes.vertical){ if (dragupperleft.y < 0) after=false; @@ -77,16 +142,26 @@ export const useAvNavSortable=(id,ref)=>{ context.onDragEnd(tdata.id,tid,after); } }, + onDragEnd:(ev)=>{ + context.handler.endDrag(); + }, draggable: true, - droppable: true + ref: (el)=>{ + context.handler.ref(id,el); + } } rt[ATTR]=id; return rt; } -export const SortModes={ - horizontal: 0, - vertical:1 -} -export const SortContext=createContext({id:undefined,mode:SortModes.vertical,onDragEnd:undefined}); +export const SortContext=({onDragEnd,id,mode,children})=>{ + return + {children} + +} From 934df174931c2a620ba53667ca4e323fc9d71c99 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 10 Aug 2024 19:12:41 +0200 Subject: [PATCH 026/138] own dnd impl working for vertical containers --- viewer/components/ItemList.jsx | 14 +-- viewer/components/WidgetFactory.jsx | 13 +-- viewer/hoc/Sortable.js | 142 ++++++++++++++++------------ viewer/util/layouthandler.js | 3 +- 4 files changed, 91 insertions(+), 81 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 0c367fc1c..1e29aeaa7 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -12,7 +12,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {SortContext, SortModes} from "../hoc/Sortable"; +import {SortContext, SortModes, useAvNavSortFrame} from "../hoc/Sortable"; const getKey=function(obj){ let rt=obj.key; @@ -23,8 +23,10 @@ const getKey=function(obj){ const Content=(props)=>{ + const sortFrameProps=useAvNavSortFrame(); return ( -
{if (props.listRef) props.listRef(el)}} onClick={(ev)=>{ @@ -109,13 +111,7 @@ const ItemList = (props) => { if (props.fontSize) { style.fontSize = props.fontSize; } - const handleDragEnd=(active,over,after)=>{ - if (after){ - if (active === (over + 1)) return; - } - else{ - if (active === (over - 1)) return; - } + const handleDragEnd=(active,over)=>{ if (props.onSortEnd){ props.onSortEnd(active,over); } diff --git a/viewer/components/WidgetFactory.jsx b/viewer/components/WidgetFactory.jsx index ffb7bcdb9..4ad61c46d 100644 --- a/viewer/components/WidgetFactory.jsx +++ b/viewer/components/WidgetFactory.jsx @@ -248,23 +248,14 @@ export const getFormatterParameters=(widget)=>{ } } } -const Dummy=(dprops)=>{ - console.log("Dummy render",dprops); - return
{ - dprops.onClick(ev); - }} className={"widget"}>XXX:{dprops.cey}
-} -const CombinedWidget=(props)=>{ +const CombinedWidget=(props)=>{ useKeyEventHandler(props,"widget") let {editableParameters,children,onClick,childProperties,style,dragId,className,...forwardProps}=props; const ddProps = useAvNavSortable(dragId); const cl=(ev)=>{ if (onClick) onClick(ev); } - const cc=(ev)=>{ - console.log("child click",ev); - } let cidx = 0; delete childProperties.style; className = (className || '') + " widget combinedWidget"; @@ -272,7 +263,7 @@ const CombinedWidget=(props)=>{ {(children||[] ).map((item) => { let Item = theFactory.createWidget(item, childProperties); cidx++; - return + return })}
} diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 38a9f0be2..6c49f1124 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -28,51 +28,82 @@ export const SortModes={ vertical:1 } +const percentOverlap=(itstart,itext,cmpstart,cmpext)=>{ + const itend=itstart+itext; + const cmpend=cmpstart+cmpext; + if (itext === 0) return 0; + if (itstart > cmpend) return 0; + if (itend< cmpstart) return 0; + let ext=0; + if (itstart < cmpstart){ + ext=(cmpend > itend)?itend-cmpstart:cmpend-cmpstart; + } + else{ + ext=(cmpend > itend)?itend-itstart:cmpend-itstart; + } + return 100*ext/itext; +} class SortHandler{ constructor() { this.refs={}; this.dragging=undefined; } ref(id,el){ + id=parseInt(id); if (el) this.refs[id]=el; else delete this.refs[id]; } - setDrag(id,el){ - this.dragging={id:id,el:el}; - console.log("start drag",id,el); - } - endDrag(){ - if (this.dragging){ - console.log("end drag", this.dragging); + findPosition(dragRect,id,mode){ + let bestMatching=[]; + let maxv=undefined; + let minv=undefined; + let minid=undefined; + let maxid=undefined; + let itemstart=(mode===SortModes.vertical)?dragRect.top:dragRect.left; + let itemext=(mode===SortModes.vertical)?dragRect.height:dragRect.width; + const se=(rect)=>{ + if (mode===SortModes.vertical){ + return {start:rect.top,ext:rect.height}; + } + else{ + return{start:rect.left,ext:rect.width}; + } } - this.dragging=undefined; - } - findPosition(dragUpperLeft,id){ - let bestMatching=undefined; const d=(rect)=>{ - return { - x: dragUpperLeft.x-rect.left, - y: dragUpperLeft.y-rect.top - } + const {start,ext}=se(rect); + if (minv === undefined || start < minv ) minv=start; + if (maxv === undefined || (start+ext) > maxv) maxv=start+ext; + return percentOverlap(itemstart,itemext,start,ext); } for (let k in this.refs){ + k=parseInt(k); + if (minid === undefined || k < minid) minid=k; + if (maxid === undefined || k > maxid) maxid=k; if (k === id) continue; const el=this.refs[k]; if (! el){ continue; } const elrect=el.getBoundingClientRect(); - if (! bestMatching){ - bestMatching={el:el,rect:elrect,diff:d(elrect)}; - } - else{ - const cd=d(elrect); - if (Math.abs(cd.y) < Math.abs(bestMatching.diff.y)){ - bestMatching={el:el,rect:elrect,diff:cd} - } + const match=d(elrect); + if (match > 0) bestMatching.push({match:match,id:k}); + } + if (bestMatching.length < 1) { + //check above/below + if (itemstart <= minv) return minid; + if ((itemstart + itemext) > maxv) return maxid + 1; + return undefined; + } + bestMatching.sort((a,b)=>{ + return b.match - a.match; + }); + if (bestMatching[0].match < 50){ + //check if we are more after + if (bestMatching[0].id === maxid && (itemstart+itemext) > maxv){ + return maxid+1; } } - return bestMatching; + return bestMatching[0].id; } } const SortContextImpl=createContext({ @@ -86,9 +117,10 @@ export const SortableProps={ dragId: PropTypes.string } -export const useAvNavSortable=(id,ref)=>{ - const ATTR='data-dragid'; - const TYPE='application-x-avnav-dnd'; +const ATTR='data-dragid'; +const CATTR='data-dragctx'; +const TYPE='application-x-avnav-dnd'; +export const useAvNavSortable=(id)=>{ const context= useContext(SortContextImpl); if (id === undefined || context.id === undefined) return {}; let rt={ @@ -101,56 +133,46 @@ export const useAvNavSortable=(id,ref)=>{ }; data.offset={x:data.client.x-data.rect.left,y:data.client.y-data.rect.top} ev.dataTransfer.setData(TYPE,JSON.stringify(data)); - context.handler.setDrag(id,ev.currentTarget); }, + draggable: true, + ref: (el)=>{ + context.handler.ref(id,el); + } + } + rt[ATTR]=id; + return rt; +} + +export const useAvNavSortFrame=()=>{ + const context= useContext(SortContextImpl); + let rt={ onDragOver:(ev)=>{ - let ta=ev.target.getAttribute(ATTR); + let ta=ev.target.getAttribute(CATTR); if ( ta !== undefined) { const tdatas=ev.dataTransfer.getData(TYPE); if (tdatas !== undefined) { - //const tdata=JSON.parse(tdatas); - //if (tdata.ctxid === context.id) { - ev.preventDefault(); - //} + ev.preventDefault(); } } }, onDrop: (ev)=>{ ev.preventDefault(); - context.handler.endDrag(); let dids=ev.dataTransfer.getData(TYPE); let tdata=JSON.parse(dids); if (tdata.ctxid !== context.id) return; - let tid=parseInt(ev.currentTarget.getAttribute(ATTR)); - if (tid === tdata.id) return; - let trect=ev.currentTarget.getBoundingClientRect(); - let toffset={x:ev.clientX-trect.left,y:ev.clientY-trect.top}; - let dragupperleft={x:toffset.x-tdata.offset.x,y:toffset.y-tdata.offset.y} let bestMatching=context.handler.findPosition({ - x:ev.clientX-tdata.offset.x, - y:ev.clientY-tdata.offset.y - },tdata.id); - console.log("best matching",bestMatching); - let after=true; - if (context.mode === SortModes.vertical){ - if (dragupperleft.y < 0) after=false; + left:ev.clientX-tdata.offset.x, + top:ev.clientY-tdata.offset.y, + height: tdata.rect.height, + width: tdata.rect.width + },tdata.id,context.mode); + //console.log("best matching",bestMatching); + if (bestMatching !== undefined && bestMatching !== tdata.id && context.onDragEnd){ + context.onDragEnd(tdata.id,bestMatching); } - else{ - if (dragupperleft.x < 0) after=false; - } - if (context.onDragEnd){ - context.onDragEnd(tdata.id,tid,after); - } - }, - onDragEnd:(ev)=>{ - context.handler.endDrag(); - }, - draggable: true, - ref: (el)=>{ - context.handler.ref(id,el); } } - rt[ATTR]=id; + rt[CATTR]=context.id; return rt; } diff --git a/viewer/util/layouthandler.js b/viewer/util/layouthandler.js index ae9287055..ac5017fa1 100644 --- a/viewer/util/layouthandler.js +++ b/viewer/util/layouthandler.js @@ -7,6 +7,7 @@ import assign from 'object-assign'; import LocalStorage, {STORAGE_NAMES} from './localStorageManager'; import defaultLayout from '../layout/default.json'; +import {SortModes} from "../hoc/Sortable"; const DEFAULT_NAME="system.default"; class LayoutHandler{ constructor(){ @@ -532,7 +533,7 @@ class LayoutHandler{ let panelData=this.getDirectPanelData(page,panel); if (!panelData) return false; if (oldIndex < 0 || oldIndex >= panelData.length) return false; - if (newIndex < 0 || newIndex >= panelData.length) return false; + if (newIndex < 0) return false; let item=panelData[oldIndex]; panelData.splice(oldIndex,1); panelData.splice(newIndex,0,item); From a9fecec35651bd94bf6492fc0fdc9878fba34868 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 21 Aug 2024 16:32:18 +0200 Subject: [PATCH 027/138] drag and drop between contexts - desktop only --- viewer/components/ItemList.jsx | 11 +++++++---- viewer/components/WidgetFactory.jsx | 11 ++++++++++- viewer/gui/GpsPage.jsx | 17 +++++++++++++++-- viewer/hoc/Sortable.js | 19 ++++++++++++++----- viewer/util/layouthandler.js | 2 +- 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 1e29aeaa7..30c91aead 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -111,9 +111,9 @@ const ItemList = (props) => { if (props.fontSize) { style.fontSize = props.fontSize; } - const handleDragEnd=(active,over)=>{ + const handleDragEnd=(active,over,id)=>{ if (props.onSortEnd){ - props.onSortEnd(active,over); + props.onSortEnd(active,over,id); } } const SortableContent = @@ -122,7 +122,8 @@ const ItemList = (props) => { return ( @@ -164,7 +165,9 @@ ItemList.propTypes={ horizontal: PropTypes.bool, reverse: PropTypes.bool, //let the index count backwards onSortEnd: PropTypes.func, - style: PropTypes.object + style: PropTypes.object, + dragFrame: PropTypes.string, + allowOther: PropTypes.bool //allow dragging from other frames }; Content.propTypes=ItemList.propTypes; export default ItemList; \ No newline at end of file diff --git a/viewer/components/WidgetFactory.jsx b/viewer/components/WidgetFactory.jsx index 4ad61c46d..34bc3565f 100644 --- a/viewer/components/WidgetFactory.jsx +++ b/viewer/components/WidgetFactory.jsx @@ -15,8 +15,10 @@ import {createEditableParameter, EditableParameter} from "./EditableParameters"; import Compare from "../util/compare"; import CloneDeep from 'clone-deep'; import MapWidget from "./MapWidget"; -import {useAvNavSortable} from "../hoc/Sortable"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; import {useKeyEventHandler} from "../util/GuiHelpers"; +import {WidgetProps} from "./WidgetBase"; +import PropTypes from "prop-types"; export const filterByEditables=(editableParameters,values)=>{ let rt={}; @@ -267,6 +269,13 @@ const CombinedWidget=(props)=>{ })}
} +CombinedWidget.propTypes={ + ...WidgetProps, + ...SortableProps, + children: PropTypes.array, + childProperties: PropTypes.object, + editableParameters: PropTypes.array +} class WidgetFactory{ diff --git a/viewer/gui/GpsPage.jsx b/viewer/gui/GpsPage.jsx index cb2a162d2..a2278c2ea 100644 --- a/viewer/gui/GpsPage.jsx +++ b/viewer/gui/GpsPage.jsx @@ -241,7 +241,6 @@ class GpsPage extends React.Component{ } componentDidMount(){ - let self=this; resizeFont(); } @@ -280,6 +279,8 @@ class GpsPage extends React.Component{ let sum = getWeightSum(panelData.list); let prop={ name: panelName, + dragFrame: panelName, + allowOther: true, className: 'widgetContainer', itemCreator: (widget)=>{ return widgetCreator(widget,sum);}, itemList: panelData.list, @@ -287,7 +288,19 @@ class GpsPage extends React.Component{ onItemClick: (item,data) => {self.onItemClick(item,data,panelData);}, onClick: ()=>{EditWidgetDialog.createDialog(undefined,panelData.page,panelData.name,{beginning:false,weight:true,types:["!map"]});}, dragdrop: LayoutHandler.isEditing(), - onSortEnd: (oldIndex,newIndex)=>LayoutHandler.moveItem(panelData.page,panelData.name,oldIndex,newIndex) + onSortEnd: (oldIndex,newIndex,frameId)=>{ + if (frameId !== panelName){ + let oldPanel=getPanelList(frameId,self.props.pageNum || 1); + if (! oldPanel || ! oldPanel.list) return; + let item=oldPanel.list[oldIndex]; + if (! item) return; + LayoutHandler.replaceItem(oldPanel.page,oldPanel.name,oldIndex); + LayoutHandler.replaceItem(panelData.page,panelData.name,newIndex,item, LayoutHandler.ADD_MODES.beforeIndex); + } + else { + LayoutHandler.moveItem(panelData.page, panelData.name, oldIndex, newIndex) + } + } }; panelList.push(prop); }); diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 6c49f1124..04773e59a 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -110,7 +110,8 @@ const SortContextImpl=createContext({ id:undefined, mode:SortModes.vertical, onDragEnd:undefined, - handler:undefined + handler:undefined, + allowOther: false }); export const SortableProps={ @@ -159,7 +160,7 @@ export const useAvNavSortFrame=()=>{ ev.preventDefault(); let dids=ev.dataTransfer.getData(TYPE); let tdata=JSON.parse(dids); - if (tdata.ctxid !== context.id) return; + if (tdata.ctxid !== context.id && ! context.allowOther) return; let bestMatching=context.handler.findPosition({ left:ev.clientX-tdata.offset.x, top:ev.clientY-tdata.offset.y, @@ -168,7 +169,7 @@ export const useAvNavSortFrame=()=>{ },tdata.id,context.mode); //console.log("best matching",bestMatching); if (bestMatching !== undefined && bestMatching !== tdata.id && context.onDragEnd){ - context.onDragEnd(tdata.id,bestMatching); + context.onDragEnd(tdata.id,bestMatching,tdata.ctxid); } } } @@ -177,13 +178,21 @@ export const useAvNavSortFrame=()=>{ } -export const SortContext=({onDragEnd,id,mode,children})=>{ +export const SortContext=({onDragEnd,id,mode,children,allowOther})=>{ return {children} } +SortContext.propTypes={ + onDragEnd: PropTypes.func, + id: PropTypes.any, + mode: PropTypes.number, + children: PropTypes.any, + allowOther: PropTypes.bool +} diff --git a/viewer/util/layouthandler.js b/viewer/util/layouthandler.js index ac5017fa1..106a619e3 100644 --- a/viewer/util/layouthandler.js +++ b/viewer/util/layouthandler.js @@ -496,7 +496,7 @@ class LayoutHandler{ return true; } } - if (index < 0 || index >= panelData.length){ + if (index < 0 ){ return false; } if (allowAdd){ From 521f473b1644f258f9734d3fdebeba925c13b0b8 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 21 Aug 2024 17:00:05 +0200 Subject: [PATCH 028/138] handle reverse lists correctly for drag and drop --- viewer/components/ItemList.jsx | 1 + viewer/components/MapPage.jsx | 16 ++++++++++++- viewer/hoc/Sortable.js | 41 +++++++++++++++++++++------------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/viewer/components/ItemList.jsx b/viewer/components/ItemList.jsx index 30c91aead..a66f4f11f 100644 --- a/viewer/components/ItemList.jsx +++ b/viewer/components/ItemList.jsx @@ -124,6 +124,7 @@ const ItemList = (props) => { onDragEnd={handleDragEnd} id={props.dragFrame||getSid()} allowOther={props.allowOther} + reverse={props.reverse} mode={props.horizontal? SortModes.horizontal:SortModes.vertical}> diff --git a/viewer/components/MapPage.jsx b/viewer/components/MapPage.jsx index 8d8635768..dbb98da72 100644 --- a/viewer/components/MapPage.jsx +++ b/viewer/components/MapPage.jsx @@ -187,7 +187,21 @@ class MapPage extends React.Component{ }} dragdrop={globalStore.getData(keys.gui.global.layoutEditing)} horizontal={mode === 'horizontal'} - onSortEnd={(oldIndex,newIndex)=>LayoutHandler.moveItem(self.props.id,panelItems.name,oldIndex,newIndex)} + allowOther={true} + dragFrame={panel} + onSortEnd={(oldIndex,newIndex,frameId)=>{ + if (frameId === panel) { + LayoutHandler.moveItem(self.props.id, panelItems.name, oldIndex, newIndex) + } + else{ + let oldPanel=self.props.panelCreator(frameId); + if (! oldPanel || ! oldPanel.list) return; + let item=oldPanel.list[oldIndex]; + if (! item) return; + LayoutHandler.replaceItem(self.props.id,oldPanel.name,oldIndex); + LayoutHandler.replaceItem(self.props.id,panelItems.name,newIndex,item, LayoutHandler.ADD_MODES.beforeIndex); + } + }} /> }; let mapOpacity=globalStore.getData(keys.properties.nightMode) ? diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 04773e59a..63ad459e9 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -44,25 +44,27 @@ const percentOverlap=(itstart,itext,cmpstart,cmpext)=>{ return 100*ext/itext; } class SortHandler{ - constructor() { + constructor(mode,reverse) { this.refs={}; this.dragging=undefined; + this.mode=mode; + this.reverse=reverse; } ref(id,el){ id=parseInt(id); if (el) this.refs[id]=el; else delete this.refs[id]; } - findPosition(dragRect,id,mode){ + findPosition(dragRect,id){ let bestMatching=[]; let maxv=undefined; let minv=undefined; let minid=undefined; let maxid=undefined; - let itemstart=(mode===SortModes.vertical)?dragRect.top:dragRect.left; - let itemext=(mode===SortModes.vertical)?dragRect.height:dragRect.width; + let itemstart=(this.mode===SortModes.vertical)?dragRect.top:dragRect.left; + let itemext=(this.mode===SortModes.vertical)?dragRect.height:dragRect.width; const se=(rect)=>{ - if (mode===SortModes.vertical){ + if (this.mode===SortModes.vertical){ return {start:rect.top,ext:rect.height}; } else{ @@ -90,8 +92,8 @@ class SortHandler{ } if (bestMatching.length < 1) { //check above/below - if (itemstart <= minv) return minid; - if ((itemstart + itemext) > maxv) return maxid + 1; + if (itemstart <= minv) return this.reverse?maxid+1:minid; + if ((itemstart + itemext) > maxv) return this.reverse?minid:maxid + 1; return undefined; } bestMatching.sort((a,b)=>{ @@ -99,8 +101,15 @@ class SortHandler{ }); if (bestMatching[0].match < 50){ //check if we are more after - if (bestMatching[0].id === maxid && (itemstart+itemext) > maxv){ - return maxid+1; + if (this.reverse){ + if (bestMatching[0].id === maxid && itemstart < minv) { + return minid; + } + } + else { + if (bestMatching[0].id === maxid && (itemstart + itemext) > maxv) { + return maxid + 1; + } } } return bestMatching[0].id; @@ -108,7 +117,6 @@ class SortHandler{ } const SortContextImpl=createContext({ id:undefined, - mode:SortModes.vertical, onDragEnd:undefined, handler:undefined, allowOther: false @@ -160,7 +168,8 @@ export const useAvNavSortFrame=()=>{ ev.preventDefault(); let dids=ev.dataTransfer.getData(TYPE); let tdata=JSON.parse(dids); - if (tdata.ctxid !== context.id && ! context.allowOther) return; + let other=tdata.ctxid !== context.id; + if (other && ! context.allowOther) return; let bestMatching=context.handler.findPosition({ left:ev.clientX-tdata.offset.x, top:ev.clientY-tdata.offset.y, @@ -168,7 +177,7 @@ export const useAvNavSortFrame=()=>{ width: tdata.rect.width },tdata.id,context.mode); //console.log("best matching",bestMatching); - if (bestMatching !== undefined && bestMatching !== tdata.id && context.onDragEnd){ + if (bestMatching !== undefined && (other || (bestMatching !== tdata.id)) && context.onDragEnd){ context.onDragEnd(tdata.id,bestMatching,tdata.ctxid); } } @@ -178,12 +187,11 @@ export const useAvNavSortFrame=()=>{ } -export const SortContext=({onDragEnd,id,mode,children,allowOther})=>{ +export const SortContext=({onDragEnd,id,mode,children,allowOther,reverse})=>{ return {children} @@ -194,5 +202,6 @@ SortContext.propTypes={ id: PropTypes.any, mode: PropTypes.number, children: PropTypes.any, - allowOther: PropTypes.bool + allowOther: PropTypes.bool, + reverse: PropTypes.bool } From 7138ed1db07644dd4d4a99429ac20fa42bf3cd8b Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 21 Aug 2024 17:35:20 +0200 Subject: [PATCH 029/138] use polyfill for dnd touch support, better handling of back to original --- viewer/App.jsx | 1 + viewer/hoc/Sortable.js | 5 ++++- viewer/package.json | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/viewer/App.jsx b/viewer/App.jsx index 1355b3b9c..6b225c8f3 100644 --- a/viewer/App.jsx +++ b/viewer/App.jsx @@ -49,6 +49,7 @@ import splitsupport from "./util/splitsupport" import leavehandler from "./util/leavehandler"; //triggers querySplitMode import fullscreen from "./components/Fullscreen"; import mapholder from "./map/mapholder"; +import 'drag-drop-touch'; const DynamicSound=Dynamic(SoundHandler); diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 63ad459e9..94063b276 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -81,7 +81,6 @@ class SortHandler{ k=parseInt(k); if (minid === undefined || k < minid) minid=k; if (maxid === undefined || k > maxid) maxid=k; - if (k === id) continue; const el=this.refs[k]; if (! el){ continue; @@ -91,6 +90,10 @@ class SortHandler{ if (match > 0) bestMatching.push({match:match,id:k}); } if (bestMatching.length < 1) { + if (minid === undefined){ + //no elements at all + return 0; + } //check above/below if (itemstart <= minv) return this.reverse?maxid+1:minid; if ((itemstart + itemext) > maxv) return this.reverse?minid:maxid + 1; diff --git a/viewer/package.json b/viewer/package.json index f4646a98b..6710dfe1d 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -64,6 +64,7 @@ "codeflask": "git+https://github.com/wellenvogel/CodeFlask.git#20220109", "core-js": "^3.37.1", "create-react-class": "^15.7.0", + "drag-drop-touch": "^1.3.1", "fullscreen-polyfill": "^1.0.4", "geodesy": "^2.4.0", "object-assign": "^4.1.1", From 72803c3e098339b7fa2ae9927e962a0a0d77c6c6 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 21 Aug 2024 17:56:23 +0200 Subject: [PATCH 030/138] make overlay dialog working with new dnd --- viewer/components/EditOverlaysDialog.jsx | 12 +++++++++--- viewer/hoc/Sortable.js | 6 ++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/viewer/components/EditOverlaysDialog.jsx b/viewer/components/EditOverlaysDialog.jsx index 817e4e657..af15c6ba8 100644 --- a/viewer/components/EditOverlaysDialog.jsx +++ b/viewer/components/EditOverlaysDialog.jsx @@ -23,6 +23,7 @@ import featureFormatters from '../util/featureFormatter'; import chartImage from '../images/Chart60.png'; import {createEditableParameter,EditableParameter} from "./EditableParameters"; import {getKnownStyleParam} from "../map/chartsourcebase"; +import {useAvNavSortable} from "../hoc/Sortable"; const filterOverlayItem=(item,opt_itemInfo)=>{ let rt=undefined; @@ -449,8 +450,9 @@ class OverlayItemDialog extends React.Component{ const BaseElement=(props)=>{ + const dd=useAvNavSortable(props.dragId,false) return( -
+
---chart--- @@ -461,8 +463,9 @@ const BaseElement=(props)=>{ } const OverlayElement=(props)=>{ + const dd=useAvNavSortable(props.dragId); return ( -
props.onClick('select')}> +
props.onClick('select')} {...dd}>
Name{props.name} @@ -496,8 +499,10 @@ const OverlayElement=(props)=>{ }; const CombinedOverlayElement=(props)=> { + const dd=useAvNavSortable(props.dragId,false) return( { } const HiddenCombinedOverlayElement=(props)=>{ - return
+ const dd=useAvNavSortable(props.dragId); + return
} /** diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 94063b276..8244cc60f 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -132,7 +132,7 @@ export const SortableProps={ const ATTR='data-dragid'; const CATTR='data-dragctx'; const TYPE='application-x-avnav-dnd'; -export const useAvNavSortable=(id)=>{ +export const useAvNavSortable=(id,opt_nodrag)=>{ const context= useContext(SortContextImpl); if (id === undefined || context.id === undefined) return {}; let rt={ @@ -146,11 +146,13 @@ export const useAvNavSortable=(id)=>{ data.offset={x:data.client.x-data.rect.left,y:data.client.y-data.rect.top} ev.dataTransfer.setData(TYPE,JSON.stringify(data)); }, - draggable: true, ref: (el)=>{ context.handler.ref(id,el); } } + if (opt_nodrag !== false){ + rt.draggable=true; + } rt[ATTR]=id; return rt; } From 790f6666fc4eb1a78c1409bcbad1831a6eda2624 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 22 Aug 2024 10:55:54 +0200 Subject: [PATCH 031/138] correctly handle storeKeys in Dynamic if they are created from a key node --- viewer/App.jsx | 7 +++---- viewer/avnav_viewer.js | 15 +++++++++------ viewer/hoc/Dynamic.jsx | 3 ++- viewer/util/keys.jsx | 9 +++++++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/viewer/App.jsx b/viewer/App.jsx index 6b225c8f3..0f41de9d9 100644 --- a/viewer/App.jsx +++ b/viewer/App.jsx @@ -492,13 +492,12 @@ class App extends React.Component { tabIndex="0" > { - lateLoads.push(script); - }) - } + const loadScripts=(loadList)=>{ let fileref=undefined; for (let i in loadList) { @@ -154,6 +149,14 @@ export default function() { document.getElementsByTagName("head")[0].appendChild(fileref) } }; + let addScripts="addScripts"; + if (getParam(addScripts)){ + let addList=[]; + getParam(addScripts).split(',').forEach((script)=>{ + addList.push(script); + }) + loadScripts(addList); + } const doLateLoads=(loadPlugins)=>{ ReactDOM.createRoot(document.getElementById('new_pages')).render(); diff --git a/viewer/hoc/Dynamic.jsx b/viewer/hoc/Dynamic.jsx index c188a8142..b1b7cae42 100644 --- a/viewer/hoc/Dynamic.jsx +++ b/viewer/hoc/Dynamic.jsx @@ -9,6 +9,7 @@ import globalStore from "../util/globalstore.jsx"; import React from 'react'; +import {KeyHelper} from "../util/keys"; export default function(Component,opt_options,opt_store){ @@ -44,7 +45,7 @@ export default function(Component,opt_options,opt_store){ if (! (storeKeys instanceof Object)){ throw Error("store keys is no object",storeKeys); } - return storeKeys; + return KeyHelper.removeNodeInfo(storeKeys); } getTranslatedStoreValues(){ const keys=this.getStoreKeys(); diff --git a/viewer/util/keys.jsx b/viewer/util/keys.jsx index 6edd7d9b6..b2d7cf559 100644 --- a/viewer/util/keys.jsx +++ b/viewer/util/keys.jsx @@ -562,6 +562,15 @@ export const KeyHelper = { */ getValueKeys:()=>{ return valueKeys; + }, + removeNodeInfo:(keys)=>{ + if (! (keys instanceof Object)) return keys; + if (keys.__path !== undefined) { + let rt={...keys}; + delete rt.__path; + return rt; + } + return keys; } }; From c2da8420826c073e5dcbd2218f55529067fa27c2 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 22 Aug 2024 11:12:15 +0200 Subject: [PATCH 032/138] add border to bottom panels when editing --- viewer/style/avnav_viewer_new.less | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/viewer/style/avnav_viewer_new.less b/viewer/style/avnav_viewer_new.less index 1496ec06e..4528fcb6d 100644 --- a/viewer/style/avnav_viewer_new.less +++ b/viewer/style/avnav_viewer_new.less @@ -1626,6 +1626,16 @@ span.valuePrefix{ .flex-direction(row); } } + &.editing{ + .bottomLeft { + border-right: 0.3em solid; + .nightBorderFade(@editingColor); + } + .bottomRight { + border-left: 0.3em solid; + .nightBorderFade(@editingColor); + } + } .widgetContainer.left{ position: absolute; bottom:0; From 1583b83a84ccbe1379bf8f66e2b5fe6894ffa21b Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 23 Aug 2024 13:06:02 +0200 Subject: [PATCH 033/138] handle drag and drop for gauges --- viewer/components/CanvasGauges.jsx | 164 +++++++++++++++-------------- viewer/hoc/Sortable.js | 2 +- 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/viewer/components/CanvasGauges.jsx b/viewer/components/CanvasGauges.jsx index 3fa722386..3b7967dc1 100644 --- a/viewer/components/CanvasGauges.jsx +++ b/viewer/components/CanvasGauges.jsx @@ -2,14 +2,14 @@ * Created by andreas on 23.02.16. */ -import React from "react"; +import React, {useEffect, useRef} from "react"; import PropTypes from 'prop-types'; -import Helper from '../util/helper.js'; -import Value from './Value.jsx'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; import {RadialGauge,LinearGauge} from 'canvas-gauges'; import base from '../base.js'; import assign from 'object-assign'; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; export const getTicks=(minValue,maxValue,number)=>{ if (minValue === undefined || maxValue === undefined || number === undefined) return; @@ -23,7 +23,7 @@ export const getTicks=(minValue,maxValue,number)=>{ } //refer to https://canvas-gauges.com/documentation/user-guide/configuration const defaultTranslateFunction=(props)=>{ - let rt=props; + let rt={...props}; let defaultColors=props.nightMode?nightColors:normalColors; if (! rt.colorText) rt.colorText=defaultColors.text; let textColorNames=['colorTitle','colorUnits','colorNumbers','colorStrokeTicks','colorMajorTicks','colorMinorTicks','colorValueText']; @@ -51,68 +51,46 @@ const nightColors={ needle: 'rgba(252, 11, 11, 0.6)' } -class Gauge extends React.Component{ - constructor(props){ - super(props); - this.canvasRef=this.canvasRef.bind(this); - this.renderCanvas=this.renderCanvas.bind(this); - GuiHelper.nameKeyEventHandler(this,"widget"); - this.gauge=undefined; +const getProps=(props)=>{ + let rt=props.translateFunction?defaultTranslateFunction(props.translateFunction({...props})):defaultTranslateFunction(props); + for (let k in rt){ + if (rt[k] === undefined) delete rt[k]; } - getProps(){ - let props=assign({},this.props); - let rt=this.props.translateFunction?defaultTranslateFunction(this.props.translateFunction(props)):defaultTranslateFunction(props); - for (let k in rt){ - if (rt[k] === undefined) delete rt[k]; - } - if (props.minValue !== undefined) props.minValue=parseFloat(props.minValue); - if (props.maxValue !== undefined) props.maxValue=parseFloat(props.maxValue); - return rt; - } - render(){ - let props=this.getProps(); - let defaultColors=props.nightMode?nightColors:normalColors; - let classes="widget canvasGauge"; - if (props.className) classes+=" "+props.className; - if (props.typeClass) classes+=" "+props.typeClass; - let style=props.style||{}; - let value=props.value; - if (typeof (this.props.formatter) === 'function'){ - value=this.props.formatter(value); + if (rt.minValue !== undefined) rt.minValue=parseFloat(rt.minValue); + if (rt.maxValue !== undefined) rt.maxValue=parseFloat(rt.maxValue); + return rt; +} + +const Gauge =(rprops)=>{ + useKeyEventHandler(rprops,"widget"); + const ddProps = useAvNavSortable(rprops.dragId); + let canvas = useRef(null); + let gauge = useRef(undefined); + useEffect(()=>{ + return ()=>{ + if (gauge.current) gauge.current.destroy(); } - let textColor=props.colorText?props.colorText:defaultColors.text; - let textStyle={color:textColor}; - return ( -
-
- {props.drawValue? -
{value}
:null} - -
- {(props.caption !== undefined )?
{props.caption}
:null} - {(props.unit !== undefined)? -
{props.unit}
- :null} -
- ); - } - componentDidUpdate(){ - this.renderCanvas(); + },[]) + let frame = useRef(null); + let value=useRef(null); + const props=getProps(rprops); + let nvalue=props.value; + if (typeof (props.formatter) === 'function'){ + nvalue=props.formatter(nvalue); } - canvasRef(item){ - this.canvas=item; - setTimeout(this.renderCanvas,0); - } - renderCanvas(){ - if (! this.canvas) return; + nvalue=parseFloat(nvalue); + if (nvalue < props.minValue) nvalue=props.minValue; + if (nvalue > props.maxValue) nvalue=props.maxValue; + const renderCanvas=()=>{ + if (!canvas.current) return; let rect=undefined; - if (this.refs.frame){ - rect=this.refs.frame.getBoundingClientRect(); + if (frame.current){ + rect=frame.current.getBoundingClientRect(); } else { - rect = this.canvas.getBoundingClientRect(); + rect = canvas.current.getBoundingClientRect(); } - let props=this.getProps(); + let props=getProps(rprops); if (props.minValue === undefined) return; if (props.maxValue === undefined) return; let makeSquare=(props.makeSquare === undefined) || props.makeSquare; @@ -123,48 +101,70 @@ class Gauge extends React.Component{ width=wh; height=wh; } - if (this.refs.value){ + if (value.current){ try { let factor=parseFloat(props.valueFontFactor||12); - let fs = parseFloat(window.getComputedStyle(this.refs.value).fontSize); + let fs = parseFloat(window.getComputedStyle(value.current).fontSize); if (fs != wh/factor) { - this.refs.value.style.fontSize=(wh/factor)+"px"; + value.current.style.fontSize=(wh/factor)+"px"; } else{ - this.refs.value.style.fontSize=undefined; + value.current.style.fontSize=undefined; } }catch(e){} } - let value=props.value; - if (typeof (props.formatter) === 'function'){ - value=props.formatter(value); - } - value=parseFloat(value); - if (value < props.minValue) value=props.minValue; - if (value > props.maxValue) value=props.maxValue; - if (! this.gauge){ + if (!gauge.current){ try { - let options = assign({}, props, {renderTo: this.canvas,width:width,height:height,value:value}); - this.gauge = new this.props.gauge(options).draw(); + let options = {...props, renderTo: canvas.current,width:width,height:height,value:nvalue}; + gauge.current = new props.gauge(options).draw(); return; }catch(e){ base.log("gauge error:"+e); } } - if (! this.gauge) return; - this.gauge.value=value; - this.gauge.update(assign({},props,{width:width,height:height,value:value})); + if (! gauge.current) return; + gauge.current.value=nvalue; + gauge.current.update({...props,width:width,height:height,value:nvalue}); } + const canvasRef = (item) => { + if (item ) { + if (item !== canvas.current) { + if (gauge.current) gauge.current.destroy(); + gauge.current=undefined; + } + canvas.current = item; + setTimeout(renderCanvas, 0); + } + } + let defaultColors=props.nightMode?nightColors:normalColors; + let classes="widget canvasGauge"; + if (props.className) classes+=" "+props.className; + if (props.typeClass) classes+=" "+props.typeClass; + let style=props.style||{}; + let textColor=props.colorText?props.colorText:defaultColors.text; + let textStyle={color:textColor}; + return ( +
+
+ {props.drawValue? +
{nvalue}
:null} + +
+ {(props.caption !== undefined )?
{props.caption}
:null} + {(props.unit !== undefined)? +
{props.unit}
+ :null} +
+ ); }; Gauge.propTypes={ + ...WidgetProps, + ...SortableProps, gauge: PropTypes.oneOfType([PropTypes.object,PropTypes.func]).isRequired, name: PropTypes.string, unit: PropTypes.string, - caption: PropTypes.string, - onClick: PropTypes.func, - className: PropTypes.string, typeClass: PropTypes.string, style: PropTypes.object, default: PropTypes.string, @@ -174,7 +174,9 @@ Gauge.propTypes={ formatter: PropTypes.oneOfType([PropTypes.string,PropTypes.func]), formatterParameters: PropTypes.array, translateFunction: PropTypes.func, //if set: a function to translate options - valueFontFactor: PropTypes.number + valueFontFactor: PropTypes.number, + minValue: PropTypes.number, + maxValue: PropTypes.number //all the options from canvas-gauges, see //https://canvas-gauges.com/documentation/user-guide/configuration }; diff --git a/viewer/hoc/Sortable.js b/viewer/hoc/Sortable.js index 8244cc60f..d236fa9f3 100644 --- a/viewer/hoc/Sortable.js +++ b/viewer/hoc/Sortable.js @@ -126,7 +126,7 @@ const SortContextImpl=createContext({ }); export const SortableProps={ - dragId: PropTypes.string + dragId: PropTypes.number } const ATTR='data-dragid'; From 1f0f84228483cba76e1ab1d086b233a47341bc46 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 23 Aug 2024 13:20:01 +0200 Subject: [PATCH 034/138] drag and drop for SK widgets --- viewer/components/SKWidgets.jsx | 101 +++++++++++--------------- viewer/components/UndefinedWidget.jsx | 27 +++---- 2 files changed, 54 insertions(+), 74 deletions(-) diff --git a/viewer/components/SKWidgets.jsx b/viewer/components/SKWidgets.jsx index 67d569f11..9a838eab5 100644 --- a/viewer/components/SKWidgets.jsx +++ b/viewer/components/SKWidgets.jsx @@ -3,9 +3,11 @@ */ import React from 'react'; -import GuiHelper from "../util/GuiHelpers"; +import {useKeyEventHandler} from "../util/GuiHelpers"; import Formatter from "../util/formatter"; import PropTypes from "prop-types"; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; const rad2deg=(rad,inDeg)=>{ if (inDeg) return parseFloat(rad); @@ -14,48 +16,40 @@ const rad2deg=(rad,inDeg)=>{ const DegreeFormatter = (value,inDeg)=> { if (value === undefined) return "???"; - return avnav.api.formatter.formatDecimal(Math.abs(rad2deg(value,inDeg)), 4, 0); + return Formatter.formatDecimal(Math.abs(rad2deg(value,inDeg)), 4, 0,false,false); }; -export class SKRollWidget extends React.Component{ - - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); - } - - render(){ - let value=DegreeFormatter(this.props.value,this.props.inDegree); - let degreeArrow = "0"; +export const SKRollWidget=(props)=>{ + useKeyEventHandler(props,"widget"); + const dd=useAvNavSortable(props.dragId) + let value=DegreeFormatter(props.value,props.inDegree); + let degreeArrow = "--"; // arrow left + Wert - if (this.props.value <0 && this.props.value !== 0){ + if (props.value <0 && props.value !== 0){ degreeArrow = "\u21D0" + value; } // value + space + arrow right - if (this.props.value >0 && this.props.value !== 0){ + if (props.value >0 && props.value !== 0){ degreeArrow = value + "\xA0\u21D2"; } - let classes="widget SKRollWidget "+this.props.className||""; + let classes="widget SKRollWidget "+props.className||""; let wdClasses="widgetData"; - if (Math.abs(rad2deg(this.props.value,this.props.inDegree)) >= this.props.criticalValue){ + if (Math.abs(rad2deg(props.value,props.inDegree)) >= props.criticalValue){ wdClasses+=" critical"; } return ( -
-
{this.props.caption}
-
{this.props.unit}
+
+
{props.caption}
+
{props.unit}
{degreeArrow}
); } -} SKRollWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, - style: PropTypes.object, - caption: PropTypes.string, + ...SortableProps, + ...WidgetProps, unit: PropTypes.string, value: PropTypes.oneOfType([PropTypes.string,PropTypes.number]), criticalValue: PropTypes.number, @@ -71,45 +65,36 @@ SKRollWidget.editableParameters={ caption: {type:'STRING',default:'Roll'} } -export class SKPitchWidget extends React.Component{ - - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget"); +export const SKPitchWidget = (props) => { + useKeyEventHandler(props, "widget") + const dd = useAvNavSortable(props.dragId); + let value = DegreeFormatter(props.value, props.inDegree); + let degreeArrow = "--"; + // arrow left + Wert + if (props.value < 0 && props.value !== 0) { + degreeArrow = value + "\xA0\u21D3"; } - - render(){ - let value=DegreeFormatter(this.props.value,this.props.inDegree); - let degreeArrow = "0"; - // arrow left + Wert - if (this.props.value <0 && this.props.value !== 0){ - degreeArrow = value + "\xA0\u21D3"; - } - // value + space + arrow right - if (this.props.value >0 && this.props.value !== 0){ - degreeArrow = value + "\xA0\u21D1"; - } - let classes="widget SKPitchWidget "+this.props.className||""; - let wdClasses="widgetData"; - if (Math.abs(rad2deg(this.props.value,this.props.inDegree)) >= this.props.criticalValue){ - wdClasses+=" critical"; - } - return ( -
-
{this.props.caption}
-
{this.props.unit}
-
{degreeArrow}
-
- ); + // value + space + arrow right + if (props.value > 0 && props.value !== 0) { + degreeArrow = value + "\xA0\u21D1"; } - + let classes = "widget SKPitchWidget " + props.className || ""; + let wdClasses = "widgetData"; + if (Math.abs(rad2deg(props.value, props.inDegree)) >= props.criticalValue) { + wdClasses += " critical"; + } + return ( +
+
{props.caption}
+
{props.unit}
+
{degreeArrow}
+
+ ); } SKPitchWidget.propTypes={ - onClick: PropTypes.func, - className: PropTypes.string, - style: PropTypes.object, - caption: PropTypes.string, + ...SortableProps, + ...WidgetProps, unit: PropTypes.string, value: PropTypes.oneOfType([PropTypes.string,PropTypes.number]), criticalValue: PropTypes.number, diff --git a/viewer/components/UndefinedWidget.jsx b/viewer/components/UndefinedWidget.jsx index 27d98dded..4a8bff7ae 100644 --- a/viewer/components/UndefinedWidget.jsx +++ b/viewer/components/UndefinedWidget.jsx @@ -3,22 +3,16 @@ */ import React from "react"; -import PropTypes from 'prop-types'; -import Helper from '../util/helper.js'; -import GuiHelper from '../util/GuiHelpers.js'; +import {useKeyEventHandler} from '../util/GuiHelpers.js'; +import {SortableProps, useAvNavSortable} from "../hoc/Sortable"; +import {WidgetProps} from "./WidgetBase"; -class UndefinedWidget extends React.Component{ - constructor(props){ - super(props); - GuiHelper.nameKeyEventHandler(this,"widget") - } - shouldComponentUpdate(nextProps,nextState) { - return false; - } - render(){ - let classes="widget undefinedWidget"; - return ( -
+const UndefinedWidget=(props)=>{ + useKeyEventHandler(props,"widget"); + const dd=useAvNavSortable() + let classes="widget undefinedWidget"; + return ( +
{this.props.name} @@ -28,9 +22,10 @@ class UndefinedWidget extends React.Component{
); } -} UndefinedWidget.propTypes={ + ...SortableProps, + ...WidgetProps }; From 3a3daefab1bb7b2dc632c0f350b92c0d993e36e6 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 23 Aug 2024 13:40:55 +0200 Subject: [PATCH 035/138] correct warnings --- viewer/App.jsx | 16 +++++++----- viewer/avnav_viewer.js | 6 ++--- viewer/components/MapPage.jsx | 9 +++---- viewer/components/SoundHandler.jsx | 42 ++++++++++++++---------------- viewer/gui/MainPage.jsx | 1 - 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/viewer/App.jsx b/viewer/App.jsx index 0f41de9d9..85c91321c 100644 --- a/viewer/App.jsx +++ b/viewer/App.jsx @@ -1,9 +1,9 @@ //avnav (C) wellenvogel 2019 -import React, { Component } from 'react'; +import React, {Component, createRef} from 'react'; import History from './util/history.js'; import Dynamic from './hoc/Dynamic.jsx'; -import keys,{KeyHelper} from './util/keys.jsx'; +import keys from './util/keys.jsx'; import MainPage from './gui/MainPage.jsx'; import InfoPage from './gui/InfoPage.jsx'; import GpsPage from './gui/GpsPage.jsx'; @@ -29,7 +29,6 @@ import SoundHandler from './components/SoundHandler.jsx'; import Toast,{ToastDisplay} from './components/Toast.jsx'; import KeyHandler from './util/keyhandler.js'; import LayoutHandler from './util/layouthandler.js'; -import assign from 'object-assign'; import AlarmHandler, {LOCAL_TYPES} from './nav/alarmhandler.js'; import GuiHelpers, {stateHelper} from './util/GuiHelpers.js'; import Mob from './components/Mob.js'; @@ -44,7 +43,7 @@ import propertyHandler from "./util/propertyhandler"; import MapHolder from "./map/mapholder"; import NavData from './nav/navdata'; import alarmhandler from "./nav/alarmhandler.js"; -import LocalStorage, {PREFIX_NAMES, STORAGE_NAMES} from './util/localStorageManager'; +import LocalStorage, {STORAGE_NAMES} from './util/localStorageManager'; import splitsupport from "./util/splitsupport" import leavehandler from "./util/leavehandler"; //triggers querySplitMode import fullscreen from "./components/Fullscreen"; @@ -91,6 +90,8 @@ class MainWrapper extends React.Component{ this.props.history.reset(); //reset history if we reach the mainpage } } +MainWrapper.propTypes=MainPage.propTypes; + const pages={ mainpage: MainWrapper, infopage: InfoPage, @@ -162,6 +163,7 @@ let lastError={ }; class App extends React.Component { + appRef=createRef(); constructor(props) { super(props); this.checkSizes=this.checkSizes.bind(this); @@ -388,8 +390,8 @@ class App extends React.Component { } checkSizes(){ if (globalStore.getData(keys.gui.global.hasActiveInputs,false)) return; - if (! this.refs.app) return; - let current=this.refs.app.getBoundingClientRect(); + if (! this.appRef.current) return; + let current=this.appRef.current.getBoundingClientRect(); if (! current) return; let small = current.width diff --git a/viewer/avnav_viewer.js b/viewer/avnav_viewer.js index b8ef6214e..aff018440 100644 --- a/viewer/avnav_viewer.js +++ b/viewer/avnav_viewer.js @@ -34,9 +34,7 @@ import splitsupport from "./util/splitsupport"; if (getParam('dimm')) avnav.testDim=true; import React from 'react'; -import ReactDOM from 'react-dom'; import propertyHandler from './util/propertyhandler'; -import OverlayDialog from './components/OverlayDialog.jsx'; import App from './App.jsx'; import keys from './util/keys.jsx'; import globalStore from './util/globalstore.jsx'; @@ -50,7 +48,7 @@ import assign from 'object-assign'; import LeaveHandler from './util/leavehandler'; import isIosSafari from '@braintree/browser-detection/is-ios-safari'; import LocalStorage, {PREFIX_NAMES} from './util/localStorageManager'; -import debugSupport from 'debugSupport.js'; +import {createRoot} from "react-dom/client"; if (! window.avnav){ @@ -159,7 +157,7 @@ export default function() { } const doLateLoads=(loadPlugins)=>{ - ReactDOM.createRoot(document.getElementById('new_pages')).render(); + createRoot(document.getElementById('new_pages')).render(); //ios browser sometimes has issues with less... setTimeout(function(){ propertyHandler.incrementSequence(); diff --git a/viewer/components/MapPage.jsx b/viewer/components/MapPage.jsx index dbb98da72..0f3b86b10 100644 --- a/viewer/components/MapPage.jsx +++ b/viewer/components/MapPage.jsx @@ -9,7 +9,7 @@ import Visible from '../hoc/Visible.jsx'; import ItemList from '../components/ItemList.jsx'; import globalStore from '../util/globalstore.jsx'; import keys from '../util/keys.jsx'; -import React from 'react'; +import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import Page from '../components/Page.jsx'; import Toast from '../components/Toast.jsx'; @@ -27,7 +27,6 @@ import mapholder from "../map/mapholder.js"; import Helper from "../util/helper"; import assign from 'object-assign'; import LocalStorage, {STORAGE_NAMES} from '../util/localStorageManager'; -import {anchorWatchDialog, isWatchActive} from "./AnchorWatchDialog"; import {DynamicTitleIcons} from "./TitleIcons"; const SHOW_MODE={ @@ -94,9 +93,9 @@ const widgetCreator=(widget,mode)=>{ }; class MapPage extends React.Component{ + mapRef=createRef(); constructor(props){ super(props); - let self=this; this.mapEvent=this.mapEvent.bind(this); this.subscribeToken=undefined; this.bottomContainer=undefined; @@ -142,7 +141,7 @@ class MapPage extends React.Component{ setShown(chartEntry.url,INFO_TYPES.info); } } - MapHolder.loadMap(this.refs.map, this.props.preventCenterDialog). + MapHolder.loadMap(this.mapRef.current, this.props.preventCenterDialog). then((result)=>{ this.computeScalePosition(); }). @@ -206,7 +205,7 @@ class MapPage extends React.Component{ }; let mapOpacity=globalStore.getData(keys.properties.nightMode) ? globalStore.getData(keys.properties.nightChartFade, 100) / 100:1; - let map=
+ let map=
; let className=self.props.className?self.props.className+" mapPage":"mapPage"; diff --git a/viewer/components/SoundHandler.jsx b/viewer/components/SoundHandler.jsx index 65f10b72d..15e7251fa 100644 --- a/viewer/components/SoundHandler.jsx +++ b/viewer/components/SoundHandler.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import PropertyHandler from '../util/propertyhandler.js'; import Toast from '../components/Toast.jsx'; @@ -7,6 +7,7 @@ import globalStore from '../util/globalstore'; import keys from '../util/keys'; class SoundHandler extends React.Component{ + audioRef=createRef(); constructor(props){ super(props); this.state={ @@ -22,20 +23,19 @@ class SoundHandler extends React.Component{ } dataChanged(){ this.volume=globalStore.getData(keys.properties.alarmVolume,50); - if (this.refs.audio){ - this.refs.audio.volume=this.volume/100.0; + if (this.audioRef.current){ + this.audioRef.current.volume=this.volume/100.0; } } render(){ - let self=this; - return