new examples buttonModules & buttonEmotion + refac
This commit is contained in:
parent
d03e3d3928
commit
b53d4528fb
@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": "stylelint-config-standard"
|
||||
"extends": "stylelint-config-standard",
|
||||
"rules": {
|
||||
"declaration-empty-line-before": "never"
|
||||
}
|
||||
}
|
||||
|
||||
3
platform/ui/gatsby-config.js
Normal file
3
platform/ui/gatsby-config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: ['gatsby-plugin-sass'],
|
||||
};
|
||||
@ -18,6 +18,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "yarn run dev",
|
||||
"dev": "concurrently \"npm run watch:css\" \"docz dev\"",
|
||||
"build": "concurrently \"npm run build:css\" \"docz build\"",
|
||||
"serve": "docz serve",
|
||||
@ -25,8 +26,13 @@
|
||||
"watch:css": "postcss src/styles/tailwind.css -o src/gatsby-theme-docz/app.css -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/core": "^10.0.27",
|
||||
"@emotion/styled": "^10.0.27",
|
||||
"classnames": "^2.2.6",
|
||||
"concurrently": "^5.1.0",
|
||||
"docz": "latest",
|
||||
"docz": "^2.2.0",
|
||||
"gatsby-plugin-sass": "^2.1.28",
|
||||
"node-sass": "^4.13.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.11.0",
|
||||
"react-dom": "^16.11.0"
|
||||
|
||||
@ -1,79 +1,18 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Button = ({
|
||||
children,
|
||||
variant = defaults.variant,
|
||||
color = defaults.color,
|
||||
size = defaults.size,
|
||||
radius = defaults.radius,
|
||||
disabled = defaults.disabled,
|
||||
elevation,
|
||||
type = defaults.type,
|
||||
startIcon: startIconProp,
|
||||
endIcon: endIconProp,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
const hasIcon = startIconProp || endIconProp;
|
||||
const getClasses = () => {
|
||||
const classes = [];
|
||||
let buttonElevation;
|
||||
|
||||
if (variant === 'text') {
|
||||
if (elevation) {
|
||||
buttonElevation = elevation;
|
||||
} else {
|
||||
buttonElevation = false;
|
||||
}
|
||||
} else {
|
||||
buttonElevation = defaults.elevation;
|
||||
}
|
||||
|
||||
classes.push(variantClasses[variant][color]);
|
||||
classes.push(radiusClasses[radius]);
|
||||
classes.push(sizeClasses[size]);
|
||||
classes.push(disabledClasses[disabled]);
|
||||
classes.push(elevationClasses[buttonElevation]);
|
||||
classes.push(className);
|
||||
|
||||
if (hasIcon) {
|
||||
classes.push(baseIconClasses);
|
||||
}
|
||||
|
||||
return classes.join(' ');
|
||||
};
|
||||
|
||||
const baseIconClasses = 'inline-flex items-center';
|
||||
const baseClasses =
|
||||
'outline-none transition duration-300 ease-in-out rounded font-bold focus:outline-none';
|
||||
|
||||
const startIcon = startIconProp && (
|
||||
<div className="mr-2">{startIconProp}</div>
|
||||
);
|
||||
|
||||
const endIcon = endIconProp && <div className="ml-2">{endIconProp}</div>;
|
||||
|
||||
return (
|
||||
<button className={`${baseClasses} ${getClasses()}`} type={type} {...rest}>
|
||||
{startIcon}
|
||||
{children}
|
||||
{endIcon}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
import classnames from 'classnames';
|
||||
|
||||
const defaults = {
|
||||
variant: 'outlined',
|
||||
variant: 'contained',
|
||||
color: 'default',
|
||||
size: 'medium',
|
||||
radius: 'medium',
|
||||
disabled: false,
|
||||
elevation: true,
|
||||
type: 'button',
|
||||
};
|
||||
|
||||
const radiusClasses = {
|
||||
none: '',
|
||||
small: 'rounded-sm',
|
||||
medium: 'rounded-md',
|
||||
large: 'rounded-lg',
|
||||
@ -85,11 +24,6 @@ const disabledClasses = {
|
||||
false: '',
|
||||
};
|
||||
|
||||
const elevationClasses = {
|
||||
true: 'shadow',
|
||||
false: 'shadow-none',
|
||||
};
|
||||
|
||||
const variantClasses = {
|
||||
text: {
|
||||
default: 'hover:bg-gray-200 text-black',
|
||||
@ -114,14 +48,55 @@ const sizeClasses = {
|
||||
large: 'py-3 px-6 text-lg',
|
||||
};
|
||||
|
||||
const Button = ({
|
||||
children,
|
||||
variant = defaults.variant,
|
||||
color = defaults.color,
|
||||
size = defaults.size,
|
||||
radius = defaults.radius,
|
||||
disabled = defaults.disabled,
|
||||
type = defaults.type,
|
||||
startIcon: startIconProp,
|
||||
endIcon: endIconProp,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
const baseClasses =
|
||||
'inline-flex items-center outline-none transition duration-300 ease-in-out font-bold focus:outline-none';
|
||||
|
||||
const startIcon = startIconProp && (
|
||||
<div className="mr-2">{startIconProp}</div>
|
||||
);
|
||||
|
||||
const endIcon = endIconProp && <div className="ml-2">{endIconProp}</div>;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classnames(
|
||||
baseClasses,
|
||||
variantClasses[variant][color],
|
||||
radiusClasses[radius],
|
||||
sizeClasses[size],
|
||||
disabledClasses[disabled],
|
||||
className
|
||||
)}
|
||||
type={type}
|
||||
{...rest}
|
||||
>
|
||||
{startIcon}
|
||||
{children}
|
||||
{endIcon}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.node,
|
||||
size: PropTypes.oneOf(['small', 'medium', 'large']),
|
||||
radius: PropTypes.oneOf(['small', 'medium', 'large', 'full']),
|
||||
radius: PropTypes.oneOf(['none', 'small', 'medium', 'large', 'full']),
|
||||
variant: PropTypes.oneOf(['text', 'outlined', 'contained']),
|
||||
color: PropTypes.oneOf(['default', 'primary', 'secondary']),
|
||||
disabled: PropTypes.bool,
|
||||
elevation: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
startIcon: PropTypes.node,
|
||||
endIcon: PropTypes.node,
|
||||
|
||||
318
platform/ui/src/components/ButtonEmotion/ButtonEmotion.jsx
Normal file
318
platform/ui/src/components/ButtonEmotion/ButtonEmotion.jsx
Normal file
@ -0,0 +1,318 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from '@emotion/styled';
|
||||
import { fade } from '../../utils/colorManipulator';
|
||||
import capitalize from '../../utils/capitalize';
|
||||
import theme from '../../styles/theme';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
'box-sizing': 'border-box',
|
||||
'min-width': '64px',
|
||||
padding: '6px 16px',
|
||||
'border-radius': '20px',
|
||||
color: theme.palette.primary.main,
|
||||
transition: 'all 300ms ease',
|
||||
'&:hover': {
|
||||
'text-decoration': 'none',
|
||||
'background-color': fade(
|
||||
theme.palette.default.main,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
'&:active': {
|
||||
outline: 'none',
|
||||
},
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
'&:disabled': {
|
||||
color: theme.palette.action.disabled,
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="text"`. */
|
||||
text: {
|
||||
padding: '6px 8px',
|
||||
},
|
||||
/* Styles applied to the root element if `variant="text"` and `color="primary"`. */
|
||||
textPrimary: {
|
||||
color: theme.palette.primary.main,
|
||||
'&:hover': {
|
||||
'background-color': fade(
|
||||
theme.palette.primary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="text"` and `color="secondary"`. */
|
||||
textSecondary: {
|
||||
color: theme.palette.secondary.main,
|
||||
'&:hover': {
|
||||
'background-color': fade(
|
||||
theme.palette.secondary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="outlined"`. */
|
||||
outlined: {
|
||||
padding: '5px 15px',
|
||||
border: `1px solid ${
|
||||
theme.palette.type === 'light'
|
||||
? 'rgba(0, 0, 0, 0.23)'
|
||||
: 'rgba(255, 255, 255, 0.23)'
|
||||
}`,
|
||||
'&:disabled': {
|
||||
border: `1px solid ${theme.palette.default.main}`,
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="outlined"` and `color="primary"`. */
|
||||
outlinedPrimary: {
|
||||
color: theme.palette.primary.main,
|
||||
border: `1px solid ${fade(
|
||||
theme.palette.primary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
)}`,
|
||||
'&:hover': {
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
'background-color': fade(
|
||||
theme.palette.primary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="outlined"` and `color="secondary"`. */
|
||||
outlinedSecondary: {
|
||||
color: theme.palette.secondary.main,
|
||||
border: `1px solid ${fade(
|
||||
theme.palette.secondary.main,
|
||||
theme.palette.action.hoverOpacity
|
||||
)}`,
|
||||
'&:hover': {
|
||||
border: `1px solid ${theme.palette.secondary.main}`,
|
||||
'background-color': fade(
|
||||
theme.palette.secondary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
'&$disabled': {
|
||||
border: `1px solid ${theme.palette.action.disabled}`,
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="contained"`. */
|
||||
contained: {
|
||||
color: '#000',
|
||||
'background-color': theme.palette.default.light,
|
||||
// 'box-shadow': theme.shadows[2],
|
||||
'&:hover': {
|
||||
'background-color': theme.palette.default.light,
|
||||
// 'box-shadow': theme.shadows[4],
|
||||
|
||||
'&$disabled': {
|
||||
'background-color': theme.palette.action.disabledBackground,
|
||||
},
|
||||
},
|
||||
'&:active': {
|
||||
// 'box-shadow': typographyows[8],
|
||||
},
|
||||
'&$disabled': {
|
||||
color: theme.palette.action.disabled,
|
||||
// 'box-shadow': theme.shadows[0],
|
||||
'background-color': theme.palette.action.disabledBackground,
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="contained"` and `color="primary"`. */
|
||||
containedPrimary: {
|
||||
color: 'white',
|
||||
'background-color': theme.palette.primary.main,
|
||||
'&:hover': {
|
||||
'background-color': fade(
|
||||
theme.palette.primary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `variant="contained"` and `color="secondary"`. */
|
||||
containedSecondary: {
|
||||
color: 'white',
|
||||
'background-color': theme.palette.secondary.main,
|
||||
'&:hover': {
|
||||
'background-color': fade(
|
||||
theme.palette.secondary.light,
|
||||
theme.palette.action.hoverOpacity
|
||||
),
|
||||
},
|
||||
},
|
||||
/* Styles applied to the root element if `size="small"` and `variant="text"`. */
|
||||
textSizeSmall: {
|
||||
padding: '4px 5px',
|
||||
fontSize: '13px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="medium"` and `variant="text"`. */
|
||||
textSizeMedium: {
|
||||
padding: '6px 9px',
|
||||
fontSize: '14px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="large"` and `variant="text"`. */
|
||||
textSizeLarge: {
|
||||
padding: '8px 11px',
|
||||
fontSize: '15px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="small"` and `variant="outlined"`. */
|
||||
outlinedSizeSmall: {
|
||||
padding: '3px 9px',
|
||||
fontSize: '13px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="medium"` and `variant="outlined"`. */
|
||||
outlinedSizeMedium: {
|
||||
padding: '5px 15px',
|
||||
fontSize: '14px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="large"` and `variant="outlined"`. */
|
||||
outlinedSizeLarge: {
|
||||
padding: '7px 21px',
|
||||
fontSize: '15px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="small"` and `variant="contained"`. */
|
||||
containedSizeSmall: {
|
||||
padding: '4px 10px',
|
||||
fontSize: '13px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="medium"` and `variant="contained"`. */
|
||||
containedSizeMedium: {
|
||||
padding: '6px 16px',
|
||||
fontSize: '13px',
|
||||
},
|
||||
/* Styles applied to the root element if `size="large"` and `variant="contained"`. */
|
||||
containedSizeLarge: {
|
||||
padding: '8px 22px',
|
||||
fontSize: '15px',
|
||||
},
|
||||
/* Styles applied to the startIcon element if supplied. */
|
||||
startIcon: {
|
||||
display: 'inherit',
|
||||
'margin-right': '8px',
|
||||
'margin-left': '-4px',
|
||||
},
|
||||
/* Styles applied to the endIcon element if supplied. */
|
||||
endIcon: {
|
||||
display: 'inherit',
|
||||
'margin-right': '-4px',
|
||||
'margin-left': '8px',
|
||||
},
|
||||
};
|
||||
|
||||
const BaseButton = styled.button`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
`;
|
||||
|
||||
const ButtonIcon = styled.div`
|
||||
${props => ({
|
||||
...styles[`${props.icon}`],
|
||||
})}
|
||||
`;
|
||||
|
||||
const ButtonComponent = styled(BaseButton)`
|
||||
/* base styles */
|
||||
${props => ({
|
||||
...styles.base,
|
||||
})}
|
||||
|
||||
/* variant styles */
|
||||
${props => ({
|
||||
...styles[`${props.variant}`],
|
||||
})}
|
||||
|
||||
/* color */
|
||||
${props => ({
|
||||
...styles[`${props.variant}${capitalize(props.color)}`],
|
||||
})}
|
||||
|
||||
/* size styles */
|
||||
${props => ({
|
||||
...styles[`${props.variant}Size${capitalize(props.size)}`],
|
||||
})}
|
||||
`;
|
||||
|
||||
const Button = props => {
|
||||
const {
|
||||
children,
|
||||
variant = defaults.variant,
|
||||
color = defaults.color,
|
||||
size = defaults.size,
|
||||
radius = defaults.radius,
|
||||
disabled = defaults.disabled,
|
||||
elevation,
|
||||
type = defaults.type,
|
||||
startIcon: startIconProp,
|
||||
endIcon: endIconProp,
|
||||
className,
|
||||
...rest
|
||||
} = props;
|
||||
const startIcon = startIconProp && (
|
||||
<ButtonIcon icon="startIcon">{startIconProp}</ButtonIcon>
|
||||
);
|
||||
|
||||
const endIcon = endIconProp && (
|
||||
<ButtonIcon icon="endIcon">{endIconProp}</ButtonIcon>
|
||||
);
|
||||
|
||||
return (
|
||||
<ButtonComponent
|
||||
variant={variant}
|
||||
color={color}
|
||||
size={size}
|
||||
radius={radius}
|
||||
disabled={disabled}
|
||||
type={type}
|
||||
{...rest}
|
||||
>
|
||||
{startIcon}
|
||||
{children}
|
||||
{endIcon}
|
||||
</ButtonComponent>
|
||||
);
|
||||
};
|
||||
|
||||
const defaults = {
|
||||
variant: 'outlined',
|
||||
color: 'default',
|
||||
size: 'large',
|
||||
radius: 'medium',
|
||||
disabled: false,
|
||||
elevation: true,
|
||||
type: 'button',
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.node,
|
||||
size: PropTypes.oneOf(['small', 'medium', 'large']),
|
||||
radius: PropTypes.oneOf(['small', 'medium', 'large', 'full']),
|
||||
variant: PropTypes.oneOf(['text', 'outlined', 'contained']),
|
||||
color: PropTypes.oneOf(['default', 'primary', 'secondary']),
|
||||
disabled: PropTypes.bool,
|
||||
elevation: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
startIcon: PropTypes.node,
|
||||
endIcon: PropTypes.node,
|
||||
className: PropTypes.node,
|
||||
};
|
||||
|
||||
export default Button;
|
||||
180
platform/ui/src/components/ButtonEmotion/ButtonEmotion.mdx
Normal file
180
platform/ui/src/components/ButtonEmotion/ButtonEmotion.mdx
Normal file
@ -0,0 +1,180 @@
|
||||
---
|
||||
name: ButtonEmotion
|
||||
menu: Components
|
||||
route: components/button-emotion
|
||||
---
|
||||
|
||||
import { Playground, Props } from 'docz';
|
||||
import Button from './';
|
||||
|
||||
# ButtonEmotion
|
||||
|
||||
Buttons are used to execute actions when users interacts with them.
|
||||
|
||||
## Properties
|
||||
|
||||
<Props of={Button} />
|
||||
|
||||
## Contained Buttons
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<Button variant="contained" style={{ margin: 5 }}>
|
||||
Default
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" style={{ margin: 5 }}>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="contained" color="secondary" style={{ margin: 5 }}>
|
||||
Secondary
|
||||
</Button>
|
||||
</>
|
||||
</Playground>
|
||||
|
||||
## Outlined Buttons
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<Button variant="outlined" style={{ margin: 5 }}>
|
||||
Default
|
||||
</Button>
|
||||
<Button variant="outlined" color="primary" style={{ margin: 5 }}>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="outlined" color="secondary" style={{ margin: 5 }}>
|
||||
Secondary
|
||||
</Button>
|
||||
</>
|
||||
</Playground>
|
||||
|
||||
## Text Buttons
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<Button variant="text" style={{ margin: 5 }}>
|
||||
Default
|
||||
</Button>
|
||||
<Button variant="text" color="primary" style={{ margin: 5 }}>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="text" color="secondary" style={{ margin: 5 }}>
|
||||
Secondary
|
||||
</Button>
|
||||
</>
|
||||
</Playground>
|
||||
|
||||
## Sizes
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<div>
|
||||
<Button variant="text" size="small" style={{ margin: 5 }}>
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="contained" size="medium" style={{ margin: 5 }}>
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="outlined" size="large" style={{ margin: 5 }}>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Small
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Medium
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
large
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
color="secondary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Small
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
color="secondary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Medium
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
color="secondary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</>
|
||||
|
||||
</Playground>
|
||||
|
||||
## Buttons with icons
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
return (
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
startIcon={
|
||||
<svg
|
||||
class="fill-current w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z" />
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
endIcon={
|
||||
<svg
|
||||
class="fill-current w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z" />
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
|
||||
</Playground>
|
||||
2
platform/ui/src/components/ButtonEmotion/index.js
Normal file
2
platform/ui/src/components/ButtonEmotion/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
import ButtonEmotion from './ButtonEmotion';
|
||||
export default ButtonEmotion;
|
||||
68
platform/ui/src/components/ButtonModules/ButtonModules.jsx
Normal file
68
platform/ui/src/components/ButtonModules/ButtonModules.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import capitalize from '../../utils/capitalize';
|
||||
import styles from './ButtonModules.module.scss';
|
||||
|
||||
const Button = props => {
|
||||
const {
|
||||
children,
|
||||
variant = defaults.variant,
|
||||
color = defaults.color,
|
||||
size = defaults.size,
|
||||
type = defaults.type,
|
||||
startIcon: startIconProp,
|
||||
endIcon: endIconProp,
|
||||
className,
|
||||
...rest
|
||||
} = props;
|
||||
const startIcon = startIconProp && (
|
||||
<div className={styles.startIcon}>{startIconProp}</div>
|
||||
);
|
||||
|
||||
const endIcon = endIconProp && (
|
||||
<div className={styles.endIcon}>{endIconProp}</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classnames(
|
||||
styles.baseButton,
|
||||
styles[variant],
|
||||
styles[`${variant}${capitalize(color)}`],
|
||||
styles[`${variant}Size${capitalize(size)}`],
|
||||
className
|
||||
)}
|
||||
type={type}
|
||||
{...rest}
|
||||
>
|
||||
{startIcon}
|
||||
{children}
|
||||
{endIcon}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const defaults = {
|
||||
variant: 'outlined',
|
||||
color: 'default',
|
||||
size: 'large',
|
||||
radius: 'medium',
|
||||
disabled: false,
|
||||
type: 'button',
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.node,
|
||||
size: PropTypes.oneOf(['small', 'medium', 'large']),
|
||||
radius: PropTypes.oneOf(['small', 'medium', 'large', 'full']),
|
||||
variant: PropTypes.oneOf(['text', 'outlined', 'contained']),
|
||||
color: PropTypes.oneOf(['default', 'primary', 'secondary']),
|
||||
disabled: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
startIcon: PropTypes.node,
|
||||
endIcon: PropTypes.node,
|
||||
className: PropTypes.node,
|
||||
};
|
||||
|
||||
export default Button;
|
||||
180
platform/ui/src/components/ButtonModules/ButtonModules.mdx
Normal file
180
platform/ui/src/components/ButtonModules/ButtonModules.mdx
Normal file
@ -0,0 +1,180 @@
|
||||
---
|
||||
name: ButtonModules
|
||||
menu: Components
|
||||
route: components/button-modules
|
||||
---
|
||||
|
||||
import { Playground, Props } from 'docz';
|
||||
import Button from './';
|
||||
|
||||
# ButtonModules
|
||||
|
||||
Buttons are used to execute actions when users interacts with them.
|
||||
|
||||
## Properties
|
||||
|
||||
<Props of={Button} />
|
||||
|
||||
## Contained Buttons
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<Button variant="contained" style={{ margin: 5 }}>
|
||||
Default
|
||||
</Button>
|
||||
<Button variant="contained" color="primary" style={{ margin: 5 }}>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="contained" color="secondary" style={{ margin: 5 }}>
|
||||
Secondary
|
||||
</Button>
|
||||
</>
|
||||
</Playground>
|
||||
|
||||
## Outlined Buttons
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<Button variant="outlined" style={{ margin: 5 }}>
|
||||
Default
|
||||
</Button>
|
||||
<Button variant="outlined" color="primary" style={{ margin: 5 }}>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="outlined" color="secondary" style={{ margin: 5 }}>
|
||||
Secondary
|
||||
</Button>
|
||||
</>
|
||||
</Playground>
|
||||
|
||||
## Text Buttons
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<Button variant="text" style={{ margin: 5 }}>
|
||||
Default
|
||||
</Button>
|
||||
<Button variant="text" color="primary" style={{ margin: 5 }}>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="text" color="secondary" style={{ margin: 5 }}>
|
||||
Secondary
|
||||
</Button>
|
||||
</>
|
||||
</Playground>
|
||||
|
||||
## Sizes
|
||||
|
||||
<Playground>
|
||||
<>
|
||||
<div>
|
||||
<Button variant="text" size="small" style={{ margin: 5 }}>
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="contained" size="medium" style={{ margin: 5 }}>
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="outlined" size="large" style={{ margin: 5 }}>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Small
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Medium
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
large
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
color="secondary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Small
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
color="secondary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Medium
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
color="secondary"
|
||||
style={{ margin: 5 }}
|
||||
>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</>
|
||||
|
||||
</Playground>
|
||||
|
||||
## Buttons with icons
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
return (
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
startIcon={
|
||||
<svg
|
||||
className="fill-current w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z" />
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{ margin: 5 }}
|
||||
endIcon={
|
||||
<svg
|
||||
className="fill-current w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z" />
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
|
||||
</Playground>
|
||||
@ -0,0 +1,213 @@
|
||||
$default: #aaa;
|
||||
$primary: #00a;
|
||||
$secondary: #a00;
|
||||
$hoverOpacity: 40%;
|
||||
|
||||
.baseButton {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
min-width: 64px;
|
||||
padding: 6px 16px;
|
||||
border-radius: 20px;
|
||||
color: inherit;
|
||||
transition: all 300ms ease;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: lighten($default, $hoverOpacity / 2);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: $default;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="text"`. */
|
||||
.text {
|
||||
padding: 6px 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($default, $hoverOpacity / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="text"` and `color="primary"`. */
|
||||
.textPrimary {
|
||||
color: $primary;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($primary, $hoverOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="text"` and `color="secondary"`. */
|
||||
.textSecondary {
|
||||
color: $secondary;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($secondary, $hoverOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="outlined"`. */
|
||||
.outlined {
|
||||
padding: 5px 15px;
|
||||
border: 1px solid $default;
|
||||
|
||||
&:disabled {
|
||||
border: 1px solid $default;
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="outlined"` and `color="primary"`. */
|
||||
.outlinedPrimary {
|
||||
color: $primary;
|
||||
border: 1px solid lighten($primary, $hoverOpacity);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $primary;
|
||||
background-color: lighten($primary, $hoverOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="outlined"` and `color="secondary"`. */
|
||||
.outlinedSecondary {
|
||||
color: $secondary;
|
||||
border: 1px solid lighten($secondary, $hoverOpacity);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $secondary;
|
||||
background-color: lighten($secondary, $hoverOpacity);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
border: 1px solid $default;
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="contained"`. */
|
||||
.contained {
|
||||
color: #fff;
|
||||
background-color: $default;
|
||||
|
||||
&:disabled {
|
||||
color: lighten($default, $hoverOpacity);
|
||||
background-color: $default;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: darken($default, $hoverOpacity / 2);
|
||||
|
||||
&:disabled {
|
||||
color: lighten($default, $hoverOpacity);
|
||||
background-color: $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="contained"` and `color="primary"`. */
|
||||
.containedPrimary {
|
||||
color: white;
|
||||
background-color: $primary;
|
||||
|
||||
&:hover {
|
||||
background-color: darken($primary, $hoverOpacity / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `variant="contained"` and `color="secondary"`. */
|
||||
.containedSecondary {
|
||||
color: white;
|
||||
background-color: $secondary;
|
||||
|
||||
&:hover {
|
||||
background-color: darken($secondary, $hoverOpacity / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="small"` and `variant="text"`. */
|
||||
.textSizeSmall {
|
||||
padding: 4px 5px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="medium"` and `variant="text"`. */
|
||||
.textSizeMedium {
|
||||
padding: 6px 9px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="large"` and `variant="text"`. */
|
||||
.textSizeLarge {
|
||||
padding: 8px 11px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="small"` and `variant="outlined"`. */
|
||||
.outlinedSizeSmall {
|
||||
padding: 3px 9px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="medium"` and `variant="outlined"`. */
|
||||
.outlinedSizeMedium {
|
||||
padding: 5px 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="large"` and `variant="outlined"`. */
|
||||
.outlinedSizeLarge {
|
||||
padding: 7px 21px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="small"` and `variant="contained"`. */
|
||||
.containedSizeSmall {
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="medium"` and `variant="contained"`. */
|
||||
.containedSizeMedium {
|
||||
padding: 6px 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Styles applied to the root element if `size="large"` and `variant="contained"`. */
|
||||
.containedSizeLarge {
|
||||
padding: 8px 22px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Styles applied to the startIcon element if supplied. */
|
||||
.startIcon {
|
||||
display: inherit;
|
||||
margin-right: 8px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
/* Styles applied to the endIcon element if supplied. */
|
||||
.endIcon {
|
||||
display: inherit;
|
||||
margin-right: -4px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
2
platform/ui/src/components/ButtonModules/index.js
Normal file
2
platform/ui/src/components/ButtonModules/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
import ButtonModules from './ButtonModules';
|
||||
export default ButtonModules;
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import t from 'prop-types';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Label = ({ children, spacing, className, text, ...rest }) => {
|
||||
const baseClasses = 'flex flex-1 flex-col text-gray-700 text-sm font-bold';
|
||||
@ -12,7 +12,7 @@ const Label = ({ children, spacing, className, text, ...rest }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<label className={getClasses()}>
|
||||
<label className={getClasses()} {...rest}>
|
||||
{text}
|
||||
{children}
|
||||
</label>
|
||||
@ -27,6 +27,8 @@ const spacing = {
|
||||
|
||||
Label.defaultProps = {};
|
||||
|
||||
Label.prototypes = {};
|
||||
Label.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default Label;
|
||||
|
||||
46
platform/ui/src/styles/theme.js
Normal file
46
platform/ui/src/styles/theme.js
Normal file
@ -0,0 +1,46 @@
|
||||
const theme = {
|
||||
palette: {
|
||||
action: {
|
||||
hoverOpacity: 0.4,
|
||||
disabledBackground: '',
|
||||
disabled: '',
|
||||
},
|
||||
default: {
|
||||
light: '#eee',
|
||||
main: '#ccc',
|
||||
dark: '#aaa',
|
||||
},
|
||||
primary: {
|
||||
light: '#0000ff',
|
||||
main: '#0000aa',
|
||||
dark: '#000099',
|
||||
},
|
||||
secondary: {
|
||||
light: '#ff0000',
|
||||
main: '#aa0000',
|
||||
dark: '#990000',
|
||||
},
|
||||
error: {
|
||||
light: '',
|
||||
main: '',
|
||||
dark: '',
|
||||
},
|
||||
info: {
|
||||
light: '',
|
||||
main: '',
|
||||
dark: '',
|
||||
},
|
||||
warning: {
|
||||
light: '',
|
||||
main: '',
|
||||
dark: '',
|
||||
},
|
||||
success: {
|
||||
light: '',
|
||||
main: '',
|
||||
dark: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default theme;
|
||||
3
platform/ui/src/utils/capitalize.js
Normal file
3
platform/ui/src/utils/capitalize.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default string => {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
};
|
||||
270
platform/ui/src/utils/colorManipulator.js
Normal file
270
platform/ui/src/utils/colorManipulator.js
Normal file
@ -0,0 +1,270 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
/**
|
||||
* Returns a number whose value is limited to the given range.
|
||||
*
|
||||
* @param {number} value The value to be clamped
|
||||
* @param {number} min The lower boundary of the output range
|
||||
* @param {number} max The upper boundary of the output range
|
||||
* @returns {number} A number in the range [min, max]
|
||||
*/
|
||||
function clamp(value, min = 0, max = 1) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (value < min || value > max) {
|
||||
console.error(
|
||||
`@ohif/ui: the value provided ${value} is out of range [${min}, ${max}].`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(Math.max(min, value), max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color from CSS hex format to CSS rgb format.
|
||||
*
|
||||
* @param {string} color - Hex color, i.e. #nnn or #nnnnnn
|
||||
* @returns {string} A CSS rgb color string
|
||||
*/
|
||||
export function hexToRgb(color) {
|
||||
color = color.substr(1);
|
||||
|
||||
const re = new RegExp(`.{1,${color.length / 3}}`, 'g');
|
||||
let colors = color.match(re);
|
||||
|
||||
if (colors && colors[0].length === 1) {
|
||||
colors = colors.map(n => n + n);
|
||||
}
|
||||
|
||||
return colors ? `rgb(${colors.map(n => parseInt(n, 16)).join(', ')})` : '';
|
||||
}
|
||||
|
||||
function intToHex(int) {
|
||||
const hex = int.toString(16);
|
||||
return hex.length === 1 ? `0${hex}` : hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color from CSS rgb format to CSS hex format.
|
||||
*
|
||||
* @param {string} color - RGB color, i.e. rgb(n, n, n)
|
||||
* @returns {string} A CSS rgb color string, i.e. #nnnnnn
|
||||
*/
|
||||
export function rgbToHex(color) {
|
||||
// Idempotent
|
||||
if (color.indexOf('#') === 0) {
|
||||
return color;
|
||||
}
|
||||
|
||||
const { values } = decomposeColor(color);
|
||||
return `#${values.map(n => intToHex(n)).join('')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color from hsl format to rgb format.
|
||||
*
|
||||
* @param {string} color - HSL color values
|
||||
* @returns {string} rgb color values
|
||||
*/
|
||||
export function hslToRgb(color) {
|
||||
color = decomposeColor(color);
|
||||
const { values } = color;
|
||||
const h = values[0];
|
||||
const s = values[1] / 100;
|
||||
const l = values[2] / 100;
|
||||
const a = s * Math.min(l, 1 - l);
|
||||
const f = (n, k = (n + h / 30) % 12) =>
|
||||
l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||
|
||||
let type = 'rgb';
|
||||
const rgb = [
|
||||
Math.round(f(0) * 255),
|
||||
Math.round(f(8) * 255),
|
||||
Math.round(f(4) * 255),
|
||||
];
|
||||
|
||||
if (color.type === 'hsla') {
|
||||
type += 'a';
|
||||
rgb.push(values[3]);
|
||||
}
|
||||
|
||||
return recomposeColor({ type, values: rgb });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with the type and values of a color.
|
||||
*
|
||||
* Note: Does not support rgb % values.
|
||||
*
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @returns {object} - A MUI color object: {type: string, values: number[]}
|
||||
*/
|
||||
export function decomposeColor(color) {
|
||||
// Idempotent
|
||||
if (color.type) {
|
||||
return color;
|
||||
}
|
||||
|
||||
if (color.charAt(0) === '#') {
|
||||
return decomposeColor(hexToRgb(color));
|
||||
}
|
||||
|
||||
const marker = color.indexOf('(');
|
||||
const type = color.substring(0, marker);
|
||||
|
||||
if (['rgb', 'rgba', 'hsl', 'hsla'].indexOf(type) === -1) {
|
||||
throw new Error(
|
||||
[
|
||||
`@ohif/ui: unsupported \`${color}\` color.`,
|
||||
'We support the following formats: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla().',
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
let values = color.substring(marker + 1, color.length - 1).split(',');
|
||||
values = values.map(value => parseFloat(value));
|
||||
|
||||
return { type, values };
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color object with type and values to a string.
|
||||
*
|
||||
* @param {object} color - Decomposed color
|
||||
* @param {string} color.type - One of: 'rgb', 'rgba', 'hsl', 'hsla'
|
||||
* @param {array} color.values - [n,n,n] or [n,n,n,n]
|
||||
* @returns {string} A CSS color string
|
||||
*/
|
||||
export function recomposeColor(color) {
|
||||
const { type } = color;
|
||||
let { values } = color;
|
||||
|
||||
if (type.indexOf('rgb') !== -1) {
|
||||
// Only convert the first 3 values to int (i.e. not alpha)
|
||||
values = values.map((n, i) => (i < 3 ? parseInt(n, 10) : n));
|
||||
} else if (type.indexOf('hsl') !== -1) {
|
||||
values[1] = `${values[1]}%`;
|
||||
values[2] = `${values[2]}%`;
|
||||
}
|
||||
|
||||
return `${type}(${values.join(', ')})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the contrast ratio between two colors.
|
||||
*
|
||||
* Formula: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
|
||||
*
|
||||
* @param {string} foreground - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @param {string} background - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @returns {number} A contrast ratio value in the range 0 - 21.
|
||||
*/
|
||||
export function getContrastRatio(foreground, background) {
|
||||
const lumA = getLuminance(foreground);
|
||||
const lumB = getLuminance(background);
|
||||
return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
|
||||
}
|
||||
|
||||
/**
|
||||
* The relative brightness of any point in a color space,
|
||||
* normalized to 0 for darkest black and 1 for lightest white.
|
||||
*
|
||||
* Formula: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
|
||||
*
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @returns {number} The relative brightness of the color in the range 0 - 1
|
||||
*/
|
||||
export function getLuminance(color) {
|
||||
color = decomposeColor(color);
|
||||
|
||||
let rgb =
|
||||
color.type === 'hsl'
|
||||
? decomposeColor(hslToRgb(color)).values
|
||||
: color.values;
|
||||
rgb = rgb.map(val => {
|
||||
val /= 255; // normalized
|
||||
return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4;
|
||||
});
|
||||
|
||||
// Truncate at 3 digits
|
||||
return Number(
|
||||
(0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken or lighten a color, depending on its luminance.
|
||||
* Light colors are darkened, dark colors are lightened.
|
||||
*
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @param {number} coefficient=0.15 - multiplier in the range 0 - 1
|
||||
* @returns {string} A CSS color string. Hex input values are returned as rgb
|
||||
*/
|
||||
export function emphasize(color, coefficient = 0.15) {
|
||||
return getLuminance(color) > 0.5
|
||||
? darken(color, coefficient)
|
||||
: lighten(color, coefficient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the absolute transparency of a color.
|
||||
* Any existing alpha values are overwritten.
|
||||
*
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @param {number} value - value to set the alpha channel to in the range 0 -1
|
||||
* @returns {string} A CSS color string. Hex input values are returned as rgb
|
||||
*/
|
||||
export function fade(color, value) {
|
||||
color = decomposeColor(color);
|
||||
value = clamp(value);
|
||||
|
||||
if (color.type === 'rgb' || color.type === 'hsl') {
|
||||
color.type += 'a';
|
||||
}
|
||||
color.values[3] = value;
|
||||
|
||||
return recomposeColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Darkens a color.
|
||||
*
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @param {number} coefficient - multiplier in the range 0 - 1
|
||||
* @returns {string} A CSS color string. Hex input values are returned as rgb
|
||||
*/
|
||||
export function darken(color, coefficient) {
|
||||
color = decomposeColor(color);
|
||||
coefficient = clamp(coefficient);
|
||||
|
||||
if (color.type.indexOf('hsl') !== -1) {
|
||||
color.values[2] *= 1 - coefficient;
|
||||
} else if (color.type.indexOf('rgb') !== -1) {
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
color.values[i] *= 1 - coefficient;
|
||||
}
|
||||
}
|
||||
return recomposeColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightens a color.
|
||||
*
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @param {number} coefficient - multiplier in the range 0 - 1
|
||||
* @returns {string} A CSS color string. Hex input values are returned as rgb
|
||||
*/
|
||||
export function lighten(color, coefficient) {
|
||||
color = decomposeColor(color);
|
||||
coefficient = clamp(coefficient);
|
||||
|
||||
if (color.type.indexOf('hsl') !== -1) {
|
||||
color.values[2] += (100 - color.values[2]) * coefficient;
|
||||
} else if (color.type.indexOf('rgb') !== -1) {
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
color.values[i] += (255 - color.values[i]) * coefficient;
|
||||
}
|
||||
}
|
||||
|
||||
return recomposeColor(color);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user