orcs-design-system
Version:
TeamForm's Design System, aka: ORCS
702 lines (699 loc) • 26.5 kB
JavaScript
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
}
}
};