UNPKG

orcs-design-system

Version:
333 lines 14.6 kB
import React, { useMemo } from "react"; import { NumericFormat } from "react-number-format"; import PropTypes from "prop-types"; import styled, { css } from "styled-components"; import { space, layout, compose } from "styled-system"; import { createShouldForwardProp, props } from "@styled-system/should-forward-prop"; import Icon from "../Icon"; import { themeGet } from "@styled-system/theme-get"; import useInputFocus from "../../hooks/useInputFocus"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const InputStyles = compose(space, layout); const shouldForwardProp = createShouldForwardProp([...props, "type", "placeholder", "defaultValue", "disabled", "maxLength", "pattern", "required", "autocomplete", "autofocus", "step", "readonly"]); const Group = styled("div").withConfig({ shouldForwardProp, displayName: "TextInput__Group", componentId: "sc-shde0o-0" })(props => css({ position: "relative", width: props.fullWidth ? "100%" : "auto", minHeight: props => props.height ?? themeGet(props.floating ? "appScale.inputHeightLarge" : "appScale.inputHeightDefault")(props) }), InputStyles); const IconWrapper = styled.label.withConfig({ displayName: "TextInput__IconWrapper", componentId: "sc-shde0o-1" })(["display:flex;align-items:center;min-height:inherit;position:absolute;width:fit-content;bottom:0;padding:0 10px;svg{opacity:0.4;}", ";"], props => props.iconLeft && !props.floating ? css(["left:0;"]) : props.iconLeft && props.floating ? css(["left:0;bottom:11px;"]) : props.iconRight && !props.floating ? css(["right:0;"]) : props.iconRight && props.floating ? css(["right:0;bottom:11px;"]) : css([""])); const InputStyle = css(["display:block;cursor:text;-moz-appearance:none;-webkit-appearance:none;appearance:none;font-family:\"Open Sans\",\"Helvetica Neue\",Helvetica,Arial,sans-serif;box-shadow:none;font-size:", ";z-index:1;border-radius:", ";transition:", ";background:", ";color:", ";width:", ";height:", ";padding:", ";border:1px solid ", ";", " &:hover{border:1px solid ", ";}&:focus{outline:0;box-shadow:", ";border:1px solid ", ";", ";}"], props => themeGet("fontSizes.2")(props), props => themeGet("radii.2")(props), props => themeGet("transition.transitionDefault")(props), props => props.disabled ? themeGet("colors.greyLightest")(props) : themeGet("colors.white")(props), props => props.disabled ? themeGet("colors.grey")(props) : themeGet("colors.greyDarkest")(props), props => props.fullWidth ? `100%` : `auto`, props => props.height ? props.height : props.floating ? themeGet("appScale.inputHeightLarge")(props) : themeGet("appScale.inputHeightDefault")(props), props => { let left = props.iconLeft ? 36 : 10; let right = props.iconRight ? 36 : 10; let top = props.floating ? 25 : 5; let bottom = 6; return `${top}px ${right}px ${bottom}px ${left}px`; }, props => props.invalid ? themeGet("colors.danger")(props) : props.valid ? themeGet("colors.success")(props) : themeGet("colors.black30")(props), props => props.floating ? css(["&::placeholder{color:transparent;}&:not(:placeholder-shown){~ label{transform:translateY(-10px);font-size:", ";}}&:-ms-input-placeholder:not(:focus){~ label{transform:translateY(-10px);font-size:", ";}}&:not(:-ms-input-placeholder){~ label{transform:translateY(-10px);font-size:", ";}}"], themeGet("fontSizes.0")(props), themeGet("fontSizes.0")(props), themeGet("fontSizes.0")(props)) : css(["&::placeholder{color:", ";}"], themeGet("colors.grey")(props)), props => props.invalid ? themeGet("colors.dangerDark")(props) : props.valid ? themeGet("colors.successDark")(props) : themeGet("colors.primary")(props), props => props.invalid ? themeGet("shadows.thickOutline")(props) + " " + themeGet("colors.danger30")(props) : props.valid ? themeGet("shadows.thickOutline")(props) + " " + themeGet("colors.success30")(props) : themeGet("shadows.thickOutline")(props) + " " + themeGet("colors.primary30")(props), props => props.invalid ? themeGet("colors.dangerDark")(props) : props.valid ? themeGet("colors.successDark")(props) : themeGet("colors.primary")(props), props => props.floating ? css(["&::placeholder{color:", ";}~ label{transform:translateY(-10px);font-size:", ";color:", ";}"], themeGet("colors.greyLight")(props), themeGet("fontSizes.0")(props), props => props.invalid ? themeGet("colors.dangerDark")(props) : props.valid ? themeGet("colors.successDark")(props) : themeGet("colors.primary")(props)) : css([""])); const Input = styled("input").attrs(props => ({ "data-testid": props["data-testid"] ? props["data-testid"] : null })).withConfig({ displayName: "TextInput__Input", componentId: "sc-shde0o-2" })(["", ""], InputStyle); const NumberInput = styled(NumericFormat).attrs(props => ({ "data-testid": props["data-testid"] ? props["data-testid"] : null })).withConfig({ displayName: "TextInput__NumberInput", componentId: "sc-shde0o-3" })(["", ""], InputStyle); const Label = styled.label.withConfig({ displayName: "TextInput__Label", componentId: "sc-shde0o-4" })(["display:block;z-index:2;text-align:left;font-size:", ";font-weight:", ";transition:", ";padding-right:", ";margin-bottom:", ";color:", ";", ";", ";"], props => themeGet("fontSizes.1")(props), props => props.bold ? themeGet("fontWeights.2")(props) : themeGet("fontWeights.1")(props), props => themeGet("transition.transitionDefault")(props), props => props.floating && props.iconRight ? `40px` : `12px`, props => props.floating ? 0 : themeGet("space.xs")(props), props => props.inverted ? themeGet("colors.white")(props) : props.valid ? themeGet("colors.successDark")(props) : props.invalid ? themeGet("colors.dangerDark")(props) : themeGet("colors.greyDarkest")(props), props => props.floating ? css(["padding-left:", ";cursor:text;position:absolute;top:", ";color:", ";"], props => props.iconLeft ? "37px" : "11px", props => { let inputHeight = themeGet("appScale.inputHeightLarge")(props); return `calc(${inputHeight} / 3)`; }, props => props.invalid ? themeGet("colors.dangerDark")(props) : props.valid ? themeGet("colors.successDark")(props) : themeGet("colors.greyDark")(props)) : css([""]), props => props.mandatory ? css(["&:after{content:\" *\";color:", ";}"], themeGet("colors.danger")(props)) : css([""])); const TextInput = /*#__PURE__*/React.forwardRef((props, ref) => { const { inverted, floating, id, bold, invalid, valid, fullWidth, mandatory, iconLeft, iconRight, InputStyles, height, focus, ariaLabel } = props; const inputRef = useInputFocus(ref, focus); const { numberProps, ...rest } = props; let getNumberInputRef = null; if (numberProps && inputRef) { getNumberInputRef = node => { inputRef.current = node; }; } const label = useMemo(() => { if (numberProps && numberProps.prefix) { return `${props.label} ${numberProps.prefix}`; } return props.label; }, [props.label, numberProps]); return /*#__PURE__*/_jsxs(Group, { fullWidth: fullWidth, height: height, ...InputStyles, children: [label && !floating ? /*#__PURE__*/_jsx(Label, { inverted: inverted, invalid: invalid, valid: valid, bold: bold, htmlFor: id, mandatory: mandatory, children: label }) : null, numberProps ? /*#__PURE__*/_jsx(NumberInput, { getInputRef: getNumberInputRef, "data-testid": props["data-testid"], id: id, "aria-label": ariaLabel, ...rest, height: height, ...numberProps }) : /*#__PURE__*/_jsx(Input, { "data-testid": props["data-testid"], height: height, ref: inputRef, id: id, "aria-label": ariaLabel, ...rest }), label && floating ? /*#__PURE__*/_jsx(Label, { floating: floating, invalid: invalid, valid: valid, bold: bold, htmlFor: id, mandatory: mandatory, iconRight: iconRight, iconLeft: iconLeft, children: label }) : null, iconLeft ? /*#__PURE__*/_jsx(IconWrapper, { htmlFor: id, iconLeft: iconLeft, floating: floating, children: /*#__PURE__*/_jsx(Icon, { icon: iconLeft, htmlFor: id, color: "black" }) }) : null, iconRight ? /*#__PURE__*/_jsx(IconWrapper, { htmlFor: id, iconRight: iconRight, floating: floating, children: /*#__PURE__*/_jsx(Icon, { icon: iconRight, htmlFor: id, color: "#black" }) }) : null] }); }); TextInput.propTypes = { /** Must be used to specify a unique ID. */ id: PropTypes.string.isRequired, /** Can be used to set a specific height. */ height: PropTypes.string, /** Specifies the text for the label. */ label: PropTypes.string, /** Specifies aria-label for TextArea. This is only required if not using the label prop.*/ ariaLabel: (props, propName) => { if (!props.label && (props[propName] == null || props[propName] === "")) { return new Error(`Missing prop \`${propName}\` not specified for TextInput component. When \`label\` is not provided, \`${propName}\` is required.`); } if (props[propName] && typeof props[propName] !== "string") { return new Error(`Invalid propType \`${propName}\` supplied to TextInput component. Expected \`string\`, received \`${typeof props[propName]}\`.`); } return null; }, /** Makes label text bold */ bold: PropTypes.bool, /** Specifies the helper/example text for the placeholder. */ placeholder: PropTypes.string, /** Specifies the type of text input, e.g. text, email, password, number */ type: PropTypes.string, /** Applies different styling for a floating animated label aesthetic */ floating: PropTypes.bool, /** Makes text box take up full width of parent */ fullWidth: PropTypes.bool, /** Applies invalid input styles */ invalid: PropTypes.bool, /** Applies valid input styles */ valid: PropTypes.bool, /** Shows asterisk to denote a mandatory field */ mandatory: PropTypes.bool, /** Applies an icon to the left of the text box with specified name. */ iconLeft: PropTypes.array, /** Applies an icon to the right of the text box with specified name. */ iconRight: PropTypes.array, /** Number format props, to render a number input textbox */ numberProps: PropTypes.object, /** Set inverted styling for dark backgrounds */ inverted: PropTypes.bool, /** Specifies the `data-testid` attribute for testing. */ "data-testid": PropTypes.string, /** Specifies any additional `space` and `layout` props for the entire component */ InputStyles: PropTypes.object, /** Focus on input */ focus: PropTypes.bool }; TextInput.__docgenInfo = { "description": "", "methods": [], "displayName": "TextInput", "props": { "id": { "description": "Must be used to specify a unique ID.", "type": { "name": "string" }, "required": true }, "height": { "description": "Can be used to set a specific height.", "type": { "name": "string" }, "required": false }, "label": { "description": "Specifies the text for the label.", "type": { "name": "string" }, "required": false }, "ariaLabel": { "description": "Specifies aria-label for TextArea. This is only required if not using the label prop.", "type": { "name": "custom", "raw": "(props, propName) => {\n if (!props.label && (props[propName] == null || props[propName] === \"\")) {\n return new Error(\n `Missing prop \\`${propName}\\` not specified for TextInput component. When \\`label\\` is not provided, \\`${propName}\\` is required.`\n );\n }\n if (props[propName] && typeof props[propName] !== \"string\") {\n return new Error(\n `Invalid propType \\`${propName}\\` supplied to TextInput component. Expected \\`string\\`, received \\`${typeof props[\n propName\n ]}\\`.`\n );\n }\n return null;\n}" }, "required": false }, "bold": { "description": "Makes label text bold", "type": { "name": "bool" }, "required": false }, "placeholder": { "description": "Specifies the helper/example text for the placeholder.", "type": { "name": "string" }, "required": false }, "type": { "description": "Specifies the type of text input, e.g. text, email, password, number", "type": { "name": "string" }, "required": false }, "floating": { "description": "Applies different styling for a floating animated label aesthetic", "type": { "name": "bool" }, "required": false }, "fullWidth": { "description": "Makes text box take up full width of parent", "type": { "name": "bool" }, "required": false }, "invalid": { "description": "Applies invalid input styles", "type": { "name": "bool" }, "required": false }, "valid": { "description": "Applies valid input styles", "type": { "name": "bool" }, "required": false }, "mandatory": { "description": "Shows asterisk to denote a mandatory field", "type": { "name": "bool" }, "required": false }, "iconLeft": { "description": "Applies an icon to the left of the text box with specified name.", "type": { "name": "array" }, "required": false }, "iconRight": { "description": "Applies an icon to the right of the text box with specified name.", "type": { "name": "array" }, "required": false }, "numberProps": { "description": "Number format props, to render a number input textbox", "type": { "name": "object" }, "required": false }, "inverted": { "description": "Set inverted styling for dark backgrounds", "type": { "name": "bool" }, "required": false }, "data-testid": { "description": "Specifies the `data-testid` attribute for testing.", "type": { "name": "string" }, "required": false }, "InputStyles": { "description": "Specifies any additional `space` and `layout` props for the entire component", "type": { "name": "object" }, "required": false }, "focus": { "description": "Focus on input", "type": { "name": "bool" }, "required": false } } }; export default TextInput;