Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions src/components/text-field-v2/TextFieldV2.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,51 @@ import type { UseOutlineBaseProps } from "~/components/use-outline";
export const getLabelProps = ({
variant,
isFocused,
value,
isLabelFloated,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а чем отличается value от isLabelFloated?

@AnastasiyaHaidanovich AnastasiyaHaidanovich Apr 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в хелпере потом value использовалось вместе с isFocused isFocused || !!value для определения стилей, к этому условию добавилось const isLabelFloated = isFocused || !!inputProps.value || alwaysShowMask; для отображения состояния поднятого лейбла и это условие вынесла в сам компонент чтобы было единообразно с компонентом TextField

}: {
variant: TextFieldV2Variant;
isFocused: boolean;
value: string | number | ReadonlyArray<string> | undefined;
isLabelFloated: boolean;
}): TypographyProps => {
const hasValue = isFocused || !!value;

const variants = {
primary: {
as: "label",
position: {
type: "absolute",
left: "32px",
top: hasValue ? "6px" : "16px",
top: isLabelFloated ? "6px" : "16px",
},
opacity: hasValue ? 50 : undefined,
opacity: isLabelFloated ? 50 : undefined,
color: "text-secondary-default",
fontSize: hasValue ? "body.mobile.xsmall" : "body.mobile.large",
fontSize: isLabelFloated ? "body.mobile.xsmall" : "body.mobile.large",
md: {
fontSize: hasValue ? "body.desktop.xsmall" : "body.desktop.large",
lineHeight: hasValue ? "body.desktop.xsmall" : "body.desktop.large",
fontSize: isLabelFloated ? "body.desktop.xsmall" : "body.desktop.large",
lineHeight: isLabelFloated
? "body.desktop.xsmall"
: "body.desktop.large",
},
lineHeight: hasValue ? "body.mobile.small" : "body.mobile.large",
lineHeight: isLabelFloated ? "body.mobile.small" : "body.mobile.large",
},

secondary: {
as: "label",
position: {
type: "absolute",
left: "12px",
top: hasValue ? "7px" : "15px",
top: isLabelFloated ? "7px" : "15px",
},
opacity: hasValue ? 50 : undefined,
opacity: isLabelFloated ? 50 : undefined,
color: isFocused ? "text-selection" : "text-secondary-default",
fontSize: hasValue ? "body.mobile.xsmall" : "body.mobile.medium",
fontSize: isLabelFloated ? "body.mobile.xsmall" : "body.mobile.medium",
md: {
fontSize: hasValue ? "body.desktop.xsmall" : "body.desktop.medium",
lineHeight: hasValue ? "body.desktop.xsmall" : "body.desktop.medium",
fontSize: isLabelFloated
? "body.desktop.xsmall"
: "body.desktop.medium",
lineHeight: isLabelFloated
? "body.desktop.xsmall"
: "body.desktop.medium",
},
lineHeight: hasValue ? "body.mobile.small" : "body.mobile.medium",
lineHeight: isLabelFloated ? "body.mobile.small" : "body.mobile.medium",
},
} as const;

Expand Down
7 changes: 7 additions & 0 deletions src/components/text-field-v2/TextFieldV2.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,13 @@ export const Mask: Story = {
customInput={<InputMask mask="+7 (999) 999-99-99" maskChar="_" />}
/>

<TextFieldV2
{...args}
alwaysShowMask
label={"Phone with alwaysShowMask"}
customInput={<InputMask mask="+5 (999) 999-99-99" maskChar="_" />}
/>

<TextFieldV2
{...args}
label={"Card"}
Expand Down
10 changes: 8 additions & 2 deletions src/components/text-field-v2/TextFieldV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ export interface TextFieldV2Props
| ((args: {
isFocused: boolean;
value?: string | ReadonlyArray<string> | number;
alwaysShowMask?: boolean;
}) => ReactNode);
icon?: (args: { isDisabled?: boolean }) => ReactNode;
errorMessage?: string | ReactNode;
className?: string;
customInput?: ReactElement;
variant?: TextFieldV2Variant;
alwaysShowMask?: boolean;
}
type Props = TextFieldV2Props & Partial<BoxProps>;

Expand All @@ -53,6 +55,7 @@ export const TextFieldV2 = forwardRef<HTMLInputElement, Props>((props, ref) => {
onChange: _onChange,
spellCheck: _spellCheck,
variant = "primary",
alwaysShowMask = false,
...rest
} = props;
const [isFocused, setIsFocused] = useState(false);
Expand All @@ -69,6 +72,8 @@ export const TextFieldV2 = forwardRef<HTMLInputElement, Props>((props, ref) => {
ref ?? inputRef,
);

const isLabelFloated = isFocused || !!inputProps.value || alwaysShowMask;

const spacing = useSpacing({
m: {
p: rest.p,
Expand Down Expand Up @@ -145,7 +150,7 @@ export const TextFieldV2 = forwardRef<HTMLInputElement, Props>((props, ref) => {

const Label =
label && typeof label !== "string" ? (
label({ isFocused, value: inputProps.value })
label({ isFocused, value: inputProps.value, alwaysShowMask })
) : (
<></>
);
Expand All @@ -162,7 +167,7 @@ export const TextFieldV2 = forwardRef<HTMLInputElement, Props>((props, ref) => {
{...getLabelProps({
variant,
isFocused,
value: inputProps.value,
isLabelFloated,
})}
>
{label}
Expand All @@ -176,6 +181,7 @@ export const TextFieldV2 = forwardRef<HTMLInputElement, Props>((props, ref) => {
...rest,
...inputProps,
ref,
alwaysShowMask,
onClick: inputProps.onClick,
className: `${border.className} ${background.className} ${spacing.className} ${dimension.className} ${typography.classNames} ${outline.className} ${className}`,
style: {
Expand Down
12 changes: 10 additions & 2 deletions src/components/text-field/TextField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,20 @@ export const Error: Story = {

export const Mask: Story = {
args: {
label: "Test mask",
mask: "+\\91 9999999999",
maskChar: "_",
m: { mb: 10 },
},
render: (args) => {
return <TextField {...args} />;
return (
<>
<TextField {...args} label={"Test mask"} />
<TextField
{...args}
alwaysShowMask
label={"Test mask with alwaysShowMask"}
/>
</>
);
},
};
25 changes: 10 additions & 15 deletions src/components/text-field/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface TextFieldProps extends AriaTextFieldOptions<"input"> {
*/
mask?: string;
maskChar?: string;
alwaysShowMask?: boolean;
}
type Props = TextFieldProps & Partial<BoxProps>;
/**
Expand All @@ -32,6 +33,7 @@ export const TextField = forwardRef<HTMLInputElement, Props>((props, ref) => {
className,
mask,
maskChar,
alwaysShowMask = false,
...indentationProps
} = props;
const [isFocused, setIsFocused] = useState(false);
Expand All @@ -44,6 +46,8 @@ export const TextField = forwardRef<HTMLInputElement, Props>((props, ref) => {
ref ?? inputRef,
);

const isLabelFloated = isFocused || !!inputProps.value || alwaysShowMask;

const InputComponent = mask ? InputMask : "input";

// border
Expand Down Expand Up @@ -96,27 +100,17 @@ export const TextField = forwardRef<HTMLInputElement, Props>((props, ref) => {
>
<Typography
as="label"
className={`pointer-events-none transition-all -translate-y-1/2 ${(isFocused || !!inputProps.value) && "opacity-50 translate-y-0"} `}
className={`pointer-events-none transition-all -translate-y-1/2 ${isLabelFloated && "opacity-50 translate-y-0"} `}
position={{
type: "absolute",
left: "30px",
top: isFocused || !!inputProps.value ? "10px" : "50%",
top: isLabelFloated ? "10px" : "50%",
}}
color={
isFocused || !!inputProps.value
? "text-secondary-hovered"
: "text-secondary-default"
}
fontSize={
isFocused || !!inputProps.value
? "body.mobile.small"
: "body.mobile.large"
}
lineHeight={
isFocused || !!inputProps.value
? "body.mobile.small"
: "body.mobile.large"
isLabelFloated ? "text-secondary-hovered" : "text-secondary-default"
}
fontSize={isLabelFloated ? "body.mobile.small" : "body.mobile.large"}
lineHeight={isLabelFloated ? "body.mobile.small" : "body.mobile.large"}
>
{label}
</Typography>
Expand All @@ -127,6 +121,7 @@ export const TextField = forwardRef<HTMLInputElement, Props>((props, ref) => {
// @ts-ignore
mask={mask}
maskChar={maskChar}
alwaysShowMask={alwaysShowMask}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не хватает примера в сторибуке

ref={ref}
inputRef={ref}
name={name}
Expand Down
Loading