Skip to content

Commit 6fd365e

Browse files
committed
feat: add draft inputbox component
1 parent 629e343 commit 6fd365e

File tree

3 files changed

+275
-0
lines changed

3 files changed

+275
-0
lines changed

src/components/inputBox/index.tsx

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import './style.scss';
2+
import * as React from 'react';
3+
import { prefixClaName, classNames } from 'mo/common/className';
4+
5+
interface IInputBox {
6+
readonly autoSelect?: boolean;
7+
readonly autoFocus?: boolean;
8+
readonly children?: React.ReactNode;
9+
readonly style?: React.CSSProperties;
10+
readonly bsStyle?: 'success' | 'warning' | 'error';
11+
readonly flexibleHeight?: boolean;
12+
readonly id?: string;
13+
readonly placeholder?: string;
14+
readonly type?: string;
15+
readonly value?: string;
16+
readonly onChange?: (value: string | number) => void;
17+
readonly onBlur?: (e: Event) => void;
18+
readonly onKeyPress?: (
19+
keyCode: number,
20+
keyChar: number | string,
21+
key: number
22+
) => void;
23+
readonly onFocus?: (e: Event) => void;
24+
}
25+
26+
export default class InputBox extends React.Component<IInputBox> {
27+
private autoSelected: boolean;
28+
private id: string | undefined;
29+
30+
constructor(props: IInputBox) {
31+
super(props);
32+
this.autoSelected = false;
33+
this.id = this.props.id;
34+
}
35+
36+
componentWillUnmount() {
37+
this.autoSelected = false;
38+
}
39+
40+
getStyleClass() {
41+
let bsStyle = this.props.bsStyle;
42+
43+
return classNames(
44+
bsStyle === 'success' ? 'has-success' : '',
45+
bsStyle === 'warning' ? 'has-warning' : '',
46+
bsStyle === 'error' ? 'has-error' : ''
47+
);
48+
}
49+
50+
renderFlexibleHeight() {
51+
const { flexibleHeight } = this.props;
52+
if (flexibleHeight) {
53+
return (
54+
<textarea
55+
ref={this.onRef.bind(this)}
56+
onKeyPress={this.onKeyPress.bind(this)}
57+
onBlur={this.onBlur.bind(this)}
58+
onFocus={this.onFocus.bind(this)}
59+
onChange={this.onChange.bind(this)}
60+
placeholder={this.props.placeholder}
61+
className={classNames('input')}
62+
autoCorrect="off"
63+
autoCapitalize="off"
64+
spellCheck="false"
65+
>
66+
{this.props.value}
67+
</textarea>
68+
);
69+
} else {
70+
return (
71+
<input
72+
ref={this.onRef.bind(this)}
73+
onKeyPress={this.onKeyPress.bind(this)}
74+
onBlur={this.onBlur.bind(this)}
75+
onFocus={this.onFocus.bind(this)}
76+
onChange={this.onChange.bind(this)}
77+
value={this.props.value}
78+
placeholder={this.props.placeholder}
79+
wrap="off"
80+
className={classNames('input')}
81+
type={this.props.type}
82+
autoCorrect="off"
83+
autoCapitalize="off"
84+
spellCheck="false"
85+
/>
86+
);
87+
}
88+
}
89+
90+
/**
91+
* onRef gets the ref of the input/textarea element after rendering or componentUnMount
92+
*/
93+
onRef(ref) {
94+
if (ref && this.props.autoFocus) {
95+
ref.focus();
96+
}
97+
// only autoselect once
98+
if (
99+
ref &&
100+
this.props.autoSelect &&
101+
ref.setSelectionRange &&
102+
this.autoSelected === false
103+
) {
104+
ref.setSelectionRange(0, this.getValueFromElement(ref).length);
105+
this.autoSelected = true;
106+
}
107+
}
108+
109+
/**
110+
* Returns the value from the element or empty string.
111+
*/
112+
getValueFromElement(element) {
113+
let value = '';
114+
// get value
115+
if (element.type === 'text') {
116+
value = element.value;
117+
} else if (element.type === 'textarea') {
118+
value = element.innerText;
119+
}
120+
121+
return value;
122+
}
123+
124+
onChange(e) {
125+
e.preventDefault();
126+
let value;
127+
value = this.getValueFromElement(e.target);
128+
if (this.props.onChange) {
129+
this.props.onChange(value);
130+
}
131+
}
132+
133+
onKeyPress(e) {
134+
this.props.onKeyPress?.(e.keyCode, e.keyChar, e.key);
135+
}
136+
137+
onBlur(e) {
138+
this.props.onBlur?.(e);
139+
}
140+
141+
onFocus(e) {
142+
e.preventDefault();
143+
144+
if (this.props.onFocus) {
145+
this.props.onFocus(e);
146+
}
147+
}
148+
149+
render() {
150+
const { style } = this.props;
151+
return (
152+
<div
153+
className={classNames(prefixClaName('inputbox'), 'idle')}
154+
style={style}
155+
>
156+
<div className="wrapper">{this.renderFlexibleHeight()}</div>
157+
</div>
158+
);
159+
}
160+
}

src/components/inputBox/style.scss

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
@import 'mo/style/common';
2+
$InputBox: 'inputbox';
3+
4+
#{prefix($InputBox)} {
5+
background-color: rgb(60, 60, 60);
6+
box-sizing: border-box;
7+
color: rgb(204, 204, 204);
8+
display: block;
9+
/* Customizable */
10+
font-size: inherit;
11+
overflow: hidden;
12+
padding: 0;
13+
position: relative;
14+
15+
.idle {
16+
border: 1px solid transparent;
17+
}
18+
19+
>.wrapper>.input,
20+
>.wrapper>.mirror {
21+
padding: 4px;
22+
}
23+
24+
>.wrapper {
25+
height: 100%;
26+
position: relative;
27+
width: 100%;
28+
29+
>.input {
30+
background-color: inherit;
31+
border: 1px solid transparent;
32+
box-sizing: border-box;
33+
color: inherit;
34+
color: rgb(204, 204, 204);
35+
display: inline-block;
36+
font-family: inherit;
37+
font-size: inherit;
38+
height: 100%;
39+
line-height: inherit;
40+
padding-right: 66px;
41+
resize: none;
42+
width: 100%;
43+
44+
&:focus {
45+
outline-color: #007fd4;
46+
}
47+
}
48+
}
49+
50+
>input {
51+
text-overflow: ellipsis;
52+
}
53+
54+
>textarea.input {
55+
background-color: inherit;
56+
color: rgb(204, 204, 204);
57+
display: block;
58+
-ms-overflow-style: none;
59+
padding-right: 66px;
60+
61+
&:focus {
62+
outline-color: #007fd4;
63+
}
64+
}
65+
66+
>textarea.input::-webkit-scrollbar {
67+
display: none;
68+
/* Chrome + Safari: hide scrollbar */
69+
}
70+
71+
>textarea.input.empty {
72+
white-space: nowrap;
73+
}
74+
75+
>.mirror {
76+
box-sizing: border-box;
77+
display: inline-block;
78+
left: 0;
79+
position: absolute;
80+
top: 0;
81+
visibility: hidden;
82+
white-space: pre-wrap;
83+
width: 100%;
84+
word-wrap: break-word;
85+
}
86+
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as React from 'react';
2+
import InpuxBox from 'mo/components/inputBox';
3+
import { storiesOf } from '@storybook/react';
4+
import { withKnobs } from '@storybook/addon-knobs';
5+
const stories = storiesOf('InpuxBox', module);
6+
stories.addDecorator(withKnobs);
7+
8+
stories.add('Basic Usage', () => {
9+
const styled: React.CSSProperties = {
10+
background: '#1e1e1e',
11+
height: 100,
12+
padding: 10,
13+
};
14+
return (
15+
<>
16+
<h2>简述</h2>
17+
<p>Inputbox</p>
18+
<h3>使用示例 1 - 基本使用</h3>
19+
<div style={styled}>
20+
<InpuxBox placeholder="Search" />
21+
<InpuxBox
22+
placeholder="replace"
23+
flexibleHeight={true}
24+
style={{ marginTop: 10 }}
25+
/>
26+
</div>
27+
</>
28+
);
29+
});

0 commit comments

Comments
 (0)