Skip to content

Commit

Permalink
feat(base-input): add base-input
Browse files Browse the repository at this point in the history
  • Loading branch information
reme3d2y committed Mar 3, 2020
1 parent fc2d810 commit 55ecad6
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 0 deletions.
130 changes: 130 additions & 0 deletions packages/base-input/src/Component.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
@import '../../themes/default.css';

:root {
--base-input-font-family: var(--font-family);

--base-input-color: var(--color-dark-indigo);
--base-input-disabled-color: var(--color-dark-indigo-60-flat);
--base-input-disabled-bg-color: var(--color-dark-indigo-15);
--base-input-bg-color: var(--color-dark-indigo-07);
--base-input-hover-bg-color: var(--color-dark-indigo-15);
--base-input-placeholder-color: var(--color-dark-indigo-60-flat);
--base-input-border-color: var(--color-dark-indigo-30-flat);
--base-input-focus-border-color: var(--color-dark-indigo);
--base-input-disabled-border-color: var(--color-dark-indigo-30-flat);
--base-input-border-radius: 4px 4px 0 0;

--base-input-side-paddings: 12px;
}

.component {
max-width: 310px;
font-family: var(--base-input-font-family);
box-sizing: border-box;
}

.component:not(.disabled) .inner:hover {
background-color: var(--base-input-hover-bg-color);
}

.disabled.focused .inner {
border-bottom-color: var(--base-input-focus-border-color);
box-shadow: inset 0 -1px var(--base-input-focus-border-color);
}

.disabled .inner {
border-bottom: 1px dashed var(--base-input-disabled-border-color);
background-color: var(--base-input-disabled-bg-color);
}

.disabled .input {
color: var(--base-input-disabled-color);
}

.inner {
display: flex;
position: relative;
background-color: var(--base-input-bg-color);
border-radius: var(--base-input-border-radius);
border-bottom: 1px solid var(--base-input-border-color);
transition: background 0.2s ease, box-shadow 0.2s ease, border 0.2s ease;
}

.input {
position: relative;
color: var(--base-input-color);
background: none;
border: none;
margin: 0;
outline: none;
transition: background 0.2s ease;
flex-grow: 1;
font-size: 16px;
line-height: 20px;

-webkit-appearance: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

.input::placeholder {
text-overflow: ellipsis;
transition: opacity .2s ease-in-out, color .2s ease-in-out;
color: var(--base-input-placeholder-color);
}

.label {
overflow: hidden;
position: absolute;
display: flex;
align-items: center;
height: 100%;
left: var(--base-input-side-paddings);
color: var(--base-input-placeholder-color);
font-size: 16px;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
transform: scale(1) translateY(0);
transform-origin: 0 100%;
transition-duration: 200ms;
transition-property: color, transform, font-size;
transition-timing-function: cubic-bezier(.25, .1, .25, 1);
}

.s {
padding: 14px var(--base-input-side-paddings) 13px var(--base-input-side-paddings);
}

.m {
padding: 18px var(--base-input-side-paddings) 17px var(--base-input-side-paddings);
}

.l {
padding: 24px var(--base-input-side-paddings) 23px var(--base-input-side-paddings);
}

.has-label .input::placeholder {
opacity: 0;
}

.has-label.focused .input::placeholder {
opacity: 1;;
}

.focused .label, .filled .label {
font-size: 14px;
line-height: 18px;
transform: scale(1) translateY(-10px);
}

.has-label .s {
padding: 23px var(--base-input-side-paddings) 4px var(--base-input-side-paddings);
}

.has-label .m {
padding: 28px var(--base-input-side-paddings) 7px var(--base-input-side-paddings);
}

.has-label .l {
padding: 34px var(--base-input-side-paddings) 13px var(--base-input-side-paddings);
}
43 changes: 43 additions & 0 deletions packages/base-input/src/Component.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Vendor
*/

import { withKnobs, select, text, boolean } from '@storybook/addon-knobs';
import React, { useState } from 'react';
import { withDesign } from 'storybook-addon-designs';
/**
* Components
*/
import { BaseInput } from './Component';

export default {
title: 'Common',
component: BaseInput,
decorators: [withDesign, withKnobs]
};

export const BaseInputStory = () => {
const [value, setValue] = useState('value');

return (
<BaseInput
size={ select('size', ['s', 'm', 'l'], 's') }
disabled={ boolean('Disabled', false) }
placeholder={ text('placeholder', '') }
label={ text('label', '') }
value={ value }
onChange={ (e: any) => setValue(e.target.value) }
/>
);
};

BaseInputStory.story = {
name: 'BaseInput',
parameters: {
design: {
type: 'figma',
// public link for testing
url: ''
}
}
};
12 changes: 12 additions & 0 deletions packages/base-input/src/Component.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Vendor
*/

// import React from 'react';
// import { render } from '@testing-library/react';

/**
* Exp
*/

// test('renders learn react link', () => {});
159 changes: 159 additions & 0 deletions packages/base-input/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* eslint-disable react/jsx-props-no-spreading */

/**
* Vendor
*/

import React, { useRef, useState } from 'react';
import cn from 'classnames';

/**
* Styles
*/

import styles from './Component.module.css';

/**
* Types
*/

export type BaseInputProps = {
/** Размер компонента */
size?: 's' | 'm' | 'l';
/** Класс компонента */
className?: string;
/** Класс компонента */
innerClassName?: string;
/** Класс компонента */
inputClassName?: string;
/** Значение поля */
value?: string;
/** Плейсхолдер */
placeholder?: string;
/** Лейбл компонента */
label?: React.ReactNode;
/** Атрибут disabled */
disabled?: boolean;
/** Атрибут required */
required?: boolean;
/** Слот слева от инпута */
leftAddons?: React.ReactNode;
/** Слот справа от инпута */
rightAddons?: React.ReactNode;
/** Слот для отображения контента снизу */
children?: React.ReactNode;
/** Обработчик фокуса инпута */
onFocus?: (event?: React.FocusEvent<HTMLInputElement>) => void;
/** Обработчик блюра инпута */
onBlur?: (event?: React.FocusEvent<HTMLInputElement>) => void;
/** */
onChange?: (event?: React.ChangeEvent<HTMLInputElement>) => void;
/** Id компонента для тестов */
dataTestId?: string;
};

/**
* Expo
*/

export const BaseInput: React.FC<BaseInputProps> = ({
size='s',
className,
innerClassName,
inputClassName,
value,
disabled,
required,
placeholder,
label,
leftAddons,
rightAddons,
children,
onFocus,
onBlur,
onChange,
dataTestId,
...restProps
}) => {
const inputRef = useRef(null);
const [focused, setFocused] = useState(false);
const [filled, setFilled] = useState(!!value);

const hasLabel = !!label;

function handleInputFocus(e: React.FocusEvent<HTMLInputElement>) {
setFocused(true);

if (onFocus) {
onFocus(e);
}
}

function handleInputBlur(e: React.FocusEvent<HTMLInputElement>) {
setFocused(false);

if (onBlur) {
onBlur(e);
}
}

function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
setFilled(!!e.target.value);

if (onChange) {
onChange(e);
}
}

return (
<div
className={ cn(
styles.component,
{
[styles.focused]: focused,
[styles.disabled]: disabled,
[styles.filled]: filled,
[styles.hasLabel]: hasLabel
},
className
) }
>
<div className={ cn(styles.inner, innerClassName) }>
{ label && (
<div className={ cn(styles.label) }>
{ label }
</div>
) }

{ leftAddons && (
<div className={ cn(styles.leftAddons) }>
{ leftAddons }
</div>
) }

<input
className={ cn(styles.input, styles[size], inputClassName) }
ref={ inputRef }
value={ value }
placeholder={ placeholder }
disabled={ disabled }
required={ required }
onChange={ handleInputChange }
onFocus={ handleInputFocus }
onBlur={ handleInputBlur }
data-test-id={ dataTestId }
// REVIEW: Думаю в данном кейсе это нормальное решение
{ ...restProps }
/>

{ rightAddons && (
<div className={ cn(styles.rightAddons) }>
{ rightAddons }
</div>
) }
</div>

{ children }
</div>
);
};
1 change: 1 addition & 0 deletions packages/base-input/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Component';
1 change: 1 addition & 0 deletions packages/themes/default.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import '../vars/colors.css';
@import '../vars/gaps.css';

:root {
--primary-color: var(--color-dark-indigo);
Expand Down

0 comments on commit 55ecad6

Please sign in to comment.