Skip to content

Commit

Permalink
feat: redesign input
Browse files Browse the repository at this point in the history
  • Loading branch information
sashtje committed Oct 11, 2023
1 parent 22e28a7 commit dca720d
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 73 deletions.
3 changes: 3 additions & 0 deletions src/shared/assets/icons/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 30 additions & 35 deletions src/shared/ui/redesigned/Input/ui/Input.module.scss
Original file line number Diff line number Diff line change
@@ -1,53 +1,48 @@
.inputWrapper {
display: flex;
align-items: center;
border-radius: 48px;
padding: 8px 16px;
border: 2px solid var(--icon-redesigned);
height: 38px;
width: 100%;
}

.addonLeft,
.addonRight {
display: flex;
}

.withAddonLeft {
padding-left: 8px;
}

.placeholder {
margin-right: 5px;
.withAddonRight {
padding-right: 8px;
}

.focused {
border: 2px solid var(--accent-redesigned);

.addonLeft svg,
.addonRight svg {
color: var(--accent-redesigned);
}
}

.input {
background: transparent;
border: none;
outline: none;
background: none;
width: 100%;
color: transparent;
color: var(--text-redesigned);
text-shadow: 0 0 0 var(--primary-color);

&:focus {
outline: none;
&::placeholder {
color: var(--hint-redesigned);
}
}

.caretWrapper {
flex-grow: 1;
position: relative;
}

.caret {
width: 9px;
height: 3px;
background: var(--primary-color);
bottom: 0;
left: 0;
position: absolute;
animation: blink 0.7s forwards infinite;
}

.readonly {
opacity: 0.7;
}

@keyframes blink {
0% {
opacity: 0;
}

50% {
opacity: 0.01;
}

100% {
opacity: 1;
}
}
61 changes: 24 additions & 37 deletions src/shared/ui/redesigned/Input/ui/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import React, {
InputHTMLAttributes,
memo,
SyntheticEvent,
useEffect,
useRef,
useState,
} from 'react';
import { InputHTMLAttributes, memo, ReactNode, useEffect, useRef, useState } from 'react';

import { classNames, Mods } from '@/shared/lib/classNames';

Expand All @@ -22,6 +15,8 @@ interface InputProps extends HTMLInputProps {
onChange?: (value: string) => void;
autofocus?: boolean;
readonly?: boolean;
addonLeft?: ReactNode;
addonRight?: ReactNode;
}

export const Input = memo((props: InputProps) => {
Expand All @@ -33,15 +28,14 @@ export const Input = memo((props: InputProps) => {
placeholder,
autofocus,
readonly,
addonLeft,
addonRight,
...otherProps
} = props;

const ref = useRef<HTMLInputElement>(null);

const [isFocused, setIsFocused] = useState(false);
const [caretPosition, setCaretPosition] = useState(0);

const isCaretVisible = isFocused && !readonly;

useEffect(() => {
if (autofocus) {
Expand All @@ -51,8 +45,6 @@ export const Input = memo((props: InputProps) => {

const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(e.target.value);

setCaretPosition(e.target.value.length);
};

const onBlur = () => {
Expand All @@ -63,36 +55,31 @@ export const Input = memo((props: InputProps) => {
setIsFocused(true);
};

const onSelect = (event: SyntheticEvent<HTMLInputElement, Event>) => {
setCaretPosition(event?.currentTarget?.selectionStart || 0);
};

const mods: Mods = {
[cls.readonly]: readonly,
[cls.focused]: isFocused,
[cls.withAddonLeft]: Boolean(addonLeft),
[cls.withAddonRight]: Boolean(addonRight),
};

return (
<div className={classNames(cls.inputWrapper, mods, [className])}>
{!!placeholder && <div className={cls.placeholder}>{`${placeholder}>`}</div>}

<div className={cls.caretWrapper}>
<input
ref={ref}
type={type}
value={value}
onChange={changeHandler}
onFocus={onFocus}
onBlur={onBlur}
onSelect={onSelect}
className={cls.input}
readOnly={readonly}
{...otherProps}
/>

{isCaretVisible && (
<span className={cls.caret} style={{ left: `${caretPosition * 9}px` }} />
)}
</div>
{!!addonLeft && <div className={cls.addonLeft}>{addonLeft}</div>}

<input
ref={ref}
type={type}
value={value}
onChange={changeHandler}
onFocus={onFocus}
onBlur={onBlur}
className={cls.input}
readOnly={readonly}
placeholder={placeholder}
{...otherProps}
/>

{!!addonRight && <div className={cls.addonRight}>{addonRight}</div>}
</div>
);
});
Expand Down
9 changes: 8 additions & 1 deletion src/widgets/ArticlesFilters/ArticlesFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ArticleTypeTabs } from '@/features/articleTypeTabs';
import { VStack } from '@/shared/ui/redesigned/Stack';
import { ArticleSortField, ArticleType } from '@/entities/Article';
import { SortOrder } from '@/shared/types/sort';
import SearchIcon from '@/shared/assets/icons/search.svg';
import { Icon } from '@/shared/ui/redesigned/Icon';

import cls from './ArticlesFilters.module.scss';

Expand Down Expand Up @@ -41,7 +43,12 @@ export const ArticlesFilters = memo((props: ArticlesFiltersProps) => {
return (
<Card className={classNames(cls.articlesFilters, {}, [className])} padding="24">
<VStack gap="32">
<Input placeholder={t('Поиск')} value={search} onChange={onChangeSearch} />
<Input
placeholder={t('Поиск')}
value={search}
onChange={onChangeSearch}
addonLeft={<Icon Svg={SearchIcon} />}
/>

<ArticleTypeTabs className={cls.tabs} value={type} onChangeType={onChangeType} />

Expand Down

0 comments on commit dca720d

Please sign in to comment.