diff --git a/package-lock.json b/package-lock.json index 5db3e1e..d5f7465 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "prop-types": "^15.8.1", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", + "react-bootstrap": "^2.8.0", "react-dom": "^18.2.0", "uuid": "^9.0.0", "webpack": "^5.88.1", @@ -1924,12 +1925,77 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "dev": true, - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-aria/ssr": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.1.tgz", + "integrity": "sha512-ovVPSD1WlRpZHt7GI9DqJrWG3OIYS+NXQ9y5HIewMJpSe+jPQmMQfyRmgX4EnvmxSlp0u04Wg/7oItcoSIb/RA==", + "dev": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz", + "integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "dev": true, + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/eslint": { "version": "8.44.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz", @@ -2007,12 +2073,27 @@ "redux": "^4.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", "dev": true }, + "node_modules/@types/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", + "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==", + "dev": true + }, "node_modules/@uidotdev/usehooks": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.1.1.tgz", @@ -2799,6 +2880,12 @@ "node": ">=6.0" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==", + "dev": true + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -2944,6 +3031,25 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.454", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.454.tgz", @@ -3408,6 +3514,15 @@ "node": ">=10.13.0" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3881,6 +3996,25 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dev": true, + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/prop-types-extra/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3948,6 +4082,36 @@ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-bootstrap": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz", + "integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.3", + "@types/react-transition-group": "^4.4.5", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3967,6 +4131,12 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true + }, "node_modules/react-redux": { "version": "7.2.9", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", @@ -3992,6 +4162,22 @@ } } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4425,6 +4611,27 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -4522,6 +4729,15 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 664e7ce..0bec725 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "jquery": "^3.7.0", "prop-types": "^15.8.1", "react": "^18.2.0", + "react-bootstrap": "^2.8.0", "react-beautiful-dnd": "^13.1.1", "react-dom": "^18.2.0", "uuid": "^9.0.0", diff --git a/sass/css/_shop.scss b/sass/css/_shop.scss index 6b4e3e0..6762ee8 100644 --- a/sass/css/_shop.scss +++ b/sass/css/_shop.scss @@ -570,6 +570,7 @@ button { display: inline; height: .875rem; margin-right: 0.2rem; + margin-left: 0.2rem; } } diff --git a/static/js/shop_components.jsx b/static/js/shop_components.jsx index 0eeecbe..f73259c 100644 --- a/static/js/shop_components.jsx +++ b/static/js/shop_components.jsx @@ -4,6 +4,7 @@ import React, {Component} from "react"; import jsonLogic from 'json-logic-js'; import {useState, useEffect} from 'react'; import {useClickAway} from "@uidotdev/usehooks"; +import {OverlayTrigger, Tooltip} from "react-bootstrap"; // https://stackoverflow.com/a/70511311 const trueTypeOf = (obj) => Object.prototype.toString.call(obj).slice(8, -1).toLowerCase(); @@ -40,9 +41,22 @@ class Radio extends Component { return (
- {this.props.icon ? : null} + {this.props.icon ? : null} {this.props.title}
+ { + this.props.tip ? + ( + {this.props.tip} + + }> + + ) + : + null + } {this.props.variants.map((variant, _) => (
; +function RadioWrapper(target, id, data, {title, variants, outvar, icon, tip}) { + return ; } class Switch extends Component { @@ -108,18 +123,32 @@ class Switch extends Component { onClick={this.handleClick} onChange={this.handleClick} /> -
); } } -function SwitchWrapper(target, id, data, {title, fallback, outvar, icon}) { - return ; +function SwitchWrapper(target, id, data, {title, fallback, outvar, icon, tip}) { + return ; } @@ -151,10 +180,24 @@ class Line extends Component { let key = this.props.id + this.props.outvar; return (
+ + { + this.props.tip ? + ( + {this.props.tip} + + }> + + ) + : + null + }
@@ -162,8 +205,9 @@ class Line extends Component { } } -function LineWrapper(target, id, data, {title, fallback, outvar, icon}) { - return ; +function LineWrapper(target, id, data, {title, fallback, outvar, icon, tip}) { + return ; } class SwitchLine extends Component { @@ -218,9 +262,22 @@ class SwitchLine extends Component { onChange={this.handleCheck} /> + { + this.props.tip ? + ( + {this.props.tip} + + }> + + ) + : + null + } @@ -229,8 +286,9 @@ class SwitchLine extends Component { } } -function SwitchLineWrapper(target, id, data, {title, fallback, outvar, icon}) { - return ; +function SwitchLineWrapper(target, id, data, {title, fallback, outvar, icon, tip}) { + return ; } diff --git a/static/js/shop_data.js b/static/js/shop_data.js index d4ae209..7591708 100644 --- a/static/js/shop_data.js +++ b/static/js/shop_data.js @@ -76,13 +76,13 @@ const shop_data = { ] }, [ - {type: "Line", args: {title: "IPv4", outvar: "ipv4", fallback: "192.168.1.75/24"}}, + {type: "Line", args: {title: "IPv4", outvar: "ipv4", fallback: "192.168.1.75/24", tip: "Set up IPv4 address used by core device"}}, {type: "SwitchLine", args: {title: "IPv6", outvar: "ipv6"}}, - {type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}}, - {type: "Switch", args: {title: "Email device_db", outvar: "out", icon: "/shop/icon-leaf.svg"}}, + {type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}, tip: "Use external clock reference: 10, 80 (beta), 100 or 125 MHz. Other variants may be provided if needed."}}, + {type: "Switch", args: {title: "Email device_db", outvar: "out", icon: "/shop/icon-leaf.svg", tip: "Send the device_db.py file by email instead of USB-drive"}}, ], [ - {type: "Switch", args: {title: "Optic fiber instead of copper cable", outvar: "optics"}} + {type: "Switch", args: {title: "Optic fiber", outvar: "optics", tip: "Use optic fiber instead of direct attach copper cable"}} ] ] }