Skip to content

Commit

Permalink
feat(vara-ui): add dark mode to Select, drop floating label (#1696)
Browse files Browse the repository at this point in the history
Co-authored-by: Nikita Yutanov <[email protected]>
  • Loading branch information
ereburg and nikitayutanov authored Jan 14, 2025
1 parent b3a1566 commit ac15549
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 157 deletions.
3 changes: 3 additions & 0 deletions utils/vara-ui/src/components/select/assets/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
148 changes: 25 additions & 123 deletions utils/vara-ui/src/components/select/select.module.scss
Original file line number Diff line number Diff line change
@@ -1,140 +1,42 @@
.root {
&.disabled {
pointer-events: none;
opacity: 0.3;
}
}

.base {
position: relative;
}

.select,
.label {
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0.009em;
color: #010500;

&.default {
font-size: 16px;
}

&.small {
font-size: 14px;
}
}
@use '../../utils' as *;

.select {
@include lightDark(color, #000, rgba(246, 246, 246, 0.9));

appearance: none;
background-color: transparent;
outline: none;
padding: 0 32px 0 0;
border: none;
cursor: pointer;
font: inherit;

outline: none;
width: 100%;
padding: 8px 32px 8px 14px;
background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4.616 5.54a.5.5 0 0 0 .768 0L9.317.82A.5.5 0 0 0 8.932 0H1.068a.5.5 0 0 0-.385.82l3.933 4.72Z' fill='%230C0C0D' fill-opacity='.5'/%3E%3C/svg%3E");
background-position: right 14px center;
background-repeat: no-repeat;
font-family: inherit;
font-size: var(--input-font-size);
font-weight: 400;
line-height: var(--input-line-height);
cursor: pointer;

&:focus,
&:not(:placeholder-shown),
&.error {
~ .label {
opacity: 0;
}
background: transparent url(./assets/arrow.svg) center right no-repeat;

~ .fieldset {
.legendLabel {
opacity: 1;
max-width: 100%;
padding: 0 4px;
}
}
&:not(:disabled)[aria-invalid='true'] {
@include lightDark(color, #ff3231, #ff3757);
}

&:focus {
~ .fieldset {
border-color: #00b387;

.legendLabel {
color: #00b387;
}
}
&:disabled,
&:disabled::placeholder {
@include lightDark(color, rgba(88, 105, 110, 0.5), rgba(156, 172, 177, 0.4));
}

&.error {
~ .fieldset {
border-color: #fc174d;

.legendLabel {
color: #fc174d;
}
}
&::placeholder {
@include lightDark(color, #58696e, #828b8e);
}

&.default {
padding-top: 13px;
padding-bottom: 13px;
&:focus {
// setting background color to override default styles for options container,
// otherwise there's some padding in firefox and color difference is visible
@include lightDark(background-color, #fff, #1d1d1f);
}

&.small {
padding-top: 8px;
padding-bottom: 8px;
option {
@include lightDark(color, #000, rgba(246, 246, 246, 0.8));
@include lightDark(background-color, #fff, #262628);
}
}

.label {
pointer-events: none;

position: absolute;
top: 8px;
left: 13px;
}

.fieldset {
min-width: 0;
margin: 0;
padding: 0 13px;
pointer-events: none;

position: absolute;
/* TODO: variables */
top: -6px;
bottom: 0;
left: 0;
right: 0;

border: 1px solid #0000003b;
border-radius: 4px;
}

.legend,
.message {
font-size: 12px;
font-weight: 400;
line-height: 1;
letter-spacing: 0.01em;
}

.legend {
opacity: 0;
max-width: 0.01px;
height: 1em;
padding: 0;

color: #313635;
}

.message {
margin: 4px 0 0 0;

color: #fc174d;
}

.block {
width: 100%;
}
13 changes: 13 additions & 0 deletions utils/vara-ui/src/components/select/select.stories.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Meta, StoryObj } from '@storybook/react';

import { Select } from './select';

type Type = typeof Select;
Expand All @@ -13,6 +14,18 @@ const meta: Meta<Type> = {
{ label: 'Option 2', value: 'option-2' },
{ label: 'Option 3', value: 'option-3' },
],
label: '',
size: 'medium',
disabled: false,
block: false,
},
argTypes: {
disabled: { control: 'boolean' },
block: { control: 'boolean' },
size: {
options: ['small', 'medium', 'large'],
control: { type: 'select' },
},
},
};

Expand Down
51 changes: 17 additions & 34 deletions utils/vara-ui/src/components/select/select.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,26 @@
import { SelectHTMLAttributes, OptionHTMLAttributes, ReactNode, useId, forwardRef } from 'react';
import cx from 'clsx';
import { SelectHTMLAttributes, OptionHTMLAttributes, forwardRef, useMemo } from 'react';

import { LabelContainer, LabelContainerProps } from '../label-container';
import styles from './select.module.scss';

type Props = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'id' | 'size'> & {
options: OptionHTMLAttributes<HTMLOptionElement>[] | Readonly<OptionHTMLAttributes<HTMLOptionElement>[]>;
size?: 'default' | 'small';
label?: string;
error?: ReactNode;
block?: boolean;
};
type Props = Omit<SelectHTMLAttributes<HTMLSelectElement>, 'id' | 'size'> &
LabelContainerProps & {
options: OptionHTMLAttributes<HTMLOptionElement>[] | Readonly<OptionHTMLAttributes<HTMLOptionElement>[]>;
};

const Select = forwardRef<HTMLSelectElement, Props>(
({ options, className, label, error, size = 'default', block, ...attrs }, ref) => {
const { disabled } = attrs;

const id = useId();

const getOptions = () => options.map((option, index) => <option key={index} {...option} />);
({ options, className, label, error, size, block, ...attrs }, ref) => {
const optionsToRender = useMemo(
() => options.map((option, index) => <option key={index} {...option} />),
[options],
);

return (
<div className={cx(styles.root, className, disabled && styles.disabled, block && styles.block)}>
<div className={styles.base}>
<select id={id} className={cx(styles.select, styles[size], error && styles.error)} ref={ref} {...attrs}>
{getOptions()}
</select>

{label && (
<label htmlFor={id} className={cx(styles.label, styles[size])}>
{label}
</label>
)}

<fieldset className={styles.fieldset}>
<legend className={cx(styles.legend, label && styles.legendLabel)}>{label}&#8203;</legend>
</fieldset>
</div>

{error && <p className={styles.message}>{error}</p>}
</div>
<LabelContainer className={className} label={label} error={error} size={size} block={block}>
<select className={styles.select} ref={ref} aria-invalid={Boolean(error)} {...attrs}>
{optionsToRender}
</select>
</LabelContainer>
);
},
);
Expand Down

0 comments on commit ac15549

Please sign in to comment.