From 24f88f31523bcf94770046ebaa101a8fffb577c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20=C3=87ivici?= Date: Tue, 5 Dec 2017 11:50:00 +0300 Subject: [PATCH] Fixed #214 --- src/components/colorpicker/ColorPicker.js | 303 ++++++++++-------- .../colorpicker/ColorPickerPanel.js | 45 +++ src/showcase/colorpicker/ColorPickerDemo.js | 62 ++-- 3 files changed, 241 insertions(+), 169 deletions(-) create mode 100644 src/components/colorpicker/ColorPickerPanel.js diff --git a/src/components/colorpicker/ColorPicker.js b/src/components/colorpicker/ColorPicker.js index 26b5b5b05b..3c55178857 100644 --- a/src/components/colorpicker/ColorPicker.js +++ b/src/components/colorpicker/ColorPicker.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; import DomHandler from '../utils/DomHandler'; import classNames from 'classnames'; +import {ColorPickerPanel} from './ColorPickerPanel'; export class ColorPicker extends Component { @@ -11,11 +11,12 @@ export class ColorPicker extends Component { value: null, style: null, className: null, + defaultColor: 'ff0000', inline: false, format: "hex", appendTo: null, disabled: false, - tabindex: null, + tabIndex: null, inputId: null, onChange: null } @@ -25,11 +26,12 @@ export class ColorPicker extends Component { value: PropTypes.any, style: PropTypes.object, className: PropTypes.string, + defaultColor: PropTypes.string, inline: PropTypes.bool, format: PropTypes.string, - appendTo: PropTypes.string, + appendTo: PropTypes.object, disabled: PropTypes.bool, - tabindex: PropTypes.string, + tabIndex: PropTypes.string, inputId: PropTypes.string, onChange: PropTypes.func } @@ -37,12 +39,9 @@ export class ColorPicker extends Component { constructor(props) { super(props); - this.selfClick = false; - this.colorDragging = false; - this.hueDragging = false; - this.defaultColor = 'ff0000'; - - this.state = {panelVisible: false}; + this.onPanelClick = this.onPanelClick.bind(this); + this.onInputClick = this.onInputClick.bind(this); + this.onInputKeydown = this.onInputKeydown.bind(this); } onHueMousedown(event) { @@ -50,16 +49,16 @@ export class ColorPicker extends Component { return; } - this.bindDocumentMousemoveListener(); - this.bindDocumentMouseupListener(); - this.hueDragging = true; + this.bindDocumentMouseMoveListener(); + this.bindDocumentMouseUpListener(); this.pickHue(event); + DomHandler.addClass(this.container, 'ui-colorpicker-dragging'); } pickHue(event) { - let top = this.hueView.getBoundingClientRect().top + document.body.scrollTop; - this.value = this.validateHSB({ + let top = this.hueView.getBoundingClientRect().top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0); + this.hsbValue = this.validateHSB({ h: Math.floor(360 * (150 - Math.max(0, Math.min(150, (event.pageY - top)))) / 150), s: 100, b: 100 @@ -75,21 +74,21 @@ export class ColorPicker extends Component { return; } - this.bindDocumentMousemoveListener(); - this.bindDocumentMouseupListener(); - this.colorDragging = true; + this.bindDocumentMouseMoveListener(); + this.bindDocumentMouseUpListener(); this.pickColor(event); + DomHandler.addClass(this.container, 'ui-colorpicker-dragging'); } pickColor(event) { let rect = this.colorSelector.getBoundingClientRect(); - let top = rect.top + document.body.scrollTop; + let top = rect.top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0); let left = rect.left + document.body.scrollLeft; let saturation = Math.floor(100 * (Math.max(0, Math.min(150, (event.pageX - left)))) / 150); let brightness = Math.floor(100 * (150 - Math.max(0, Math.min(150, (event.pageY - top)))) / 150); - this.value = this.validateHSB({ - h: this.value.h, + this.hsbValue = this.validateHSB({ + h: this.hsbValue.h, s: saturation, b: brightness }); @@ -101,103 +100,105 @@ export class ColorPicker extends Component { updateModel() { switch(this.props.format) { case 'hex': - this.onModelChange(this.HSBtoHEX(this.value)); + this.onChange(this.HSBtoHEX(this.hsbValue)); break; case 'rgb': - this.onModelChange(this.HSBtoRGB(this.value)); + this.onChange(this.HSBtoRGB(this.hsbValue)); break; case 'hsb': - this.onModelChange(this.value); + this.onChange(this.hsbValue); break; default: break; } + + this.selfModelUpdate = true; } - writeValue(value) { - if(value) { - switch(this.props.format) { + toHSB(value) { + let hsb; + + if (value) { + switch (this.props.format) { case 'hex': - this.value = this.HEXtoHSB(value); - break; - + hsb = this.HEXtoHSB(value); + break; + case 'rgb': - this.value = this.RGBtoHSB(value); - break; - + hsb = this.RGBtoHSB(value); + break; + case 'hsb': - this.value = value; - break; - + hsb = value; + break; + default: - break; + break; } } else { - this.value = this.HEXtoHSB(this.defaultColor); + hsb = this.HEXtoHSB(this.props.defaultColor); } - - this.updateColorSelector(); - this.updateUI(); + + return hsb; } - onModelChange(value) { + updateHSBValue(value) { + this.hsbValue = this.toHSB(value); + } + + areHSBEqual(val1, val2) { + return val1.h === val2.h && val1.s === val2.s && val1.b === val2.b; + } + + onChange(value) { if(this.props.onChange) { this.props.onChange({ - originalEvent: event, value: value }) } } updateColorSelector() { - this.colorSelector.style.backgroundColor = '#' + this.HSBtoHEX(this.value); + this.colorSelector.style.backgroundColor = '#' + this.HSBtoHEX(this.hsbValue); } updateUI() { - this.colorHandle.style.left = Math.floor(150 * this.value.s / 100) + 'px'; - this.colorHandle.style.top = Math.floor(150 * (100 - this.value.b) / 100) + 'px'; - this.hueHandle.style.top = Math.floor(150 - (150 * this.value.h / 360)) + 'px'; + this.colorHandle.style.left = Math.floor(150 * this.hsbValue.s / 100) + 'px'; + this.colorHandle.style.top = Math.floor(150 * (100 - this.hsbValue.b) / 100) + 'px'; + this.hueHandle.style.top = Math.floor(150 - (150 * this.hsbValue.h / 360)) + 'px'; if(this.input) { - this.input.style.backgroundColor = '#' + this.HSBtoHEX(this.value); + this.input.style.backgroundColor = '#' + this.HSBtoHEX(this.hsbValue); } } show() { - var zIndex = DomHandler.getZindex(); - this.panel.style.zIndex = String(zIndex); - this.setState({panelVisible: true}); - this.shown = true; + this.panel.element.style.zIndex = DomHandler.getZindex(); + this.alignPanel(); + DomHandler.fadeIn(this.panel.element, 250); + this.panel.element.style.display = 'block'; + this.bindDocumentClickListener(); } hide() { - this.setState({panelVisible: false}); + this.panel.element.style.display = 'none'; this.unbindDocumentClickListener(); } - - onShow() { - this.alignPanel(); - this.bindDocumentClickListener(); - } - - alignPanel() { - if(this.appendTo) - DomHandler.absolutePosition(this.panel, this.input); - else - DomHandler.relativePosition(this.panel, this.input); - } - + onInputClick() { - this.selfClick = true; + if(this.documentClickListener) { + this.selfClick = true; + } + this.togglePanel(); } togglePanel() { - if(!this.state.panelVisible) + if(!this.panel.element.offsetParent) this.show(); else this.hide(); @@ -228,14 +229,14 @@ export class ColorPicker extends Component { bindDocumentClickListener() { if(!this.documentClickListener) { - this.documentClickListener = this.docClickListener.bind(this); + this.documentClickListener = this.onDocumentClick.bind(this); document.addEventListener('click', this.documentClickListener); } } - docClickListener() { + onDocumentClick() { if(!this.selfClick) { - this.setState({panelVisible: false}); + this.hide(); this.unbindDocumentClickListener(); } @@ -249,48 +250,49 @@ export class ColorPicker extends Component { } } - bindDocumentMousemoveListener() { - if(!this.documentMousemoveListener) { - this.documentMousemoveListener = this.docMouseMoveListener.bind(this); - document.addEventListener('mousemove', this.documentMousemoveListener); + bindDocumentMouseMoveListener() { + if(!this.documentMouseMoveListener) { + this.documentMouseMoveListener = this.onDocumentMouseMove.bind(this); + document.addEventListener('mousemove', this.documentMouseMoveListener); } } - docMouseMoveListener() { - if(this.colorDragging) { + onDocumentMouseMove() { + if (this.colorDragging) { this.pickColor(event); } - - if(this.hueDragging) { + + if (this.hueDragging) { this.pickHue(event); } } - unbindDocumentMousemoveListener() { - if(this.documentMousemoveListener) { - document.removeEventListener('mousemove', this.documentMousemoveListener); - this.documentMousemoveListener = null; + unbindDocumentMouseMoveListener() { + if (this.documentMouseMoveListener) { + document.removeEventListener('mousemove', this.documentMouseMoveListener); + this.documentMouseMoveListener = null; } } - bindDocumentMouseupListener() { - if(!this.documentMouseupListener) { - this.documentMouseupListener = this.docMouseUpListener.bind(this); - document.addEventListener('mouseup', this.documentMouseupListener); + bindDocumentMouseUpListener() { + if(!this.documentMouseUpListener) { + this.documentMouseUpListener = this.onDocumentMouseUp.bind(this); + document.addEventListener('mouseup', this.documentMouseUpListener); } } - docMouseUpListener() { + onDocumentMouseUp() { this.colorDragging = false; this.hueDragging = false; - this.unbindDocumentMousemoveListener(); - this.unbindDocumentMouseupListener(); + DomHandler.removeClass(this.container, 'ui-colorpicker-dragging'); + this.unbindDocumentMouseMoveListener(); + this.unbindDocumentMouseUpListener(); } - unbindDocumentMouseupListener() { - if(this.documentMouseupListener) { - document.removeEventListener('mouseup', this.documentMouseupListener); - this.documentMouseupListener = null; + unbindDocumentMouseUpListener() { + if (this.documentMouseUpListener) { + document.removeEventListener('mouseup', this.documentMouseUpListener); + this.documentMouseUpListener = null; } } @@ -416,65 +418,100 @@ export class ColorPicker extends Component { return this.RGBtoHEX(this.HSBtoRGB(hsb)); } - componentDidMount() { - this.writeValue(this.props.value); + componentWillMount() { + this.updateHSBValue(this.props.value); } - - componentDidUpdate(prevProps, prevState) { - if(this.shown) { - this.onShow(); - this.shown = false; - } + + componentDidMount() { + this.updateColorSelector(); + this.updateUI(); } + componentWillReceiveProps(nextProps) { + if(!this.selfModelUpdate) { + this.updateHSBValue(nextProps.value); + this.updateColorSelector(); + this.updateUI(); + } + else { + this.selfModelUpdate = false; + } + } + componentWillUnmount() { this.unbindDocumentClickListener(); + this.unbindDocumentMouseMoveListener(); + this.unbindDocumentMouseUpListener(); } - render() { - var className = classNames('ui-colorpicker ui-widget', this.props.className, { - 'ui-colorpicker-overlay':!this.props.inline, - 'ui-colorpicker-dragging': this.colorDragging||this.hueDragging - }); - - if(!this.props.inline) { - var inputClass = classNames('ui-colorpicker-preview ui-inputtext ui-state-default ui-corner-all', { - 'ui-state-disabled': this.props.disabled - }), - input = ( this.input = ReactDOM.findDOMNode(el)} type="text" className={inputClass} readOnly="readonly" id={this.props.inputId} tabIndex={this.props.tabindex} disabled={this.props.disabled} - onClick={this.onInputClick.bind(this)} onKeyDown={this.onInputKeydown.bind(this)} />); + alignPanel() { + if (this.props.appendTo) { + DomHandler.absolutePosition(this.panel.element, this.container); + this.panel.element.style.minWidth = DomHandler.getWidth(this.container) + 'px'; + } + else { + DomHandler.relativePosition(this.panel.element, this.container); } + } - var colorSelector = ( -
this.colorSelector = ReactDOM.findDOMNode(el)} className="ui-colorpicker-color-selector" onMouseDown={this.onColorMousedown.bind(this)}> + renderColorSelector() { + return ( +
this.colorSelector = el} className="ui-colorpicker-color-selector" onMouseDown={this.onColorMousedown.bind(this)}>
-
this.colorHandle = ReactDOM.findDOMNode(el)} className="ui-colorpicker-color-handle">
+
this.colorHandle = el} className="ui-colorpicker-color-handle">
- ), - hue = ( -
this.hueView = ReactDOM.findDOMNode(el)} className="ui-colorpicker-hue" onMouseDown={this.onHueMousedown.bind(this)}> -
this.hueHandle = ReactDOM.findDOMNode(el)} className="ui-colorpicker-hue-handle">
+ ); + } + + renderHue() { + return ( +
this.hueView = el} className="ui-colorpicker-hue" onMouseDown={this.onHueMousedown.bind(this)}> +
this.hueHandle = el} className="ui-colorpicker-hue-handle">
); + } - var panelClass = classNames('ui-colorpicker-panel ui-corner-all', { - 'ui-colorpicker-overlay-panel ui-shadow':!this.props.inline, - 'ui-state-disabled': this.props.disabled - }), - panel = ( -
this.panel = ReactDOM.findDOMNode(el)} className={panelClass} onClick={this.onPanelClick.bind(this)} style={{'display': (this.props.inline ? 'block' : (this.state.panelVisible ? 'block' : 'none'))}}> -
- {colorSelector} - {hue} -
+ renderContent() { + let colorSelector = this.renderColorSelector(); + let hue = this.renderHue(); + + return ( +
+ {colorSelector} + {hue}
); + } + + renderInput() { + if (!this.props.inline) { + let inputClassName = classNames('ui-colorpicker-preview ui-inputtext ui-state-default ui-corner-all', { + 'ui-state-disabled': this.props.disabled + }); + + return ( + this.input = el} type="text" className={inputClassName} readOnly="readonly" id={this.props.inputId} tabIndex={this.props.tabIndex} disabled={this.props.disabled} + onClick={this.onInputClick} onKeyDown={this.onInputKeydown} /> + ); + } + else { + return null; + } + } + + render() { + let className = classNames('ui-colorpicker ui-widget', this.props.className, {'ui-colorpicker-overlay': !this.props.inline}); + let content = this.renderContent(); + let input = this.renderInput(); return ( -
+
this.container = el} id={this.props.id} style={this.props.style} className={className}> {input} - {panel} + this.panel = el} appendTo={this.props.appendTo} onClick={this.onPanelClick} + inline={this.props.inline} disabled={this.props.disabled}> + {content} +
); } diff --git a/src/components/colorpicker/ColorPickerPanel.js b/src/components/colorpicker/ColorPickerPanel.js new file mode 100644 index 0000000000..a76b61860f --- /dev/null +++ b/src/components/colorpicker/ColorPickerPanel.js @@ -0,0 +1,45 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; +import classNames from 'classnames'; + +export class ColorPickerPanel extends Component { + + static defaultProps = { + appendTo: null, + inline: false, + disabled: false, + onClick: null + } + + static propsTypes = { + appendTo: PropTypes.object, + inline: PropTypes.bool, + disabled: PropTypes.bool, + onClick: PropTypes.func + } + + renderElement() { + let className = classNames('ui-colorpicker-panel ui-corner-all', { + 'ui-colorpicker-overlay-panel ui-shadow': !this.props.inline, + 'ui-state-disabled': this.props.disabled + }); + + return ( +
this.element = el} className={className} onClick={this.props.onClick}> + {this.props.children} +
+ ); + } + + render() { + let element = this.renderElement(); + + if (this.props.appendTo) { + return ReactDOM.createPortal(element, this.props.appendTo); + } + else { + return element; + } + } +} \ No newline at end of file diff --git a/src/showcase/colorpicker/ColorPickerDemo.js b/src/showcase/colorpicker/ColorPickerDemo.js index 848f91a4a7..13af1e5054 100644 --- a/src/showcase/colorpicker/ColorPickerDemo.js +++ b/src/showcase/colorpicker/ColorPickerDemo.js @@ -7,17 +7,10 @@ export class ColorPickerDemo extends Component { constructor() { super(); - this.state = {color1: null, color2: '1976D2'}; - this.onOverlayChange = this.onOverlayChange.bind(this); - this.onInlineChange = this.onInlineChange.bind(this); - } - - onInlineChange(e) { - this.setState({color1: e.value}); - } - - onOverlayChange(e) { - this.setState({color2: e.value}); + this.state = { + color1: null, + color2: '1976D2' + }; } render() { @@ -32,11 +25,11 @@ export class ColorPickerDemo extends Component {

Inline

- + this.setState({ color1: e.value })}>

Selected Color: {this.state.color1}

Overlay

- + this.setState({ color2: e.value })}>

Selected Color: {this.state.color2}

@@ -126,6 +119,12 @@ import {ColorPicker} from 'primereact/components/colorpicker/ColorPicker'; null Style class of the component. + + defaultColor + string + ff0000 + Default color to display when value is null. + inline boolean @@ -140,9 +139,9 @@ import {ColorPicker} from 'primereact/components/colorpicker/ColorPicker'; appendTo - any + DOM element null - Target element to attach the overlay, valid values are "body" or a local template variable of another element. + DOM element instance where the dialog should be mounted. disabled @@ -151,7 +150,7 @@ import {ColorPicker} from 'primereact/components/colorpicker/ColorPicker'; When present, it specifies that the component should be disabled. - tabindex + tabIndex number null Index of the element in tabbing order. @@ -179,9 +178,7 @@ import {ColorPicker} from 'primereact/components/colorpicker/ColorPicker'; onChange - event.originalEvent: Browser event
- event.value: Selected color - + event.value: Selected color Callback to invoke when a color is selected. @@ -254,28 +251,23 @@ import {ColorPicker} from 'primereact/components/colorpicker/ColorPicker'; {` +import React, {Component} from 'react'; +import {ColorPicker} from 'primereact/components/colorpicker/ColorPicker'; + export class ColorPickerDemo extends Component { constructor() { super(); - this.state = {color1: null, color2: '1976D2'}; - - this.onOverlayChange = this.onOverlayChange.bind(this); - this.onInlineChange = this.onInlineChange.bind(this); - } - - onInlineChange(e) { - this.setState({color1: e.value}); - } - - onOverlayChange(e) { - this.setState({color2: e.value}); + this.state = { + color1: null, + color2: '1976D2' + }; } render() { return (
-
+

ColorPicker

ColorPicker is an input component to select a color.

@@ -284,15 +276,13 @@ export class ColorPickerDemo extends Component {

Inline

- + this.setState({ color1: e.value })}>

Selected Color: {this.state.color1}

Overlay

- + this.setState({ color2: e.value })}>

Selected Color: {this.state.color2}

- -
) }