Skip to content

Commit 29a06c4

Browse files
committed
Moved the logic for <ColorSelector> into a new separate file 'webpack_in/color_selector.jsx'.
1 parent 3d48cfb commit 29a06c4

File tree

2 files changed

+199
-188
lines changed

2 files changed

+199
-188
lines changed

webpack_in/color_selector.jsx

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// This file 'color_selector.jsx' is part of an example for building a multi-widget React
2+
// front-end app step by step as outlined in the tutorial blog at
3+
// http://maratbn.com/blogs/2018/07/02/react-multi-widget/
4+
5+
6+
import PropTypes from 'prop-types';
7+
import React from 'react';
8+
9+
10+
import Styles from './styles.es';
11+
12+
13+
class ButtonForCounter extends React.Component {
14+
constructor(props) {
15+
super(props);
16+
17+
this._onCountStart = () => {
18+
this._flagCount = true;
19+
20+
const doCount = () => {
21+
if (!this._flagCount || this.props.isDisabled) {
22+
return;
23+
}
24+
25+
this.props.onCount();
26+
27+
setTimeout(doCount, 25);
28+
};
29+
30+
doCount();
31+
};
32+
33+
this._onCountStop = () => {
34+
this._flagCount = false;
35+
};
36+
}
37+
38+
render() {
39+
return (<button disabled={ this.props.isDisabled }
40+
onMouseDown={ this._onCountStart }
41+
onMouseUp={ this._onCountStop }
42+
onMouseLeave={ this._onCountStop }
43+
onTouchStart={ this._onCountStart }
44+
onTouchEnd={ this._onCountStop }
45+
onTouchCancel={ this._onCountStop }>{ this.props.caption }</button>);
46+
}
47+
}
48+
49+
ButtonForCounter.propTypes = {
50+
caption: PropTypes.string.isRequired,
51+
isDisabled: PropTypes.bool,
52+
onCount: PropTypes.func.isRequired
53+
};
54+
55+
class ColorComponentEntry extends React.Component {
56+
render() {
57+
return (
58+
<div style={{ display: 'inline-block', margin: '1em' }}>
59+
{ this.props.label }:
60+
<input type='text'
61+
size='4'
62+
maxLength='4'
63+
style={{ textAlign: 'center' }}
64+
value={ this.props.value }
65+
onChange={ event => {
66+
const strValueEntered = event.target.value;
67+
68+
// Need to normalize the user input to a valid
69+
// value, which is 0 <= valid <= 255
70+
71+
// Inputs of non-digits will be ignored.
72+
if (!strValueEntered.match(/^\d*$/g)) {
73+
return;
74+
}
75+
76+
// Value must be converted to an integer.
77+
const convertValue = strValueToConvert => {
78+
// Blank / falsy input will be treated as 0.
79+
if (!strValueToConvert) {
80+
return 0;
81+
}
82+
83+
const valueConverted = parseInt(strValueToConvert);
84+
85+
// If the integer is <= 255 then it is a valid
86+
// value and safe to return.
87+
if (valueConverted <= 255) {
88+
return valueConverted;
89+
}
90+
91+
// Otherwise will remove the left-most digit
92+
// and try to convert again until the value
93+
// becomes <= 255
94+
return convertValue(strValueToConvert.substr(1));
95+
};
96+
97+
this.props.onChangeValue(convertValue(strValueEntered));
98+
}} />
99+
<ButtonForCounter caption="&#9650;"
100+
isDisabled={ this.props.value === 255 }
101+
onCount={ () => {
102+
const valueNew = this.props.value + 1;
103+
104+
if (valueNew > 255) {
105+
return;
106+
}
107+
108+
this.props.onChangeValue(valueNew);
109+
}} />
110+
<ButtonForCounter caption="&#9660;"
111+
isDisabled={ this.props.value === 0 }
112+
onCount={ () => {
113+
const valueNew = this.props.value - 1;
114+
115+
if (valueNew < 0) {
116+
return;
117+
}
118+
119+
this.props.onChangeValue(valueNew);
120+
}} />
121+
</div>
122+
);
123+
}
124+
}
125+
126+
ColorComponentEntry.propTypes = {
127+
label: PropTypes.string.isRequired,
128+
value: PropTypes.number.isRequired,
129+
onChangeValue: PropTypes.func.isRequired
130+
};
131+
132+
class ColorSelector extends React.Component {
133+
constructor(props) {
134+
super(props);
135+
136+
this.state = {
137+
color: {
138+
r: 255,
139+
g: 255,
140+
b: 255
141+
}
142+
};
143+
}
144+
145+
render() {
146+
const updateStateForColor = (component, value) => {
147+
const color = {
148+
...this.state.color
149+
};
150+
151+
color[component] = value;
152+
153+
this.setState({
154+
...this.state,
155+
color
156+
});
157+
};
158+
159+
const { color } = this.state;
160+
161+
const colorOpposite = {
162+
r: 255 - color.r,
163+
g: 255 - color.g,
164+
b: 255 - color.b
165+
};
166+
167+
return (
168+
<div style={ Styles.common }>
169+
<div style={{ margin: '1em auto',
170+
width: '70%',
171+
height: '3em',
172+
border: `solid 2px rgb(${colorOpposite.r},
173+
${colorOpposite.g},
174+
${colorOpposite.b})`,
175+
backgroundColor: `rgb(${color.r}, ${color.g}, ${color.b})`}} />
176+
<div style={{ marginTop: '-1em'}}>
177+
<ColorComponentEntry label="R" value={ this.state.color.r } onChangeValue={
178+
(value) => {
179+
updateStateForColor('r', value);
180+
}
181+
} />
182+
<ColorComponentEntry label="G" value={ this.state.color.g } onChangeValue={
183+
(value) => {
184+
updateStateForColor('g', value);
185+
}
186+
} />
187+
<ColorComponentEntry label="B" value={ this.state.color.b } onChangeValue={
188+
(value) => {
189+
updateStateForColor('b', value);
190+
}
191+
} />
192+
</div>
193+
</div>
194+
);
195+
}
196+
}
197+
198+
export default ColorSelector;

0 commit comments

Comments
 (0)