UNPKG

orcs-design-system

Version:
702 lines (699 loc) 26.5 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; const _excluded = ["ariaLabel", "variant"]; 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 from "react"; import styled, { ThemeProvider, css } from "styled-components"; import PropTypes from "prop-types"; import { space, layout, color, background, border, compose, variant } from "styled-system"; import shouldForwardProp from "@styled-system/should-forward-prop"; import Icon from "../Icon"; import Loading from "../Loading"; import { themeGet } from "@styled-system/theme-get"; import { Link } from "react-router-dom"; import { omit } from "lodash"; // Custom shouldForwardProp that also filters out borderRadius and borderColor import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const customShouldForwardProp = prop => { // Filter out styled-system props that shouldn't go to DOM if (["borderRadius", "borderColor"].includes(prop)) { return false; } return shouldForwardProp(prop); }; const getOutlineStyle = color => css(["outline:0;box-shadow:", ";"], props => [themeGet("shadows.thinOutline")(props), themeGet(color)(props)].join(" ")); const getVariantStyles = props => { // Handle disabled state - override variant if disabled if (props.disabled) { return css(["background:", ";color:", ";border-color:", ";&:hover{background:", ";color:", ";border-color:", ";}"], themeGet("colors.greyLighter")(props), themeGet("colors.grey")(props), themeGet("colors.greyLighter")(props), themeGet("colors.greyLighter")(props), themeGet("colors.grey")(props), themeGet("colors.greyLighter")(props)); } // Special handling for AI variant with gradient if (props.variant === "ai") { return css(["background:linear-gradient( 135deg,", " 0%,", " 100% );color:", ";border-color:", ";&:hover{background:linear-gradient( 135deg,", " 0%,", " 100% );color:", ";border-color:", ";}&:focus{", "}"], themeGet("colors.primaryLight"), themeGet("colors.secondaryLight"), themeGet("colors.white"), themeGet("colors.secondaryLight"), themeGet("colors.primary"), themeGet("colors.secondary"), themeGet("colors.white"), themeGet("colors.secondary"), getOutlineStyle("colors.secondary")); } return variant({ variants: { default: { background: themeGet("colors.primary")(props), color: themeGet("colors.white")(props), borderColor: themeGet("colors.primary")(props), "&:hover": { background: themeGet("colors.primaryDark")(props), color: themeGet("colors.white")(props), borderColor: themeGet("colors.primaryDark")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.primaryDarker")(props)) } }, success: { background: themeGet("colors.success")(props), color: themeGet("colors.white")(props), borderColor: themeGet("colors.success")(props), "&:hover": { background: themeGet("colors.successDark")(props), color: themeGet("colors.white")(props), borderColor: themeGet("colors.successDark")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.successDarker")(props)) } }, successAlternate: { background: themeGet("colors.greyLightest")(props), color: themeGet("colors.success")(props), borderColor: themeGet("colors.greyLightest")(props), "&:hover": { background: themeGet("colors.greyLighter")(props), color: themeGet("colors.success")(props), borderColor: themeGet("colors.greyLighter")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.successLight")(props)) } }, danger: { background: themeGet("colors.danger")(props), color: themeGet("colors.white")(props), borderColor: themeGet("colors.danger")(props), "&:hover": { background: themeGet("colors.dangerDark")(props), color: themeGet("colors.white")(props), borderColor: themeGet("colors.dangerDark")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.dangerDarker")(props)) } }, dangerAlternate: { background: themeGet("colors.greyLightest")(props), color: themeGet("colors.danger")(props), borderColor: themeGet("colors.greyLightest")(props), "&:hover": { background: themeGet("colors.greyLighter")(props), color: themeGet("colors.danger")(props), borderColor: themeGet("colors.greyLighter")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.dangerLight")(props)) } }, ghost: { background: themeGet("colors.primaryLightest")(props), color: themeGet("colors.primaryDark")(props), borderColor: themeGet("colors.primaryLightest")(props), "&:hover": { background: themeGet("colors.primaryLighter")(props), color: themeGet("colors.primaryDarker")(props), borderColor: themeGet("colors.primaryLighter")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.primaryLight")(props)) } }, borderBlue: { background: themeGet("colors.white")(props), color: themeGet("colors.primary")(props), borderColor: themeGet("colors.primary")(props), "&:hover": { background: themeGet("colors.primaryLightest")(props), color: themeGet("colors.primaryDark")(props), borderColor: themeGet("colors.primaryDark")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.primaryDark")(props)) } }, borderGrey: { background: themeGet("colors.white")(props), color: themeGet("colors.greyDarkest")(props), borderColor: themeGet("colors.greyDarkest")(props), "&:hover": { background: themeGet("colors.primaryLightest")(props), color: themeGet("colors.greyDarkest")(props), borderColor: themeGet("colors.greyDarkest")(props) }, "&:focus": { outline: 0, boxShadow: "".concat(themeGet("shadows.thinOutline")(props), " ").concat(themeGet("colors.greyDarkest")(props)) } }, disabled: { background: themeGet("colors.greyLighter")(props), color: themeGet("colors.grey")(props), borderColor: themeGet("colors.greyLighter")(props), "&:hover": { background: themeGet("colors.greyLighter")(props), color: themeGet("colors.grey")(props), borderColor: themeGet("colors.greyLighter")(props) } } } })(props); }; const getSpace = getter => props => themeGet("space.".concat(getter(props)))(props); const buttonStyles = /*#__PURE__*/css(["background:", ";color:", ";border-color:", ";display:flex;align-items:center;justify-content:center;appearance:none;box-shadow:none;margin:0;text-decoration:none;text-align:center;font-family:", ";font-weight:", ";border-radius:", ";transition:", ";border-width:", ";cursor:", ";width:", ";height:auto;font-size:", ";padding:", " ", ";svg{margin-right:", ";margin-left:", ";}&:hover{background:", ";border-color:", ";border-width:", ";border-style:solid;}&:focus{outline:0;box-shadow:", " ", ";}", " ", ""], themeGet("colors.primary"), themeGet("colors.white"), themeGet("colors.primary"), themeGet("fonts.main"), themeGet("fontWeights.2"), themeGet("radii.2"), themeGet("transition.transitionDefault"), themeGet("borderWidths.1"), props => props.disabled ? "not-allowed" : props.isLoading ? "progress" : "pointer", props => props.fullWidth ? "100%" : "auto", props => { let fontSize = 2; if (props.large && props.iconOnly) fontSize = 5; if (props.large) fontSize = 3; if (props.small) fontSize = 1; return themeGet("fontSizes.".concat(fontSize))(props); }, getSpace(props => props.large ? "s" : props.small ? "xxs" : "xs"), getSpace(props => props.large ? "r" : props.small ? "s" : "between"), getSpace(props => !props.iconLeft ? "" : props.small ? "xs" : "s"), getSpace(props => !props.iconRight ? "" : props.small ? "xs" : "s"), themeGet("colors.primaryDark"), themeGet("colors.primaryDark"), themeGet("borderWidths.1"), themeGet("shadows.thinOutline"), themeGet("colors.primaryDarker"), getVariantStyles, compose(space, layout, color, background, border)); const attrs = props => ({ "data-testid": props.dataTestId || props["data-testid"], disabled: props.disabled || props.variant == "disabled", className: "".concat(props.className || "", " variant-").concat(props.variant || "default") }); const StyledButton = /*#__PURE__*/styled("button").withConfig({ shouldForwardProp: customShouldForwardProp }).attrs(attrs).withConfig({ displayName: "StyledButton", componentId: "sc-10uojnk-0" })(["", ""], buttonStyles); const linkStyles = /*#__PURE__*/css(["width:", ";display:", ";align-items:", ";"], props => props.width || "fit-content", props => props.height || props.width ? "flex" : props.display || "inline-block", props => props.alignItems || "center"); const StyledButtonLink = /*#__PURE__*/styled.a.withConfig({ shouldForwardProp: customShouldForwardProp }).attrs(attrs).withConfig({ displayName: "StyledButtonLink", componentId: "sc-10uojnk-1" })(["", " ", ""], buttonStyles, linkStyles); const StyledReactButtonLink = /*#__PURE__*/styled(Link).withConfig({ shouldForwardProp: customShouldForwardProp }).attrs(attrs).withConfig({ displayName: "StyledReactButtonLink", componentId: "sc-10uojnk-2" })(["", " ", ""], buttonStyles, linkStyles); const buttonPropTypes = { /** Large button */ large: PropTypes.bool, /** Small button */ small: PropTypes.bool, /** Specifies alternate button colours/styles. */ variant: PropTypes.oneOf(["success", "successAlternate", "danger", "dangerAlternate", "ghost", "disabled", "default", "ai", "borderBlue", "borderGrey"]), /** Full width button that takes up all available space of parent */ fullWidth: PropTypes.bool, /** Adds a spinner animation to indicate loading or processing */ isLoading: PropTypes.bool, /** Styles button to fit an icon on the left of text. Uses Icon component. */ iconLeft: PropTypes.bool, /** Styles button to fit an icon on the right of text. Uses Icon component. */ iconRight: PropTypes.bool, /** New functionality to specify an `Icon` on the left side without having to include it as a child. */ leftIcon: PropTypes.array, /** New functionality to specify an `Icon` on the right side without having to include it as a child. */ rightIcon: PropTypes.array, /** Styles button to suit having only an icon. Uses Icon component. */ iconOnly: PropTypes.bool, /** Specifies whether the button is disabled. */ disabled: PropTypes.bool, /** The text label on the button is passed as a child. Keep this text short and descriptive. Use an action word or confirmation if possible. */ children: PropTypes.node, /** Adds additional styling to the rendered `<button>` using `space`, `layout`, `color` and `border` prop categories */ ButtonStyles: PropTypes.object, /** Specifies the `data-testid` attribute for testing. */ dataTestId: PropTypes.string, /** Specifies aria-label for iconOnly buttons. This is only required if the iconOnly button is used, as it doesn't have supporting text for accessibility.*/ ariaLabel: (props, propName) => { if (props.iconOnly && (props[propName] == null || props[propName] === "")) { return new Error("Missing prop `".concat(propName, "` not specified for Button component. When `iconOnly` is true, `").concat(propName, "` is required.")); } if (props[propName] && typeof props[propName] !== "string") { return new Error("Invalid propType for `".concat(propName, "` supplied to Button component. Expected `string`, received `").concat(typeof props[propName], "`.")); } return null; }, /** Specifies the color theme object. */ theme: PropTypes.object }; // Export variant colors for use in stories and other components export const VARIANT_COLORS = { default: { color: "colors.white", background: "colors.primary", hovered: { color: "colors.white", background: "colors.primaryDark" } }, success: { color: "colors.white", background: "colors.success", hovered: { color: "colors.white", background: "colors.successDark" } }, successAlternate: { color: "colors.success", background: "colors.greyLightest", hovered: { color: "colors.success", background: "colors.greyLighter" } }, danger: { color: "colors.white", background: "colors.danger", hovered: { color: "colors.white", background: "colors.dangerDark" } }, dangerAlternate: { color: "colors.danger", background: "colors.greyLightest", hovered: { color: "colors.danger", background: "colors.greyLighter" } }, ghost: { color: "colors.primaryDark", background: "colors.primaryLightest", hovered: { color: "colors.primaryDarker", background: "colors.primaryLighter" } }, disabled: { color: "colors.grey", background: "colors.greyLighter", hovered: { color: "colors.grey", background: "colors.greyLighter" } }, ai: { color: "colors.white", background: "colors.primaryLight", hovered: { color: "colors.white", background: "colors.primary" } }, borderBlue: { color: "colors.primary", background: "colors.white", hovered: { color: "colors.primaryDark", background: "colors.primaryLightest" } }, borderGrey: { color: "colors.greyDarkest", background: "colors.white", hovered: { color: "colors.greyDarkest", background: "colors.primaryLightest" } } }; const renderButton = (ButtonComponent, _ref) => { let { ariaLabel, variant = "default" } = _ref, props = _objectWithoutProperties(_ref, _excluded); return /*#__PURE__*/_jsxs(ButtonComponent, _objectSpread(_objectSpread(_objectSpread({ variant: variant, borderStyle: "solid" }, props.ButtonStyles), props), {}, { "aria-label": ariaLabel, ref: props.ref, children: [props.leftIcon && /*#__PURE__*/_jsx(Icon, { icon: props.leftIcon, mr: props.small ? "xxs" : "xs" }), props.children, props.rightIcon && /*#__PURE__*/_jsx(Icon, { icon: props.rightIcon, ml: props.small ? "xxs" : "xs" }), props.isLoading && /*#__PURE__*/_jsx(Loading, { inverted: true, ml: "s" })] })); }; export const ButtonLink = /*#__PURE__*/React.forwardRef((props, ref) => { const { theme } = props; const component = renderButton(props.to ? StyledReactButtonLink : StyledButtonLink, _objectSpread(_objectSpread({}, omit(props, "isLoading")), {}, { ref })); return theme ? /*#__PURE__*/_jsx(ThemeProvider, { theme: theme, children: component }) : component; }); ButtonLink.propTypes = _objectSpread(_objectSpread({}, buttonPropTypes), {}, { target: PropTypes.string, /** Link to navigate user to */ href: PropTypes.string }); export const Button = /*#__PURE__*/React.forwardRef((props, ref) => { const { theme } = props; const component = renderButton(StyledButton, _objectSpread(_objectSpread({}, props), {}, { ref })); return theme ? /*#__PURE__*/_jsx(ThemeProvider, { theme: theme, children: component }) : component; }); Button.__docgenInfo = { "description": "", "methods": [], "displayName": "Button", "props": { "large": { "description": "Large button", "type": { "name": "bool" }, "required": false }, "small": { "description": "Small button", "type": { "name": "bool" }, "required": false }, "variant": { "description": "Specifies alternate button colours/styles.", "type": { "name": "enum", "value": [{ "value": "\"success\"", "computed": false }, { "value": "\"successAlternate\"", "computed": false }, { "value": "\"danger\"", "computed": false }, { "value": "\"dangerAlternate\"", "computed": false }, { "value": "\"ghost\"", "computed": false }, { "value": "\"disabled\"", "computed": false }, { "value": "\"default\"", "computed": false }, { "value": "\"ai\"", "computed": false }, { "value": "\"borderBlue\"", "computed": false }, { "value": "\"borderGrey\"", "computed": false }] }, "required": false }, "fullWidth": { "description": "Full width button that takes up all available space of parent", "type": { "name": "bool" }, "required": false }, "isLoading": { "description": "Adds a spinner animation to indicate loading or processing", "type": { "name": "bool" }, "required": false }, "iconLeft": { "description": "Styles button to fit an icon on the left of text. Uses Icon component.", "type": { "name": "bool" }, "required": false }, "iconRight": { "description": "Styles button to fit an icon on the right of text. Uses Icon component.", "type": { "name": "bool" }, "required": false }, "leftIcon": { "description": "New functionality to specify an `Icon` on the left side without having to include it as a child.", "type": { "name": "array" }, "required": false }, "rightIcon": { "description": "New functionality to specify an `Icon` on the right side without having to include it as a child.", "type": { "name": "array" }, "required": false }, "iconOnly": { "description": "Styles button to suit having only an icon. Uses Icon component.", "type": { "name": "bool" }, "required": false }, "disabled": { "description": "Specifies whether the button is disabled.", "type": { "name": "bool" }, "required": false }, "children": { "description": "The text label on the button is passed as a child. Keep this text short and descriptive. Use an action word or confirmation if possible.", "type": { "name": "node" }, "required": false }, "ButtonStyles": { "description": "Adds additional styling to the rendered `<button>` using `space`, `layout`, `color` and `border` prop categories", "type": { "name": "object" }, "required": false }, "dataTestId": { "description": "Specifies the `data-testid` attribute for testing.", "type": { "name": "string" }, "required": false }, "ariaLabel": { "description": "Specifies aria-label for iconOnly buttons. This is only required if the iconOnly button is used, as it doesn't have supporting text for accessibility.", "type": { "name": "custom", "raw": "(props, propName) => {\n if (props.iconOnly && (props[propName] == null || props[propName] === \"\")) {\n return new Error(\n `Missing prop \\`${propName}\\` not specified for Button component. When \\`iconOnly\\` is true, \\`${propName}\\` is required.`\n );\n }\n if (props[propName] && typeof props[propName] !== \"string\") {\n return new Error(\n `Invalid propType for \\`${propName}\\` supplied to Button component. Expected \\`string\\`, received \\`${typeof props[\n propName\n ]}\\`.`\n );\n }\n return null;\n}" }, "required": false }, "theme": { "description": "Specifies the color theme object.", "type": { "name": "object" }, "required": false }, "onClick": { "description": "Function to run when the `Button` is clicked", "type": { "name": "func" }, "required": false } } }; export default Button; Button.propTypes = _objectSpread(_objectSpread({}, buttonPropTypes), {}, { /** Function to run when the `Button` is clicked */ onClick: PropTypes.func }); ButtonLink.__docgenInfo = { "description": "", "methods": [], "displayName": "ButtonLink", "props": { "large": { "description": "Large button", "type": { "name": "bool" }, "required": false }, "small": { "description": "Small button", "type": { "name": "bool" }, "required": false }, "variant": { "description": "Specifies alternate button colours/styles.", "type": { "name": "enum", "value": [{ "value": "\"success\"", "computed": false }, { "value": "\"successAlternate\"", "computed": false }, { "value": "\"danger\"", "computed": false }, { "value": "\"dangerAlternate\"", "computed": false }, { "value": "\"ghost\"", "computed": false }, { "value": "\"disabled\"", "computed": false }, { "value": "\"default\"", "computed": false }, { "value": "\"ai\"", "computed": false }, { "value": "\"borderBlue\"", "computed": false }, { "value": "\"borderGrey\"", "computed": false }] }, "required": false }, "fullWidth": { "description": "Full width button that takes up all available space of parent", "type": { "name": "bool" }, "required": false }, "isLoading": { "description": "Adds a spinner animation to indicate loading or processing", "type": { "name": "bool" }, "required": false }, "iconLeft": { "description": "Styles button to fit an icon on the left of text. Uses Icon component.", "type": { "name": "bool" }, "required": false }, "iconRight": { "description": "Styles button to fit an icon on the right of text. Uses Icon component.", "type": { "name": "bool" }, "required": false }, "leftIcon": { "description": "New functionality to specify an `Icon` on the left side without having to include it as a child.", "type": { "name": "array" }, "required": false }, "rightIcon": { "description": "New functionality to specify an `Icon` on the right side without having to include it as a child.", "type": { "name": "array" }, "required": false }, "iconOnly": { "description": "Styles button to suit having only an icon. Uses Icon component.", "type": { "name": "bool" }, "required": false }, "disabled": { "description": "Specifies whether the button is disabled.", "type": { "name": "bool" }, "required": false }, "children": { "description": "The text label on the button is passed as a child. Keep this text short and descriptive. Use an action word or confirmation if possible.", "type": { "name": "node" }, "required": false }, "ButtonStyles": { "description": "Adds additional styling to the rendered `<button>` using `space`, `layout`, `color` and `border` prop categories", "type": { "name": "object" }, "required": false }, "dataTestId": { "description": "Specifies the `data-testid` attribute for testing.", "type": { "name": "string" }, "required": false }, "ariaLabel": { "description": "Specifies aria-label for iconOnly buttons. This is only required if the iconOnly button is used, as it doesn't have supporting text for accessibility.", "type": { "name": "custom", "raw": "(props, propName) => {\n if (props.iconOnly && (props[propName] == null || props[propName] === \"\")) {\n return new Error(\n `Missing prop \\`${propName}\\` not specified for Button component. When \\`iconOnly\\` is true, \\`${propName}\\` is required.`\n );\n }\n if (props[propName] && typeof props[propName] !== \"string\") {\n return new Error(\n `Invalid propType for \\`${propName}\\` supplied to Button component. Expected \\`string\\`, received \\`${typeof props[\n propName\n ]}\\`.`\n );\n }\n return null;\n}" }, "required": false }, "theme": { "description": "Specifies the color theme object.", "type": { "name": "object" }, "required": false }, "target": { "description": "", "type": { "name": "string" }, "required": false }, "href": { "description": "Link to navigate user to", "type": { "name": "string" }, "required": false } } };