UNPKG

orcs-design-system

Version:
355 lines (353 loc) 16.6 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; const _excluded = ["numberProps"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } 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); // For Group (div): block styled-system props and input-specific props so they don't leak to DOM const shouldForwardProp = createShouldForwardProp([...props, "type", "placeholder", "defaultValue", "disabled", "maxLength", "pattern", "required", "autocomplete", "autofocus", "step", "readonly"]); // For Input/NumberInput: only block styled-system props + invalid/valid (so type, placeholder, value reach the DOM) const inputShouldForwardProp = prop => prop !== "invalid" && prop !== "valid" && createShouldForwardProp(props)(prop); const Group = /*#__PURE__*/styled("div").withConfig({ shouldForwardProp, displayName: "Group", componentId: "sc-shde0o-0" })(props => css({ position: "relative", width: props.fullWidth ? "100%" : "auto", minHeight: props => { var _props$height; return (_props$height = props.height) !== null && _props$height !== void 0 ? _props$height : themeGet(props.floating ? "appScale.inputHeightLarge" : "appScale.inputHeightDefault")(props); } }), InputStyles); const IconWrapper = /*#__PURE__*/styled.label.withConfig({ displayName: "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 = /*#__PURE__*/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.0")(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 "".concat(top, "px ").concat(right, "px ").concat(bottom, "px ").concat(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 = /*#__PURE__*/styled("input").withConfig({ shouldForwardProp: inputShouldForwardProp }).attrs(props => ({ "data-testid": props["data-testid"] ? props["data-testid"] : null })).withConfig({ displayName: "Input", componentId: "sc-shde0o-2" })(["", ""], InputStyle); const NumberInput = /*#__PURE__*/styled(NumericFormat).withConfig({ shouldForwardProp: inputShouldForwardProp }).attrs(props => ({ "data-testid": props["data-testid"] ? props["data-testid"] : null })).withConfig({ displayName: "NumberInput", componentId: "sc-shde0o-3" })(["", ""], InputStyle); const Label = /*#__PURE__*/styled.label.withConfig({ displayName: "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.0")(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(".concat(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 } = props, rest = _objectWithoutProperties(props, _excluded); let getNumberInputRef = null; if (numberProps && inputRef) { getNumberInputRef = node => { inputRef.current = node; }; } const label = useMemo(() => { if (numberProps && numberProps.prefix) { return "".concat(props.label, " ").concat(numberProps.prefix); } return props.label; }, [props.label, numberProps]); return /*#__PURE__*/_jsxs(Group, _objectSpread(_objectSpread({ 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, _objectSpread(_objectSpread(_objectSpread({ getInputRef: getNumberInputRef, "data-testid": props["data-testid"], id: id, "aria-label": ariaLabel, "aria-invalid": invalid !== null && invalid !== void 0 ? invalid : undefined, invalid: invalid, valid: valid }, rest), numberProps), {}, { type: numberProps.type != null ? numberProps.type : rest.type === "number" ? "text" : rest.type || "text", height: height })) : /*#__PURE__*/_jsx(Input, _objectSpread({ "data-testid": props["data-testid"], height: height, ref: inputRef, id: id, "aria-label": ariaLabel, "aria-invalid": invalid !== null && invalid !== void 0 ? invalid : undefined, invalid: invalid, valid: valid }, 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 `".concat(propName, "` not specified for TextInput component. When `label` is not provided, `").concat(propName, "` is required.")); } if (props[propName] && typeof props[propName] !== "string") { return new Error("Invalid propType `".concat(propName, "` supplied to TextInput component. Expected `string`, received `").concat(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;