Add text options validation
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
be50b2a3c3
commit
51c9031f24
@ -73,6 +73,10 @@
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.options-invalid {
|
||||
box-shadow: 0 0 0 .25rem rgba(229, 62, 62, 0.25)!important;
|
||||
--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important;
|
||||
}
|
||||
/*
|
||||
##Device = Tablets, Ipads (portrait)
|
||||
##Screen = B/w 768px to 1024px
|
||||
|
@ -1,22 +1,25 @@
|
||||
import React, {Component} from "react";
|
||||
import {Tip} from "./Tip";
|
||||
import {Validation} from "../validation";
|
||||
|
||||
class Line extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Initialize the state object with the initial values from the props
|
||||
this.state = {
|
||||
text: props.outvar in props.data ? props.data[props.outvar] : (props.fallback ? props.fallback : "")
|
||||
text: props.outvar in props.data ? props.data[props.outvar] : (props.fallback ? props.fallback : ""),
|
||||
valid: true
|
||||
};
|
||||
// Bind the event handler to this
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.props.target.construct(this.props.outvar, this.state.text);
|
||||
}
|
||||
|
||||
handleClick(element) {
|
||||
handleChange(element) {
|
||||
let text = element.target.value;
|
||||
this.setState({
|
||||
text: text
|
||||
text: text,
|
||||
valid: this.props.validator ? this.props.validator(text) : true
|
||||
});
|
||||
this.props.target.update(this.props.outvar, text);
|
||||
}
|
||||
@ -39,14 +42,14 @@ class Line extends Component {
|
||||
{this.props.title}:
|
||||
</label>
|
||||
{this.props.tip && <Tip id={this.props.id + "tooltip"} tip={this.props.tip}/>}
|
||||
<input type="text" className="form-control form-control-sm" id={key} onChange={this.handleClick}
|
||||
<input type="text" className={`form-control form-control-sm ${this.state.valid ? "" : "options-invalid"}`} id={key} onChange={this.handleChange}
|
||||
value={this.state.text}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function LineWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes}) {
|
||||
export function LineWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes, validator}) {
|
||||
return <Line target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id}
|
||||
id={id} data={data} classes={classes}/>;
|
||||
id={id} data={data} classes={classes} validator={validator && Validation[validator.name](validator.params)}/>;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import React, {Component} from "react";
|
||||
import {Tip} from "./Tip";
|
||||
import {Validation} from "../validation";
|
||||
|
||||
class SwitchLine extends Component {
|
||||
constructor(props) {
|
||||
@ -7,7 +8,8 @@ class SwitchLine extends Component {
|
||||
// Initialize the state object with the initial values from the props
|
||||
this.state = {
|
||||
text: props.outvar in props.data ? props.data[props.outvar].text : (props.fallback ? props.fallback.text : ""),
|
||||
checked: props.outvar in props.data ? props.data[props.outvar].checked : (props.fallback ? props.fallback.checked : false)
|
||||
checked: props.outvar in props.data ? props.data[props.outvar].checked : (props.fallback ? props.fallback.checked : false),
|
||||
valid: true
|
||||
};
|
||||
// Bind the event handler to this
|
||||
this.handleText = this.handleText.bind(this);
|
||||
@ -18,7 +20,8 @@ class SwitchLine extends Component {
|
||||
handleText(element) {
|
||||
let new_state = {
|
||||
...this.state,
|
||||
text: element.target.value
|
||||
text: element.target.value,
|
||||
valid: this.props.validator ? this.props.validator(element.target.value) : true
|
||||
}
|
||||
this.setState(new_state);
|
||||
this.props.target.update(this.props.outvar, new_state);
|
||||
@ -39,6 +42,7 @@ class SwitchLine extends Component {
|
||||
return {
|
||||
checked: props.data[props.outvar].checked,
|
||||
text: props.data[props.outvar].text,
|
||||
valid: this.props.validator ? this.props.validator(props.data[props.outvar].text) : true
|
||||
}
|
||||
}
|
||||
return null
|
||||
@ -64,14 +68,14 @@ class SwitchLine extends Component {
|
||||
</label>
|
||||
{this.props.tip && <Tip id={this.props.id + "tooltip"} tip={this.props.tip}/>}
|
||||
</div>
|
||||
<input type="text" className="form-control form-control-sm" id={key + "line"} onChange={this.handleText}
|
||||
<input type="text" className={`form-control form-control-sm ${this.state.valid ? "" : "options-invalid"}`} id={key + "line"} onChange={this.handleText}
|
||||
value={this.state.text} disabled={!this.state.checked}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function SwitchLineWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes}) {
|
||||
export function SwitchLineWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes, validator}) {
|
||||
return <SwitchLine target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id}
|
||||
id={id} data={data} classes={classes}/>;
|
||||
id={id} data={data} classes={classes} validator={validator && Validation[validator.name](validator.params)}/>;
|
||||
}
|
||||
|
51
static/js/shop/options/validation.js
Normal file
51
static/js/shop/options/validation.js
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
const ipv4 = (params) => {
|
||||
const ipv4WithMaskPattern = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(0|1[0-9]|2[0-9]|3[0-2]|[0-9])$/;
|
||||
return (text) => {
|
||||
return ipv4WithMaskPattern.test(text);
|
||||
}
|
||||
}
|
||||
|
||||
const ipv6 = (params) => {
|
||||
const ipv6WithMaskPattern = /(^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(\/(\d{1,2}|1[0-1]\d|12[0-8]))(%.+)?\s*$)/;
|
||||
|
||||
|
||||
return (text) => {
|
||||
return ipv6WithMaskPattern.test(text);
|
||||
}
|
||||
}
|
||||
|
||||
const ipv4or6 = (params) => {
|
||||
const ipv4Local = ipv4(params);
|
||||
const ipv6Local = ipv6(params);
|
||||
return (text) => {
|
||||
return ipv4Local(text) || ipv6Local(text);
|
||||
}
|
||||
}
|
||||
|
||||
const frequency = (params) => {
|
||||
const factors = {
|
||||
"mhz": 1e6,
|
||||
"khz": 1e3,
|
||||
"hz": 1e1,
|
||||
"ghz": 1e9,
|
||||
};
|
||||
|
||||
return (text) => {
|
||||
const splited = text.split(/(\s+)/);
|
||||
const numerator = parseFloat(splited[0]);
|
||||
if (splited.length !== 3 || isNaN(numerator)) return false;
|
||||
const factor = factors[splited[2].toLowerCase()];
|
||||
if (!factor) return false;
|
||||
const realFreq = factor * numerator;
|
||||
return realFreq >= (params.min || 10*factors.mhz) && realFreq <= (params.max || 1*factors.ghz);
|
||||
}
|
||||
}
|
||||
|
||||
export const Validation = {
|
||||
ipv4: ipv4,
|
||||
ipv6: ipv6,
|
||||
ipv4or6: ipv4or6,
|
||||
frequency: frequency
|
||||
};
|
@ -194,9 +194,14 @@ const shop_data = {
|
||||
]
|
||||
},
|
||||
[
|
||||
{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}, tip: "Use external clock reference: 10, 80 (beta), 100 or 125 MHz. Other variants may be provided if needed."}}
|
||||
{type: "Line", args: {title: "IPv4", outvar: "ipv4", fallback: "192.168.1.75/24",
|
||||
tip: "Set up IPv4 address and mask used by core device", validator: {name: "ipv4"}}},
|
||||
{type: "SwitchLine", args: {title: "IPv6", outvar: "ipv6",
|
||||
tip: "Set up IPv6 address and prefix used by core device",
|
||||
validator: {name: "ipv6"}}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false},
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 1e9}},
|
||||
tip: "Use external clock reference: 10, 80 (beta), 100 or 125 MHz. Other variants may be provided if needed."}}
|
||||
],
|
||||
[
|
||||
{type: "Switch", args: {title: "Optical fiber", outvar: "optics", tip: "Use optical fiber instead of direct attach copper cable"}},
|
||||
@ -262,9 +267,17 @@ const shop_data = {
|
||||
]
|
||||
},
|
||||
[
|
||||
{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}, tip: "Use external clock reference: 10, 80 (beta), 100 or 125 MHz. Other variants may be provided if needed."}}
|
||||
{type: "Line", args: {title: "IPv4", outvar: "ipv4",
|
||||
validator: {name: "ipv4"},
|
||||
fallback: "192.168.1.75/24",
|
||||
tip: "Set up IPv4 address used by core device"}},
|
||||
{type: "SwitchLine", args: {title: "IPv6", outvar: "ipv6",
|
||||
tip: "Set up IPv6 address and prefix used by core device",
|
||||
validator: {name: "ipv6"}}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 1e9}},
|
||||
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: "Optical fiber", outvar: "optics", tip: "Use optical fiber instead of direct attach copper cable"}},
|
||||
@ -658,7 +671,9 @@ const shop_data = {
|
||||
"if": [
|
||||
{"var": "mono_eem"},
|
||||
[
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 1e9}},
|
||||
fallback: {text: "125 MHz", checked: false}}},
|
||||
],
|
||||
[
|
||||
{type: "Switch", args: {title: "Synchronization", outvar: "sync", tip: "Synchronize phases across Urukuls"}},
|
||||
@ -667,7 +682,9 @@ const shop_data = {
|
||||
{"var": "sync"},
|
||||
null,
|
||||
[
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 1e9}},
|
||||
fallback: {text: "125 MHz", checked: false}}},
|
||||
{
|
||||
"if": [
|
||||
{"var": "ext_data.has_sampler"},
|
||||
@ -716,7 +733,9 @@ const shop_data = {
|
||||
datasheet_name: '4410/4412 Urukul datasheet',
|
||||
options: [
|
||||
{type: "Switch", args: {title: "Use 1 EEM", outvar: "mono_eem", tip: "Use one EEM port setup. RF switch and synchronization will be unavailable."}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}}
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 1e9}},
|
||||
fallback: {text: "125 MHz", checked: false}}}
|
||||
],
|
||||
size: 'small',
|
||||
type: 'urukul',
|
||||
@ -747,7 +766,9 @@ const shop_data = {
|
||||
'The upconverter is optional, if you would like the baseband version please leave us a note.'
|
||||
],
|
||||
options: [
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 1e9}},
|
||||
fallback: {text: "125 MHz", checked: false}}},
|
||||
{type: "Radio", args: {title: "Variant", outvar: "variant", variants: ["Baseband", "Upconverter"], fallback: 1}},
|
||||
],
|
||||
size: 'small',
|
||||
@ -1025,7 +1046,10 @@ const shop_data = {
|
||||
'Can be controlled by Kasli or work stand-alone with PoE supply.'
|
||||
],
|
||||
options: [
|
||||
{type: "SwitchLine", args: {title: "IP", outvar: "ip", fallback: {text: "DHCP", checked: false}, tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "IP", outvar: "ip",
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}},
|
||||
{type: "Switch", args: {title: "Term #0", outvar: "term_0", tip: "Enable termination on ADC channel #0"}},
|
||||
{type: "Switch", args: {title: "Term #1", outvar: "term_1", tip: "Enable termination on ADC channel #1"}}
|
||||
@ -1057,7 +1081,9 @@ const shop_data = {
|
||||
'Large frequency changes take several milliseconds.',
|
||||
],
|
||||
options: [
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}}
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 600e6}},
|
||||
fallback: {text: "125 MHz", checked: false}}}
|
||||
],
|
||||
size: 'small',
|
||||
type: null,
|
||||
@ -1084,7 +1110,9 @@ const shop_data = {
|
||||
'Each Almazny channel outputs twice the frequency of its corresponding Mirny channel.',
|
||||
],
|
||||
options: [
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}}
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
validator: {name: "frequency", params: {min: 10e6, max: 600e6}},
|
||||
fallback: {text: "125 MHz", checked: false}}}
|
||||
],
|
||||
size: 'big',
|
||||
type: null,
|
||||
@ -1166,9 +1194,13 @@ const shop_data = {
|
||||
'AD9959 DDS (500MSPS, 10-bit).'
|
||||
],
|
||||
options: [
|
||||
{type: "SwitchLine", args: {title: "IP", outvar: "ip", fallback: {text: "DHCP", checked: false}, tip: "Set up IP address used by the device"}},
|
||||
{type: "SwitchLine", args: {title: "IP", outvar: "ip",
|
||||
validator: {name: "ipv4or6"},
|
||||
fallback: {text: "DHCP", checked: false},
|
||||
tip: "Set up IP address used by the device"}},
|
||||
{type: "Switch", args: {title: "Ext power", outvar: "ext_pwr", "tip": "Use external power supply in order to reduce number of used EEM connectors"}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk", fallback: {text: "125 MHz", checked: false}}},
|
||||
{type: "SwitchLine", args: {title: "Ext CLK", outvar: "ext_clk",
|
||||
fallback: {text: "125 MHz", checked: false}, validator: {name: "frequency", params: {min: 10e6, max: 1e9}}}},
|
||||
{type: "Switch", args: {title: "Termination #0", outvar: "term_0", tip: "Enable termination on ADC channel #0"}},
|
||||
{type: "Switch", args: {title: "Termination #1", outvar: "term_1", tip: "Enable termination on ADC channel #1"}}
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user