import { css } from '@tesseract-design/goofy-goober'; export enum ButtonSize { SMALL = 'small', MEDIUM = 'medium', LARGE = 'large', } export enum ButtonVariant { OUTLINE = 'outline', FILLED = 'filled', } export type ButtonBaseArgs = { /** * Size of the component. */ size: ButtonSize, /** * Will the component occupy the whole width of its container? */ block: boolean, /** * Stylistic variant of the component. */ variant: ButtonVariant, /** * Will the component display a surrounding border? */ border: boolean, /** * Will the component reject any activation? */ disabled: boolean, /** * Will the component conserve visual space? */ compact: boolean, /** * Is the component an item inside a menu? */ menuItem: boolean, } const MIN_HEIGHTS: Record = { [ButtonSize.SMALL]: '2.5rem', [ButtonSize.MEDIUM]: '3rem', [ButtonSize.LARGE]: '4rem', }; export const Button = ({ size, block, variant, disabled, compact, }: ButtonBaseArgs): string => css.cx( css` box-sizing: border-box; vertical-align: middle; appearance: none; font: inherit; font-family: var(--font-family-base, sans-serif); text-transform: uppercase; font-weight: bolder; border-radius: 0.25rem; justify-content: center; align-items: center; position: relative; border: 0; user-select: none; text-decoration: none; white-space: nowrap; line-height: 1; & > :first-child::before { box-shadow: 0 0 0 0 var(--color-accent, blue); transition-property: box-shadow; transition-duration: 150ms; transition-timing-function: linear; } &:disabled { opacity: 0.5; cursor: not-allowed; } &::-moz-focus-inner { outline: 0; border: 0; } &:focus > :first-child::before { box-shadow: 0 0 0 0.375rem var(--color-accent, blue); } &:disabled > :first-child::before { box-shadow: 0 0 0 0 var(--color-accent, blue) !important; } `, css.dynamic({ 'min-height': MIN_HEIGHTS[size], }), css.if (disabled) ( css` --color-accent: var(--color-primary, blue); opacity: 0.5; cursor: not-allowed; ` ).else ( css` cursor: pointer; &:hover { --color-accent: var(--color-hover, blue); outline: 0; } &:focus { --color-accent: var(--color-hover, blue); outline: 0; } &:active { --color-accent: var(--color-active, red); outline: 0; } &:hover > :first-child::before { box-shadow: 0 0 0 0.375rem var(--color-accent, blue); } ` ), css.if (block) ( css` width: 100%; display: flex; ` ).else ( css` display: inline-flex; ` ), css.if (compact) ( css` font-stretch: condensed; padding: 0 0.5rem; ` ).else( css` padding: 0 1rem; ` ), css.if (variant === ButtonVariant.FILLED) ( css` background-color: var(--color-accent, blue); color: var(--color-bg, white) !important; ` ), css.if (variant === ButtonVariant.OUTLINE) ( css` background-color: var(--color-bg, white); color: var(--color-accent, blue); ` ), ); export const Border = ({ border }: ButtonBaseArgs): string => css.cx( css.if (border) ( css` border-color: var(--color-accent, blue); box-sizing: border-box; display: inline-block; border-width: 0.125rem; border-style: solid; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: inherit; pointer-events: none; &::before { position: absolute; top: 0; left: 0; width: 100%; height: 100%; content: ''; border-radius: 0.125rem; opacity: 0.5; pointer-events: none; } ` ), ); export const Label = ({ compact, menuItem, }: ButtonBaseArgs): string => css.cx( css` display: block; flex-grow: 1; flex-basis: 0; min-width: 0; `, css.if (compact || menuItem) ( css` text-align: left; ` ).else ( css` text-align: center; ` ), css.if (compact) ( css` & ~ :last-child { margin-right: -0.5rem; } ` ).else ( css` & ~ :last-child { margin-right: -1rem; } ` ), ); export const BadgeContainer = ({ size, }: ButtonBaseArgs): string => css.cx( css` width: 2rem; text-align: center; flex-shrink: 0; & + * { margin-left: -0.5rem; } `, css.nest('&:last-child')( css.dynamic({ width: MIN_HEIGHTS[size], }) ), ); export const OverflowText = (): string => css.cx( css` width: 100%; display: block; overflow: hidden; text-overflow: ellipsis; height: 1.1em; line-height: 1; `, ); export const IndicatorWrapper = ({ size }: ButtonBaseArgs): string => css.cx( css` flex-shrink: 0; box-sizing: border-box; display: grid; place-content: center; padding: 0 1rem; z-index: 1; pointer-events: none; line-height: 1; user-select: none; `, css.dynamic({ width: `calc(${MIN_HEIGHTS[size]} * 0.75)`, height: MIN_HEIGHTS[size], }), ); export const Indicator = () => css.cx( css` width: 1.5em; height: 1.5em; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; `, ); export const MainText = () => css.cx( css` width: 100%; `, ); export const Subtext = () => css.cx( css` display: block; height: 1.1em; line-height: 1.1; width: 100%; font-size: 0.875em; text-transform: none; font-weight: var(--font-weight-base, normal); `, );