@liguelead/design-system 0.0.36 → 0.0.38
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.
- package/components/Alert/Alert.style.ts +1 -1
- package/components/Alert/Alert.tsx +3 -1
- package/components/Alert/Alert.variants.ts +2 -4
- package/components/Button/Button.appearance.ts +3 -4
- package/components/Button/Button.styles.ts +0 -1
- package/components/Button/Button.tsx +4 -7
- package/components/Button/Button.types.ts +1 -1
- package/components/Checkbox/Checkbox.tsx +5 -5
- package/components/Combobox/Combobox.styles.ts +1 -1
- package/components/Combobox/Combobox.tsx +1 -1
- package/components/Dialog/Dialog.style.ts +2 -1
- package/components/Dialog/Dialog.tsx +6 -8
- package/components/IconButton/IconButton.tsx +3 -1
- package/components/LinkButton/LinkButton.tsx +3 -1
- package/components/PageWrapper/PageWrapper.tsx +1 -1
- package/components/RadioCardGroup/RadioCardGroup.stories.tsx +203 -0
- package/components/RadioCardGroup/RadioCardGroup.styles.ts +198 -0
- package/components/RadioCardGroup/RadioCardGroup.tsx +159 -0
- package/components/RadioCardGroup/RadioCardGroup.types.ts +29 -0
- package/components/RadioCardGroup/index.ts +2 -0
- package/components/SplitButton/SplitButton.tsx +1 -1
- package/components/Stepper/Stepper.appearance.ts +57 -0
- package/components/Stepper/Stepper.stories.tsx +300 -0
- package/components/Stepper/Stepper.styles.ts +179 -0
- package/components/Stepper/Stepper.tsx +118 -0
- package/components/Stepper/Stepper.types.ts +27 -0
- package/components/Stepper/index.ts +7 -0
- package/components/Tabs/Tabs.tsx +2 -0
- package/components/TextField/TextField.stories.tsx +109 -2
- package/components/TextField/TextField.styles.ts +44 -0
- package/components/TextField/TextField.tsx +130 -8
- package/components/TextField/TextField.types.ts +11 -1
- package/components/Toaster/Toaster.ts +5 -19
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useTheme } from 'styled-components'
|
|
1
2
|
import Text from '../Text'
|
|
2
3
|
import LinkButton from '../LinkButton'
|
|
3
4
|
|
|
@@ -21,7 +22,8 @@ const Alert = ({
|
|
|
21
22
|
title,
|
|
22
23
|
hasButton
|
|
23
24
|
}: TAlertProps) => {
|
|
24
|
-
const
|
|
25
|
+
const theme = useTheme()
|
|
26
|
+
const alertVariant = AlertVariant(variant, theme)
|
|
25
27
|
|
|
26
28
|
return (
|
|
27
29
|
<AlertContainer $variant={alertVariant} className={className}>
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { parseColor } from '../../utils'
|
|
2
|
-
import {
|
|
2
|
+
import { DefaultTheme } from 'styled-components'
|
|
3
3
|
|
|
4
4
|
type TVariant = 'success' | 'danger' | 'warning' | 'info' | 'default'
|
|
5
5
|
|
|
6
|
-
export const AlertVariant = (variant: TVariant) => {
|
|
7
|
-
const theme = useTheme()
|
|
8
|
-
|
|
6
|
+
export const AlertVariant = (variant: TVariant, theme: DefaultTheme) => {
|
|
9
7
|
const variants = {
|
|
10
8
|
success: `
|
|
11
9
|
border-left: 8px solid ${parseColor(theme.colors.success100)};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { ButtonVariantTypes } from './Button.types'
|
|
2
2
|
import { colorType } from '../../types'
|
|
3
|
-
import {
|
|
3
|
+
import { DefaultTheme } from 'styled-components'
|
|
4
4
|
import { darkenOrLighten, getTextColor, parseColor, getHoverColor } from '../../utils'
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export const ButtonVariant = (
|
|
8
8
|
color: colorType,
|
|
9
|
-
variant: ButtonVariantTypes
|
|
9
|
+
variant: ButtonVariantTypes,
|
|
10
|
+
theme: DefaultTheme
|
|
10
11
|
) => {
|
|
11
|
-
const theme = useTheme();
|
|
12
|
-
|
|
13
12
|
const colorValue = theme.colors[color];
|
|
14
13
|
|
|
15
14
|
if (!colorValue) {
|
|
@@ -11,7 +11,6 @@ export interface StyledButtonProps extends ButtonProps {
|
|
|
11
11
|
export const StyledButton = styled.button<StyledButtonProps>`
|
|
12
12
|
position: relative;
|
|
13
13
|
display: flex;
|
|
14
|
-
outline: none !important;
|
|
15
14
|
justify-content: center;
|
|
16
15
|
align-items: center;
|
|
17
16
|
width: ${({ $fullWidth }) => ($fullWidth ? '100%' : 'auto')};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
|
+
import { useTheme } from 'styled-components'
|
|
2
3
|
import { StyledButton, RippleContainer } from './Button.styles'
|
|
3
4
|
import { RippleInterface, ButtonProps } from './Button.types'
|
|
4
5
|
import { ButtonSizes } from './Button.sizes'
|
|
@@ -16,14 +17,11 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
16
17
|
type = 'button',
|
|
17
18
|
...rest
|
|
18
19
|
}) => {
|
|
20
|
+
const theme = useTheme()
|
|
19
21
|
const [ripples, setRipples] = useState<RippleInterface[]>([])
|
|
20
22
|
|
|
21
23
|
const buttonSize = ButtonSizes(size)
|
|
22
|
-
const buttonVariant = ButtonVariant(color, variant)
|
|
23
|
-
|
|
24
|
-
const removeRipple = (ripples: RippleInterface[], rippleId: string) => {
|
|
25
|
-
return ripples.filter(r => r.id !== rippleId)
|
|
26
|
-
}
|
|
24
|
+
const buttonVariant = ButtonVariant(color, variant, theme)
|
|
27
25
|
|
|
28
26
|
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
29
27
|
const rect = e.currentTarget.getBoundingClientRect()
|
|
@@ -37,7 +35,6 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
37
35
|
}
|
|
38
36
|
setRipples(prev => [...prev, newRipple])
|
|
39
37
|
|
|
40
|
-
// Chama o onClick fornecido
|
|
41
38
|
if (onClick) {
|
|
42
39
|
onClick(e)
|
|
43
40
|
}
|
|
@@ -65,7 +62,7 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
65
62
|
height: 20
|
|
66
63
|
}}
|
|
67
64
|
onAnimationEnd={() =>
|
|
68
|
-
setRipples(prev =>
|
|
65
|
+
setRipples(prev => prev.filter(r => r.id !== ripple.id))
|
|
69
66
|
}
|
|
70
67
|
/>
|
|
71
68
|
))}
|
|
@@ -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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
2
|
import { CheckboxProps } from './Checkbox.types'
|
|
3
3
|
import {
|
|
4
4
|
CheckboxWrapper,
|
|
@@ -31,9 +31,9 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|
|
31
31
|
)
|
|
32
32
|
const [ripples, setRipples] = useState<RippleInterface[]>([])
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (checked !== undefined) setInternalChecked(checked)
|
|
36
|
+
}, [checked])
|
|
37
37
|
|
|
38
38
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
39
39
|
onChange?.(e)
|
|
@@ -78,7 +78,7 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|
|
78
78
|
height: 20
|
|
79
79
|
}}
|
|
80
80
|
onAnimationEnd={() =>
|
|
81
|
-
setRipples(prev =>
|
|
81
|
+
setRipples(prev => prev.filter(r => r.id !== ripple.id))
|
|
82
82
|
}
|
|
83
83
|
/>
|
|
84
84
|
))}
|
|
@@ -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;
|
|
@@ -9,6 +9,7 @@ export const Overlay = styled(DialogPrimitive.Overlay)`
|
|
|
9
9
|
background: rgba(0, 0, 0, 0.3);
|
|
10
10
|
position: fixed;
|
|
11
11
|
inset: 0;
|
|
12
|
+
z-index: 9999;
|
|
12
13
|
`
|
|
13
14
|
|
|
14
15
|
export const Content = styled(DialogPrimitive.Content)<{$centerContent?: boolean}>`
|
|
@@ -27,7 +28,7 @@ export const Content = styled(DialogPrimitive.Content)<{$centerContent?: boolean
|
|
|
27
28
|
top: 8vh;
|
|
28
29
|
left: 50%;
|
|
29
30
|
transform: translateX(-50%);
|
|
30
|
-
z-index:
|
|
31
|
+
z-index: 10000;
|
|
31
32
|
`
|
|
32
33
|
|
|
33
34
|
export const TitleDescriptionContainer = styled.div<{$centerContent?: boolean, $variant?: variant}>`
|
|
@@ -40,7 +40,7 @@ export const Dialog: React.FC<TDialogProps> = ({
|
|
|
40
40
|
onOpenChange={onOpenChange}
|
|
41
41
|
>
|
|
42
42
|
{trigger
|
|
43
|
-
&& <DialogPrimitive.Trigger asChild
|
|
43
|
+
&& <DialogPrimitive.Trigger asChild>
|
|
44
44
|
{trigger}
|
|
45
45
|
</DialogPrimitive.Trigger>
|
|
46
46
|
}
|
|
@@ -85,15 +85,13 @@ export const Dialog: React.FC<TDialogProps> = ({
|
|
|
85
85
|
)}
|
|
86
86
|
|
|
87
87
|
<ButtonContainer $centerContent={centerContent}>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<Button
|
|
91
|
-
variant="neutralOutline"
|
|
92
|
-
>
|
|
88
|
+
{cancelButton && (
|
|
89
|
+
<DialogPrimitive.Close asChild>
|
|
90
|
+
<Button variant="neutralOutline">
|
|
93
91
|
{cancelLabel}
|
|
94
92
|
</Button>
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
</DialogPrimitive.Close>
|
|
94
|
+
)}
|
|
97
95
|
|
|
98
96
|
{confirmButton && (
|
|
99
97
|
<Button
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
|
+
import { useTheme } from 'styled-components'
|
|
2
3
|
import { ButtonProps, RippleInterface } from '../Button/Button.types'
|
|
3
4
|
import { ButtonVariant } from '../Button/Button.appearance'
|
|
4
5
|
import { IconButtonSizes } from './IconButton.sizes'
|
|
@@ -15,10 +16,11 @@ const IconButton: React.FC<ButtonProps> = ({
|
|
|
15
16
|
onClick,
|
|
16
17
|
...rest
|
|
17
18
|
}) => {
|
|
19
|
+
const theme = useTheme()
|
|
18
20
|
const [ripples, setRipples] = useState<RippleInterface[]>([])
|
|
19
21
|
|
|
20
22
|
const buttonSize = IconButtonSizes(size)
|
|
21
|
-
const buttonVariant = ButtonVariant(color, variant)
|
|
23
|
+
const buttonVariant = ButtonVariant(color, variant, theme)
|
|
22
24
|
|
|
23
25
|
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
24
26
|
const rect = e.currentTarget.getBoundingClientRect()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
+
import { useTheme } from 'styled-components'
|
|
2
3
|
import { LinkAnchor, StyledLinkButton } from './LinkButton.style'
|
|
3
4
|
import { RippleContainer } from '../Button/Button.styles'
|
|
4
5
|
import { RippleInterface } from '../Button/Button.types'
|
|
@@ -19,8 +20,9 @@ const LinkButton: React.FC<LinkButtonProps> = ({
|
|
|
19
20
|
size = 'md',
|
|
20
21
|
...rest
|
|
21
22
|
}) => {
|
|
23
|
+
const theme = useTheme()
|
|
22
24
|
const [ripples, setRipples] = useState<RippleInterface[]>([])
|
|
23
|
-
const buttonVariant = ButtonVariant(color, variant)
|
|
25
|
+
const buttonVariant = ButtonVariant(color, variant, theme)
|
|
24
26
|
const buttonSize = LinkButtonSizes(size)
|
|
25
27
|
|
|
26
28
|
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
@@ -16,7 +16,7 @@ const PageWrapper = styled.div.withConfig({
|
|
|
16
16
|
})<WrapperProps>`
|
|
17
17
|
width: 100%;
|
|
18
18
|
height: 100%;
|
|
19
|
-
max-width: ${({width}) => width + 'px'
|
|
19
|
+
max-width: ${({width}) => width ? width + 'px' : '100%'};
|
|
20
20
|
margin: 0 auto;
|
|
21
21
|
padding: ${({padding}) =>
|
|
22
22
|
padding ? spacing[padding] + 'px' : spacing.spacing16 + 'px'};
|
|
@@ -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,198 @@
|
|
|
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
|
+
}
|
|
103
|
+
`
|
|
104
|
+
|
|
105
|
+
export const Divider = styled.div`
|
|
106
|
+
width: 100%;
|
|
107
|
+
height: 1px;
|
|
108
|
+
background: ${({ theme }) => parseColor(theme.colors.neutral300)};
|
|
109
|
+
`
|
|
110
|
+
|
|
111
|
+
export const CardContent = styled.div`
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
gap: ${spacing.spacing8}px;
|
|
115
|
+
`
|
|
116
|
+
|
|
117
|
+
export const DescriptionWrapper = styled.div<{ $expanded: boolean; $maxLines: number }>`
|
|
118
|
+
position: relative;
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
max-height: ${({ $expanded, $maxLines }) =>
|
|
121
|
+
$expanded ? 'none' : `calc(${lineHeight.lineHeight16}px * ${$maxLines})`};
|
|
122
|
+
`
|
|
123
|
+
|
|
124
|
+
export const DescriptionText = styled.p`
|
|
125
|
+
font-size: ${fontSize.fontSize12}px;
|
|
126
|
+
font-weight: ${fontWeight.fontWeight400};
|
|
127
|
+
line-height: ${lineHeight.lineHeight16}px;
|
|
128
|
+
color: ${({ theme }) => parseColor(theme.colors.textDark)};
|
|
129
|
+
margin: 0;
|
|
130
|
+
`
|
|
131
|
+
|
|
132
|
+
export const SeeMoreButton = styled.button`
|
|
133
|
+
background: none;
|
|
134
|
+
border: none;
|
|
135
|
+
padding: 0;
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
font-size: ${fontSize.fontSize12}px;
|
|
138
|
+
font-weight: ${fontWeight.fontWeight500};
|
|
139
|
+
line-height: ${lineHeight.lineHeight16}px;
|
|
140
|
+
color: ${({ theme }) => parseColor(theme.colors.primary)};
|
|
141
|
+
text-align: left;
|
|
142
|
+
margin-top: 2px;
|
|
143
|
+
|
|
144
|
+
&:hover {
|
|
145
|
+
text-decoration: underline;
|
|
146
|
+
}
|
|
147
|
+
`
|
|
148
|
+
|
|
149
|
+
export const BadgesSection = styled.div`
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
gap: ${spacing.spacing8}px;
|
|
153
|
+
`
|
|
154
|
+
|
|
155
|
+
export const BadgesLabel = styled.p`
|
|
156
|
+
font-size: ${fontSize.fontSize12}px;
|
|
157
|
+
font-weight: ${fontWeight.fontWeight500};
|
|
158
|
+
line-height: ${lineHeight.lineHeight16}px;
|
|
159
|
+
color: ${({ theme }) => parseColor(theme.colors.textMedium)};
|
|
160
|
+
margin: 0;
|
|
161
|
+
white-space: nowrap;
|
|
162
|
+
`
|
|
163
|
+
|
|
164
|
+
export const BadgesList = styled.div`
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-wrap: wrap;
|
|
167
|
+
gap: ${spacing.spacing4}px;
|
|
168
|
+
align-items: center;
|
|
169
|
+
`
|
|
170
|
+
|
|
171
|
+
export const GroupWrapper = styled.div<{ $columns?: number; $minCardWidth?: string; $scrollable?: boolean; $maxHeight?: string }>`
|
|
172
|
+
display: flex;
|
|
173
|
+
flex-wrap: wrap;
|
|
174
|
+
flex-direction: ${({ $columns }) => ($columns ? 'row' : 'column')};
|
|
175
|
+
gap: ${spacing.spacing12}px;
|
|
176
|
+
width: 100%;
|
|
177
|
+
|
|
178
|
+
${({ $scrollable, $maxHeight }) => $scrollable && `
|
|
179
|
+
overflow-y: auto;
|
|
180
|
+
max-height: ${$maxHeight ?? '400px'};
|
|
181
|
+
padding-right: ${spacing.spacing4}px;
|
|
182
|
+
|
|
183
|
+
scrollbar-width: thin;
|
|
184
|
+
scrollbar-color: #7c7c83 #cfcfd1;
|
|
185
|
+
|
|
186
|
+
&::-webkit-scrollbar {
|
|
187
|
+
width: 8px;
|
|
188
|
+
}
|
|
189
|
+
&::-webkit-scrollbar-track {
|
|
190
|
+
background: #cfcfd1;
|
|
191
|
+
border-radius: 100px;
|
|
192
|
+
}
|
|
193
|
+
&::-webkit-scrollbar-thumb {
|
|
194
|
+
background: #7c7c83;
|
|
195
|
+
border-radius: 100px;
|
|
196
|
+
}
|
|
197
|
+
`}
|
|
198
|
+
`
|