@cimpress/react-components
Version:
React components to support the MCP styleguide
220 lines (212 loc) • 7.46 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import React, { useEffect, useState } from 'react';
import { css, cx } from '@emotion/css';
import { useMemoizedId } from './utils';
import cvar from './theme/cvar';
import SuccessSvg from './icons/SuccessSvg';
import WarningSvg from './icons/WarningSvg';
import ErrorSvg from './icons/ErrorSvg';
const isTextTruthy = (val) => val !== undefined && val !== null && val.toString() !== '';
export const formGroupCss = css `
position: relative;
margin-bottom: ${cvar('spacing-16')};
input {
box-shadow: none;
padding: ${cvar('spacing-16')};
transition: all 0.2s ease-out;
&:focus {
outline: none;
box-shadow: none;
border-color: ${cvar('color-border-interactive')};
}
}
textarea {
box-shadow: none;
padding: ${cvar('spacing-24')} ${cvar('spacing-16')} ${cvar('spacing-12')};
height: auto;
&:focus {
outline: none;
box-shadow: none;
border-color: ${cvar('color-border-interactive')};
}
}
label {
color: ${cvar('color-text-label')};
font-weight: normal;
position: absolute;
left: ${cvar('spacing-16')};
top: ${cvar('spacing-16')};
line-height: 16px;
margin-bottom: 0;
transition: all 0.2s ease-out;
z-index: 5;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 90%;
}
`;
export const formGroupActiveCss = css `
input {
padding: ${cvar('spacing-24')} ${cvar('spacing-16')} ${cvar('spacing-8')};
}
textarea {
padding-top: ${cvar('spacing-24')};
}
label {
top: ${cvar('spacing-4')};
font-size: 12px;
}
`;
export const controlLabelCss = css `
margin-bottom: 0;
font-size: inherit;
line-height: 1.5;
`;
const textAreaLabel = css `
top: 1px;
z-index: 5;
background-color: ${cvar('color-background')};
padding: 4px;
`;
const isRequired = css `
label:after {
content: ' *';
color: ${cvar('color-required-asterisk')};
}
`;
const isDisabled = css `
input {
color: ${cvar('color-text-label')};
background: ${cvar('color-textfield-disabled')};
}
`;
const formControl = css `
height: ${cvar('spacing-48')};
border: 1px solid ${cvar('color-border-default')};
`;
const inputGroup = css `
zindex: 0;
position: relative;
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
> .crc-text-field {
position: relative;
flex: 1 1 auto;
width: 1%;
margin-bottom: 0;
+ .crc-text-field {
margin-left: 1px;
}
}
`;
const inputGroupButton = css `
> button {
padding: ${cvar('spacing-12')} ${cvar('spacing-24')};
border-bottom-right-radius: ${cvar('spacing-2')};
border-top-right-radius: ${cvar('spacing-2')};
line-height: 18px;
height: ${cvar('spacing-48')};
border-bottom-left-radius: 0px;
border-top-left-radius: 0px;
}
`;
const helpBlock = css `
display: block;
margin-top: ${cvar('spacing-4')};
`;
const textfieldColorMap = {
success: 'color-border-success',
warning: 'color-border-warning',
error: 'color-border-error',
};
const statusIconSvg = {
success: React.createElement(SuccessSvg, null),
warning: React.createElement(WarningSvg, null),
error: React.createElement(ErrorSvg, null),
};
const statusCss = (status) => css `
input {
box-shadow: none;
padding-right: ${cvar('spacing-32')};
&:focus {
border-color: ${cvar(textfieldColorMap[status])};
}
}
.crc-text-field {
border-color: ${cvar(textfieldColorMap[status])};
}
svg {
position: absolute;
right: ${cvar('spacing-12')};
top: ${cvar('spacing-16')};
color: ${cvar(textfieldColorMap[status])};
transition: all 0.2s ease-out;
}
`;
const TextInputBase = React.forwardRef((_a, ref) => {
var { autoFocus, className, disabled, helpText, id, inputStyle, label, name, onBlur, onChange, onClick, onFocus, onKeyDown, placeholder, required, rightAddon, status, style, type, value, pattern } = _a, rest = __rest(_a, ["autoFocus", "className", "disabled", "helpText", "id", "inputStyle", "label", "name", "onBlur", "onChange", "onClick", "onFocus", "onKeyDown", "placeholder", "required", "rightAddon", "status", "style", "type", "value", "pattern"]);
const [float, setFloat] = useState(isTextTruthy(value));
const uniqueId = useMemoizedId({ label: `${name}-${placeholder}-${label}`, id });
useEffect(() => {
setFloat(isTextTruthy(value));
}, [setFloat, value]);
const handleBlur = (e) => {
setFloat(isTextTruthy(value));
onBlur && onBlur(e);
};
const handleFocus = (e) => {
setFloat(true);
onFocus && onFocus(e);
};
const handleChange = (e) => {
const isValid = !pattern || !!e.target.value.match(new RegExp(`^${pattern}$`));
onChange && onChange(Object.assign(Object.assign({}, e), { isValid }));
};
const inputId = id || uniqueId;
const isTextArea = type === 'textarea';
const wrapperClassNames = cx('crc-text-input', formGroupCss, className, {
[formGroupActiveCss]: !!label && float,
[statusCss(status)]: !!status,
[isRequired]: !!required,
[isDisabled]: !!disabled,
});
const inputProps = Object.assign(Object.assign({}, rest), { autoFocus,
disabled, id: inputId, name, onBlur: handleBlur, onChange: handleChange, onClick, onFocus: handleFocus, onKeyDown,
placeholder,
required, style: inputStyle, value,
pattern, type: !isTextArea ? type : undefined });
const inputElement = isTextArea ? (React.createElement("textarea", Object.assign({}, inputProps, { className: cx(formControl, 'crc-text-field'), ref: ref }))) : (React.createElement("input", Object.assign({}, inputProps, { className: cx(formControl, 'crc-text-field'), ref: ref })));
const labelElement = label ? (React.createElement("label", { className: cx(controlLabelCss, { [textAreaLabel]: isTextArea }), htmlFor: inputId, title: label }, label)) : null;
return (React.createElement("div", { className: wrapperClassNames, style: style },
React.createElement("div", { className: inputGroup },
inputElement,
labelElement,
rightAddon && React.createElement("span", { className: inputGroupButton }, rightAddon)),
!!status && statusIconSvg[status],
helpText ? React.createElement("small", { className: helpBlock }, helpText) : null));
});
TextInputBase.displayName = 'TextField';
TextInputBase.defaultProps = {
autoFocus: false,
className: '',
disabled: false,
required: false,
type: 'text',
value: '',
};
export const TextField = React.forwardRef((props, ref) => (React.createElement(TextInputBase, Object.assign({}, props, { onChange: props.onChange, ref: ref }))));
export const TextArea = React.forwardRef((props, ref) => (React.createElement(TextInputBase, Object.assign({}, props, { type: "textarea", onChange: props.onChange, ref: ref }))));
//# sourceMappingURL=TextInput.js.map