@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,7 +1,8 @@
|
|
|
1
|
-
import React, { forwardRef, useState } from 'react'
|
|
1
|
+
import React, { forwardRef, useEffect, useRef, useState } from 'react'
|
|
2
2
|
import { TextFieldProps } from './TextField.types'
|
|
3
3
|
import { StateInterface, TextFieldStates } from './TextField.states'
|
|
4
4
|
import {
|
|
5
|
+
CharCount,
|
|
5
6
|
FileButton,
|
|
6
7
|
FileInputContainer,
|
|
7
8
|
FileName,
|
|
@@ -10,6 +11,9 @@ import {
|
|
|
10
11
|
InputWrapper,
|
|
11
12
|
Label,
|
|
12
13
|
StyledInput,
|
|
14
|
+
StyledTextArea,
|
|
15
|
+
TextAreaFooter,
|
|
16
|
+
TextAreaWrapper,
|
|
13
17
|
Wrapper
|
|
14
18
|
} from './TextField.styles'
|
|
15
19
|
|
|
@@ -17,6 +21,7 @@ import { textFieldSizes } from './TextField.sizes'
|
|
|
17
21
|
import { EyeIcon, EyeClosedIcon } from '@phosphor-icons/react'
|
|
18
22
|
import getState from './utils/getState'
|
|
19
23
|
import RequiredAsterisk from '../RequiredAsterisk'
|
|
24
|
+
import Button from '../Button'
|
|
20
25
|
|
|
21
26
|
const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
22
27
|
(
|
|
@@ -39,12 +44,22 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
39
44
|
register,
|
|
40
45
|
requiredSymbol = false,
|
|
41
46
|
placeholder,
|
|
47
|
+
showVariableButton = false,
|
|
48
|
+
variableButtonLabel = 'Variável',
|
|
49
|
+
maxLength,
|
|
50
|
+
htmlMaxLength = false,
|
|
51
|
+
onTextAreaChange,
|
|
42
52
|
...props
|
|
43
53
|
},
|
|
44
54
|
ref
|
|
45
55
|
) => {
|
|
46
56
|
const [passwordVisible, setPasswordVisible] = useState(false)
|
|
47
57
|
const [selectedFileName, setSelectedFileName] = useState('')
|
|
58
|
+
const [textAreaValue, setTextAreaValue] = useState(
|
|
59
|
+
(value as string) ?? ''
|
|
60
|
+
)
|
|
61
|
+
const textAreaRef = useRef<HTMLTextAreaElement>(null)
|
|
62
|
+
const varCountRef = useRef(0)
|
|
48
63
|
const state = getState(disabled, !!error)
|
|
49
64
|
const textFieldState: StateInterface = TextFieldStates(state)
|
|
50
65
|
const textFieldSize = textFieldSizes(size, !!leftIcon, !!rightIcon)
|
|
@@ -131,6 +146,108 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
131
146
|
e.stopPropagation()
|
|
132
147
|
setIsDragging(false)
|
|
133
148
|
}
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (value !== undefined) {
|
|
151
|
+
setTextAreaValue(value as string)
|
|
152
|
+
}
|
|
153
|
+
}, [value])
|
|
154
|
+
|
|
155
|
+
const handleInsertVariable = () => {
|
|
156
|
+
const el = textAreaRef.current
|
|
157
|
+
if (!el) return
|
|
158
|
+
|
|
159
|
+
const matches = textAreaValue.match(/\{\{(\d+)\}\}/g) ?? []
|
|
160
|
+
const usedNumbers = matches.map(m => parseInt(m.replace(/\D/g, ''), 10))
|
|
161
|
+
let next = 1
|
|
162
|
+
while (usedNumbers.includes(next)) next++
|
|
163
|
+
varCountRef.current = next
|
|
164
|
+
const variable = `{{${next}}}`
|
|
165
|
+
const start = el.selectionStart ?? textAreaValue.length
|
|
166
|
+
const end = el.selectionEnd ?? textAreaValue.length
|
|
167
|
+
const newValue =
|
|
168
|
+
textAreaValue.slice(0, start) +
|
|
169
|
+
variable +
|
|
170
|
+
textAreaValue.slice(end)
|
|
171
|
+
|
|
172
|
+
if (maxLength !== undefined && newValue.length > maxLength) return
|
|
173
|
+
|
|
174
|
+
setTextAreaValue(newValue)
|
|
175
|
+
|
|
176
|
+
requestAnimationFrame(() => {
|
|
177
|
+
el.focus()
|
|
178
|
+
el.setSelectionRange(
|
|
179
|
+
start + variable.length,
|
|
180
|
+
start + variable.length
|
|
181
|
+
)
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const handleTextAreaChange = (
|
|
186
|
+
e: React.ChangeEvent<HTMLTextAreaElement>
|
|
187
|
+
) => {
|
|
188
|
+
setTextAreaValue(e.target.value)
|
|
189
|
+
onTextAreaChange?.(e)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (type === 'textarea') {
|
|
193
|
+
const charCount = textAreaValue.length
|
|
194
|
+
const isOver = maxLength !== undefined && charCount > maxLength
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<Wrapper
|
|
198
|
+
className={className}
|
|
199
|
+
size={textFieldSize}
|
|
200
|
+
$themefication={textFieldState}
|
|
201
|
+
>
|
|
202
|
+
{label && (
|
|
203
|
+
<Label>
|
|
204
|
+
{label}{' '}
|
|
205
|
+
{requiredSymbol && <RequiredAsterisk />}
|
|
206
|
+
</Label>
|
|
207
|
+
)}
|
|
208
|
+
<TextAreaWrapper>
|
|
209
|
+
<StyledTextArea
|
|
210
|
+
ref={textAreaRef}
|
|
211
|
+
value={textAreaValue}
|
|
212
|
+
disabled={disabled}
|
|
213
|
+
placeholder={placeholder}
|
|
214
|
+
maxLength={htmlMaxLength ? maxLength : undefined}
|
|
215
|
+
onChange={handleTextAreaChange}
|
|
216
|
+
aria-invalid={!!error}
|
|
217
|
+
aria-describedby={
|
|
218
|
+
helperText || error
|
|
219
|
+
? `${label}-helper`
|
|
220
|
+
: undefined
|
|
221
|
+
}
|
|
222
|
+
/>
|
|
223
|
+
</TextAreaWrapper>
|
|
224
|
+
<TextAreaFooter>
|
|
225
|
+
{maxLength !== undefined && (
|
|
226
|
+
<CharCount $isOver={isOver}>
|
|
227
|
+
{charCount}/{maxLength}
|
|
228
|
+
</CharCount>
|
|
229
|
+
)}
|
|
230
|
+
{showVariableButton && (
|
|
231
|
+
<Button
|
|
232
|
+
size="sm"
|
|
233
|
+
variant="ghost"
|
|
234
|
+
type="button"
|
|
235
|
+
disabled={disabled}
|
|
236
|
+
onClick={handleInsertVariable}
|
|
237
|
+
aria-label={variableButtonLabel}
|
|
238
|
+
>
|
|
239
|
+
{variableButtonLabel}
|
|
240
|
+
</Button>
|
|
241
|
+
)}
|
|
242
|
+
</TextAreaFooter>
|
|
243
|
+
{(helperText || error) && (
|
|
244
|
+
<HelperText id={`${label}-helper`}>
|
|
245
|
+
{error?.message || helperText}
|
|
246
|
+
</HelperText>
|
|
247
|
+
)}
|
|
248
|
+
</Wrapper>
|
|
249
|
+
)
|
|
250
|
+
}
|
|
134
251
|
|
|
135
252
|
if (type === 'file') {
|
|
136
253
|
return (
|
|
@@ -138,9 +255,11 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
138
255
|
className={className}
|
|
139
256
|
size={textFieldSize}
|
|
140
257
|
$themefication={textFieldState}>
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
258
|
+
{label && (
|
|
259
|
+
<Label>
|
|
260
|
+
{label} {requiredSymbol && <RequiredAsterisk />}
|
|
261
|
+
</Label>
|
|
262
|
+
)}
|
|
144
263
|
<InputWrapper
|
|
145
264
|
as="label"
|
|
146
265
|
onDragOver={handleDragOver}
|
|
@@ -181,10 +300,13 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
181
300
|
<Wrapper
|
|
182
301
|
className={className}
|
|
183
302
|
size={textFieldSize}
|
|
184
|
-
$themefication={textFieldState}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
303
|
+
$themefication={textFieldState}
|
|
304
|
+
>
|
|
305
|
+
{label && (
|
|
306
|
+
<Label>
|
|
307
|
+
{label} {requiredSymbol && <RequiredAsterisk />}
|
|
308
|
+
</Label>
|
|
309
|
+
)}
|
|
188
310
|
<InputWrapper>
|
|
189
311
|
{leftIcon && (
|
|
190
312
|
<IconWrapper onClick={handleLeftIcon}>
|
|
@@ -20,6 +20,16 @@ export interface TextFieldProps<TFieldValues extends FieldValues = FieldValues>
|
|
|
20
20
|
rightIcon?: React.ReactNode
|
|
21
21
|
error?: TFieldValues
|
|
22
22
|
requiredSymbol?: boolean
|
|
23
|
-
type?: 'text' | 'password' | 'email' | 'number' | 'file'
|
|
23
|
+
type?: 'text' | 'password' | 'email' | 'number' | 'file' | 'textarea'
|
|
24
24
|
register?: UseFormRegisterReturn<string>
|
|
25
|
+
/** Habilita o botão de inserir variável (apenas para type="textarea") */
|
|
26
|
+
showVariableButton?: boolean
|
|
27
|
+
/** Label do botão de variável */
|
|
28
|
+
variableButtonLabel?: string
|
|
29
|
+
/** Limite máximo de caracteres (exibido no rodapé quando type="textarea") */
|
|
30
|
+
maxLength?: number
|
|
31
|
+
/** Passa o maxLength nativo ao <textarea>, bloqueando digitação pelo browser. Default: false */
|
|
32
|
+
htmlMaxLength?: boolean
|
|
33
|
+
/** Callback chamado quando o valor do textarea muda */
|
|
34
|
+
onTextAreaChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
|
|
25
35
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toast, ToastOptions } from
|
|
1
|
+
import { toast, ToastOptions } from 'react-toastify'
|
|
2
2
|
|
|
3
3
|
const defaultOptions: ToastOptions = {
|
|
4
4
|
position: 'top-right',
|
|
@@ -11,23 +11,6 @@ const defaultOptions: ToastOptions = {
|
|
|
11
11
|
|
|
12
12
|
export type ToastType = 'success' | 'error' | 'warning' | 'info'
|
|
13
13
|
|
|
14
|
-
export const triggerToast = (type: ToastType, message: string, options?: ToastOptions) => {
|
|
15
|
-
const mergedOptions = { ...defaultOptions, ...options }
|
|
16
|
-
|
|
17
|
-
switch (type) {
|
|
18
|
-
case 'success':
|
|
19
|
-
return toast.success(message, mergedOptions)
|
|
20
|
-
case 'error':
|
|
21
|
-
return toast.error(message, mergedOptions)
|
|
22
|
-
case 'warning':
|
|
23
|
-
return toast.warning(message, mergedOptions)
|
|
24
|
-
case 'info':
|
|
25
|
-
return toast.info(message, mergedOptions)
|
|
26
|
-
default:
|
|
27
|
-
return toast(message, mergedOptions)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
14
|
const Toaster = {
|
|
32
15
|
success: (message: string, options?: ToastOptions) =>
|
|
33
16
|
toast.success(message, { ...defaultOptions, ...options }),
|
|
@@ -42,4 +25,7 @@ const Toaster = {
|
|
|
42
25
|
toast.info(message, { ...defaultOptions, ...options })
|
|
43
26
|
}
|
|
44
27
|
|
|
45
|
-
export
|
|
28
|
+
export const triggerToast = (type: ToastType, message: string, options?: ToastOptions) =>
|
|
29
|
+
Toaster[type](message, options)
|
|
30
|
+
|
|
31
|
+
export default Toaster
|