import { css } from '@tesseract-design/goofy-goober' export enum CheckControlAppearance { TICK_BOX = 'tick-box', BUTTON = 'button', SWITCH = 'switch', } export enum CheckControlType { /** * One or more of this component within its group can be selected. */ CHECKBOX = 'checkbox', /** * At most one of this component within its group can be selected. */ RADIO = 'radio', } export type CheckControlBaseArgs = { /** * Will the component conserve visual space? */ compact: boolean, /** * Appearance of the component. */ appearance: CheckControlAppearance, /** * Will the component occupy the whole width of its container? */ block: boolean, /** * Type of the component defining its behavior. */ type: CheckControlType, /** * Label to display signifying the component's unselected state. */ uncheckedLabel: boolean, } export const CheckStateContainer = ({ appearance, type, }: CheckControlBaseArgs): string => css.cx( css` position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; &:disabled + * { cursor: not-allowed; } &:first-child + * > :first-child + * + * { align-items: flex-start; text-align: left; } &:checked + * { --color-accent: var(--color-active, Highlight); outline: 0; } &:indeterminate[type="checkbox"] + * { --color-accent: var(--color-active, Highlight); outline: 0; } &:indeterminate[type="checkbox"] + * > :first-child + * > * > :first-child { display: none; } &:checked + * > :first-child + * > * > :first-child + * { display: none; } &:focus + * { --color-accent: var(--color-hover, red); outline: 0; } &:focus[type="checkbox"] + * { --color-accent: var(--color-hover, red); outline: 0; } &:focus + * > :first-child::before { box-shadow: 0 0 0 0.375rem var(--color-accent, blue); } `, css.nest('&:checked + * > :first-child + * > *') ( css.if ( appearance === CheckControlAppearance.TICK_BOX || appearance === CheckControlAppearance.BUTTON ) ( css.if (type === 'checkbox') ( css` width: 1.5em; height: 1.5em; ` ), css.if (type === 'radio') ( css` width: 1em; height: 1em; ` ), ), css.if (appearance === CheckControlAppearance.SWITCH) ( css` width: 1em; height: 1em; margin-right: 0; margin-left: 1em; ` ), ), css.nest('&:first-child + *') ( css` cursor: pointer; `, css.if (appearance === CheckControlAppearance.BUTTON) ( css` display: flex; `, ), css.if (appearance === CheckControlAppearance.SWITCH) ( css` width: 1em; height: 1em; `, ), ), css.nest('&:indeterminate[type="checkbox"] + * > :first-child + * > *') ( css.if ( appearance === CheckControlAppearance.BUTTON || appearance === CheckControlAppearance.TICK_BOX ) ( css` width: 1.5em; height: 1.5em; ` ), css.if (appearance === CheckControlAppearance.SWITCH) ( css` width: 1em; height: 1em; margin-right: 0.5em; margin-left: 0.5em; ` ) ), css.nest('&:indeterminate[type="checkbox"] + * > :first-child + * > * > :first-child + *') ( css.if ( appearance === CheckControlAppearance.BUTTON || appearance === CheckControlAppearance.TICK_BOX ) ( css` display: block; ` ), ), css.nest('&:checked + * > :first-child + * > * > :first-child') ( css.if ( appearance === CheckControlAppearance.BUTTON || appearance === CheckControlAppearance.TICK_BOX ) ( css` display: block; ` ), ), ); export const ClickArea = (): string => css.cx( css` display: contents; ` ); export const CheckIndicatorArea = ({ compact, appearance, type, uncheckedLabel, }: CheckControlBaseArgs): string => css.cx( css` display: inline-grid; vertical-align: middle; place-content: center; position: relative; background-color: var(--color-bg, white); box-shadow: 0 0 0 0.125rem var(--color-bg, white); color: var(--color-accent, blue); overflow: hidden; &::before { content: ''; width: 100%; height: 100%; position: absolute; top: 0; left: 0; border-radius: inherit; border-width: 0.125rem; border-style: solid; box-sizing: border-box; } `, css.if (appearance === CheckControlAppearance.TICK_BOX) ( css` width: 1.5em; height: 1.5em; `, css.if (type === CheckControlType.CHECKBOX) ( css` border-radius: 0.25rem; ` ), css.if (type === CheckControlType.RADIO) ( css` border-radius: 50%; ` ), ), css.if (appearance === CheckControlAppearance.BUTTON) ( css` width: 1.5em; height: 1.5em; `, css.if (!compact) ( css` margin-left: -0.25rem; ` ), css.if (type === CheckControlType.CHECKBOX) ( css` border-radius: 0.25rem; ` ), css.if (type === CheckControlType.RADIO) ( css` border-radius: 50%; ` ), ), css.if (appearance === CheckControlAppearance.SWITCH) ( css` width: 2.5em; height: 1.5em; border-radius: 0.75em; `, css.if(uncheckedLabel) ( css.dynamic({ 'margin-left': compact ? '0.375rem' : '0.75rem', }) ), ), css.nest('& + *') ( css.dynamic({ 'margin-left': compact ? '0.375rem' : '0.75rem', }) ) ); export const CheckIndicatorWrapper = ({ appearance, }: CheckControlBaseArgs): string => css.cx( css` flex-shrink: 0; display: grid; position: relative; background-color: var(--color-accent, blue); overflow: hidden; border-radius: inherit; `, css.if( appearance === CheckControlAppearance.TICK_BOX || appearance === CheckControlAppearance.BUTTON ) ( css` width: 0; height: 0; ` ), css.if(appearance === CheckControlAppearance.SWITCH) ( css` width: 1em; height: 1em; margin-right: 1em; transition-property: margin-left, margin-right; transition-duration: 150ms; transition-timing-function: ease-out; `, ), ); export const CheckIndicator = ({ appearance, }: CheckControlBaseArgs) => css.cx( css` fill: none; stroke: var(--color-bg, white); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; width: 1.5em; height: 1.5em; `, css.if(appearance === CheckControlAppearance.SWITCH) ( css` display: none; ` ) ); export const ClickAreaWrapper = ({ block, appearance, uncheckedLabel, }: CheckControlBaseArgs) => css.cx( css` vertical-align: middle; `, css.dynamic({ display: block ? 'block' : 'inline-block', }), css.if (appearance === CheckControlAppearance.TICK_BOX) ( css` padding-left: 2.25rem; text-indent: -2.25rem; ` ), css.if (appearance === CheckControlAppearance.SWITCH) ( css.if (!uncheckedLabel) ( css` padding-left: 3.25rem; text-indent: -3.25rem; ` ), ), ); export const Subtext = () => css.cx( css` font-size: 0.875em; ` );