vcc-ui
Version:
VCC UI is a collection of React UI Components that can be used for developing front-end applications at Volvo Car Corporation.
181 lines (172 loc) • 4.59 kB
JavaScript
import React from 'react';
import { useFela } from 'react-fela';
import { func, string, array, bool, node, object, oneOfType } from 'prop-types';
import { Block } from '../block';
import { Spinner } from '../spinner';
import { getThemeStyle } from '../../get-theme-style';
import { deprecate } from '../../deprecate';
const arrowColor = ({ theme, disabled }) =>
disabled ? theme.tokens.inputDisabledControl : theme.tokens.inputControl;
const styles = ({ loading, disabled, theme, isValid }) => ({
appearance: 'none',
color: '#333',
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 13 6' fill='${encodeURIComponent(
arrowColor({ theme, disabled })
)}' %3E %3Cpath d='M6.49987275 4.13397441L11.78483479.5 12.5 1.37426775 6.49986464 5.5.5 1.3743604l.7151756-.87426061z'%3E %3C/path%3E %3C/svg%3E")`,
backgroundSize: '15px 15px',
backgroundColor: theme.tokens.inputBackground,
backgroundPosition: 'right 18px center',
backgroundRepeat: 'no-repeat',
userSelect: 'none',
overflow: 'hidden',
margin: '0',
padding: `${theme.tokens.inputPaddingVertical}px ${theme.tokens.inputPaddingHorizontal}px`,
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%',
boxSizing: 'border-box',
borderRadius: 0,
borderWidth: '1px',
borderStyle: 'solid',
borderColor: isValid
? theme.tokens.inputBorder
: theme.color.foreground.alert,
display: 'block',
':disabled': {
color: theme.tokens.inputDisabledForeground,
},
'& option:disabled': {
color: theme.tokens.inputDisabledForeground,
},
':invalid': {
color: theme.tokens.inputPlaceholder,
},
':focus:invalid': {
color: theme.tokens.inputPlaceholder,
},
'::-ms-expand': {
display: 'none',
},
'::-ms-value': {
background: 'none',
color: theme.tokens.inputForeground,
},
':valid': {
color: theme.tokens.inputForeground,
},
'& option': {
color: theme.tokens.inputForeground,
},
':-moz-focusring': {
color: 'transparent',
textShadow: '0 0 0 #000',
},
outline: 0,
cursor: disabled ? 'not-allowed' : 'pointer',
':focus': {
borderColor: theme.tokens.inputBorderFocus,
},
extend: [
{
condition: loading,
style: {
backgroundImage: 'none',
display: 'flex',
justifyContent: 'center',
width: '100%',
},
},
],
});
export const SelectInput = React.forwardRef(
(
{
children,
value,
placeholder,
disabled,
options,
isValid = true,
loading = false,
...props
},
ref
) => {
const { theme } = useFela();
const renderOptions = options =>
options.map(option => (
<option
key={option.value}
value={option.value}
disabled={option.disabled}
>
{option.label}
</option>
));
const renderOptGroups = options => {
return Object.keys(options).map(name => (
<optgroup label={name} key={name}>
{renderOptions(options[name])}
</optgroup>
));
};
// TODO: remove in 2.0.0
deprecate(
'The `options` prop on has been deprecated in favor of passing children. It will be removed with version 2.0.0.',
!!options
);
if (loading) {
return (
<Block
ref={ref}
extend={[
styles({ loading, disabled, theme, isValid }),
getThemeStyle('selectInput', theme, {
disabled,
}),
]}
>
<Spinner color={theme.tokens.inputForeground} size={27} />
</Block>
);
}
return (
<Block
ref={ref}
as="select"
extend={[
styles({ loading, disabled, theme, isValid }),
getThemeStyle('selectInput', theme, {
disabled,
}),
]}
value={value || (placeholder && '')}
required={true}
disabled={disabled}
{...props}
>
{placeholder && (
<Block as="option" value={''} disabled hidden>
{placeholder}
</Block>
)}
{children
? children
: Array.isArray(options)
? renderOptions(options)
: renderOptGroups(options)}
</Block>
);
}
);
SelectInput.propTypes = {
onChange: func.isRequired,
options: oneOfType([array, object]),
placeholder: string,
value: string,
disabled: bool,
loading: bool,
children: node,
isValid: bool,
};
SelectInput.displayName = 'SelectInput';