1 |
|
2 |
|
3 | import React from 'react';
|
4 | import PropTypes from 'prop-types';
|
5 | import classNames from 'classnames';
|
6 | import { mapToCssModules, warnOnce, tagPropType } from './utils';
|
7 |
|
8 | const propTypes = {
|
9 | children: PropTypes.node,
|
10 | type: PropTypes.string,
|
11 | size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
12 | bsSize: PropTypes.string,
|
13 | valid: PropTypes.bool,
|
14 | invalid: PropTypes.bool,
|
15 | tag: tagPropType,
|
16 | innerRef: PropTypes.oneOfType([
|
17 | PropTypes.object,
|
18 | PropTypes.func,
|
19 | PropTypes.string,
|
20 | ]),
|
21 | plaintext: PropTypes.bool,
|
22 | addon: PropTypes.bool,
|
23 | className: PropTypes.string,
|
24 | cssModule: PropTypes.object,
|
25 | };
|
26 |
|
27 | class Input extends React.Component {
|
28 | constructor(props) {
|
29 | super(props);
|
30 | this.getRef = this.getRef.bind(this);
|
31 | this.focus = this.focus.bind(this);
|
32 | }
|
33 |
|
34 | getRef(ref) {
|
35 | if (this.props.innerRef) {
|
36 | this.props.innerRef(ref);
|
37 | }
|
38 | this.ref = ref;
|
39 | }
|
40 |
|
41 | focus() {
|
42 | if (this.ref) {
|
43 | this.ref.focus();
|
44 | }
|
45 | }
|
46 |
|
47 | render() {
|
48 | let {
|
49 | className,
|
50 | cssModule,
|
51 | type = 'text',
|
52 | bsSize,
|
53 | valid,
|
54 | invalid,
|
55 | tag,
|
56 | addon,
|
57 | plaintext,
|
58 | innerRef,
|
59 | ...attributes
|
60 | } = this.props;
|
61 |
|
62 | const checkInput = ['switch', 'radio', 'checkbox'].indexOf(type) > -1;
|
63 | const isNotaNumber = /\D/g;
|
64 |
|
65 | const textareaInput = type === 'textarea';
|
66 | const selectInput = type === 'select';
|
67 | const rangeInput = type === 'range';
|
68 | let Tag = tag || (selectInput || textareaInput ? type : 'input');
|
69 |
|
70 | let formControlClass = 'form-control';
|
71 |
|
72 | if (plaintext) {
|
73 | formControlClass = `${formControlClass}-plaintext`;
|
74 | Tag = tag || 'input';
|
75 | } else if (rangeInput) {
|
76 | formControlClass = 'form-range';
|
77 | } else if (selectInput) {
|
78 | formControlClass = 'form-select';
|
79 | } else if (checkInput) {
|
80 | if (addon) {
|
81 | formControlClass = null;
|
82 | } else {
|
83 | formControlClass = 'form-check-input';
|
84 | }
|
85 | }
|
86 |
|
87 | if (attributes.size && isNotaNumber.test(attributes.size)) {
|
88 | warnOnce(
|
89 | 'Please use the prop "bsSize" instead of the "size" to bootstrap\'s input sizing.',
|
90 | );
|
91 | bsSize = attributes.size;
|
92 | delete attributes.size;
|
93 | }
|
94 |
|
95 | const classes = mapToCssModules(
|
96 | classNames(
|
97 | className,
|
98 | invalid && 'is-invalid',
|
99 | valid && 'is-valid',
|
100 | bsSize
|
101 | ? selectInput
|
102 | ? `form-select-${bsSize}`
|
103 | : `form-control-${bsSize}`
|
104 | : false,
|
105 | formControlClass,
|
106 | ),
|
107 | cssModule,
|
108 | );
|
109 |
|
110 | if (Tag === 'input' || (tag && typeof tag === 'function')) {
|
111 | attributes.type = type === 'switch' ? 'checkbox' : type;
|
112 | }
|
113 |
|
114 | if (
|
115 | attributes.children &&
|
116 | !(
|
117 | plaintext ||
|
118 | type === 'select' ||
|
119 | typeof Tag !== 'string' ||
|
120 | Tag === 'select'
|
121 | )
|
122 | ) {
|
123 | warnOnce(
|
124 | `Input with a type of "${type}" cannot have children. Please use "value"/"defaultValue" instead.`,
|
125 | );
|
126 | delete attributes.children;
|
127 | }
|
128 |
|
129 | return (
|
130 | <Tag
|
131 | {...attributes}
|
132 | ref={innerRef}
|
133 | className={classes}
|
134 | aria-invalid={invalid}
|
135 | />
|
136 | );
|
137 | }
|
138 | }
|
139 |
|
140 | Input.propTypes = propTypes;
|
141 |
|
142 | export default Input;
|