/** * External dependencies */ import { css, SerializedStyles } from '@emotion/react'; import styled from '@emotion/styled'; // eslint-disable-next-line no-restricted-imports import type { CSSProperties, ReactNode } from 'react'; /** * Internal dependencies */ import type { GeChiUIComponentProps } from '../../ui/context'; import { Flex, FlexItem } from '../../flex'; import { Text } from '../../text'; import { COLORS, rtl } from '../../utils'; import type { LabelPosition } from '../types'; type ContainerProps = { disabled?: boolean; hideLabel?: boolean; __unstableInputWidth?: CSSProperties[ 'width' ]; labelPosition?: LabelPosition; }; type RootProps = { isFocused?: boolean; labelPosition?: LabelPosition; }; const rootFocusedStyles = ( { isFocused }: RootProps ) => { if ( ! isFocused ) return ''; return css( { zIndex: 1 } ); }; const rootLabelPositionStyles = ( { labelPosition }: RootProps ) => { switch ( labelPosition ) { case 'top': return css` align-items: flex-start; flex-direction: column; `; case 'bottom': return css` align-items: flex-start; flex-direction: column-reverse; `; case 'edge': return css` justify-content: space-between; `; default: return ''; } }; export const Root = styled( Flex )< RootProps >` position: relative; border-radius: 2px; padding-top: 0; ${ rootFocusedStyles } ${ rootLabelPositionStyles } `; const containerDisabledStyles = ( { disabled }: ContainerProps ) => { const backgroundColor = disabled ? COLORS.ui.backgroundDisabled : COLORS.ui.background; return css( { backgroundColor } ); }; // Normalizes the margins from the (components/ui/flex/) container. const containerMarginStyles = ( { hideLabel }: ContainerProps ) => { return hideLabel ? css( { margin: '0 !important' } ) : null; }; const containerWidthStyles = ( { __unstableInputWidth, labelPosition, }: ContainerProps ) => { if ( ! __unstableInputWidth ) return css( { width: '100%' } ); if ( labelPosition === 'side' ) return ''; if ( labelPosition === 'edge' ) { return css( { flex: `0 0 ${ __unstableInputWidth }`, } ); } return css( { width: __unstableInputWidth } ); }; export const Container = styled.div< ContainerProps >` align-items: center; box-sizing: border-box; border-radius: inherit; display: flex; flex: 1; position: relative; ${ containerDisabledStyles } ${ containerMarginStyles } ${ containerWidthStyles } `; type Size = 'default' | 'small'; type InputProps = { disabled?: boolean; inputSize?: Size; isDragging?: boolean; dragCursor?: CSSProperties[ 'cursor' ]; }; const disabledStyles = ( { disabled }: InputProps ) => { if ( ! disabled ) return ''; return css( { color: COLORS.ui.textDisabled, } ); }; const fontSizeStyles = ( { inputSize: size }: InputProps ) => { const sizes = { default: '13px', small: '11px', }; const fontSize = sizes[ size as Size ] || sizes.default; const fontSizeMobile = '16px'; if ( ! fontSize ) return ''; return css` font-size: ${ fontSizeMobile }; @media ( min-width: 600px ) { font-size: ${ fontSize }; } `; }; const sizeStyles = ( { inputSize: size }: InputProps ) => { const sizes = { default: { height: 30, lineHeight: 1, minHeight: 30, }, small: { height: 24, lineHeight: 1, minHeight: 24, }, }; const style = sizes[ size as Size ] || sizes.default; return css( style ); }; const dragStyles = ( { isDragging, dragCursor }: InputProps ) => { let defaultArrowStyles: SerializedStyles | undefined; let activeDragCursorStyles: SerializedStyles | undefined; if ( isDragging ) { defaultArrowStyles = css` cursor: ${ dragCursor }; user-select: none; &::-webkit-outer-spin-button, &::-webkit-inner-spin-button { -webkit-appearance: none !important; margin: 0 !important; } `; } if ( isDragging && dragCursor ) { activeDragCursorStyles = css` &:active { cursor: ${ dragCursor }; } `; } return css` ${ defaultArrowStyles } ${ activeDragCursorStyles } `; }; // TODO: Resolve need to use &&& to increase specificity // https://github.com/GeChiUI/gutenberg/issues/18483 export const Input = styled.input< InputProps >` &&& { background-color: transparent; box-sizing: border-box; border: none; box-shadow: none !important; color: ${ COLORS.black }; display: block; margin: 0; outline: none; padding-left: 8px; padding-right: 8px; width: 100%; ${ dragStyles } ${ disabledStyles } ${ fontSizeStyles } ${ sizeStyles } &::-webkit-input-placeholder { line-height: normal; } } `; const labelPadding = ( { labelPosition, }: { labelPosition?: LabelPosition; } ) => { let paddingBottom = 4; if ( labelPosition === 'edge' || labelPosition === 'side' ) { paddingBottom = 0; } return css( { paddingTop: 0, paddingBottom } ); }; const BaseLabel = styled( Text )< { labelPosition?: LabelPosition } >` &&& { box-sizing: border-box; color: currentColor; display: block; margin: 0; max-width: 100%; z-index: 1; ${ labelPadding } overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } `; export const Label = ( props: GeChiUIComponentProps< { labelPosition?: LabelPosition; children: ReactNode }, 'label', false > ) => ; export const LabelWrapper = styled( FlexItem )` max-width: calc( 100% - 10px ); `; type BackdropProps = { disabled?: boolean; isFocused?: boolean; }; const backdropFocusedStyles = ( { disabled, isFocused, }: BackdropProps ): SerializedStyles => { let borderColor = isFocused ? COLORS.ui.borderFocus : COLORS.ui.border; let boxShadow; if ( isFocused ) { boxShadow = `0 0 0 1px ${ COLORS.ui.borderFocus } inset`; } if ( disabled ) { borderColor = COLORS.ui.borderDisabled; } return css( { boxShadow, borderColor, borderStyle: 'solid', borderWidth: 1, } ); }; export const BackdropUI = styled.div< BackdropProps >` &&& { box-sizing: border-box; border-radius: inherit; bottom: 0; left: 0; margin: 0; padding: 0; pointer-events: none; position: absolute; right: 0; top: 0; ${ backdropFocusedStyles } ${ rtl( { paddingLeft: 2 } ) } } `; export const Prefix = styled.span` box-sizing: border-box; display: block; `; export const Suffix = styled.span` box-sizing: border-box; display: block; `;