|
- import { css } from '@tesseract-design/goofy-goober';
-
- export enum TextControlSize {
- SMALL = 'small',
- MEDIUM = 'medium',
- LARGE = 'large',
- }
-
- export enum TextControlStyle {
- DEFAULT = 'default',
- ALTERNATE = 'alternate',
- }
-
- export const MIN_HEIGHTS: Record<TextControlSize, string> = {
- [TextControlSize.SMALL]: '2.5rem',
- [TextControlSize.MEDIUM]: '3rem',
- [TextControlSize.LARGE]: '4rem',
- };
-
- const LABEL_VERTICAL_PADDING_SIZES: Record<TextControlSize, string> = {
- [TextControlSize.SMALL]: '0.125rem',
- [TextControlSize.MEDIUM]: '0.25rem',
- [TextControlSize.LARGE]: '0.375rem',
- };
-
- const INPUT_FONT_SIZES: Record<TextControlSize, string> = {
- [TextControlSize.SMALL]: '0.75em',
- [TextControlSize.MEDIUM]: '0.85em',
- [TextControlSize.LARGE]: '1em',
- };
-
- const SECONDARY_TEXT_SIZES: Record<TextControlSize, string> = {
- [TextControlSize.SMALL]: '0.6em',
- [TextControlSize.MEDIUM]: '0.725em',
- [TextControlSize.LARGE]: '0.85em',
- };
-
- const MULTILINE_VERTICAL_PADDING_FACTORS: Record<TextControlSize, string> = {
- [TextControlSize.SMALL]: '1.25',
- [TextControlSize.MEDIUM]: '1.2',
- [TextControlSize.LARGE]: '1.45',
- };
-
- const ALTERNATE_VERTICAL_PADDING_FACTORS: Record<TextControlSize, string> = {
- [TextControlSize.SMALL]: '1.75',
- [TextControlSize.MEDIUM]: '1.35',
- [TextControlSize.LARGE]: '1.25',
- };
-
- export type TextControlBaseArgs = {
- /**
- * Will the component occupy the whole width of its container?
- */
- block: boolean,
- /**
- * Stylistic variant of the component.
- */
- style: TextControlStyle,
- /**
- * Will the component display a surrounding border?
- */
- border: boolean,
- /**
- * Does the component include an additional indicator for labels?
- */
- indicator: boolean,
- /**
- * Size of the component.
- */
- size: TextControlSize,
- /**
- * Can the size of the component be changed?
- */
- resizable: boolean,
- /**
- * Does this component have predefined values?
- */
- predefinedValues: boolean,
- }
-
- export const Root = ({
- block,
- }: TextControlBaseArgs): string => css.cx(
- css`
- vertical-align: middle;
- position: relative;
- border-radius: 0.25rem;
- font-family: var(--font-family-base, sans-serif);
- max-width: 100%;
- &:focus-within {
- --color-accent: var(--color-hover, red);
- }
- & > span {
- 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;
- z-index: 2;
- pointer-events: none;
- transition-property: border-color;
- }
- & > span::before {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- content: '';
- border-radius: 0.125rem;
- opacity: 0.5;
- pointer-events: none;
- box-shadow: 0 0 0 0 var(--color-accent, blue);
- transition-property: box-shadow;
- transition-duration: 150ms;
- transition-timing-function: linear;
- }
- &:focus-within > span::before {
- box-shadow: 0 0 0 0.375rem var(--color-accent, blue);
- }
- `,
- css.dynamic({
- display: block ? 'block' : 'inline-block',
- }),
- );
-
- export const LabelWrapper = ({
- style,
- border,
- indicator,
- size,
- }: TextControlBaseArgs): string => css.cx(
- css`
- color: var(--color-accent, blue);
- box-sizing: border-box;
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- font-weight: bolder;
- z-index: 1;
- pointer-events: none;
- transition-property: color;
- line-height: 0.65;
- user-select: none;
- `,
- css.dynamic({
- 'padding-bottom': LABEL_VERTICAL_PADDING_SIZES[size],
- 'font-size': SECONDARY_TEXT_SIZES[size],
- }),
- css.if (border) (
- css`
- background-color: var(--color-bg, white);
- `
- ),
- css.if (style === TextControlStyle.ALTERNATE) (
- css.dynamic({
- 'padding-top': `calc(${LABEL_VERTICAL_PADDING_SIZES[size]} * 0.5)`,
- }),
- css.if (border) (
- css`
- padding-left: 0.5rem;
- `,
- css.dynamic({
- 'padding-right': indicator ? MIN_HEIGHTS[size] : '0.5rem',
- }),
- ),
- css.if (!border && indicator) (
- css.dynamic({
- 'padding-right': MIN_HEIGHTS[size],
- }),
- ),
- ),
- css.if (style === TextControlStyle.DEFAULT) (
- css`
- padding-left: 0.5rem;
- `,
- css.dynamic({
- 'padding-top': LABEL_VERTICAL_PADDING_SIZES[size],
- 'padding-right': !indicator ? '0.5rem' : MIN_HEIGHTS[size],
- }),
- ),
- )
-
- export const Input = ({
- style,
- size,
- indicator,
- border,
- resizable,
- predefinedValues,
- }: TextControlBaseArgs): string => css.cx(
- css`
- appearance: none;
- display: block;
- box-sizing: border-box;
- position: relative;
- border: 0;
- border-radius: inherit;
- margin: 0;
- font-family: inherit;
- min-width: 8rem;
- max-width: 100%;
- width: 100%;
- z-index: 1;
- transition-property: background-color, color;
- &:focus {
- outline: 0;
- color: var(--color-fg, black);
- }
- &:disabled {
- cursor: not-allowed;
- opacity: 0.5;
- }
- &:disabled ~ * {
- opacity: 0.5;
- }
- `,
- css.media('only screen') (
- css`
- background-color: var(--color-bg, white);
- color: var(--color-fg, black);
- `
- ),
- css.dynamic({
- 'min-height': MIN_HEIGHTS[size],
- 'font-size': INPUT_FONT_SIZES[size],
- }),
- css.if (resizable) (
- css`
- resize: vertical;
- `
- ),
- css.if (predefinedValues) (
- css`
- cursor: pointer;
- `
- ),
- css.if (border) (
- css`
- background-color: var(--color-bg, white);
- `
- ),
- css.if (style === TextControlStyle.ALTERNATE) (
- css`
- padding-bottom: 0;
- `,
- css.dynamic({
- 'padding-top': resizable
- ? `calc(${SECONDARY_TEXT_SIZES[size]} * 2.5)`
- : `calc(${SECONDARY_TEXT_SIZES[size]} * 2)`,
- 'line-height': `calc(${MULTILINE_VERTICAL_PADDING_FACTORS[size]} * 1.1)`,
- }),
- css.if (border) (
- css`
- padding-left: 0.5rem;
- `,
- css.dynamic({
- 'padding-right': indicator ? MIN_HEIGHTS[size] : '0.5rem',
- }),
- ),
- css.if (!border && indicator) (
- css.dynamic({
- 'padding-right': MIN_HEIGHTS[size],
- }),
- )
- ),
- css.if (style === TextControlStyle.DEFAULT) (
- css`
- padding-left: 1rem;
- `,
- css.dynamic({
- 'padding-right': !indicator ? '1rem' : MIN_HEIGHTS[size],
- 'line-height': `calc(${MULTILINE_VERTICAL_PADDING_FACTORS[size]} * 1.1)`,
- }),
- css.if (resizable) (
- css.dynamic({
- 'padding-top': `calc(${SECONDARY_TEXT_SIZES[size]} * ${MULTILINE_VERTICAL_PADDING_FACTORS[size]})`,
- 'padding-bottom': `calc(${SECONDARY_TEXT_SIZES[size]} * ${MULTILINE_VERTICAL_PADDING_FACTORS[size]})`,
- })
- ),
- css.if (!resizable) (
- css.dynamic({
- 'padding-bottom': `calc(${SECONDARY_TEXT_SIZES[size]} * ${MULTILINE_VERTICAL_PADDING_FACTORS[size]} * 0.5)`,
- })
- )
- ),
- )
-
- export const HintWrapper = ({
- style,
- size,
- border,
- }: TextControlBaseArgs): string => css.cx(
- css`
- box-sizing: border-box;
- position: absolute;
- left: 0;
- font-size: 0.85em;
- max-width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- z-index: 1;
- pointer-events: none;
- user-select: none;
- line-height: 0;
- `,
- css.if (border) (
- css`
- background-color: var(--color-bg, white);
- `
- ),
- css.if (style === TextControlStyle.ALTERNATE) (
- css`
- line-height: 1.25;
- `,
- css.dynamic({
- top: `calc(${SECONDARY_TEXT_SIZES[size]} * ${ALTERNATE_VERTICAL_PADDING_FACTORS[size]})`,
- 'font-size': SECONDARY_TEXT_SIZES[size],
- }),
- css.if (border) (
- css`
- padding-left: 0.5rem;
- &:last-child {
- padding-right: 0.5rem;
- }
- `,
- css.dynamic({
- 'padding-right': MIN_HEIGHTS[size],
- })
- )
- ),
- css.if (style === TextControlStyle.DEFAULT) (
- css`
- bottom: 0;
- padding-left: 1rem;
- line-height: 1.25;
- &:last-child {
- padding-right: 1rem;
- }
- `,
- css.dynamic({
- 'padding-bottom': `calc(${LABEL_VERTICAL_PADDING_SIZES[size]} * 0.9)`,
- 'padding-right': MIN_HEIGHTS[size],
- 'font-size': SECONDARY_TEXT_SIZES[size],
- })
- )
- )
-
- export const Hint = (): string => css.cx(
- css`
- opacity: 0.5;
- `
- );
-
- export const IndicatorWrapper = ({
- size
- }: TextControlBaseArgs): string => css.cx(
- css`
- color: var(--color-accent, blue);
- box-sizing: border-box;
- position: absolute;
- bottom: 0;
- right: 0;
- display: grid;
- place-content: center;
- padding: 0 1rem;
- z-index: 2;
- pointer-events: none;
- transition-property: color;
- line-height: 1;
- user-select: none;
- `,
- css.dynamic({
- width: MIN_HEIGHTS[size],
- height: MIN_HEIGHTS[size],
- }),
- );
-
- export const Indicator = (): string => css.cx(
- css`
- width: 1.5em;
- height: 1.5em;
- fill: none;
- stroke: currentColor;
- stroke-width: 2;
- stroke-linecap: round;
- stroke-linejoin: round;
- `,
- );
|