commit
fefa0ab2e6
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
Input Styles
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import 'src/style/modules/influx-colors';
|
||||||
|
@import 'src/style/modules/variables';
|
||||||
|
@import 'src/style/modules/mixins';
|
||||||
|
|
||||||
|
$input-bg: $g2-kevlar;
|
||||||
|
$input-disabled-bg: $g3-castle;
|
||||||
|
|
||||||
|
$input-text: $g15-platinum;
|
||||||
|
$input-border: $g5-pepper;
|
||||||
|
$input-hover-text: $g17-whisper;
|
||||||
|
$input-hover-border: $g7-graphite;
|
||||||
|
$input-focus-text: $g20-white;
|
||||||
|
$input-focus-border: $c-pool;
|
||||||
|
$input-disabled-text: $g9-mountain;
|
||||||
|
|
||||||
|
$input-field-z: 1;
|
||||||
|
$input-shadow-z: $input-field-z + 1;
|
||||||
|
$input-status-z: $input-field-z + 2;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
position: relative;
|
||||||
|
z-index: $input-field-z;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: $ix-text-font;
|
||||||
|
transition:
|
||||||
|
background-color 0.25s ease,
|
||||||
|
border-color 0.25s ease,
|
||||||
|
box-shadow 0.25s ease,
|
||||||
|
color 0.25s ease;
|
||||||
|
outline: none;
|
||||||
|
appearance: none;
|
||||||
|
border-radius: $radius;
|
||||||
|
background-color: $input-bg;
|
||||||
|
color: $input-text;
|
||||||
|
border: $ix-border solid $input-border;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $input-hover-border;
|
||||||
|
color: $input-hover-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
color: $input-focus-text;
|
||||||
|
border-color: $input-focus-border;
|
||||||
|
box-shadow: 0 0 6px 0 $input-focus-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled],
|
||||||
|
&[disabled]:hover {
|
||||||
|
cursor: default;
|
||||||
|
border-color: $input-border;
|
||||||
|
background-color: $input-disabled-bg;
|
||||||
|
color: $input-disabled-text;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-input-placeholder {
|
||||||
|
color: $input-disabled-text;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
&::-moz-placeholder {
|
||||||
|
color: $input-disabled-text;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
&:-ms-input-placeholder {
|
||||||
|
color: $input-disabled-text;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
&:-moz-placeholder {
|
||||||
|
color: $input-disabled-text;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Input Icons (Including Status)
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.input-icon,
|
||||||
|
.input-status {
|
||||||
|
pointer-events: none;
|
||||||
|
top: 50%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: $input-status-z;
|
||||||
|
transition: color 0.25s ease;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-status {
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: $input-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field:hover + .input-icon {
|
||||||
|
color: $input-hover-text
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field:focus + .input-icon {
|
||||||
|
color: $input-focus-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input--disabled .input-icon,
|
||||||
|
.input--disabled .input-field:hover + .input-icon {
|
||||||
|
color: $input-disabled-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-shadow {
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
z-index: $input-shadow-z;
|
||||||
|
height: calc(100% - #{$ix-border * 2});
|
||||||
|
top: $ix-border;
|
||||||
|
border-radius: 0 3px 3px 0;
|
||||||
|
@include gradient-h(rgba($input-bg, 0), $input-bg);
|
||||||
|
border-style: solid;
|
||||||
|
border-color: $input-bg;
|
||||||
|
border-width: 0;
|
||||||
|
transition: opacity 0.25s ease;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-status + .input-shadow {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Size Modifiers
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
@mixin inputSizeModifier($fontSize, $padding, $height) {
|
||||||
|
height: $height;
|
||||||
|
font-size: $fontSize;
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
font-size: $fontSize;
|
||||||
|
padding: 0 $padding;
|
||||||
|
height: $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.input--has-icon .input-field {
|
||||||
|
padding-left: $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
left: ($height / 2) + $ix-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-status {
|
||||||
|
right: $height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-shadow {
|
||||||
|
width: $height;
|
||||||
|
right: $padding;
|
||||||
|
border-right-width: $padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-spinner {
|
||||||
|
width: $height / 2;
|
||||||
|
height: $height / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-xs {
|
||||||
|
@include inputSizeModifier($form-xs-font, $form-xs-padding, $form-xs-height);
|
||||||
|
}
|
||||||
|
.input-sm {
|
||||||
|
@include inputSizeModifier($form-sm-font, $form-sm-padding, $form-sm-height);
|
||||||
|
}
|
||||||
|
.input-md {
|
||||||
|
@include inputSizeModifier($form-md-font, $form-md-padding, $form-md-height);
|
||||||
|
}
|
||||||
|
.input-lg {
|
||||||
|
@include inputSizeModifier($form-lg-font, $form-lg-padding, $form-lg-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Valid State
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.input--valid {
|
||||||
|
.input-status {
|
||||||
|
color: $c-rainforest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Error State
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.input--error {
|
||||||
|
.input-status {
|
||||||
|
color: $c-dreamsicle;
|
||||||
|
}
|
||||||
|
.input-field {
|
||||||
|
border-color: $c-curacao
|
||||||
|
}
|
||||||
|
.input-field:hover {
|
||||||
|
border-color: $c-dreamsicle;
|
||||||
|
}
|
||||||
|
.input-field:focus {
|
||||||
|
border-color: $c-dreamsicle;
|
||||||
|
box-shadow: 0 0 6px 0 $c-fire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Loading State
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.input--loading {
|
||||||
|
.input-status {
|
||||||
|
color: $c-pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes LoadingSpinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-spinner {
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation-duration: 0.85s;
|
||||||
|
animation-name: LoadingSpinner;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
border: $ix-border solid $g5-pepper;
|
||||||
|
border-top-color: $c-pool;
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {
|
||||||
|
Component,
|
||||||
|
CSSProperties,
|
||||||
|
ChangeEvent,
|
||||||
|
KeyboardEvent,
|
||||||
|
} from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {ComponentStatus, ComponentSize, IconFont} from 'src/reusable_ui/types'
|
||||||
|
|
||||||
|
// Styles
|
||||||
|
import './Input.scss'
|
||||||
|
|
||||||
|
export enum InputType {
|
||||||
|
Text = 'text',
|
||||||
|
Number = 'number',
|
||||||
|
Password = 'password',
|
||||||
|
Email = 'email',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value?: string
|
||||||
|
placeholder?: string
|
||||||
|
onChange?: (e: ChangeEvent<HTMLInputElement>) => void
|
||||||
|
onBlur?: () => void
|
||||||
|
onFocus?: () => void
|
||||||
|
onKeyPress?: (e: KeyboardEvent<HTMLInputElement>) => void
|
||||||
|
onKeyUp?: (e: KeyboardEvent<HTMLInputElement>) => void
|
||||||
|
onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void
|
||||||
|
size?: ComponentSize
|
||||||
|
icon?: IconFont
|
||||||
|
status?: ComponentStatus
|
||||||
|
autoFocus?: boolean
|
||||||
|
spellCheck?: boolean
|
||||||
|
type?: InputType
|
||||||
|
widthPixels?: number
|
||||||
|
titleText?: string
|
||||||
|
disabledTitleText?: string
|
||||||
|
customClass?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class Input extends Component<Props> {
|
||||||
|
public static defaultProps: Partial<Props> = {
|
||||||
|
value: '',
|
||||||
|
placeholder: '',
|
||||||
|
titleText: '',
|
||||||
|
disabledTitleText: 'This input is disabled',
|
||||||
|
size: ComponentSize.Small,
|
||||||
|
status: ComponentStatus.Default,
|
||||||
|
autoFocus: false,
|
||||||
|
spellCheck: false,
|
||||||
|
type: InputType.Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {
|
||||||
|
status,
|
||||||
|
type,
|
||||||
|
value,
|
||||||
|
placeholder,
|
||||||
|
autoFocus,
|
||||||
|
spellCheck,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
onFocus,
|
||||||
|
onKeyPress,
|
||||||
|
onKeyUp,
|
||||||
|
onKeyDown,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={this.className} style={this.containerStyle}>
|
||||||
|
<input
|
||||||
|
title={this.title}
|
||||||
|
type={type}
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
autoFocus={autoFocus}
|
||||||
|
spellCheck={spellCheck}
|
||||||
|
onChange={onChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onKeyPress={onKeyPress}
|
||||||
|
onKeyUp={onKeyUp}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
className="input-field"
|
||||||
|
disabled={status === ComponentStatus.Disabled}
|
||||||
|
/>
|
||||||
|
{this.icon}
|
||||||
|
{this.statusIndicator}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get icon(): JSX.Element {
|
||||||
|
const {icon} = this.props
|
||||||
|
|
||||||
|
if (icon) {
|
||||||
|
return <span className={`input-icon icon ${icon}`} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private get title(): string {
|
||||||
|
const {titleText, disabledTitleText, status} = this.props
|
||||||
|
|
||||||
|
if (status === ComponentStatus.Disabled) {
|
||||||
|
return disabledTitleText
|
||||||
|
}
|
||||||
|
|
||||||
|
return titleText
|
||||||
|
}
|
||||||
|
|
||||||
|
private get statusIndicator(): JSX.Element {
|
||||||
|
const {status} = this.props
|
||||||
|
|
||||||
|
if (status === ComponentStatus.Loading) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="input-status">
|
||||||
|
<div className="input-spinner" />
|
||||||
|
</div>
|
||||||
|
<div className="input-shadow" />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === ComponentStatus.Error) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className={`input-status icon ${IconFont.AlertTriangle}`} />
|
||||||
|
<div className="input-shadow" />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === ComponentStatus.Valid) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className={`input-status icon ${IconFont.Checkmark}`} />
|
||||||
|
<div className="input-shadow" />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="input-shadow" />
|
||||||
|
}
|
||||||
|
|
||||||
|
private get className(): string {
|
||||||
|
const {size, status, icon, customClass} = this.props
|
||||||
|
|
||||||
|
return classnames('input', {
|
||||||
|
[`input-${size}`]: size,
|
||||||
|
'input--has-icon': icon,
|
||||||
|
'input--valid': status === ComponentStatus.Valid,
|
||||||
|
'input--error': status === ComponentStatus.Error,
|
||||||
|
'input--loading': status === ComponentStatus.Loading,
|
||||||
|
'input--disabled': status === ComponentStatus.Disabled,
|
||||||
|
[`${customClass}`]: customClass,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private get containerStyle(): CSSProperties {
|
||||||
|
const {widthPixels} = this.props
|
||||||
|
|
||||||
|
if (widthPixels) {
|
||||||
|
return {width: `${widthPixels}px`}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {width: '100%'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Input
|
|
@ -16,10 +16,10 @@ export enum ComponentSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ComponentStatus {
|
export enum ComponentStatus {
|
||||||
Default = '',
|
Default = 'default',
|
||||||
Loading = 'loading',
|
Loading = 'loading',
|
||||||
Error = 'error',
|
Error = 'error',
|
||||||
Valiid = 'valid',
|
Valid = 'valid',
|
||||||
Disabled = 'disabled',
|
Disabled = 'disabled',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue