X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FUI.git;a=blobdiff_plain;f=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fsrc%2Fcomponents%2Fmodel%2FLeafEditor.jsx;fp=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fsrc%2Fcomponents%2Fmodel%2FLeafEditor.jsx;h=1a00d2838898bb3c4865438c00e267e67cbaa3cb;hp=0000000000000000000000000000000000000000;hb=03156e335275de1dafbc2a816e98006afdf249bf;hpb=f2dc2462571800e62cba969964de621dca09299c diff --git a/skyquake/plugins/composer/src/src/components/model/LeafEditor.jsx b/skyquake/plugins/composer/src/src/components/model/LeafEditor.jsx new file mode 100644 index 000000000..1a00d2838 --- /dev/null +++ b/skyquake/plugins/composer/src/src/components/model/LeafEditor.jsx @@ -0,0 +1,298 @@ +/* + * + * Copyright 2016-2017 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import _debounce from 'lodash/debounce'; +import React from 'react' +import ClassNames from 'classnames' +import changeCase from 'change-case' +import Property from '../../libraries/model/DescriptorModelMetaProperty' +import CatalogDataStore from '../../stores/CatalogDataStore' +import _isInt from 'validator/lib/isInt' +import _toInt from 'validator/lib/toInt' +import _isFloat from 'validator/lib/isFloat' +import _toFloat from 'validator/lib/toFloat' +import _trim from 'validator/lib/trim' +import _isIP from 'validator/lib/isIP' + +import { startEditing, endEditing, onFocusPropertyFormInputElement } from './EditDescriptorUtils' +import Select from './Select' + +function validateRequired(isRequired, value) { + value = value.trim(); + return isRequired && !value ? { success: false, message: "A value is required." } : { success: true, value: null }; +} + +function editorExitHandler(isValueRequired, onExit, onError, event) { + const value = event.target.value; + const result = validateRequired(isValueRequired, value); + onExit && onExit(result); + endEditing(); +} + +function Enumeration(props) { + const { id, property, title, readOnly, onChange, onError, onExit } = props; + let value = props.value; + const enumeration = Property.getEnumeration(property, value); + const hasDefaultValue = !!property['default-value']; + const required = property.mandatory || hasDefaultValue; + if (!value && hasDefaultValue) { + value = property['default-value']; + } + return ( + + ); +} + +function Boolean(props) { + const { id, property, title, readOnly, onChange, onError, onExit } = props; + let value = props.value; + const hasDefaultValue = !!property['default-value']; + const required = property.mandatory || hasDefaultValue; + const typeOfValue = typeof value; + if (typeOfValue === 'number' || typeOfValue === 'boolean') { + value = value ? 'TRUE' : 'FALSE'; + } else if (value) { + value = value.toUpperCase(); + } else { + if (hasDefaultValue) { + value = ('' + property['default-value']).toUpperCase(); + } + } + const options = [ + { name: "TRUE", value: 'TRUE' }, + { name: "FALSE", value: 'FALSE' } + ] + return ( + + ); +} + +function getValidator(property) { + function validateInteger(constraints, value) { + return _isInt(value, constraints) ? { success: true, value: _toInt(value) } : + { success: false, value, message: "The value is not an integer or does not meet the property constraints." }; + } + function validateDecimal(constraints, value) { + return _isFloat(value, constraints) ? { success: true, value: _toFloat(value) } : + { success: false, value, message: "The value is not a decimal number or does not meet the property constraints." }; + } + function validateProperty(validator, errorMessage, value) { + return validator(value) ? { success: true, value } : + { success: false, value, message: errorMessage }; + } + const name = property.name; + if (name === 'ip-address' || name.endsWith('-ip-address')) { + return validateProperty.bind(null, _isIP, "The value is not a valid ip address.") + } + switch (property['data-type']) { + case 'int8': + return validateInteger.bind(null, { min: -128, max: 127 }); + case 'int16': + return validateInteger.bind(null, { min: -32768, max: 32767 }); + case 'int32': + return validateInteger.bind(null, { min: -2147483648, max: 2147483647 }); + case 'int64': + return validateInteger.bind(null, null); + case 'uint8': + return validateInteger.bind(null, { min: 0, max: 255 }); + case 'uint16': + return validateInteger.bind(null, { min: 0, max: 65535 }); + case 'uint32': + return validateInteger.bind(null, { min: 0, max: 4294967295 }); + case 'uint64': + return validateInteger.bind(null, { min: 0 }); + case 'decimal64': + return validateDecimal.bind(null, null) + case 'string': + default: + return function (value) { return { success: true, value } }; + } +} + +function messageTemplate(strings, ...keys) { + return (function (...vars) { + let helpInfo = vars.reduce((o, info) => Object.assign(o, info), {}); + return keys.reduce((s, key, i) => { + return s + helpInfo[key] + strings[i + 1]; + }, strings[0]); + }); +} + +const errorMessage = messageTemplate`"${'value'}" is ${'error'}. ${'message'}`; + +class Input extends React.Component { + constructor(props) { + super(props); + let originalValue = props.value ? props.value : null; // normalize empty value + this.state = { originalValue }; + } + + componentWillReceiveProps(nextProps) { + const { value } = nextProps + if (value !== this.state.originalValue) { + let originalValue = value ? value : null; // normalize empty value + this.setState({ originalValue }) + } + } + + render() { + const { id, property, value, title, readOnly, onChange, onError, onExit } = this.props; + const { originalValue } = this.state; + const isGuid = Property.isGuid(property); + const className = ClassNames(property.name + '-input', { '-is-guid': isGuid, '-is-required': required }); + const placeholder = property.name; + const required = property.mandatory; + + const validator = getValidator(property); + function handleValueChanged(newValue) { + newValue = newValue.trim(); + const result = !newValue ? validateRequired(required, newValue) : validator(newValue); + result.success ? (originalValue !== result.value) && onChange(result.value) : onError(result.message); + } + const changeHandler = _debounce(handleValueChanged, 2000); + function onInputChange(e) { + e.preventDefault(); + changeHandler(_trim(e.target.value)); + } + function onBlur(e) { + changeHandler.cancel(); + const value = _trim(e.target.value); + const result = !value ? validateRequired(required, value) : validator(value); + if (result.success) { + // just in case we missed it by cancelling the debouncer + (originalValue !== result.value) && onChange(result.value); + } + onExit(result); + } + if (property['preserve-line-breaks']) { + return ( +