UNPKG

@kiwicom/orbit-components

Version:

<div align="center"> <a href="https://orbit.kiwi" target="_blank"> <img alt="orbit-components" src="https://orbit.kiwi/wp-content/uploads/2018/08/orbit-components.png" srcset="https://orbit.kiwi/wp-content/uploads/2018/08/orbit-components@2x.png 2x"

244 lines (212 loc) 6.41 kB
// @flow import * as React from "react"; import styled from "styled-components"; import defaultTokens from "../defaultTokens"; import FormLabel from "../FormLabel"; import { FakeInput, Input, InputContainer } from "../InputField"; import { SelectContainer } from "../Select"; import FormFeedback from "../FormFeedback"; import { SIZE_OPTIONS, TOKENS } from "./consts"; import type { Props, State } from "./index"; const getToken = name => ({ theme, size }) => { const tokens = { [TOKENS.height]: { [SIZE_OPTIONS.SMALL]: theme.orbit.heightInputSmall, [SIZE_OPTIONS.NORMAL]: theme.orbit.heightInputNormal, }, [TOKENS.heightLine]: { [SIZE_OPTIONS.SMALL]: "16px", [SIZE_OPTIONS.NORMAL]: "24px", }, }; return tokens[name][size]; }; const FakeGroup = styled(({ children, className }) => <div className={className}>{children}</div>)` width: 100%; position: absolute; z-index: 1; box-sizing: border-box; height: ${getToken(TOKENS.height)}; border-radius: ${({ theme }) => theme.orbit.borderRadiusNormal}; box-shadow: ${({ theme }) => `inset 0 0 0 ${theme.orbit.borderWidthInput} ${theme.orbit.borderColorInput}`}; // Normal state box-shadow: ${({ theme, error }) => error && `inset 0 0 0 ${theme.orbit.borderWidthInput} ${ theme.orbit.borderColorInputError }`}; // Error state box-shadow: ${({ theme, active }) => active && `inset 0 0 0 ${theme.orbit.borderWidthInputFocus} ${ theme.orbit.borderColorInputFocus }`}; // Active state background-color: ${({ disabled, theme }) => disabled ? theme.orbit.backgroundInputDisabled : theme.orbit.backgroundInput}; font-size: ${({ theme }) => theme.orbit.fontSizeInputNormal}; transition: box-shadow ${({ theme }) => theme.orbit.durationFast} ease-in-out; margin-top: 23px; &:hover { box-shadow: inset 0 0 0 ${({ theme, error }) => `${theme.orbit.borderWidthInput} ${ error ? theme.orbit.borderColorInputErrorHover : theme.orbit.borderColorInputHover }`}; } `; FakeGroup.defaultProps = { theme: defaultTokens, }; const StyledChildren = styled.div` display: flex; position: relative; `; const StyledChild = styled.div` flex: ${({ flex }) => flex}; padding-right: ${({ theme }) => theme.orbit.spaceXSmall}; :last-child { padding-right: 0; } `; StyledChild.defaultProps = { theme: defaultTokens, }; const StyledInputGroup = styled(({ children, className, dataTest }) => ( <div className={className} data-test={dataTest}> {children} </div> ))` display: flex; flex-direction: column; position: relative; ${StyledChild} { ${FakeInput} { box-shadow: none; background-color: transparent; display: flex; align-items: center; justify-content: flex-end; } ${SelectContainer} { background-color: transparent; > select { box-shadow: none; background-color: transparent; &:focus { box-shadow: none; } } } ${InputContainer}:after, ${SelectContainer}:after { content: " "; position: absolute; right: 0; height: ${getToken(TOKENS.heightLine)}; width: 1px; background-color: ${({ theme, error, active }) => error && !active ? theme.orbit.borderColorInputError : theme.orbit.borderColorInput}; transition: background-color ${({ theme }) => theme.orbit.durationFast} ease-in-out; display: block; z-index: 2; } &:last-child { ${InputContainer}:after, ${SelectContainer}:after { content: none; } } } ${StyledChild} ${FormLabel} { display: ${({ label }) => label && "none"}; } ${Input}:focus ~ ${FakeInput} { box-shadow: none; } `; StyledInputGroup.defaultProps = { theme: defaultTokens, }; class InputGroup extends React.PureComponent<Props, State> { state = { active: false, filled: false, }; componentDidMount() { this.isFilled(); } componentDidUpdate() { this.isFilled(); } isFilled = () => this.setState({ filled: !React.Children.map( this.props.children, child => child.props.value !== undefined && child.props.value !== "", ).includes(false), }); handleFocus = (ev: SyntheticInputEvent<HTMLInputElement>) => { const { onFocus } = this.props; this.setState({ active: true }); if (onFocus) { onFocus(ev); } }; handleBlur = (ev: SyntheticInputEvent<HTMLInputElement>) => { const { onBlur } = this.props; this.isFilled(); this.setState({ active: false }); if (onBlur) { onBlur(ev); } }; handleChange = (ev: SyntheticInputEvent<HTMLInputElement>) => { const { onChange } = this.props; this.isFilled(); if (onChange) { onChange(ev); } }; render() { const { children, label, flex = "0 1 auto", size = SIZE_OPTIONS.NORMAL, help, error, dataTest, } = this.props; const { active, filled } = this.state; return ( <StyledInputGroup label={label} error={error} active={active} size={size} dataTest={dataTest}> {label && <FormLabel filled={filled}>{label}</FormLabel>} <StyledChildren> {React.Children.map(children, (item, key) => { // either array, array with one length or string // if it's not defined, use the first or string const childFlex = Array.isArray(flex) && flex.length !== 1 ? flex[key] || flex[0] : flex; return ( <StyledChild flex={childFlex}> { <item.type {...item.props} size={size} label={undefined} help={undefined} error={undefined} onChange={this.handleChange} onBlur={this.handleBlur} onFocus={this.handleFocus} /> } </StyledChild> ); })} </StyledChildren> <FakeGroup label={label} error={error} active={active} size={size} /> {!error && help && <FormFeedback type="help">{help}</FormFeedback>} {error && <FormFeedback type="error">{error}</FormFeedback>} </StyledInputGroup> ); } } export default InputGroup;