src/input/styled-components.ts (458 lines of code) (raw):

/* Copyright (c) Uber Technologies, Inc. This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. */ import { styled } from '../styles'; import type { Theme, Font } from '../styles/types'; import type { StyleObject } from 'styletron-standard'; import { ADJOINED, SIZE } from './constants'; import type { SharedProps, Size } from './types'; import type { SharedStyleProps } from '../textarea/types'; import DeleteAlt from '../icon/delete-alt'; export const StyledMaskToggleButton = styled< 'button', { $size: Size; $isFocusVisible: boolean; $theme: Theme; } >('button', ({ $theme, $size, $isFocusVisible }) => { const pad = { [SIZE.mini]: $theme.sizing.scale400, [SIZE.compact]: $theme.sizing.scale400, [SIZE.default]: $theme.sizing.scale300, [SIZE.large]: $theme.sizing.scale200, }[$size]; return { display: 'flex', alignItems: 'center', borderTopStyle: 'none', borderBottomStyle: 'none', borderLeftStyle: 'none', borderRightStyle: 'none', background: 'none', paddingLeft: pad, paddingRight: pad, outline: $isFocusVisible ? `solid 3px ${$theme.colors.borderAccent}` : 'none', color: $theme.colors.contentPrimary, }; }); StyledMaskToggleButton.displayName = 'StyledMaskToggleButton'; export const StyledClearIconContainer = styled< 'div', { $size: Size; $alignTop: boolean; $theme: Theme; } >('div', ({ $alignTop = false, $size, $theme }) => { const pad = { [SIZE.mini]: $theme.sizing.scale200, [SIZE.compact]: $theme.sizing.scale200, [SIZE.default]: $theme.sizing.scale100, [SIZE.large]: $theme.sizing.scale0, }[$size]; return { display: 'flex', alignItems: $alignTop ? 'flex-start' : 'center', paddingLeft: pad, paddingRight: pad, paddingTop: $alignTop ? $theme.sizing.scale500 : '0px', color: $theme.colors.contentPrimary, }; }); StyledClearIconContainer.displayName = 'StyledClearIconContainer'; export const StyledClearIcon = styled< typeof DeleteAlt, { $isFocusVisible: boolean; } >(DeleteAlt, ({ $theme, $isFocusVisible }) => ({ cursor: 'pointer', outline: $isFocusVisible ? `solid 3px ${$theme.colors.borderAccent}` : 'none', })); StyledClearIcon.displayName = 'StyledClearIcon'; function getInputPadding( // @ts-ignore size, // @ts-ignore sizing ): { paddingTop: string; paddingBottom: string; paddingLeft: string; paddingRight: string; } { // @ts-ignore return { [SIZE.mini]: { paddingTop: sizing.scale100, paddingBottom: sizing.scale100, paddingLeft: sizing.scale550, paddingRight: sizing.scale550, }, [SIZE.compact]: { paddingTop: sizing.scale200, paddingBottom: sizing.scale200, paddingLeft: sizing.scale550, paddingRight: sizing.scale550, }, [SIZE.default]: { paddingTop: sizing.scale400, paddingBottom: sizing.scale400, paddingLeft: sizing.scale550, paddingRight: sizing.scale550, }, [SIZE.large]: { paddingTop: sizing.scale550, paddingBottom: sizing.scale550, paddingLeft: sizing.scale550, paddingRight: sizing.scale550, }, }[size]; } function getRootPadding( // @ts-ignore adjoined, // @ts-ignore size, // @ts-ignore sizing, // @ts-ignore direction, // @ts-ignore hasIconTrailing ): { paddingLeft: string; paddingRight: string; } { let ifLeftPad = adjoined === ADJOINED.both || (adjoined === ADJOINED.left && direction !== 'rtl') || (adjoined === ADJOINED.right && direction === 'rtl') || (hasIconTrailing && direction === 'rtl'); let ifRightPad = adjoined === ADJOINED.both || (adjoined === ADJOINED.right && direction !== 'rtl') || (adjoined === ADJOINED.left && direction === 'rtl') || (hasIconTrailing && direction !== 'rtl'); return { paddingLeft: ifLeftPad ? sizing.scale550 : '0px', paddingRight: ifRightPad ? sizing.scale550 : '0px', }; } // @ts-ignore function getFont(size, typography): Font { // @ts-ignore return { [SIZE.mini]: typography.font100, [SIZE.compact]: typography.font200, [SIZE.default]: typography.font300, [SIZE.large]: typography.font400, }[size]; } function getRootColors( // @ts-ignore $disabled, // @ts-ignore $isFocused, // @ts-ignore $error, $positive = false, // @ts-ignore colors ): { borderLeftColor: string; borderRightColor: string; borderTopColor: string; borderBottomColor: string; backgroundColor: string; } { if ($disabled) { return { borderLeftColor: colors.inputFillDisabled, borderRightColor: colors.inputFillDisabled, borderTopColor: colors.inputFillDisabled, borderBottomColor: colors.inputFillDisabled, backgroundColor: colors.inputFillDisabled, }; } if ($isFocused) { return { borderLeftColor: colors.borderSelected, borderRightColor: colors.borderSelected, borderTopColor: colors.borderSelected, borderBottomColor: colors.borderSelected, backgroundColor: colors.inputFillActive, }; } if ($error) { return { borderLeftColor: colors.inputBorderError, borderRightColor: colors.inputBorderError, borderTopColor: colors.inputBorderError, borderBottomColor: colors.inputBorderError, backgroundColor: colors.inputFillError, }; } if ($positive) { return { borderLeftColor: colors.inputBorderPositive, borderRightColor: colors.inputBorderPositive, borderTopColor: colors.inputBorderPositive, borderBottomColor: colors.inputBorderPositive, backgroundColor: colors.inputFillPositive, }; } return { borderLeftColor: colors.inputBorder, borderRightColor: colors.inputBorder, borderTopColor: colors.inputBorder, borderBottomColor: colors.inputBorder, backgroundColor: colors.inputFill, }; } function getRootBorderRadius( // @ts-ignore size, // @ts-ignore borders ): { borderTopLeftRadius: string; borderBottomLeftRadius: string; borderTopRightRadius: string; borderBottomRightRadius: string; } { let radius = borders.inputBorderRadius; if (size === SIZE.mini) { radius = borders.inputBorderRadiusMini; } return { borderTopLeftRadius: radius, borderBottomLeftRadius: radius, borderTopRightRadius: radius, borderBottomRightRadius: radius, }; } export const getRootStyles = (props: { $adjoined: keyof typeof ADJOINED; $isFocused?: boolean; $error?: boolean; $disabled?: boolean; $positive?: boolean; $size: Size; $theme: Theme; $hasIconTrailing?: boolean; }): StyleObject => { const { $isFocused, $adjoined, $error, $disabled, $positive, $size, $theme, $theme: { borders, colors, sizing, typography, animation }, $hasIconTrailing, } = props; return { boxSizing: 'border-box', display: 'flex', overflow: 'hidden', width: '100%', borderLeftWidth: '2px', borderRightWidth: '2px', borderTopWidth: '2px', borderBottomWidth: '2px', borderLeftStyle: 'solid', borderRightStyle: 'solid', borderTopStyle: 'solid', borderBottomStyle: 'solid', transitionProperty: 'border', transitionDuration: animation.timing200, transitionTimingFunction: animation.easeOutCurve, ...getRootBorderRadius($size, borders), ...getFont($size, typography), ...getRootColors($disabled, $isFocused, $error, $positive, colors), ...getRootPadding($adjoined, $size, sizing, $theme.direction, $hasIconTrailing), }; }; export const Root = styled<'div', SharedProps>('div', getRootStyles); Root.displayName = 'Root'; // InputEnhancer type InputEnhancerStyles = { paddingRight: string; paddingLeft: string; }; // @ts-ignore function getInputEnhancerPadding($size, sizing): InputEnhancerStyles { // @ts-ignore return { [SIZE.mini]: { paddingRight: sizing.scale400, paddingLeft: sizing.scale400, }, [SIZE.compact]: { paddingRight: sizing.scale400, paddingLeft: sizing.scale400, }, [SIZE.default]: { paddingRight: sizing.scale300, paddingLeft: sizing.scale300, }, [SIZE.large]: { paddingRight: sizing.scale200, paddingLeft: sizing.scale200, }, }[$size]; } // @ts-ignore function getInputEnhancerColors($disabled, $isFocused, $error, $positive, colors) { if ($disabled) { return { color: colors.inputEnhancerTextDisabled, backgroundColor: colors.inputFillDisabled, }; } if ($isFocused) { return { color: colors.contentPrimary, backgroundColor: colors.inputFillActive, }; } if ($error) { return { color: colors.contentPrimary, backgroundColor: colors.inputFillError, }; } if ($positive) { return { color: colors.contentPrimary, backgroundColor: colors.inputFillPositive, }; } return { color: colors.contentPrimary, backgroundColor: colors.inputFill, }; } export const InputEnhancer = styled<'div', SharedProps>('div', (props) => { const { $size, $disabled, $isFocused, $error, $positive, $theme: { colors, sizing, typography, animation }, } = props; return { display: 'flex', alignItems: 'center', justifyContent: 'center', transitionProperty: 'color, background-color', transitionDuration: animation.timing200, transitionTimingFunction: animation.easeOutCurve, ...getFont($size, typography), ...getInputEnhancerPadding($size, sizing), ...getInputEnhancerColors($disabled, $isFocused, $error, $positive, colors), }; }); InputEnhancer.displayName = 'InputEnhancer'; // InputContainer // @ts-ignore function getInputContainerColors($disabled, $isFocused, $error, $positive, colors): StyleObject { if ($disabled) { return { color: colors.inputTextDisabled, backgroundColor: colors.inputFillDisabled, }; } if ($isFocused) { return { color: colors.contentPrimary, backgroundColor: colors.inputFillActive, }; } if ($error) { return { color: colors.contentPrimary, backgroundColor: colors.inputFillError, }; } if ($positive) { return { color: colors.contentPrimary, backgroundColor: colors.inputFillPositive, }; } return { color: colors.contentPrimary, backgroundColor: colors.inputFill, }; } export const getInputContainerStyles = (props: { $isFocused?: boolean; $error?: boolean; $disabled?: boolean; $positive?: boolean; $size: Size; $theme: Theme; }): StyleObject => { const { $isFocused, $error, $disabled, $positive, $size, $theme: { colors, typography, animation }, } = props; return { display: 'flex', width: '100%', transitionProperty: 'background-color', transitionDuration: animation.timing200, transitionTimingFunction: animation.easeOutCurve, ...getFont($size, typography), ...getInputContainerColors($disabled, $isFocused, $error, $positive, colors), }; }; export const InputContainer = styled<'div', SharedProps>('div', getInputContainerStyles); InputContainer.displayName = 'InputContainer'; // @ts-ignore function getInputColors($disabled, $isFocused, $error, colors): StyleObject { if ($disabled) { return { color: colors.inputTextDisabled, '-webkit-text-fill-color': colors.inputTextDisabled, caretColor: colors.contentPrimary, '::placeholder': { color: colors.inputPlaceholderDisabled, }, }; } return { color: colors.contentPrimary, caretColor: colors.contentPrimary, '::placeholder': { color: colors.inputPlaceholder, }, }; } export const getInputStyles = ( props: | (SharedProps & { $theme: Theme; }) | (SharedStyleProps & { $theme: Theme; }) ): StyleObject => { const { $disabled, $isFocused, $error, $size, $theme: { colors, sizing, typography }, } = props; return { boxSizing: 'border-box', backgroundColor: 'transparent', borderLeftWidth: 0, borderRightWidth: 0, borderTopWidth: 0, borderBottomWidth: 0, borderLeftStyle: 'none', borderRightStyle: 'none', borderTopStyle: 'none', borderBottomStyle: 'none', outline: 'none', width: '100%', // See https://stackoverflow.com/a/33811151 minWidth: 0, maxWidth: '100%', cursor: $disabled ? 'not-allowed' : 'text', margin: '0', // @ts-ignore paddingTop: '0', // @ts-ignore paddingBottom: '0', // @ts-ignore paddingLeft: '0', // @ts-ignore paddingRight: '0', ...getFont($size, typography), ...getInputPadding($size, sizing), ...getInputColors($disabled, $isFocused, $error, colors), }; }; export const Input = styled<'input', SharedProps>('input', getInputStyles); Input.displayName = 'Input';