diff --git a/utils/vara-ui/src/components/label-container/index.ts b/utils/vara-ui/src/components/label-container/index.ts new file mode 100644 index 000000000..64a4a00f2 --- /dev/null +++ b/utils/vara-ui/src/components/label-container/index.ts @@ -0,0 +1,5 @@ +import { LabelContainer, LabelContainerProps } from './label-container'; +import labelContainerStyles from './label-container.module.scss'; + +export { LabelContainer, labelContainerStyles }; +export type { LabelContainerProps }; diff --git a/utils/vara-ui/src/components/label-container/label-container.module.scss b/utils/vara-ui/src/components/label-container/label-container.module.scss new file mode 100644 index 000000000..138bd202e --- /dev/null +++ b/utils/vara-ui/src/components/label-container/label-container.module.scss @@ -0,0 +1,87 @@ +@use '../../utils.scss' as *; + +.container { + display: flex; + flex-direction: column; + gap: var(--gap); +} + +.label { + @include lightDark(color, #58696e, #828b8e); + + font-size: var(--label-font-size); + font-weight: 400; + line-height: 20px; +} + +.inputWrapper { + @include lightDark(border, 1px solid #d0d5dd, 1px solid rgba(255, 255, 255, 0.04)); + + display: flex; + gap: 4px; + + padding: var(--input-y-padding) 14px; + + border-radius: 4px; + box-shadow: 0px 1px 2px 0px #1018280d; + + &:focus-within { + @include lightDark(border-color, rgba(0, 255, 196, 0.6), rgba(0, 255, 196, 0.6)); + } + + &:has(:not(:disabled)[aria-invalid='true']) { + @include lightDark(border-color, rgba(255, 50, 49, 0.8), #d73b4f); + } + + &:has(:disabled) { + @include lightDark(background-color, #eceded, rgba(40, 44, 48, 0.1)); + } +} + +.error { + @include lightDark(color, #ff3231, #ff3757); + + padding: 0 14px; + + font-size: 12px; + font-weight: 400; + line-height: 21.6px; + + :has(:disabled) & { + @include lightDark(color, #58696e, #828b8e); + } +} + +.block { + width: 100%; +} + +.small { + --gap: 4px; + + --input-y-padding: 6px; + --input-font-size: 12px; + --input-line-height: 20px; + + --label-font-size: 12px; +} + +.medium { + --gap: 6px; + + --input-y-padding: 10px; + --input-font-size: 16px; + --input-line-height: 22px; + + --label-font-size: 14px; +} + +.large { + --gap: 6px; + + --input-y-padding: 15px; + --input-font-size: 16px; + --input-line-height: 22px; + + --label-font-size: 14px; +} diff --git a/utils/vara-ui/src/components/label-container/label-container.tsx b/utils/vara-ui/src/components/label-container/label-container.tsx new file mode 100644 index 000000000..3c8f708c7 --- /dev/null +++ b/utils/vara-ui/src/components/label-container/label-container.tsx @@ -0,0 +1,27 @@ +import { PropsWithChildren, ReactNode } from 'react'; +import cx from 'clsx'; + +import styles from './label-container.module.scss'; + +type Props = PropsWithChildren & { + size?: 'small' | 'medium' | 'large'; + label?: string; + error?: ReactNode; + block?: boolean; + className?: string; +}; + +function LabelContainer({ size = 'medium', label, className, block, children, error }: Props) { + return ( + + {label && {label}} + + {children} + + {error && {error}} + + ); +} + +export { LabelContainer }; +export type { Props as LabelContainerProps }; diff --git a/utils/vara-ui/src/components/textarea/helpers.ts b/utils/vara-ui/src/components/textarea/helpers.ts deleted file mode 100644 index 35dc6ef03..000000000 --- a/utils/vara-ui/src/components/textarea/helpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -const textareaSizes = ['default', 'medium', 'small'] as const; -type ITextareaSizes = (typeof textareaSizes)[number]; - -export { textareaSizes }; -export type { ITextareaSizes }; diff --git a/utils/vara-ui/src/components/textarea/textarea.module.scss b/utils/vara-ui/src/components/textarea/textarea.module.scss index 5ce509139..1f8c31547 100644 --- a/utils/vara-ui/src/components/textarea/textarea.module.scss +++ b/utils/vara-ui/src/components/textarea/textarea.module.scss @@ -1,253 +1,29 @@ -.root { - &.disabled { - cursor: not-allowed; - - * { - pointer-events: none; - } - - .fieldset { - background-color: #eceded; - border-color: #d0d5dd; - } - - .textarea:disabled { - cursor: not-allowed; - } - - .textarea, - .label { - color: #58696e80; - } - } -} - -.base { - position: relative; - user-select: none; -} +@use '../../utils.scss' as *; .textarea { - outline: none; - resize: none; - background-color: transparent; - border: none; - font: inherit; - - width: 100%; - padding-right: 14px; - padding-left: 14px; - - &::placeholder { - color: #58696e; - } - - &:not(:focus) { - &::placeholder { - color: transparent; - } - } - - &:focus, - &:not(:placeholder-shown), - &.error { - ~ .label { - opacity: 0; - } - - ~ .fieldset { - .legendLabel { - opacity: 1; - max-width: 100%; - padding: 0 4px; - } - } - } - - &:focus { - ~ .fieldset { - border-color: #53eece; - - .legendLabel { - color: #53eece; - } - } - } - - &.error { - ~ .label { - color: #ff3231cc; - } - - ~ .fieldset { - border-color: #ff3231cc; - - .legendLabel { - color: #ff3231cc; - } - } - } - - &.default { - padding-top: 14px; - padding-bottom: 14px; - } - - &.medium { - padding-top: 9px; - padding-bottom: 9px; - } - - &.small { - padding-top: 6px; - padding-bottom: 6px; - } -} - -.label { - pointer-events: none; - - position: absolute; - left: 13px; - - &.default { - top: 14px; - } - - &.medium { - top: 9px; - } + @include lightDark(color, #000, rgba(246, 246, 246, 0.9)); - &.small { - top: 6px; - } -} - -.textarea, -.label { - font-weight: 400; - line-height: 24px; - color: #000000; - - &.default { - font-size: 16px; - } - - &.medium { - font-size: 16px; - } - - &.small { - font-size: 14px; - line-height: 20px; - } -} - -.fieldset { - min-width: 0; - margin: 0; - padding: 0 13px; - pointer-events: none; - - position: absolute; - /* TODO: variables */ - top: -6px; - bottom: 0; - left: 0; - right: 0; - z-index: -1; - - border-radius: 4px; - background-color: #ffffff; - border: 1px solid #d0d5dd; - box-shadow: 0 1px 2px 0 #1018280d; -} - -.legend, -.message { - font-size: 12px; - font-weight: 400; - line-height: 1; -} - -.legend { - opacity: 0; - max-width: 0.01px; - height: 1em; padding: 0; - - color: #313635; -} - -.message { - margin: 4px 0 0 0; - color: #ff3231cc; -} - -.block { + background: transparent; + border: none; + outline: none; width: 100%; -} - - -:global(.dark-theme) { - .root { - &.disabled { - .fieldset { - background-color: #282c301a; - border-color: #ffffff0a; - } - - .textarea, - .label { - color: #9cacb166; - } - } - } + font-family: inherit; + font-size: var(--input-font-size); + font-weight: 400; + line-height: var(--input-line-height); + resize: none; - .fieldset { - background-color: transparent; - border: 1px solid rgba(255, 255, 255, 0.04); - box-shadow: 0 1px 2px 0 #1018280d; + &:not(:disabled)[aria-invalid='true'] { + @include lightDark(color, #ff3231, #ff3757); } - .label, - .textarea { - color: #828b8e; + &:disabled, + &:disabled::placeholder { + @include lightDark(color, rgba(88, 105, 110, 0.5), rgba(156, 172, 177, 0.4)); } - .textarea { - color: #f6f6f6e5; - - &:focus::placeholder { - color: #828b8e; - } - - &:focus { - ~ .fieldset { - border-color: #0fa885; - - .legendLabel { - color: #0fa885; - } - } - } - - &.error { - ~ .label { - color: #d73b4f; - } - - ~ .fieldset { - border-color: #d73b4f; - - .legendLabel { - color: #d73b4f; - } - } - } - - .message { - color: #d73b4f; - } + &::placeholder { + @include lightDark(color, #58696e, #828b8e); } } diff --git a/utils/vara-ui/src/components/textarea/textarea.stories.ts b/utils/vara-ui/src/components/textarea/textarea.stories.ts index c59c25893..ae2bc94db 100644 --- a/utils/vara-ui/src/components/textarea/textarea.stories.ts +++ b/utils/vara-ui/src/components/textarea/textarea.stories.ts @@ -1,6 +1,6 @@ import { Meta, StoryObj } from '@storybook/react'; + import { Textarea } from './textarea'; -import { textareaSizes } from './helpers'; type Type = typeof Textarea; type Story = StoryObj; @@ -10,7 +10,7 @@ const meta: Meta = { component: Textarea, args: { label: '', - size: 'default', + size: 'medium', disabled: false, block: false, placeholder: 'Placeholder', @@ -19,7 +19,7 @@ const meta: Meta = { disabled: { control: 'boolean' }, block: { control: 'boolean' }, size: { - options: textareaSizes, + options: ['small', 'medium', 'large'], control: { type: 'select' }, }, }, diff --git a/utils/vara-ui/src/components/textarea/textarea.tsx b/utils/vara-ui/src/components/textarea/textarea.tsx index f65ab9d93..31c024117 100644 --- a/utils/vara-ui/src/components/textarea/textarea.tsx +++ b/utils/vara-ui/src/components/textarea/textarea.tsx @@ -1,47 +1,16 @@ -import { TextareaHTMLAttributes, ReactNode, useId, forwardRef } from 'react'; -import cx from 'clsx'; +import { TextareaHTMLAttributes, forwardRef } from 'react'; + +import { LabelContainer, LabelContainerProps } from '../label-container'; import styles from './textarea.module.scss'; -import type { ITextareaSizes } from './helpers.ts'; -type Props = Omit, 'id' | 'size'> & { - size?: ITextareaSizes; - label?: string; - error?: ReactNode; - block?: boolean; -}; +type Props = Omit, 'id' | 'size'> & LabelContainerProps; const Textarea = forwardRef( - ({ className, label, error, size = 'default', rows = 5, placeholder = ' ', block, ...attrs }, ref) => { - const { disabled } = attrs; - - const id = useId(); - + ({ className, label, error, size, rows = 5, block, ...attrs }, ref) => { return ( - - - - - {label && ( - - {label} - - )} - - - {label} - - - - {error && {error}} - + + + ); }, );
{error}