feat: add input

This commit is contained in:
2025-06-03 12:48:49 +03:00
parent 88176e3546
commit cb799f8057
4 changed files with 182 additions and 26 deletions

View File

@@ -22,3 +22,4 @@ $color-darkgray: #999999;
$color-text: #333333; $color-text: #333333;
$color-text-light: #222222; $color-text-light: #222222;
$color-mark: #E96526; $color-mark: #E96526;
$color-error: #FF0000;

View File

@@ -1,14 +1,113 @@
.Container { .input {
position: relative;
display: flex; display: flex;
flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: flex-start;
padding: 15px 40px; width: 100%;
width: max-content; height: 42px;
border: 1px solid $color-darkgray; border: 1px solid $color-darkgray;
border-radius: 28px; border-radius: 28px;
background: $color-white;
//@include iftablet {
// height: rem(53px);
//}
&__self {
width: 100%;
height: 100%;
padding: 22px 16px 6px 16px;
font-size: 14px;
line-height: 100%;
color: $color-text;
opacity: 0.9;
//@include iftablet {
// padding: rem(26px) rem(16px) rem(6px) rem(16px);
// font-size: rem(16px);
// line-height: 130%;
//}
} }
.Input { &__placeholder {
position: absolute;
top: 12px;
left: 16px;
font-size: 14px;
line-height: 130%;
color: $color-darkgray;
opacity: 0.4;
transition: all 0.15s ease-in;
//@include iftablet {
// top: rem(16px);
// font-size: rem(16px);
//}
}
&__placeholder_active {
top: 6px;
left: 16px;
font-size: 11px;
color: $color-darkgray;
//@include iftablet {
// top: rem(6px);
// left: rem(16px);
// font-size: rem(14px);
//}
}
&--error {
border-color: $color-error !important;
}
&__errorMessage {
margin-top: 10px;
font-size: 12px;
line-height: 130%;
color: $color-error;
//@include iftablet {
// margin-top: 6px;
//}
}
&--disabled {
cursor: not-allowed;
color: $color-lightgray;
}
&__password-rules {
display: flex;
align-items: center;
margin-top: 6px;
gap: 6px;
//@include iftablet {
// gap: rem(10px);
//}
//@include ifdesktop {
// margin-top: rem(9px);
//}
&__rule-block {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
&__rule-text {
font-family: $font-open-sans;
font-size: 11px;
line-height: 100%;
color: rgba(35, 48, 56, 0.7);
//@include iftablet {
// font-size: rem(14px);
//}
}
}
} }

View File

@@ -1,22 +1,69 @@
'use client';
import s from './input.module.scss'; import s from './input.module.scss';
import { clsx } from 'clsx';
import { DetailedHTMLProps, InputHTMLAttributes } from 'react';
type InputProps = { import React, { InputHTMLAttributes, useState } from 'react';
outerClassName?: string; import clsx from 'clsx';
variant?: 'default' | 'outlined';
} & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
export default function input({ type InputPropsType = {
outerClassName, className?: string;
variant = 'default', placeholder: string;
errorMessage?: string | boolean;
} & InputHTMLAttributes<HTMLInputElement>;
const Input = ({
placeholder,
errorMessage,
disabled,
className,
onChange,
...props ...props
}: InputProps) { }: InputPropsType) => {
const [onFocus, setOnFocus] = useState(false);
const hasValue =
typeof props.value === 'string'
? props.value.length > 0
: props.value !== undefined && props.value !== null;
return ( return (
<div>
<div <div
className={clsx(s.Container, s['Container_' + variant], outerClassName)} className={clsx(s.input, className, {
[s['input--error']]: !!errorMessage,
})}
> >
<input {...props} className={clsx(s.Input, s['Input_' + variant])} /> <label
className={clsx(s.input__placeholder, {
[s.input__placeholder_active]: hasValue || onFocus,
})}
>
{placeholder}
</label>
<input
{...props}
onChange={onChange}
onFocus={(event) => {
if (typeof props.onFocus !== 'undefined') {
props.onFocus(event);
}
setOnFocus(true);
}}
onBlur={(event) => {
if (typeof props.onBlur !== 'undefined') {
props.onBlur(event);
}
setOnFocus(false);
}}
disabled={disabled}
className={clsx(s.input__self, { [s['input--disabled']]: disabled })}
/>
</div>
{errorMessage && (
<span className={s.input__errorMessage}>{errorMessage}</span>
)}
</div> </div>
); );
} };
export default Input;

View File

@@ -1,3 +1,4 @@
'use client';
import s from './offer.module.scss'; import s from './offer.module.scss';
import { Button, Mark, Input } from '@shared/ui'; import { Button, Mark, Input } from '@shared/ui';
@@ -11,8 +12,11 @@ import gridFour from '@public/images/grid-4.png';
import gridFive from '@public/images/grid-5.png'; import gridFive from '@public/images/grid-5.png';
import gridSix from '@public/images/grid-6.png'; import gridSix from '@public/images/grid-6.png';
import bgForm from '@public/images/bg-form.jpg'; import bgForm from '@public/images/bg-form.jpg';
import { useState } from 'react';
export default function Offer() { export default function Offer() {
const [name, setName] = useState('');
return ( return (
<section className={s.Offer}> <section className={s.Offer}>
<h2 className={s.Title}> <h2 className={s.Title}>
@@ -38,7 +42,12 @@ export default function Offer() {
</ul> </ul>
<div className={s.RowForm}> <div className={s.RowForm}>
<Input type='text' placeholder='+79991234567' /> <Input type='text' placeholder='+79991234567' />
<Input type='text' placeholder='Ваше имя' /> <Input
type='text'
placeholder=''
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Button>Узнать подробности</Button> <Button>Узнать подробности</Button>
</div> </div>
<div className={s.Inner}> <div className={s.Inner}>