diff --git a/package-lock.json b/package-lock.json index 43007e5..7755096 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1262,6 +1262,11 @@ "@hapi/hoek": "^8.3.0" } }, + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" + }, "@jest/console": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", @@ -8272,6 +8277,11 @@ "object-visit": "^1.0.0" } }, + "material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -11032,6 +11042,19 @@ "resolved": "https://registry.npmjs.org/react-collapse/-/react-collapse-5.0.1.tgz", "integrity": "sha512-cN2tkxBWizhPQ2JHfe0aUSJtmMthKA17NZkTElpiQ2snQAAi1hssXZ2fv88rAPNNvG5ss4t0PbOZT0TIl9Lk3Q==" }, + "react-color": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.18.1.tgz", + "integrity": "sha512-X5XpyJS6ncplZs74ak0JJoqPi+33Nzpv5RYWWxn17bslih+X7OlgmfpmGC1fNvdkK7/SGWYf1JJdn7D2n5gSuQ==", + "requires": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.11", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + } + }, "react-contexify": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/react-contexify/-/react-contexify-4.1.1.tgz", @@ -11533,6 +11556,14 @@ "prop-types": "^15.6.2" } }, + "reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "requires": { + "lodash": "^4.0.1" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -13446,6 +13477,11 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", diff --git a/package.json b/package.json index 1370a79..0c51a06 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "react-bootstrap": "^1.3.0", "react-bootstrap-time-picker": "^2.0.1", "react-collapse": "^5.0.1", + "react-color": "^2.18.1", "react-contexify": "^4.1.1", "react-d3": "^0.4.0", "react-dnd": "^10.0.2", diff --git a/src/widget/edit-widget/color-picker.js b/src/widget/edit-widget/color-picker.js new file mode 100644 index 0000000..2861333 --- /dev/null +++ b/src/widget/edit-widget/color-picker.js @@ -0,0 +1,130 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import { SketchPicker } from 'react-color'; +import Dialog from '../../common/dialogs/dialog'; + + +class ColorPicker extends React.Component { + valid = true; + + constructor(props) { + super(props); + + this.state = { + widget: {} + }; + } + + static getDerivedStateFromProps(props, state){ + + return { + widget: props.widget + }; + } + + hexToRgb = (hex,opacity) => { + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + a: opacity + } : null; + } + + handleChangeComplete = (color) => { + + let temp = this.state.widget; + + if(this.props.controlId === 'strokeStyle'){ + temp.customProperties.zones[this.props.zoneIndex]['strokeStyle'] = color.hex; + } + else{ + let parts = this.props.controlId.split('.'); + let isCustomProperty = true; + + if (parts.length === 1){ + isCustomProperty = false; + } + + isCustomProperty ? temp[parts[0]][parts[1]] = color.hex : temp[this.props.controlId] = color.hex; + isCustomProperty ? temp[parts[0]][parts[1] + "_opacity"] = color.rgb.a : temp[this.props.controlId +"_opacity"] = color.rgb.a; + } + + this.setState({ widget: temp }); + }; + + onClose = canceled => { + if (canceled) { + if (this.props.onClose != null) { + this.props.onClose(); + } + + return; + } + + if (this.valid && this.props.onClose != null) { + this.props.onClose(this.state.widget); + } + }; + + render() { + let disableOpacity = false; + let hexColor; + let opacity = 1; + let parts = this.props.controlId.split('.'); + let isCustomProperty = true; + if (parts.length === 1){ + isCustomProperty = false; + } + + if((this.state.widget.type === "Box" && parts[1] === "border_color") || this.props.controlId === 'strokeStyle'){ + disableOpacity = true; + } + if(this.props.controlId === 'strokeStyle'){ + if(typeof this.state.widget.customProperties.zones[this.props.zoneIndex] !== 'undefined'){ + hexColor = this.state.widget.customProperties.zones[this.props.zoneIndex]['strokeStyle']; + } + } + else{ + hexColor = isCustomProperty ? this.state.widget[parts[0]][parts[1]]: this.state.widget[this.props.controlId]; + opacity = isCustomProperty ? this.state.widget[parts[0]][parts[1] + "_opacity"]: this.state.widget[this.props.controlId + "_opacity"]; + + } + + + let rgbColor = this.hexToRgb(hexColor, opacity); + + + + return this.onClose(c)} valid={true}> +
+ + + +
; + } +} + +export default ColorPicker; diff --git a/src/widget/edit-widget/edit-widget-color-control.js b/src/widget/edit-widget/edit-widget-color-control.js index 2158a55..c50039f 100644 --- a/src/widget/edit-widget/edit-widget-color-control.js +++ b/src/widget/edit-widget/edit-widget-color-control.js @@ -16,31 +16,21 @@ ******************************************************************************/ import React, { Component } from 'react'; -import { FormGroup, Col, Row, FormCheck, FormLabel } from 'react-bootstrap'; -import classNames from 'classnames'; -import { scaleOrdinal } from 'd3-scale'; -import {schemeCategory10} from 'd3-scale-chromatic' +import { FormGroup, OverlayTrigger, Tooltip , FormLabel, Button } from 'react-bootstrap'; +import ColorPicker from './color-picker' +import Icon from "../../common/icon"; + // schemeCategory20 no longer available in d3 class EditWidgetColorControl extends Component { - static get ColorPalette() { - let colorCount = 0; - const colors = []; - const colorScale = scaleOrdinal(schemeCategory10); - while (colorCount < 10) { colors.push(colorScale(colorCount)); colorCount++; } - colors.unshift('#000', '#FFF'); // include black and white - - return colors; - } - constructor(props) { super(props); this.state = { - widget: { - - } + widget: {}, + showColorPicker: false, + originalColor: null }; } @@ -50,47 +40,69 @@ class EditWidgetColorControl extends Component { }; } - render() { - + openColorPicker = () =>{ let parts = this.props.controlId.split('.'); let isCustomProperty = true; if (parts.length === 1){ isCustomProperty = false; } + let color = (isCustomProperty ? this.props.widget[parts[0]][parts[1]] : this.props.widget[this.props.controlId]); + + this.setState({showColorPicker: true, originalColor: color}); + } + + closeEditModal = (data) => { + this.setState({showColorPicker: false}) + if(typeof data === 'undefined'){ + let parts = this.props.controlId.split('.'); + let isCustomProperty = true; + if (parts.length === 1) { + isCustomProperty = false; + } + + let temp = this.state.widget; + isCustomProperty ? temp[parts[0]][parts[1]] = this.state.originalColor : temp[this.props.controlId] = this.state.originalColor; + this.setState({ widget: temp }); + } + } + + render() { + let parts = this.props.controlId.split('.'); + let isCustomProperty = true; + if (parts.length === 1){ + isCustomProperty = false; + } + let color = (isCustomProperty ? this.props.widget[parts[0]][parts[1]] : this.props.widget[this.props.controlId]); + let opacity = (isCustomProperty ? this.props.widget[parts[0]][parts[1] + "_opacity"] : this.props.widget[this.props.controlId + "_opacity"]); + let style = { + backgroundColor: color, + opacity: opacity, + width: '260px', + height: '40px' + } + + let tooltipText = "Change color and opacity"; + if(this.state.widget.type === "Box" && parts[1] === "border_color"){ + tooltipText = "Change border color"; + } + + return ( - - - - { this.props.label } - - - { - EditWidgetColorControl.ColorPalette.map( (color, idx ) => { - let colorStyle = { - background: color, - borderColor: color - }; + + {this.props.label} - let checkedClass = classNames({ - 'checked': idx === (isCustomProperty ? this.state.widget[parts[0]][parts[1]] : this.state.widget[this.props.controlId]) - }); +
+ {tooltipText} } > + + +
- return ( this.props.handleChange({target: { id: this.props.controlId, value: idx}})} />) - } - ) - } - -
-
) + this.closeEditModal(data)} widget={this.state.widget} controlId={this.props.controlId} /> + + + ) } } diff --git a/src/widget/edit-widget/edit-widget-color-zones-control.js b/src/widget/edit-widget/edit-widget-color-zones-control.js index 504aaef..4ade39f 100644 --- a/src/widget/edit-widget/edit-widget-color-zones-control.js +++ b/src/widget/edit-widget/edit-widget-color-zones-control.js @@ -16,12 +16,11 @@ ******************************************************************************/ import React from 'react'; -import { FormGroup, FormLabel, Button } from 'react-bootstrap'; - +import { FormGroup, FormControl, Table, FormLabel, Button, Tooltip, OverlayTrigger } from 'react-bootstrap'; +import ColorPicker from './color-picker' import Icon from '../../common/icon'; -import Table from '../../common/table'; -import TableColumn from '../../common/table-column'; +import {Collapse} from 'react-collapse'; class EditWidgetColorZonesControl extends React.Component { constructor(props) { @@ -33,7 +32,12 @@ class EditWidgetColorZonesControl extends React.Component { zones: [] } }, - selectedZones: [] + selectedZone: null, + selectedIndex: null, + showColorPicker: false, + originalColor: null, + minValue: 0, + maxValue: 100 }; } @@ -46,24 +50,39 @@ class EditWidgetColorZonesControl extends React.Component { addZone = () => { // add row const widget = this.state.widget; - widget.customProperties.zones.push({ strokeStyle: 'ffffff', min: 0, max: 100 }); + widget.customProperties.zones.push({ strokeStyle: '#d3cbcb', min: 0, max: 100 }); + + if(widget.customProperties.zones.length > 0){ + let length = widget.customProperties.zones.length - this.setState({ widget }); + for(let i= 0 ; i < length; i++){ + widget.customProperties.zones[i].min = i* 100/length; + widget.customProperties.zones[i].max = (i+1)* 100/length; + } + } + + this.setState({ widget, selectedZone: null, selectedIndex: null }); this.sendEvent(widget); } removeZones = () => { - // remove zones - const widget = this.state.widget; + + let temp = this.state.widget; - this.state.selectedZones.forEach(row => { - widget.customProperties.zones.splice(row, 1); - }); + temp.customProperties.zones.splice(this.state.selectedIndex, 1); - this.setState({ selectedZones: [], widget }); + if(temp.customProperties.zones.length > 0){ + let length = temp.customProperties.zones.length + + for(let i= 0 ; i < length; i++){ + temp.customProperties.zones[i].min = i* 100/length; + temp.customProperties.zones[i].max = (i+1)* 100/length; + } + } + + this.setState({widget: temp,selectedZone: null, selectedIndex: null}); - this.sendEvent(widget); } changeCell = (event, row, column) => { @@ -83,6 +102,63 @@ class EditWidgetColorZonesControl extends React.Component { this.sendEvent(widget); } + editColorZone = (index) => { + if(this.state.selectedIndex !== index){ + this.setState({selectedZone: this.state.widget.customProperties.zones[index], selectedIndex: index , minValue: this.state.widget.customProperties.zones[index].min, maxValue: this.state.widget.customProperties.zones[index].max}); + } + else{ + this.setState({selectedZone: null, selectedIndex: null}); + } + } + + openColorPicker = () =>{ + + let color = this.state.selectedZone.strokeStyle; + + this.setState({showColorPicker: true, originalColor: color}); + } + + closeEditModal = (data) => { + this.setState({showColorPicker: false}) + if(typeof data === 'undefined'){ + + let temp = this.state.selectedZone; + temp.strokeStyle = this.state.originalColor; + + this.setState({ selectedZone : temp }); + } + } + + handleMinChange = (e) => { + + if(e.target.value < 0) return; + this.setState({minValue: e.target.value}); + + let temp = this.state.widget; + temp.customProperties.zones[this.state.selectedIndex]['min'] = e.target.value; + + if(this.state.selectedIndex !== 0){ + temp.customProperties.zones[this.state.selectedIndex - 1]['max'] = e.target.value + } + + this.setState({ widget: temp }); + } + + handleMaxChange = (e) => { + + if(e.target.value > 100) return; + this.setState({maxValue: e.target.value}); + + let temp = this.state.widget; + temp.customProperties.zones[this.state.selectedIndex]['max'] = e.target.value; + + if(this.state.selectedIndex !== this.state.widget.customProperties.zones.length -1){ + temp.customProperties.zones[this.state.selectedIndex + 1]['min'] = e.target.value + } + + this.setState({ widget: temp }); + } + sendEvent(widget) { // create event const event = { @@ -95,37 +171,84 @@ class EditWidgetColorZonesControl extends React.Component { this.props.handleChange(event); } - checkedCell = (row, event) => { - // update selected rows - const selectedZones = this.state.selectedZones; - - if (event.target.checked) { - if (selectedZones.indexOf(row) === -1) { - selectedZones.push(row); - } - } else { - let index = selectedZones.indexOf(row); - if (row > -1) { - selectedZones.splice(index, 1); - } - } - - this.setState({ selectedZones }); - } + render() { + + + let tempColor = 'FFFFFF'; + let collapse = false; + if(this.state.selectedZone !== null){ + collapse = true; + tempColor = this.state.selectedZone.strokeStyle; + } + + let pickerStyle = { + backgroundColor: tempColor, + width: '260px', + height: '40px', + marginTop: '20px' + } + return Color zones + - - - - - +
+ { + this.state.widget.customProperties.zones.map((zone, idx) => { + let color = zone.strokeStyle; + let width = (zone.max - zone.min)*(260/100); + let style = { + backgroundColor: color, + width: width, + height: '40px' + } + + + + return ( + ) + } + ) + + } +
+ + Change color} > + + +
+ + + + + +
+ Min: + this.handleMinChange(e)} /> + + Max: + this.handleMaxChange(e)} /> +
- - - + + + + this.closeEditModal(data)} widget={this.state.widget} zoneIndex={this.state.selectedIndex} controlId={'strokeStyle'} />
; } } diff --git a/src/widget/edit-widget/edit-widget-control-creator.js b/src/widget/edit-widget/edit-widget-control-creator.js index 2434e7a..bb0d5b5 100644 --- a/src/widget/edit-widget/edit-widget-control-creator.js +++ b/src/widget/edit-widget/edit-widget-control-creator.js @@ -122,7 +122,6 @@ export default function CreateControls(widgetType = null, widget = null, session DialogControls.push( handleChange(e)} />, handleChange(e)} />, - handleChange(e)} /> ); break; case 'Label': diff --git a/src/widget/widget-factory.js b/src/widget/widget-factory.js index 8f99c8a..59ebedf 100644 --- a/src/widget/widget-factory.js +++ b/src/widget/widget-factory.js @@ -63,8 +63,10 @@ class WidgetFactory { widget.minHeight = 5; widget.width = 20; widget.height = 20; - widget.customProperties.on_color = 6; - widget.customProperties.off_color = 8; + widget.customProperties.on_color = '#4287f5'; + widget.customProperties.on_color_opacity = 1; + widget.customProperties.off_color = '#4287f5'; + widget.customProperties.off_color_opacity = 1; widget.customProperties.threshold = 0.5; break; case 'Value': @@ -102,7 +104,8 @@ class WidgetFactory { widget.height = 35; widget.name = 'Label'; widget.customProperties.textSize = 32; - widget.customProperties.fontColor = 0; + widget.customProperties.fontColor = '#4287f5'; + widget.customProperties.fontColor_opacity = 1; widget.customProperties.resizeTopBottomLock = true; break; case 'Image': @@ -118,8 +121,8 @@ class WidgetFactory { widget.minHeight = 50; widget.width = 100; widget.height = 100; - widget.customProperties.background_color = 1; - widget.customProperties.font_color = 0; + widget.customProperties.background_color = '#4287f5'; + widget.customProperties.font_color = '#4287f5'; widget.customProperties.on_value = 1; widget.customProperties.off_value = 0; widget.customProperties.toggle = false; @@ -167,9 +170,10 @@ class WidgetFactory { widget.minHeight = 50; widget.width = 100; widget.height = 100; - widget.customProperties.border_color = 0; - widget.customProperties.background_color = 9; - widget.customProperties.background_color_opacity = 0.5; + widget.customProperties.border_color = '#4287f5'; + widget.customProperties.border_color_opacity = 1; + widget.customProperties.background_color = '#961520'; + widget.customProperties.background_color_opacity = 1; widget.z = 0; break; /*case 'HTML': @@ -183,7 +187,8 @@ class WidgetFactory { case 'Line': widget.height = 100; widget.width = 100; - widget.customProperties.border_color = 0; + widget.customProperties.border_color = '#4287f5'; + widget.customProperties.border_color_opacity = 1; widget.customProperties.border_width = 2; widget.customProperties.rotation = 0; break; diff --git a/src/widget/widgets/box.js b/src/widget/widgets/box.js index 88e7c67..bd0dbee 100644 --- a/src/widget/widgets/box.js +++ b/src/widget/widgets/box.js @@ -17,16 +17,13 @@ import React, { Component } from 'react'; -import EditWidgetColorControl from '../edit-widget/edit-widget-color-control'; class WidgetBox extends Component { render() { - let colors = EditWidgetColorControl.ColorPalette; - let colorStyle = { - borderColor: colors[this.props.widget.customProperties.border_color], - backgroundColor: colors[this.props.widget.customProperties.background_color], + borderColor: this.props.widget.customProperties.border_color, + backgroundColor: this.props.widget.customProperties.background_color, opacity: this.props.widget.customProperties.background_color_opacity, } diff --git a/src/widget/widgets/gauge.js b/src/widget/widgets/gauge.js index e1f8bfc..b33494a 100644 --- a/src/widget/widgets/gauge.js +++ b/src/widget/widgets/gauge.js @@ -219,7 +219,7 @@ class WidgetGauge extends Component { const step = (maxValue - minValue) / 100; zones = zones.map(zone => { - return Object.assign({}, zone, { min: (zone.min * step) + +minValue, max: zone.max * step + +minValue, strokeStyle: '#' + zone.strokeStyle }); + return Object.assign({}, zone, { min: (zone.min * step) + +minValue, max: zone.max * step + +minValue, strokeStyle: zone.strokeStyle }); }); } diff --git a/src/widget/widgets/label.js b/src/widget/widgets/label.js index a7431e2..2a29e1f 100644 --- a/src/widget/widgets/label.js +++ b/src/widget/widgets/label.js @@ -17,13 +17,13 @@ import React, { Component } from 'react'; -import EditWidgetColorControl from '../edit-widget/edit-widget-color-control'; class WidgetLabel extends Component { render() { const style = { fontSize: this.props.widget.customProperties.textSize + 'px', - color: EditWidgetColorControl.ColorPalette[this.props.widget.customProperties.fontColor] + color: this.props.widget.customProperties.fontColor, + opacity: this.props.widget.customProperties.fontColor_opacity, }; return ( diff --git a/src/widget/widgets/lamp.js b/src/widget/widgets/lamp.js index c6fd5e6..d43aadf 100644 --- a/src/widget/widgets/lamp.js +++ b/src/widget/widgets/lamp.js @@ -17,7 +17,6 @@ import React, { Component } from 'react'; -import EditWidgetColorControl from '../edit-widget/edit-widget-color-control'; class WidgetLamp extends Component { constructor(props) { @@ -61,16 +60,21 @@ class WidgetLamp extends Component { render() { - let colors = EditWidgetColorControl.ColorPalette; let color; + let opacity; - if (Number(this.state.value) > Number(this.props.widget.customProperties.threshold)) - color = colors[this.props.widget.customProperties.on_color]; - else - color = colors[this.props.widget.customProperties.off_color]; + if (Number(this.state.value) > Number(this.props.widget.customProperties.threshold)){ + color = this.props.widget.customProperties.on_color; + opacity = this.props.widget.customProperties.on_color_opacity; + } + else{ + color = this.props.widget.customProperties.off_color; + opacity = this.props.widget.customProperties.off_color_opacity; + } let style = { backgroundColor: color, + opacity: opacity } return ( diff --git a/src/widget/widgets/line.js b/src/widget/widgets/line.js index 242122c..94a33df 100644 --- a/src/widget/widgets/line.js +++ b/src/widget/widgets/line.js @@ -17,8 +17,6 @@ import React, { Component } from 'react'; -import EditWidgetColorControl from '../edit-widget/edit-widget-color-control'; - class WidgetLine extends Component { // WidgetLine is newly created when widget props are changed and saved constructor(props) { @@ -45,7 +43,7 @@ class WidgetLine extends Component { // or from the state (widget in edit mode) let width = this.props.widget.width; let height = this.props.widget.height; - + if (this.state.editing) { width = this.state.width; height = this.state.height; @@ -83,7 +81,7 @@ class WidgetLine extends Component { const lineStyle = { - stroke: EditWidgetColorControl.ColorPalette[this.props.widget.customProperties.border_color], + stroke: '' + this.props.widget.customProperties.border_color, strokeWidth: '' + this.props.widget.customProperties.border_width + 'px' }; @@ -93,4 +91,4 @@ class WidgetLine extends Component { } } -export default WidgetLine; \ No newline at end of file +export default WidgetLine;