From 8772cf44c97374355547236699e42434fd18696c Mon Sep 17 00:00:00 2001 From: Eugene Kotsarev Date: Thu, 9 Jan 2025 13:11:39 +0300 Subject: [PATCH] feat(vara-ui): add transitions and dark mode to `Checkbox` (#1690) Co-authored-by: Nikita Yutanov --- .../src/components/checkbox/assets/check.svg | 10 + .../components/checkbox/checkbox.module.scss | 173 ++++++++++++++++-- .../components/checkbox/checkbox.stories.ts | 18 +- .../src/components/checkbox/checkbox.tsx | 33 ++-- 4 files changed, 201 insertions(+), 33 deletions(-) create mode 100644 utils/vara-ui/src/components/checkbox/assets/check.svg diff --git a/utils/vara-ui/src/components/checkbox/assets/check.svg b/utils/vara-ui/src/components/checkbox/assets/check.svg new file mode 100644 index 0000000000..590ad7957e --- /dev/null +++ b/utils/vara-ui/src/components/checkbox/assets/check.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/utils/vara-ui/src/components/checkbox/checkbox.module.scss b/utils/vara-ui/src/components/checkbox/checkbox.module.scss index ba56fb55f0..743b003760 100644 --- a/utils/vara-ui/src/components/checkbox/checkbox.module.scss +++ b/utils/vara-ui/src/components/checkbox/checkbox.module.scss @@ -1,36 +1,179 @@ +@use '../../utils' as *; + .label { + --color: #000; + --error-color: rgba(255, 51, 51, 0.8); + + @include darkMode() { + --color: #fff; + --error-color: #d73b4f; + } + display: flex; align-items: center; + gap: var(--gap); + + font-size: var(--font-size); + font-weight: 400; + line-height: var(--line-height); + color: var(--color); + cursor: pointer; - &.disabled { + &:has(.input:disabled) { pointer-events: none; - opacity: 0.3; } } .input { appearance: none; - cursor: inherit; - margin: 0 10px 0 0; + margin: 0; } -.checkbox { - width: 15px; - height: 15px; - background-image: url("data:image/svg+xml,%3Csvg width='15' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='.5' y='.5' width='14' height='14' rx='1.5' stroke='%23909090'/%3E%3C/svg%3E"); +.checkbox.box { + --background-color: transparent; + --border-color: #d0d5dd; + --disabled-background-color: #eceded; + --checked-color: #00ffc4; + + @include darkMode() { + --background-color: rgba(255, 255, 255, 0.03); + --border-color: rgba(255, 255, 255, 0.04); + --disabled-background-color: rgba(40, 44, 48, 0.1); + --checked-color: #30ffcf; + } + + width: var(--checkbox-size); + height: var(--checkbox-size); + + display: flex; + align-items: center; + + background-color: var(--background-color); + border: 1px solid var(--border-color); + border-radius: 2px; + + transition: 0.25s border-color ease, 0.25s background-color ease; + + &::before { + content: ''; + opacity: 0; + + width: 100%; + height: 100%; + + background-color: #000; + mask: url(./assets/check.svg) center/cover no-repeat; + + transition: 0.25s opacity ease, 0.25s background-color ease; + + .input:checked + & { + opacity: 1; + } + + .input:not(:disabled)[aria-invalid='true'] + & { + background-color: #fff; + } + + .input:disabled + & { + background-color: var(--border-color); + } + } + + .input:not(:disabled):checked + & { + background-color: var(--checked-color); + border-color: var(--checked-color); + } + + .input:not(:disabled)[aria-invalid='true'] + & { + border-color: var(--error-color); + } + + .input:not(:disabled):checked[aria-invalid='true'] + & { + background-color: var(--error-color); + } - &:checked { - background-image: url("data:image/svg+xml,%3Csvg width='15' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='.5' y='.5' width='14' height='14' rx='1.5' stroke='%230ED3A3'/%3E%3Cpath d='M12.163 4.45a.75.75 0 0 1 0 1.058l-5.595 5.955a.75.75 0 0 1-.547.232.75.75 0 0 1-.548-.232L3.643 9.49a.75.75 0 1 1 1.088-1.027l1.32 1.402 5.047-5.37a.75.75 0 0 1 1.065-.045Z' fill='%230ED3A3'/%3E%3C/svg%3E"); + .input:disabled + & { + background-color: var(--disabled-background-color); } } -.switch { - width: 25px; +.switch.box { + --border-color: #909090; + --checked-color: #0ed3a3; + --disabled-color: rgba(144, 144, 144, 0.3); + + @include darkMode() { + --border-color: rgba(255, 255, 255, 0.08); + --checked-color: #00ffc4; + --disabled-color: rgba(255, 255, 255, 0.03); + } + + width: 26px; height: 15px; - background-image: url("data:image/svg+xml,%3Csvg width='25' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='.5' y='.5' width='24' height='14' rx='7'/%3E%3Ccircle cx='7.5' cy='7.5' r='3.5' fill='%23909090'/%3E%3Crect x='.5' y='.5' width='24' height='14' rx='7' stroke='%23909090'/%3E%3C/svg%3E"); + padding: 0 4px; + + display: flex; + align-items: center; + + border: 1px solid var(--border-color); + border-radius: 16px; + + transition: 0.25s border-color ease; + + &::before { + content: ''; + + width: 7px; + height: 7px; + + background-color: var(--border-color); + border-radius: 50%; + + transition: transform 0.25s ease, background-color 0.25s ease; + + .input:checked + & { + transform: translateX(9px); + } - &:checked { - background-image: url("data:image/svg+xml,%3Csvg width='25' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='17.5' cy='7.5' r='3.5' fill='%230ED3A3'/%3E%3Crect x='.5' y='.5' width='24' height='14' rx='7' stroke='%230ED3A3'/%3E%3C/svg%3E"); + .input:not(:disabled):checked + & { + background-color: var(--checked-color); + } + + .input:not(:disabled)[aria-invalid='true'] + & { + background-color: var(--error-color); + } + + .input:disabled + & { + background-color: var(--disabled-color); + } + } + + .input:not(:disabled):checked + & { + border-color: var(--checked-color); + } + + .input:not(:disabled)[aria-invalid='true'] + & { + border-color: var(--error-color); + } + + .input:disabled + & { + border-color: var(--disabled-color); } } + +.default { + --gap: 12px; + --font-size: 14px; + --line-height: 20px; + + --checkbox-size: 18px; +} + +.small { + --gap: 10px; + --font-size: 12px; + --line-height: 22px; + + --checkbox-size: 15px; +} diff --git a/utils/vara-ui/src/components/checkbox/checkbox.stories.ts b/utils/vara-ui/src/components/checkbox/checkbox.stories.ts index 85b2361a09..fbf0ed1706 100644 --- a/utils/vara-ui/src/components/checkbox/checkbox.stories.ts +++ b/utils/vara-ui/src/components/checkbox/checkbox.stories.ts @@ -7,7 +7,23 @@ type Story = StoryObj; const meta: Meta = { title: 'Checkbox', component: Checkbox, - args: { label: 'Label' }, + args: { + label: 'Label', + disabled: false, + size: 'default', + type: 'checkbox', + error: undefined, + }, + argTypes: { + size: { + options: ['small', 'default'], + control: { type: 'select' }, + }, + type: { + options: ['checkbox', 'switch'], + control: { type: 'select' }, + }, + }, }; const Default: Story = { diff --git a/utils/vara-ui/src/components/checkbox/checkbox.tsx b/utils/vara-ui/src/components/checkbox/checkbox.tsx index a8d5387945..d9a3d09834 100644 --- a/utils/vara-ui/src/components/checkbox/checkbox.tsx +++ b/utils/vara-ui/src/components/checkbox/checkbox.tsx @@ -1,28 +1,27 @@ import { InputHTMLAttributes, forwardRef } from 'react'; import cx from 'clsx'; + import styles from './checkbox.module.scss'; -type Props = InputHTMLAttributes & { +type Props = Omit, 'size'> & { label: string; - type?: 'switch'; + type?: 'switch' | 'checkbox'; + size?: 'small' | 'default'; + error?: string; }; -const Checkbox = forwardRef(({ label, className, type, ...attrs }, ref) => { - const { disabled } = attrs; - - return ( - + ); + }, +); export { Checkbox }; export type { Props as CheckboxProps };