|
- import * as React from 'react'
- import * as PropTypes from 'prop-types'
- import styled from 'styled-components'
- import { Size, SizeMap } from '../../services/utilities'
- import stringify from '../../services/stringify'
-
- export type Variant = 'outline' | 'primary'
-
- const MIN_HEIGHTS: SizeMap<string | number> = {
- small: '2.5rem',
- medium: '3rem',
- large: '4rem',
- }
-
- const Base = styled('button')({
- 'appearance': 'none',
- 'padding': '0 1rem',
- 'font': 'inherit',
- 'fontFamily': 'var(--font-family-base)',
- 'textTransform': 'uppercase',
- 'fontWeight': 'bolder',
- 'borderRadius': '0.25rem',
- 'placeContent': 'center',
- 'position': 'relative',
- 'cursor': 'pointer',
- 'border': 0,
- 'userSelect': 'none',
- 'textDecoration': 'none',
- 'transitionProperty': 'background-color, color',
- 'whiteSpace': 'nowrap',
- 'lineHeight': 1,
- ':focus': {
- '--color-accent': 'var(--color-active, Highlight)',
- 'outline': 0,
- },
- ':disabled': {
- opacity: 0.5,
- cursor: 'not-allowed',
- },
- '::-moz-focus-inner': {
- outline: 0,
- border: 0,
- },
- })
-
- Base.displayName = 'button'
-
- const Border = styled('span')({
- 'borderColor': 'var(--color-accent, blue)',
- 'boxSizing': 'border-box',
- 'display': 'inline-block',
- 'borderWidth': '0.125rem',
- 'borderStyle': 'solid',
- 'position': 'absolute',
- 'top': 0,
- 'left': 0,
- 'width': '100%',
- 'height': '100%',
- 'borderRadius': 'inherit',
- 'pointerEvents': 'none',
- 'transitionProperty': 'border-color',
- '::before': {
- position: 'absolute',
- top: 0,
- left: 0,
- width: '100%',
- height: '100%',
- content: "''",
- borderRadius: '0.125rem',
- opacity: 0.5,
- pointerEvents: 'none',
- },
- [`${Base}:focus &::before`]: {
- boxShadow: '0 0 0 0.375rem var(--color-accent, blue)',
- },
- })
-
- Border.displayName = 'span'
-
- const propTypes = {
- /**
- * Size of the component.
- */
- size: PropTypes.oneOf<Size>(['small', 'medium', 'large']),
- /**
- * Variant of the component.
- */
- variant: PropTypes.oneOf<Exclude<Variant, 'unknown'>>(['outline', 'primary']),
- /**
- * Should the component take up the remaining space parallel to the content flow?
- */
- block: PropTypes.bool,
- /**
- * Text to identify the action associated upon activation of the component.
- */
- children: PropTypes.any,
- /**
- * Can the component be activated?
- */
- disabled: PropTypes.bool,
- }
-
- type Props = PropTypes.InferProps<typeof propTypes>
-
- const defaultVariantStyleSet: React.CSSProperties = {
- backgroundColor: 'transparent',
- color: 'var(--color-accent, blue)',
- }
-
- const variantStyleSets: Record<Variant, React.CSSProperties> = {
- outline: defaultVariantStyleSet,
- primary: {
- backgroundColor: 'var(--color-accent, blue)',
- color: 'var(--color-bg, white)',
- },
- }
-
- const Button = React.forwardRef<HTMLButtonElement, Props>(
- ({ size = 'medium', variant = 'outline', block = false, disabled = false, children, ...etcProps }, ref) => {
- const { [variant as Variant]: theVariantStyleSet = defaultVariantStyleSet } = variantStyleSets
-
- return (
- <Base
- {...etcProps}
- ref={ref}
- disabled={disabled!}
- style={{
- ...theVariantStyleSet,
- minHeight: MIN_HEIGHTS[size as Size],
- width: block ? '100%' : undefined,
- display: block ? 'grid' : 'inline-grid',
- }}
- >
- <Border />
- {stringify(children)}
- </Base>
- )
- },
- )
-
- Button.propTypes = propTypes
-
- Button.displayName = 'Button'
-
- export default Button
|