@dotconnor/grommet
Version:
focus on the essential experience
324 lines (281 loc) • 12.5 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React, { Children, cloneElement, forwardRef, useContext, useState } from 'react';
import styled, { ThemeContext } from 'styled-components';
import { defaultProps } from '../../default-props';
import { focusStyle, parseMetricToNum } from '../../utils';
import { Box } from '../Box';
import { CheckBox } from '../CheckBox';
import { CheckBoxGroup } from '../CheckBoxGroup';
import { RadioButtonGroup } from '../RadioButtonGroup';
import { Text } from '../Text';
import { TextInput } from '../TextInput';
import { FormContext } from '../Form/FormContext';
var grommetInputNames = ['TextInput', 'Select', 'MaskedInput', 'TextArea', 'DateInput'];
var grommetInputPadNames = ['CheckBox', 'CheckBoxGroup', 'RadioButtonGroup', 'RangeInput'];
var isGrommetInput = function isGrommetInput(comp) {
return comp && (grommetInputNames.indexOf(comp.displayName) !== -1 || grommetInputPadNames.indexOf(comp.displayName) !== -1);
};
var FormFieldBox = styled(Box).withConfig({
displayName: "FormField__FormFieldBox",
componentId: "m9hood-0"
})(["", " ", ""], function (props) {
return props.focus && focusStyle({
justBorder: true
});
}, function (props) {
return props.theme.formField && props.theme.formField.extend;
});
var FormFieldContentBox = styled(Box).withConfig({
displayName: "FormField__FormFieldContentBox",
componentId: "m9hood-1"
})(["", ""], function (props) {
return props.focus && focusStyle({
justBorder: true
});
});
var StyledMessageContainer = styled(Box).withConfig({
displayName: "FormField__StyledMessageContainer",
componentId: "m9hood-2"
})(["", ""], function (props) {
return props.messageType && props.theme.formField[props.messageType].container && props.theme.formField[props.messageType].container.extend;
});
var Message = function Message(_ref) {
var error = _ref.error,
info = _ref.info,
message = _ref.message,
type = _ref.type,
rest = _objectWithoutPropertiesLoose(_ref, ["error", "info", "message", "type"]);
var theme = useContext(ThemeContext) || defaultProps.theme;
if (message) {
var icon;
var containerProps;
if (type) {
icon = theme.formField[type] && theme.formField[type].icon;
containerProps = theme.formField[type] && theme.formField[type].container;
}
var messageContent;
if (typeof message === 'string') messageContent = /*#__PURE__*/React.createElement(Text, rest, message);else messageContent = /*#__PURE__*/React.createElement(Box, rest, message);
return icon || containerProps ? /*#__PURE__*/React.createElement(StyledMessageContainer, _extends({
direction: "row",
messageType: type
}, containerProps), icon && /*#__PURE__*/React.createElement(Box, {
flex: false
}, icon), messageContent) : messageContent;
}
return null;
};
var Input = function Input(_ref2) {
var component = _ref2.component,
disabled = _ref2.disabled,
invalid = _ref2.invalid,
name = _ref2.name,
_onChange = _ref2.onChange,
rest = _objectWithoutPropertiesLoose(_ref2, ["component", "disabled", "invalid", "name", "onChange"]);
var formContext = useContext(FormContext);
var _formContext$useFormI = formContext.useFormInput(name, rest.value),
value = _formContext$useFormI[0],
setValue = _formContext$useFormI[1];
var InputComponent = component || TextInput; // Grommet input components already check for FormContext
// and, using their `name`, end up calling the useFormInput.setValue()
// already. For custom components, we expect they will call
// this onChange() and we'll call setValue() here, primarily
// for backwards compatibility.
var extraProps = isGrommetInput(InputComponent) ? {
focusIndicator: false,
onChange: _onChange,
plain: true
} : {
value: value,
onChange: function onChange(event) {
setValue(event.value !== undefined ? event.value : event.target.value);
if (_onChange) _onChange(event);
}
};
return /*#__PURE__*/React.createElement(InputComponent, _extends({
name: name,
disabled: disabled,
"aria-invalid": invalid || undefined
}, rest, extraProps));
};
var FormField = /*#__PURE__*/forwardRef(function (_ref3, ref) {
var children = _ref3.children,
className = _ref3.className,
component = _ref3.component,
contentProps = _ref3.contentProps,
disabled = _ref3.disabled,
errorProp = _ref3.error,
help = _ref3.help,
htmlFor = _ref3.htmlFor,
infoProp = _ref3.info,
label = _ref3.label,
margin = _ref3.margin,
name = _ref3.name,
_onBlur = _ref3.onBlur,
_onFocus = _ref3.onFocus,
pad = _ref3.pad,
required = _ref3.required,
style = _ref3.style,
validate = _ref3.validate,
rest = _objectWithoutPropertiesLoose(_ref3, ["children", "className", "component", "contentProps", "disabled", "error", "help", "htmlFor", "info", "label", "margin", "name", "onBlur", "onFocus", "pad", "required", "style", "validate"]);
var theme = useContext(ThemeContext) || defaultProps.theme;
var formContext = useContext(FormContext);
var _formContext$useFormF = formContext.useFormField({
error: errorProp,
info: infoProp,
name: name,
required: required,
validate: validate
}),
error = _formContext$useFormF.error,
info = _formContext$useFormF.info,
inForm = _formContext$useFormF.inForm,
contextOnBlur = _formContext$useFormF.onBlur;
var _useState = useState(),
focus = _useState[0],
setFocus = _useState[1];
var formFieldTheme = theme.formField;
var themeBorder = formFieldTheme.border; // This is here for backwards compatibility. In case the child is a grommet
// input component, set plain and focusIndicator props, if they aren't
// already set.
var wantContentPad = component && (component === CheckBox || component === CheckBoxGroup || component === RadioButtonGroup);
var contents = themeBorder && children && Children.map(children, function (child) {
if (child && child.type && grommetInputPadNames.indexOf(child.type.displayName) !== -1) {
wantContentPad = true;
}
if (child && child.type && grommetInputNames.indexOf(child.type.displayName) !== -1 && child.props.plain === undefined && child.props.focusIndicator === undefined) {
return /*#__PURE__*/cloneElement(child, {
plain: true,
focusIndicator: false
});
}
return child;
}) || children; // put rest on container, unless we use internal Input
var containerRest = rest;
if (inForm) {
if (!contents) containerRest = {};
contents = contents || /*#__PURE__*/React.createElement(Input, _extends({
component: component,
disabled: disabled,
invalid: !!error,
name: name,
label: component === CheckBox ? label : undefined
}, rest));
}
var themeContentProps = _extends({}, formFieldTheme.content);
if (!pad && !wantContentPad) {
themeContentProps.pad = undefined;
}
if (themeBorder && themeBorder.position === 'inner') {
if (error && formFieldTheme.error) {
themeContentProps.background = formFieldTheme.error.background;
} else if (disabled && formFieldTheme.disabled) {
themeContentProps.background = formFieldTheme.disabled.background;
}
}
if (!themeBorder) {
contents = /*#__PURE__*/React.createElement(Box, _extends({}, themeContentProps, contentProps), contents);
}
var borderColor;
if (disabled && formFieldTheme.disabled.border && formFieldTheme.disabled.border.color) {
borderColor = formFieldTheme.disabled.border.color;
} else if (error && themeBorder && themeBorder.error.color) {
borderColor = themeBorder.error.color || 'status-critical';
} else if (focus && formFieldTheme.focus && formFieldTheme.focus.border && formFieldTheme.focus.border.color) {
borderColor = formFieldTheme.focus.border.color;
} else {
borderColor = themeBorder && themeBorder.color || 'border';
}
var labelStyle = _extends({}, formFieldTheme.label);
if (disabled) {
labelStyle.color = formFieldTheme.disabled && formFieldTheme.disabled.label ? formFieldTheme.disabled.label.color : labelStyle.color;
}
var abut;
var abutMargin;
var outerStyle = style;
if (themeBorder) {
var innerProps = themeBorder.position === 'inner' ? {
border: _extends({}, themeBorder, {
side: themeBorder.side || 'bottom',
color: borderColor
}),
round: formFieldTheme.round,
focus: focus
} : {};
contents = /*#__PURE__*/React.createElement(FormFieldContentBox, _extends({}, themeContentProps, innerProps, contentProps), contents);
var mergedMargin = margin || formFieldTheme.margin;
abut = themeBorder.position === 'outer' && (themeBorder.side === 'all' || themeBorder.side === 'horizontal' || !themeBorder.side) && !(mergedMargin && (typeof mergedMargin === 'string' && mergedMargin !== 'none' || mergedMargin.bottom && mergedMargin.bottom !== 'none' || mergedMargin.horizontal && mergedMargin.horizontal !== 'none'));
if (abut) {
// marginBottom is set to overlap adjacent fields
abutMargin = {
bottom: '-1px'
};
if (margin) {
abutMargin = margin;
} else if (themeBorder.size) {
// if the user defines a margin,
// then the default margin below will be overridden
abutMargin = {
bottom: "-" + parseMetricToNum(theme.global.borderSize[themeBorder.size] || themeBorder.size) + "px"
};
}
outerStyle = _extends({
position: focus ? 'relative' : undefined,
zIndex: focus ? 10 : undefined
}, style);
}
}
var outerBackground;
if (themeBorder && themeBorder.position === 'outer') {
if (error && formFieldTheme.error && formFieldTheme.error.background) {
outerBackground = formFieldTheme.error.background;
} else if (focus && formFieldTheme.focus && formFieldTheme.focus.background && formFieldTheme.focus.background.color) {
outerBackground = formFieldTheme.focus.background.color;
} else if (disabled && formFieldTheme.disabled && formFieldTheme.disabled.background) {
outerBackground = formFieldTheme.disabled.background;
}
}
var outerProps = themeBorder && themeBorder.position === 'outer' ? {
border: _extends({}, themeBorder, {
color: borderColor
}),
round: formFieldTheme.round,
focus: focus
} : {};
return /*#__PURE__*/React.createElement(FormFieldBox, _extends({
ref: ref,
className: className,
background: outerBackground,
margin: abut ? abutMargin : margin || _extends({}, formFieldTheme.margin)
}, outerProps, {
style: outerStyle,
onFocus: function onFocus(event) {
setFocus(true);
if (_onFocus) _onFocus(event);
},
onBlur: function onBlur(event) {
setFocus(false);
if (contextOnBlur) contextOnBlur(event);
if (_onBlur) _onBlur(event);
}
}, containerRest), label && component !== CheckBox || help ? /*#__PURE__*/React.createElement(React.Fragment, null, label && component !== CheckBox && /*#__PURE__*/React.createElement(Text, _extends({
as: "label",
htmlFor: htmlFor
}, labelStyle), label), /*#__PURE__*/React.createElement(Message, _extends({
message: help
}, formFieldTheme.help))) : undefined, contents, /*#__PURE__*/React.createElement(Message, _extends({
type: "error",
message: error
}, formFieldTheme.error)), /*#__PURE__*/React.createElement(Message, _extends({
type: "info",
message: info
}, formFieldTheme.info)));
});
FormField.displayName = 'FormField';
var FormFieldDoc;
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require
FormFieldDoc = require('./doc').doc(FormField);
}
var FormFieldWrapper = FormFieldDoc || FormField;
export { FormFieldWrapper as FormField };