@liguelead/design-system 0.0.37 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { colorType } from '../../types'
3
3
  export type ButtonSizeTypes = 'sm' | 'md' | 'lg'
4
4
  export type ButtonVariantTypes = 'solid' | 'outline' | 'ghost' | 'neutralOutline' | 'neutralGhost'
5
5
 
6
- export interface ButtonProps {
6
+ export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color' | 'onClick'> {
7
7
  variant?: ButtonVariantTypes
8
8
  children: React.ReactNode
9
9
  className?: string
@@ -80,7 +80,7 @@ export const PopoverContent = styled(Popover.Content)<{
80
80
  $width: number | string
81
81
  }>`
82
82
  width: ${({ $width }) =>
83
- typeof $width === 'number' ? `${$width}px` : $width};
83
+ typeof $width === 'number' ? `${$width}px` : $width === '100%' ? 'var(--radix-popover-trigger-width)' : $width};
84
84
  background: ${({ theme }) => parseColor(theme.colors.white)};
85
85
  border: 1px solid ${({ theme }) => parseColor(theme.colors.neutral400)};
86
86
  border-radius: 4px;
@@ -33,7 +33,7 @@ const Combobox = (props: ComboboxProps) => {
33
33
  searchPlaceholder = 'Pesquisar',
34
34
  emptyText = 'Sem resultados.',
35
35
  disabled = false,
36
- width = 280,
36
+ width = '100%',
37
37
  size = 'md',
38
38
  onCreate,
39
39
  createLabel = 'Adicionar novo'
@@ -0,0 +1,203 @@
1
+ import { useState } from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import { ArticleNyTimesIcon, ChatCircleIcon, EnvelopeSimpleIcon } from '@phosphor-icons/react'
4
+ import RadioCardGroup from './RadioCardGroup'
5
+
6
+ const LONG_DESC = 'Ideal para comunicações diretas com alta taxa de entrega. Suporta texto simples, links e variáveis personalizadas. Amplamente compatível com todos os dispositivos móveis, sem necessidade de internet ou aplicativo instalado.'
7
+
8
+ const meta: Meta<typeof RadioCardGroup> = {
9
+ title: 'Form/RadioCardGroup',
10
+ component: RadioCardGroup,
11
+ parameters: { layout: 'centered' },
12
+ tags: ['autodocs'],
13
+ }
14
+
15
+ export default meta
16
+ type Story = StoryObj<typeof meta>
17
+
18
+ export const Default: Story = {
19
+ render: () => {
20
+ const [selected, setSelected] = useState('sms')
21
+ return (
22
+ <div style={{ width: 320 }}>
23
+ <RadioCardGroup
24
+ name="channel"
25
+ value={selected}
26
+ onChange={setSelected}
27
+ options={[
28
+ {
29
+ value: 'sms',
30
+ label: 'SMS',
31
+ icon: <ChatCircleIcon size={20} />,
32
+ description: LONG_DESC,
33
+ descriptionMaxLines: 2,
34
+ badgesLabel: 'O que contém:',
35
+ badges: ['Texto até 1.600 caracteres'],
36
+ },
37
+ {
38
+ value: 'email',
39
+ label: 'E-mail',
40
+ icon: <EnvelopeSimpleIcon size={20} />,
41
+ description: 'Envio de mensagens por e-mail.',
42
+ badgesLabel: 'O que contém:',
43
+ badges: ['Texto', 'Imagens', 'Links'],
44
+ },
45
+ {
46
+ value: 'rcs',
47
+ label: 'RCS',
48
+ icon: <ArticleNyTimesIcon size={20} />,
49
+ description: 'Mensagens ricas com mídia.',
50
+ badgesLabel: 'O que contém:',
51
+ badges: ['Texto', 'Imagens', 'Botões', 'Carrossel'],
52
+ },
53
+ ]}
54
+ />
55
+ </div>
56
+ )
57
+ },
58
+ }
59
+
60
+ export const WithSeeMore: Story = {
61
+ name: 'Ver mais (descrição longa)',
62
+ render: () => {
63
+ const [selected, setSelected] = useState('a')
64
+ return (
65
+ <div style={{ width: 320 }}>
66
+ <RadioCardGroup
67
+ name="see-more"
68
+ value={selected}
69
+ onChange={setSelected}
70
+ options={[
71
+ {
72
+ value: 'a',
73
+ label: 'Canal A',
74
+ icon: <ChatCircleIcon size={20} />,
75
+ description: LONG_DESC,
76
+ descriptionMaxLines: 2,
77
+ badges: ['Feature 1', 'Feature 2'],
78
+ },
79
+ {
80
+ value: 'b',
81
+ label: 'Canal B',
82
+ icon: <EnvelopeSimpleIcon size={20} />,
83
+ description: 'Descrição curta, sem botão de ver mais.',
84
+ badges: ['Feature 3'],
85
+ },
86
+ ]}
87
+ />
88
+ </div>
89
+ )
90
+ },
91
+ }
92
+
93
+ export const Scrollable: Story = {
94
+ name: 'Com scroll thin',
95
+ render: () => {
96
+ const [selected, setSelected] = useState('a')
97
+ return (
98
+ <div style={{ width: 320 }}>
99
+ <RadioCardGroup
100
+ name="scroll"
101
+ value={selected}
102
+ onChange={setSelected}
103
+ scrollable
104
+ maxHeight="300px"
105
+ options={Array.from({ length: 6 }, (_, i) => ({
106
+ value: String(i),
107
+ label: `Opção ${i + 1}`,
108
+ icon: <ChatCircleIcon size={20} />,
109
+ description: `Descrição da opção ${i + 1}.`,
110
+ badges: [`Badge ${i + 1}`],
111
+ }))}
112
+ />
113
+ </div>
114
+ )
115
+ },
116
+ }
117
+
118
+ export const TwoColumns: Story = {
119
+ name: '2 colunas',
120
+ render: () => {
121
+ const [selected, setSelected] = useState('a')
122
+ return (
123
+ <div style={{ width: 600 }}>
124
+ <RadioCardGroup
125
+ name="grid"
126
+ value={selected}
127
+ onChange={setSelected}
128
+ columns={2}
129
+ options={[
130
+ { value: 'a', label: 'SMS', icon: <ChatCircleIcon size={20} />, description: 'Texto simples.', badges: ['160 chars'] },
131
+ { value: 'b', label: 'E-mail', icon: <EnvelopeSimpleIcon size={20} />, description: 'HTML completo.', badges: ['Ilimitado'] },
132
+ { value: 'c', label: 'RCS', icon: <ArticleNyTimesIcon size={20} />, description: 'Mídia rica.', badges: ['Botões', 'Carrossel'] },
133
+ { value: 'd', label: 'Push', icon: <ChatCircleIcon size={20} />, description: 'Notificação push.', badges: ['Deep link'] },
134
+ ]}
135
+ />
136
+ </div>
137
+ )
138
+ },
139
+ }
140
+
141
+ export const WithoutIcon: Story = {
142
+ render: () => {
143
+ const [selected, setSelected] = useState('a')
144
+ return (
145
+ <div style={{ width: 320 }}>
146
+ <RadioCardGroup
147
+ name="plan"
148
+ value={selected}
149
+ onChange={setSelected}
150
+ options={[
151
+ { value: 'a', label: 'Plano Básico', description: 'Funcionalidades essenciais.', badgesLabel: 'Inclui:', badges: ['5 usuários', '10GB'] },
152
+ { value: 'b', label: 'Plano Pro', description: 'Para times maiores.', badgesLabel: 'Inclui:', badges: ['Ilimitado', '100GB', 'Suporte'] },
153
+ ]}
154
+ />
155
+ </div>
156
+ )
157
+ },
158
+ }
159
+
160
+ export const Disabled: Story = {
161
+ render: () => (
162
+ <div style={{ width: 320 }}>
163
+ <RadioCardGroup
164
+ name="disabled"
165
+ value="a"
166
+ disabled
167
+ options={[
168
+ {
169
+ value: 'a',
170
+ label: 'Opção desabilitada',
171
+ icon: <ChatCircleIcon size={20} />,
172
+ description: 'Este grupo está desabilitado.',
173
+ badges: ['Indisponível'],
174
+ },
175
+ ]}
176
+ />
177
+ </div>
178
+ ),
179
+ }
180
+
181
+ export const CardWidth: Story = {
182
+ name: 'Largura dos cards (min/max)',
183
+ render: () => {
184
+ const [selected, setSelected] = useState('a')
185
+ return (
186
+ <div style={{ width: 700 }}>
187
+ <RadioCardGroup
188
+ name="width"
189
+ value={selected}
190
+ onChange={setSelected}
191
+ columns={3}
192
+ minCardWidth="160px"
193
+ maxCardWidth="240px"
194
+ options={[
195
+ { value: 'a', label: 'SMS', icon: <ChatCircleIcon size={20} />, description: 'Texto simples.', badges: ['160 chars'] },
196
+ { value: 'b', label: 'E-mail', icon: <EnvelopeSimpleIcon size={20} />, description: 'HTML completo.', badges: ['Ilimitado'] },
197
+ { value: 'c', label: 'RCS', icon: <ArticleNyTimesIcon size={20} />, description: 'Mídia rica.', badges: ['Botões'] },
198
+ ]}
199
+ />
200
+ </div>
201
+ )
202
+ },
203
+ }
@@ -0,0 +1,199 @@
1
+ import styled from 'styled-components'
2
+ import { fontSize, fontWeight, lineHeight, radius, spacing } from '@liguelead/foundation'
3
+ import { parseColor } from '../../utils'
4
+
5
+ export const CardWrapper = styled.label<{
6
+ $checked?: boolean
7
+ $disabled?: boolean
8
+ $error?: boolean
9
+ $columns?: number
10
+ $minCardWidth?: string
11
+ $maxCardWidth?: string
12
+ }>`
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: ${spacing.spacing16}px;
16
+ padding: ${spacing.spacing24}px;
17
+ border-radius: ${radius.radius8}px;
18
+ border: 1px solid ${({ theme, $checked, $error }) =>
19
+ $error
20
+ ? parseColor(theme.colors.danger200)
21
+ : $checked
22
+ ? parseColor(theme.colors.primary)
23
+ : parseColor(theme.colors.neutral400)};
24
+ background: ${({ theme, $checked, $error }) =>
25
+ $checked && !$error
26
+ ? parseColor(theme.colors.primaryLight)
27
+ : parseColor(theme.colors.white)};
28
+ cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')};
29
+ opacity: ${({ $disabled }) => ($disabled ? 0.5 : 1)};
30
+ transition: border-color 0.2s ease, background 0.2s ease;
31
+ flex: ${({ $columns }) => ($columns ? `1 1 calc(${100 / $columns}% - ${spacing.spacing12}px)` : '1 1 100%')};
32
+ min-width: ${({ $minCardWidth }) => $minCardWidth ?? '0'};
33
+ max-width: ${({ $maxCardWidth }) => $maxCardWidth ?? 'none'};
34
+
35
+ &:hover:not([data-disabled='true']) {
36
+ border-color: ${({ theme, $error }) =>
37
+ $error
38
+ ? parseColor(theme.colors.danger200)
39
+ : parseColor(theme.colors.primary)};
40
+ }
41
+ `
42
+
43
+ export const CardRadioRow = styled.div`
44
+ display: flex;
45
+ align-items: flex-start;
46
+ gap: ${spacing.spacing12}px;
47
+ `
48
+
49
+ export const RadioCircle = styled.input`
50
+ appearance: none;
51
+ width: ${spacing.spacing16}px;
52
+ height: ${spacing.spacing16}px;
53
+ min-width: ${spacing.spacing16}px;
54
+ border: 1px solid ${({ theme }) => parseColor(theme.colors.neutral400)};
55
+ border-radius: 50%;
56
+ background: ${({ theme }) => parseColor(theme.colors.white)};
57
+ position: relative;
58
+ cursor: pointer;
59
+ transition: all 0.2s ease;
60
+ margin-top: 2px;
61
+
62
+ &:checked {
63
+ border-color: ${({ theme }) => parseColor(theme.colors.neutral300)};
64
+
65
+ &::after {
66
+ content: '';
67
+ position: absolute;
68
+ top: 50%;
69
+ left: 50%;
70
+ transform: translate(-50%, -50%);
71
+ width: 8px;
72
+ height: 8px;
73
+ border-radius: 50%;
74
+ background: ${({ theme }) => parseColor(theme.colors.primary)};
75
+ }
76
+ }
77
+
78
+ &:disabled {
79
+ cursor: not-allowed;
80
+ border-color: ${({ theme }) => parseColor(theme.colors.neutral400)};
81
+ background: ${({ theme }) => parseColor(theme.colors.neutral100)};
82
+ }
83
+ `
84
+
85
+ export const CardTitleRow = styled.div`
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 6px;
89
+ `
90
+
91
+ export const CardIconWrapper = styled.div`
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ width: 20px;
96
+ height: 20px;
97
+ flex-shrink: 0;
98
+
99
+ svg {
100
+ width: 20px;
101
+ height: 20px;
102
+ color: ${({ theme }) => parseColor(theme.colors.neutral1000)};
103
+ }
104
+ `
105
+
106
+ export const Divider = styled.div`
107
+ width: 100%;
108
+ height: 1px;
109
+ background: ${({ theme }) => parseColor(theme.colors.neutral300)};
110
+ `
111
+
112
+ export const CardContent = styled.div`
113
+ display: flex;
114
+ flex-direction: column;
115
+ gap: ${spacing.spacing8}px;
116
+ `
117
+
118
+ export const DescriptionWrapper = styled.div<{ $expanded: boolean; $maxLines: number }>`
119
+ position: relative;
120
+ overflow: hidden;
121
+ max-height: ${({ $expanded, $maxLines }) =>
122
+ $expanded ? 'none' : `calc(${lineHeight.lineHeight16}px * ${$maxLines})`};
123
+ `
124
+
125
+ export const DescriptionText = styled.p`
126
+ font-size: ${fontSize.fontSize12}px;
127
+ font-weight: ${fontWeight.fontWeight400};
128
+ line-height: ${lineHeight.lineHeight16}px;
129
+ color: ${({ theme }) => parseColor(theme.colors.textDark)};
130
+ margin: 0;
131
+ `
132
+
133
+ export const SeeMoreButton = styled.button`
134
+ background: none;
135
+ border: none;
136
+ padding: 0;
137
+ cursor: pointer;
138
+ font-size: ${fontSize.fontSize12}px;
139
+ font-weight: ${fontWeight.fontWeight500};
140
+ line-height: ${lineHeight.lineHeight16}px;
141
+ color: ${({ theme }) => parseColor(theme.colors.primary)};
142
+ text-align: left;
143
+ margin-top: 2px;
144
+
145
+ &:hover {
146
+ text-decoration: underline;
147
+ }
148
+ `
149
+
150
+ export const BadgesSection = styled.div`
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: ${spacing.spacing8}px;
154
+ `
155
+
156
+ export const BadgesLabel = styled.p`
157
+ font-size: ${fontSize.fontSize12}px;
158
+ font-weight: ${fontWeight.fontWeight500};
159
+ line-height: ${lineHeight.lineHeight16}px;
160
+ color: ${({ theme }) => parseColor(theme.colors.textMedium)};
161
+ margin: 0;
162
+ white-space: nowrap;
163
+ `
164
+
165
+ export const BadgesList = styled.div`
166
+ display: flex;
167
+ flex-wrap: wrap;
168
+ gap: ${spacing.spacing4}px;
169
+ align-items: center;
170
+ `
171
+
172
+ export const GroupWrapper = styled.div<{ $columns?: number; $minCardWidth?: string; $scrollable?: boolean; $maxHeight?: string }>`
173
+ display: flex;
174
+ flex-wrap: wrap;
175
+ flex-direction: ${({ $columns }) => ($columns ? 'row' : 'column')};
176
+ gap: ${spacing.spacing12}px;
177
+ width: 100%;
178
+
179
+ ${({ $scrollable, $maxHeight }) => $scrollable && `
180
+ overflow-y: auto;
181
+ max-height: ${$maxHeight ?? '400px'};
182
+ padding-right: ${spacing.spacing4}px;
183
+
184
+ scrollbar-width: thin;
185
+ scrollbar-color: #7c7c83 #cfcfd1;
186
+
187
+ &::-webkit-scrollbar {
188
+ width: 8px;
189
+ }
190
+ &::-webkit-scrollbar-track {
191
+ background: #cfcfd1;
192
+ border-radius: 100px;
193
+ }
194
+ &::-webkit-scrollbar-thumb {
195
+ background: #7c7c83;
196
+ border-radius: 100px;
197
+ }
198
+ `}
199
+ `
@@ -0,0 +1,159 @@
1
+ import { useState, useRef, useEffect } from 'react'
2
+ import { Badge } from '../Badge'
3
+ import { RadioCardGroupProps } from './RadioCardGroup.types'
4
+ import {
5
+ GroupWrapper,
6
+ CardWrapper,
7
+ CardRadioRow,
8
+ RadioCircle,
9
+ CardTitleRow,
10
+ CardIconWrapper,
11
+ Divider,
12
+ CardContent,
13
+ DescriptionWrapper,
14
+ DescriptionText,
15
+ SeeMoreButton,
16
+ BadgesSection,
17
+ BadgesLabel,
18
+ BadgesList,
19
+ } from './RadioCardGroup.styles'
20
+ import Text from '../Text'
21
+
22
+ const DEFAULT_MAX_LINES = 3
23
+
24
+ interface DescriptionWithToggleProps {
25
+ text: string
26
+ maxLines?: number
27
+ }
28
+
29
+ const DescriptionWithToggle = ({ text, maxLines = DEFAULT_MAX_LINES }: DescriptionWithToggleProps) => {
30
+ const [expanded, setExpanded] = useState(false)
31
+ const [isClamped, setIsClamped] = useState(false)
32
+ const ref = useRef<HTMLParagraphElement>(null)
33
+
34
+ useEffect(() => {
35
+ const el = ref.current
36
+ if (!el) return
37
+ setIsClamped(el.scrollHeight > el.clientHeight + 1)
38
+ }, [text, maxLines])
39
+
40
+ return (
41
+ <>
42
+ <DescriptionWrapper $expanded={expanded} $maxLines={maxLines}>
43
+ <DescriptionText ref={ref}>{text}</DescriptionText>
44
+ </DescriptionWrapper>
45
+ {(isClamped || expanded) && (
46
+ <SeeMoreButton
47
+ type="button"
48
+ onClick={(e) => {
49
+ e.preventDefault()
50
+ e.stopPropagation()
51
+ setExpanded(v => !v)
52
+ }}
53
+ >
54
+ {expanded ? 'Ver menos' : 'Ver mais'}
55
+ </SeeMoreButton>
56
+ )}
57
+ </>
58
+ )
59
+ }
60
+
61
+ const RadioCardGroup = <TFieldValues extends object = object>({
62
+ name,
63
+ options,
64
+ value,
65
+ onChange,
66
+ disabled = false,
67
+ className,
68
+ error,
69
+ columns,
70
+ minCardWidth,
71
+ maxCardWidth,
72
+ scrollable,
73
+ maxHeight,
74
+ ...rest
75
+ }: RadioCardGroupProps<TFieldValues>) => {
76
+ const hasError = !!error
77
+
78
+ return (
79
+ <GroupWrapper
80
+ className={className}
81
+ $columns={columns}
82
+ $minCardWidth={minCardWidth}
83
+ $scrollable={scrollable}
84
+ $maxHeight={maxHeight}
85
+ {...rest}
86
+ >
87
+ {options.map((option) => {
88
+ const isChecked = value === option.value
89
+ const isDisabled = disabled || !!option.disabled
90
+
91
+ return (
92
+ <CardWrapper
93
+ key={option.value}
94
+ $checked={isChecked}
95
+ $disabled={isDisabled}
96
+ $error={hasError}
97
+ $columns={columns}
98
+ $minCardWidth={minCardWidth}
99
+ $maxCardWidth={maxCardWidth}
100
+ data-disabled={isDisabled}
101
+ >
102
+ <CardRadioRow>
103
+ <RadioCircle
104
+ type="radio"
105
+ name={name}
106
+ value={option.value}
107
+ checked={isChecked}
108
+ disabled={isDisabled}
109
+ onChange={(e) => {
110
+ option.register?.onChange(e)
111
+ onChange?.(option.value)
112
+ }}
113
+ {...option.register}
114
+ />
115
+ <CardTitleRow>
116
+ {option.icon && (
117
+ <CardIconWrapper>{option.icon}</CardIconWrapper>
118
+ )}
119
+ <Text color="textDark" weight="fontWeight500" size="body02" tag="p">
120
+ {option.label}
121
+ </Text>
122
+ </CardTitleRow>
123
+ </CardRadioRow>
124
+
125
+ {(option.description || (option.badges && option.badges.length > 0)) && (
126
+ <>
127
+ <Divider />
128
+ <CardContent>
129
+ {option.description && (
130
+ <DescriptionWithToggle
131
+ text={option.description}
132
+ maxLines={option.descriptionMaxLines}
133
+ />
134
+ )}
135
+ {option.badges && option.badges.length > 0 && (
136
+ <BadgesSection>
137
+ {option.badgesLabel && (
138
+ <BadgesLabel>{option.badgesLabel}</BadgesLabel>
139
+ )}
140
+ <BadgesList>
141
+ {option.badges.map((badge, i) => (
142
+ <Badge key={i} color={isChecked ? 'primary' : 'primaryLight'}>
143
+ {badge}
144
+ </Badge>
145
+ ))}
146
+ </BadgesList>
147
+ </BadgesSection>
148
+ )}
149
+ </CardContent>
150
+ </>
151
+ )}
152
+ </CardWrapper>
153
+ )
154
+ })}
155
+ </GroupWrapper>
156
+ )
157
+ }
158
+
159
+ export default RadioCardGroup
@@ -0,0 +1,29 @@
1
+ import React from 'react'
2
+ import { FieldValues, UseFormRegisterReturn } from 'react-hook-form'
3
+
4
+ export interface RadioCardOption {
5
+ value: string
6
+ label: string
7
+ icon?: React.ReactNode
8
+ description?: string
9
+ descriptionMaxLines?: number
10
+ badges?: string[]
11
+ badgesLabel?: string
12
+ disabled?: boolean
13
+ register?: UseFormRegisterReturn<string>
14
+ }
15
+
16
+ export interface RadioCardGroupProps<TFieldValues extends FieldValues = FieldValues>
17
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
18
+ name: string
19
+ options: RadioCardOption[]
20
+ value?: string
21
+ onChange?: (value: string) => void
22
+ disabled?: boolean
23
+ error?: TFieldValues
24
+ columns?: number
25
+ minCardWidth?: string
26
+ maxCardWidth?: string
27
+ scrollable?: boolean
28
+ maxHeight?: string
29
+ }
@@ -0,0 +1,2 @@
1
+ export { default } from './RadioCardGroup'
2
+ export type { RadioCardGroupProps, RadioCardOption } from './RadioCardGroup.types'
@@ -0,0 +1,57 @@
1
+ import { css } from 'styled-components'
2
+ import { useTheme } from 'styled-components'
3
+ import { parseColor } from '../../utils'
4
+ import { StepState } from './Stepper.types'
5
+
6
+ export const StepIndicatorAppearance = ($state: StepState) => {
7
+ const theme = useTheme()
8
+ const colors = theme.colors
9
+
10
+ if ($state === 'completed') {
11
+ return css`
12
+ width: 28px;
13
+ height: 28px;
14
+ background-color: ${parseColor(colors.primary)};
15
+ border: 2px solid ${parseColor(colors.primary)};
16
+ color: ${parseColor(colors.white)};
17
+ `
18
+ }
19
+
20
+ if ($state === 'active') {
21
+ return css`
22
+ width: 28px;
23
+ height: 28px;
24
+ background-color: transparent;
25
+ border: 2.5px solid ${parseColor(colors.primary)};
26
+ color: ${parseColor(colors.primary)};
27
+ box-shadow: 0 0 0 4px ${parseColor(colors.primary)}22;
28
+ `
29
+ }
30
+
31
+ if ($state === 'error') {
32
+ return css`
33
+ width: 28px;
34
+ height: 28px;
35
+ background-color: transparent;
36
+ border: 2px solid ${parseColor(colors.danger200)};
37
+ color: ${parseColor(colors.danger200)};
38
+ `
39
+ }
40
+
41
+ return css`
42
+ width: 28px;
43
+ height: 28px;
44
+ background-color: transparent;
45
+ border: none;
46
+ color: transparent;
47
+
48
+ &::before {
49
+ content: '';
50
+ display: block;
51
+ width: 10px;
52
+ height: 10px;
53
+ border-radius: 50%;
54
+ background-color: ${parseColor(colors.neutral400)};
55
+ }
56
+ `
57
+ }