UNPKG

7.64 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import clsx from 'clsx';
6import { isFilled, isAdornedStart } from '../InputBase/utils';
7import withStyles from '../styles/withStyles';
8import capitalize from '../utils/capitalize';
9import isMuiElement from '../utils/isMuiElement';
10import FormControlContext from './FormControlContext';
11export const styles = {
12 /* Styles applied to the root element. */
13 root: {
14 display: 'inline-flex',
15 flexDirection: 'column',
16 position: 'relative',
17 // Reset fieldset default style.
18 minWidth: 0,
19 padding: 0,
20 margin: 0,
21 border: 0,
22 verticalAlign: 'top' // Fix alignment issue on Safari.
23
24 },
25
26 /* Styles applied to the root element if `margin="normal"`. */
27 marginNormal: {
28 marginTop: 16,
29 marginBottom: 8
30 },
31
32 /* Styles applied to the root element if `margin="dense"`. */
33 marginDense: {
34 marginTop: 8,
35 marginBottom: 4
36 },
37
38 /* Styles applied to the root element if `fullWidth={true}`. */
39 fullWidth: {
40 width: '100%'
41 }
42};
43/**
44 * Provides context such as filled/focused/error/required for form inputs.
45 * Relying on the context provides high flexibility and ensures that the state always stays
46 * consistent across the children of the `FormControl`.
47 * This context is used by the following components:
48 *
49 * - FormLabel
50 * - FormHelperText
51 * - Input
52 * - InputLabel
53 *
54 * You can find one composition example below and more going to [the demos](/components/text-fields/#components).
55 *
56 * ```jsx
57 * <FormControl>
58 * <InputLabel htmlFor="my-input">Email address</InputLabel>
59 * <Input id="my-input" aria-describedby="my-helper-text" />
60 * <FormHelperText id="my-helper-text">We'll never share your email.</FormHelperText>
61 * </FormControl>
62 * ```
63 *
64 * ⚠️Only one input can be used within a FormControl.
65 */
66
67const FormControl = /*#__PURE__*/React.forwardRef(function FormControl(props, ref) {
68 const {
69 children,
70 classes,
71 className,
72 color = 'primary',
73 component: Component = 'div',
74 disabled = false,
75 error = false,
76 fullWidth = false,
77 focused: visuallyFocused,
78 hiddenLabel = false,
79 margin = 'none',
80 required = false,
81 size,
82 variant = 'standard'
83 } = props,
84 other = _objectWithoutPropertiesLoose(props, ["children", "classes", "className", "color", "component", "disabled", "error", "fullWidth", "focused", "hiddenLabel", "margin", "required", "size", "variant"]);
85
86 const [adornedStart, setAdornedStart] = React.useState(() => {
87 // We need to iterate through the children and find the Input in order
88 // to fully support server-side rendering.
89 let initialAdornedStart = false;
90
91 if (children) {
92 React.Children.forEach(children, child => {
93 if (!isMuiElement(child, ['Input', 'Select'])) {
94 return;
95 }
96
97 const input = isMuiElement(child, ['Select']) ? child.props.input : child;
98
99 if (input && isAdornedStart(input.props)) {
100 initialAdornedStart = true;
101 }
102 });
103 }
104
105 return initialAdornedStart;
106 });
107 const [filled, setFilled] = React.useState(() => {
108 // We need to iterate through the children and find the Input in order
109 // to fully support server-side rendering.
110 let initialFilled = false;
111
112 if (children) {
113 React.Children.forEach(children, child => {
114 if (!isMuiElement(child, ['Input', 'Select'])) {
115 return;
116 }
117
118 if (isFilled(child.props, true)) {
119 initialFilled = true;
120 }
121 });
122 }
123
124 return initialFilled;
125 });
126 const [_focused, setFocused] = React.useState(false);
127 const focused = visuallyFocused !== undefined ? visuallyFocused : _focused;
128
129 if (disabled && focused) {
130 setFocused(false);
131 }
132
133 let registerEffect;
134
135 if (process.env.NODE_ENV !== 'production') {
136 // eslint-disable-next-line react-hooks/rules-of-hooks
137 const registeredInput = React.useRef(false);
138
139 registerEffect = () => {
140 if (registeredInput.current) {
141 console.error(['Material-UI: There are multiple InputBase components inside a FormControl.', 'This is not supported. It might cause infinite rendering loops.', 'Only use one InputBase.'].join('\n'));
142 }
143
144 registeredInput.current = true;
145 return () => {
146 registeredInput.current = false;
147 };
148 };
149 }
150
151 const onFilled = React.useCallback(() => {
152 setFilled(true);
153 }, []);
154 const onEmpty = React.useCallback(() => {
155 setFilled(false);
156 }, []);
157 const childContext = {
158 adornedStart,
159 setAdornedStart,
160 color,
161 disabled,
162 error,
163 filled,
164 focused,
165 fullWidth,
166 hiddenLabel,
167 margin: (size === 'small' ? 'dense' : undefined) || margin,
168 onBlur: () => {
169 setFocused(false);
170 },
171 onEmpty,
172 onFilled,
173 onFocus: () => {
174 setFocused(true);
175 },
176 registerEffect,
177 required,
178 variant
179 };
180 return /*#__PURE__*/React.createElement(FormControlContext.Provider, {
181 value: childContext
182 }, /*#__PURE__*/React.createElement(Component, _extends({
183 className: clsx(classes.root, className, margin !== 'none' && classes[`margin${capitalize(margin)}`], fullWidth && classes.fullWidth),
184 ref: ref
185 }, other), children));
186});
187process.env.NODE_ENV !== "production" ? FormControl.propTypes = {
188 // ----------------------------- Warning --------------------------------
189 // | These PropTypes are generated from the TypeScript type definitions |
190 // | To update them edit the d.ts file and run "yarn proptypes" |
191 // ----------------------------------------------------------------------
192
193 /**
194 * The contents of the form control.
195 */
196 children: PropTypes.node,
197
198 /**
199 * Override or extend the styles applied to the component.
200 * See [CSS API](#css) below for more details.
201 */
202 classes: PropTypes.object,
203
204 /**
205 * @ignore
206 */
207 className: PropTypes.string,
208
209 /**
210 * The color of the component. It supports those theme colors that make sense for this component.
211 */
212 color: PropTypes.oneOf(['primary', 'secondary']),
213
214 /**
215 * The component used for the root node.
216 * Either a string to use a HTML element or a component.
217 */
218 component: PropTypes
219 /* @typescript-to-proptypes-ignore */
220 .elementType,
221
222 /**
223 * If `true`, the label, input and helper text should be displayed in a disabled state.
224 */
225 disabled: PropTypes.bool,
226
227 /**
228 * If `true`, the label should be displayed in an error state.
229 */
230 error: PropTypes.bool,
231
232 /**
233 * If `true`, the component will be displayed in focused state.
234 */
235 focused: PropTypes.bool,
236
237 /**
238 * If `true`, the component will take up the full width of its container.
239 */
240 fullWidth: PropTypes.bool,
241
242 /**
243 * If `true`, the label will be hidden.
244 * This is used to increase density for a `FilledInput`.
245 * Be sure to add `aria-label` to the `input` element.
246 */
247 hiddenLabel: PropTypes.bool,
248
249 /**
250 * If `dense` or `normal`, will adjust vertical spacing of this and contained components.
251 */
252 margin: PropTypes.oneOf(['dense', 'none', 'normal']),
253
254 /**
255 * If `true`, the label will indicate that the input is required.
256 */
257 required: PropTypes.bool,
258
259 /**
260 * The size of the text field.
261 */
262 size: PropTypes.oneOf(['medium', 'small']),
263
264 /**
265 * The variant to use.
266 */
267 variant: PropTypes.oneOf(['filled', 'outlined', 'standard'])
268} : void 0;
269export default withStyles(styles, {
270 name: 'MuiFormControl'
271})(FormControl);
\No newline at end of file