import * as React from 'react' import * as PropTypes from 'prop-types' import styled from 'styled-components' import stringify from '../../services/stringify' import { Size, SizeMap } from '../../services/utilities' import Icon from '../Icon/Icon' const MIN_HEIGHTS: SizeMap = { small: '2.5rem', medium: '3rem', large: '4rem', } const LABEL_VERTICAL_PADDING_SIZES: SizeMap = { small: '0.125rem', medium: '0.25rem', large: '0.5rem', } const VERTICAL_PADDING_SIZES: SizeMap = { small: '0.6rem', medium: '0.85rem', large: '1.25rem', } const INPUT_FONT_SIZES: SizeMap = { small: '0.85em', medium: '0.85em', large: '1em', } const SECONDARY_TEXT_SIZES: SizeMap = { small: '0.65em', medium: '0.75em', large: '0.85em', } const ComponentBase = styled('div')({ position: 'relative', borderRadius: '0.25rem', fontFamily: 'var(--font-family-base, sans-serif)', maxWidth: '100%', ':focus-within': { '--color-accent': 'var(--color-active, Highlight)', }, }) ComponentBase.displayName = 'div' const CaptureArea = styled('label')({ display: 'block', }) CaptureArea.displayName = 'label' const LabelWrapper = styled('span')({ color: 'var(--color-accent, blue)', boxSizing: 'border-box', position: 'absolute', top: 0, left: 0, paddingLeft: '0.5rem', fontSize: '0.85em', maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontWeight: 'bolder', zIndex: 2, pointerEvents: 'none', transitionProperty: 'color', lineHeight: 1, userSelect: 'none', }) LabelWrapper.displayName = 'span' const Input = styled('select')({ display: 'block', backgroundColor: 'var(--color-bg, white)', color: 'var(--color-fg, black)', appearance: 'none', boxSizing: 'border-box', position: 'relative', border: 0, paddingLeft: '1rem', margin: 0, font: 'inherit', minHeight: '4rem', minWidth: '3rem', maxWidth: '100%', width: '100%', zIndex: 1, cursor: 'pointer', transitionProperty: 'background-color, color', ':focus': { outline: 0, }, ':disabled': { cursor: 'not-allowed', }, '::-moz-focus-inner': { outline: 0, border: 0, }, }) Input.displayName = 'select' const Border = styled('span')({ borderColor: 'var(--color-accent, blue)', boxSizing: 'border-box', display: 'inline-block', borderWidth: '0.125rem', borderStyle: 'solid', position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', borderRadius: 'inherit', zIndex: 2, pointerEvents: 'none', transitionProperty: 'border-color', '::before': { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', content: "''", borderRadius: '0.125rem', opacity: 0.5, pointerEvents: 'none', }, [`${ComponentBase}:focus-within &::before`]: { boxShadow: '0 0 0 0.375rem var(--color-accent, blue)', }, }) const HintWrapper = styled('span')({ boxSizing: 'border-box', position: 'absolute', bottom: 0, left: 0, paddingLeft: '1rem', fontSize: '0.85em', opacity: 0.5, maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', zIndex: 2, pointerEvents: 'none', lineHeight: 1, userSelect: 'none', }) const IndicatorWrapper = styled('span')({ color: 'var(--color-accent, blue)', boxSizing: 'border-box', position: 'absolute', top: 0, right: 0, height: '100%', display: 'grid', placeContent: 'center', padding: '0 1rem', zIndex: 1, pointerEvents: 'none', transitionProperty: 'color', lineHeight: 1, userSelect: 'none', }) const propTypes = { /** * Short textual description indicating the nature of the component's value. */ label: PropTypes.any, /** * Short textual description as guidelines for valid input values. */ hint: PropTypes.any, /** * Size of the component. */ size: PropTypes.oneOf(['small', 'medium', 'large']), /** * Can multiple values be selected? */ multiple: PropTypes.bool, /** * Is the component active? */ disabled: PropTypes.bool, /** * Name of the form field associated with this component. */ name: PropTypes.string, /** * Does the button display a border? */ border: PropTypes.bool, /** * Event handler triggered when the component changes value. */ onChange: PropTypes.func, /** * Event handler triggered when the component receives focus. */ onFocus: PropTypes.func, /** * Event handler triggered when the component loses focus. */ onBlur: PropTypes.func, } type Props = PropTypes.InferProps const Select = React.forwardRef( ( { label = '', hint = '', size = 'medium', multiple = false, disabled = false, name, children, border = false, onChange, onFocus, onBlur, ...etcProps }, ref, ) => { return ( {border && } {stringify(label)} {stringify(label).length > 0 && ' '} {children} {stringify(hint).length > 0 && ' '} {stringify(hint).length > 0 && ( ({stringify(hint)}) )} {!multiple && ( )} ) }, ) Select.propTypes = propTypes Select.displayName = 'Select' export default Select