Skip to content

Commit 902cc78

Browse files
committed
src init
0 parents  commit 902cc78

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
*.log
3+
.idea
4+
dist

src/SmartTextarea.jsx

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import React, { Component, PropTypes } from 'react';
2+
3+
export const LEFT_EXIT = 'LEFT_EXIT';
4+
export const RIGHT_EXIT = 'RIGHT_EXIT';
5+
export const ENTER_EXIT = 'ENTER_EXIT';
6+
export const ESC_EXIT = 'ESC_EXIT';
7+
export const TAB_EXIT = 'TAB_EXIT';
8+
export const DEL_EXIT = 'DEL_EXIT';
9+
export const BCSP_EXIT = 'BCSP_EXIT';
10+
11+
const HEIGHT_SHIFT = 8;
12+
13+
const propTypes = {
14+
defaultValue: PropTypes.string,
15+
onSelect: PropTypes.func,
16+
handleFuncKeys: PropTypes.bool,
17+
hintText: PropTypes.string,
18+
hintColor: PropTypes.string,
19+
style: PropTypes.object,
20+
styleMouseOver: PropTypes.object,
21+
onMouseOver: PropTypes.func,
22+
onMouseOut: PropTypes.func,
23+
styleFocus: PropTypes.object,
24+
focus: PropTypes.number /* null*/,
25+
focusDelay: PropTypes.number /* null*/,
26+
onFocus: PropTypes.func,
27+
onBlur: PropTypes.func,
28+
onFuncKeyDown: PropTypes.func,
29+
onChange: PropTypes.func,
30+
onChangeHeight: PropTypes.func,
31+
onSelecting: PropTypes.func,
32+
disabled: PropTypes.bool,
33+
};
34+
35+
const defaultProps = {
36+
handleFuncKeys: false,
37+
hintText: 'hint text...',
38+
hintColor: 'rgba(57, 57, 57, 0.6)',
39+
style: {},
40+
styleMouseOver: {},
41+
onMouseOver: () => {},
42+
onMouseOut: () => {},
43+
styleFocus: {},
44+
focus: null, /* =pos */
45+
focusDelay: null,
46+
onFocus: () => {},
47+
onBlur: () => {},
48+
onFuncKeyDown: () => {},
49+
onChange: () => {},
50+
onChangeHeight: () => {},
51+
onSelecting: () => {},
52+
};
53+
54+
class SmartTextarea extends Component {
55+
constructor(props) {
56+
super(props);
57+
58+
this.state = {
59+
text: props.defaultValue,
60+
height: 30,
61+
width: 100,
62+
onMouseOver: false,
63+
focusOn: false,
64+
};
65+
this.shadow = null;
66+
this.input = null;
67+
this.ready = false;
68+
this.readyTrans = 1;
69+
70+
this.styleComn = {
71+
width: '100%',
72+
font: 'inherit',
73+
padding: 0,
74+
border: 'none',
75+
resize: 'none',
76+
overflowY: 'hidden',
77+
outline: 'none',
78+
};
79+
this.styleInput = {
80+
backgroundColor: 'transparent',
81+
color: 'rgb(66, 66, 66)',
82+
};
83+
this.styleTransition = { transition: 'height 200ms cubic-bezier(0.23, 1, 0.32, 1) 0ms' };
84+
85+
this.selection = '';
86+
87+
this.focus = this.focus.bind(this);
88+
this.onmousemove = this.onmousemove.bind(this);
89+
this.updateComp = this.updateComp.bind(this);
90+
this.onkeyDown = this.onkeyDown.bind(this);
91+
this.onchange = this.onchange.bind(this);
92+
this.onfocus = this.onfocus.bind(this);
93+
this.onblur = this.onblur.bind(this);
94+
}
95+
96+
componentDidMount() {
97+
this.updateComp(undefined, () =>
98+
this.props.onChangeHeight(this.state.height + HEIGHT_SHIFT)
99+
);
100+
}
101+
102+
103+
componentWillReceiveProps(nextProps) {
104+
if (this.props.focus !== null && this.props.focus !== undefined) {
105+
this.setState({ focusOn: true });
106+
}
107+
this.updateComp((nextProps.defaultValue !== this.props.defaultValue) ?
108+
nextProps.defaultValue : null);
109+
}
110+
111+
112+
componentWillUpdate(nextProps, nextState) {
113+
if (nextState.height !== this.state.height) {
114+
this.props.onChangeHeight(nextState.height + HEIGHT_SHIFT);
115+
}
116+
}
117+
118+
componentDidUpdate(prevProps) {
119+
if (this.props.focus !== null && !prevProps.focus) {
120+
if (this.props.focusDelay) {
121+
setTimeout(() => {this.focus(this.props.focus);}, this.props.focusDelay);
122+
} else {
123+
this.focus(this.props.focus);
124+
}
125+
}
126+
}
127+
128+
onkeyDown(event) {
129+
if (!this.props.handleFuncKeys) return;
130+
const stopit = () => {event.stopPropagation(); event.preventDefault(); };
131+
132+
const keyCode = event.keyCode;
133+
const selectionStart = this.input.selectionStart;
134+
const textLength = this.input.textLength;
135+
136+
if ((keyCode === 37 || keyCode === 38) && selectionStart === 0) {
137+
stopit();
138+
this.props.onFuncKeyDown(keyCode, LEFT_EXIT, selectionStart);
139+
}
140+
if ((keyCode === 39 || keyCode === 40) && selectionStart === textLength) {
141+
stopit();
142+
this.props.onFuncKeyDown(keyCode, RIGHT_EXIT, selectionStart);
143+
}
144+
if (keyCode === 13) {
145+
stopit();
146+
this.props.onFuncKeyDown(keyCode, ENTER_EXIT, selectionStart);
147+
}
148+
if (keyCode === 27) {
149+
stopit();
150+
this.props.onFuncKeyDown(keyCode, ESC_EXIT, selectionStart);
151+
}
152+
if (keyCode === 46 && selectionStart === textLength) {
153+
stopit();
154+
this.props.onFuncKeyDown(keyCode, DEL_EXIT, selectionStart);
155+
}
156+
if (keyCode === 8 && selectionStart === 0) {
157+
stopit();
158+
this.props.onFuncKeyDown(keyCode, BCSP_EXIT, selectionStart);
159+
}
160+
if (keyCode === 9) {
161+
stopit();
162+
this.props.onFuncKeyDown(keyCode, TAB_EXIT, selectionStart);
163+
}
164+
}
165+
166+
onchange(event) {
167+
const input = event.target;
168+
this.updateComp(input.value);
169+
this.props.onChange(input.value);
170+
}
171+
172+
onfocus(event) {
173+
this.setState({ focusOn: true });
174+
this.props.onFocus(event);
175+
}
176+
177+
onblur(event) {
178+
this.setState({ focusOn: false });
179+
this.props.onBlur(event);
180+
}
181+
182+
onmousemove() {
183+
const selection = this.input.value.slice(this.input.selectionStart,
184+
this.input.selectionEnd);
185+
if (this.selection !== selection) {
186+
this.selection = selection;
187+
this.props.onSelecting(this.selection);
188+
}
189+
}
190+
191+
focus(pos) {
192+
if (this.input) {
193+
this.input.focus();
194+
const position = (pos >= 0) ? pos : this.input.textLength + pos + 1;
195+
this.input.setSelectionRange(position, position); // fixme
196+
}
197+
}
198+
199+
updateComp(text, callback) {
200+
let updText = text;
201+
if (updText == undefined) {
202+
updText = this.state.text;
203+
}
204+
this.shadow.value = updText;
205+
this.setState({
206+
text: updText,
207+
height: this.shadow.scrollHeight,
208+
width: this.input.clientWidth,
209+
}, callback);
210+
}
211+
212+
213+
textareaInit(elem) {
214+
if (!elem) return;
215+
if (this.shadow && this.input) return;
216+
217+
if (elem.name === 'shadow') {
218+
this.shadow = elem;
219+
}
220+
if (elem.name === 'input') {
221+
this.input = elem;
222+
}
223+
}
224+
225+
226+
render() {
227+
const styleMouseOver = this.state.onMouseOver ? this.props.styleMouseOver : {};
228+
const styleFocus = this.state.focusOn ? this.props.styleFocus : {};
229+
const styleTrans = this.readyTrans < 0 ? this.styleTransition : {};
230+
const styleDiv = {
231+
marginTop: 0,
232+
marginLeft: 0,
233+
height: this.state.height + HEIGHT_SHIFT,
234+
...styleTrans,
235+
...this.props.style,
236+
...styleMouseOver,
237+
...styleFocus,
238+
};
239+
const emptyText = this.state.focusOn ? '' : this.props.hintText;
240+
const emptyColr = this.state.focusOn ? '' : this.props.hintColor;
241+
this.readyTrans--;
242+
return (
243+
<div
244+
name="SmartTextarea"
245+
className="smart-textarea"
246+
style={styleDiv}
247+
onMouseOver={(e) => this.setState({ onMouseOver: true }, this.props.onMouseOver(e))}
248+
onMouseOut ={(e) => this.setState({ onMouseOver: false }, this.props.onMouseOut(e))}
249+
onMouseMove={this.onmousemove}
250+
>
251+
<div style={{ margin: 0, height: this.state.height + HEIGHT_SHIFT, overflow: 'hidden' }}>
252+
253+
<textarea
254+
name="input"
255+
className="smart-textarea-input"
256+
type="text"
257+
value={this.state.text || emptyText}
258+
onKeyDown={this.onkeyDown}
259+
onChange={this.onchange}
260+
onFocus={this.onfocus}
261+
onBlur ={this.onblur}
262+
rows="1"
263+
style={{
264+
marginTop: 2,
265+
height: this.state.height + 0,
266+
...this.styleComn,
267+
...this.styleInput,
268+
color: this.state.text ? this.props.style.color : emptyColr,
269+
}}
270+
ref={this.textareaInit.bind(this)}
271+
disabled={this.props.disabled}
272+
onSelect={this.props.onSelect}
273+
/>
274+
275+
<textarea
276+
name="shadow"
277+
type="text"
278+
value={this.state.text || emptyText}
279+
rows="1"
280+
onChange={() => (null)}
281+
style={{ visibility: 'hidden', height: 1, marginTop: -5,
282+
top: -15, position: 'relative',
283+
...this.styleComn }}
284+
ref={this.textareaInit.bind(this)}
285+
/>
286+
287+
</div>
288+
289+
</div>);
290+
}
291+
292+
}
293+
294+
295+
SmartTextarea.propTypes = propTypes;
296+
SmartTextarea.defaultProps = defaultProps;
297+
298+
export default SmartTextarea;
299+
300+
/*
301+
<div style={{'-webkit-user-modify':'read-write'}}>{this.state.text}</div>
302+
303+
*/
304+

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import SmartTextarea from './SmartTextarea.jsx';
2+
export default SmartTextarea;

0 commit comments

Comments
 (0)