Skip to content

Instantly share code, notes, and snippets.

@lucax88x
Created November 10, 2020 09:04
Show Gist options
  • Save lucax88x/96dea5a8baeff69b8f2481464d675519 to your computer and use it in GitHub Desktop.
Save lucax88x/96dea5a8baeff69b8f2481464d675519 to your computer and use it in GitHub Desktop.
/* eslint-disable react/button-has-type */
import { useStyledTheme } from '@pfb/hooks/useTheme';
import { Theme } from '@pfb/models/theme.model';
import {
ButtonHTMLAttributes,
PropsWithChildren,
useCallback,
MouseEvent,
forwardRef,
ReactNode,
} from 'react';
import { keyframes } from '@emotion/core';
export interface ButtonProps {
primary?: boolean;
error?: boolean;
link?: boolean;
transparent?: boolean;
after?: ReactNode;
}
const getColorPair = (theme: Theme, props: ButtonProps) => {
if (props.primary) {
return theme.colors.primary;
}
if (props.error) {
return theme.colors.error;
}
if (props.link) {
return {
backgroundColor: 'transparent',
color: theme.elements.link.color,
borderColor: 'transparent',
};
}
if (props.transparent) {
return {
backgroundColor: 'transparent',
color: theme.elements.link.color,
borderColor: 'transparent',
};
}
return theme.colors.default;
};
const rippleTransition = keyframes`
to {
transform: scale(4);
opacity: 0;
}`;
const styles = (props: {
primary: boolean;
error: boolean;
link: boolean;
transparent: boolean;
}) => (theme: Theme) => {
const colorPairByProps = getColorPair(theme, props);
return {
button: {
width: '100%',
color: colorPairByProps.color,
borderRadius: '4px',
fontWeight: 'bold' as const,
padding: `${theme.spacing(1)} ${theme.spacing(4)}`,
border: '1px solid',
borderColor: colorPairByProps.borderColor,
backgroundColor: colorPairByProps.backgroundColor,
position: 'relative' as const,
overflow: 'hidden',
transition: 'background 400ms',
outline: '0',
boxShadow:
!props.link && !props.transparent && '0 0 0.5rem rgba(0, 0, 0, 0.3)',
cursor: 'pointer',
gridGap: theme.spacing(1),
display: 'grid',
gridAutoFlow: 'column',
'span.ripple': {
position: 'absolute' as const,
borderRadius: '50%',
transform: 'scale(0)',
animation: `${rippleTransition} 600ms linear`,
backgroundColor: 'rgba(255, 255, 255, 0.7)',
},
':disabled': {
backgroundColor: theme.palette.neutral,
borderColor: theme.palette.darkerNeutral,
color: theme.palette.darkerNeutral,
cursor: 'not-allowed',
},
},
};
};
export const Button = forwardRef<
HTMLButtonElement,
PropsWithChildren<ButtonProps & ButtonHTMLAttributes<unknown>>
>((props, ref) => {
const {
children,
primary = false,
error = false,
link = false,
transparent = false,
after,
onClick,
...otherProps
} = props;
const styled = useStyledTheme(styles({ primary, error, link, transparent }));
console.log(styled.button.backgroundColor, 'transparent', transparent);
const createRipple = useCallback(
(event: MouseEvent<HTMLButtonElement>) => {
if (!!onClick) {
onClick(event);
}
const button = event.currentTarget;
const circle = document.createElement('span');
const diameter = Math.max(button.clientWidth, button.clientHeight);
const radius = diameter / 2;
circle.style.width = `${diameter}px`;
circle.style.height = `${diameter}px`;
circle.style.left = `${event.clientX - button.offsetLeft - radius}px`;
circle.style.top = `${event.clientY - button.offsetTop - radius}px`;
circle.classList.add('ripple');
circle.addEventListener('animationend', () => button.removeChild(circle));
button.appendChild(circle);
},
[onClick],
);
return (
<button
css={styled.button}
{...otherProps}
ref={ref}
onClick={createRipple}
>
{styled.button.backgroundColor}
{children}
{!!after && after}
</button>
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment