masked-components-cli 1.5.0
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/README.md +189 -0
- package/dist/cli.js +103 -0
- package/package.json +40 -0
- package/templates/masked-buttons/MaskedButton/Base/BaseButton.styles.ts +155 -0
- package/templates/masked-buttons/MaskedButton/Base/BaseButton.tsx +75 -0
- package/templates/masked-buttons/MaskedButton/MaskedButton.tsx +38 -0
- package/templates/masked-buttons/MaskedButton/MaskedButton.types.ts +67 -0
- package/templates/masked-buttons/MaskedButton/variants/Default/DefaultButton.styles.ts +53 -0
- package/templates/masked-buttons/MaskedButton/variants/Default/DefaultButton.tsx +10 -0
- package/templates/masked-buttons/MaskedButton/variants/Ghost/GhostButton.styles.ts +52 -0
- package/templates/masked-buttons/MaskedButton/variants/Ghost/GhostButton.tsx +10 -0
- package/templates/masked-buttons/MaskedButton/variants/Gradient/GradientButton.styles.ts +52 -0
- package/templates/masked-buttons/MaskedButton/variants/Gradient/GradientButton.tsx +10 -0
- package/templates/masked-buttons/MaskedButton/variants/Link/LinkButton.styles.ts +55 -0
- package/templates/masked-buttons/MaskedButton/variants/Link/LinkButton.tsx +10 -0
- package/templates/masked-buttons/MaskedButton/variants/Neon/NeonButton.styles.ts +57 -0
- package/templates/masked-buttons/MaskedButton/variants/Neon/NeonButton.tsx +10 -0
- package/templates/masked-buttons/MaskedButton/variants/Outline/OutlineButton.styles.ts +53 -0
- package/templates/masked-buttons/MaskedButton/variants/Outline/OutlineButton.tsx +10 -0
- package/templates/masked-buttons/MaskedButton/variants/Toggle/ToggleButton.styles.ts +70 -0
- package/templates/masked-buttons/MaskedButton/variants/Toggle/ToggleButton.tsx +15 -0
- package/templates/masked-cards/MaskedCards/MaskedCards.styles.ts +0 -0
- package/templates/masked-cards/MaskedCards/MaskedCards.tsx +0 -0
- package/templates/masked-cards/MaskedCards/MaskedCardsStyles.ts +0 -0
- package/templates/masked-input/MaskedInput/FormikMaskedInput.tsx +44 -0
- package/templates/masked-input/MaskedInput/MaskedInput.styles.ts +298 -0
- package/templates/masked-input/MaskedInput/MaskedInput.tsx +42 -0
- package/templates/masked-input/MaskedInput/MaskedInput.types.ts +79 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikCurrencyInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikFileInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikMaskedTextInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikPasswordInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikSearchInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikSelectInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikTextInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/formikVariants/FormikTextareaInput.tsx +13 -0
- package/templates/masked-input/MaskedInput/variants/CurrencyInput.tsx +100 -0
- package/templates/masked-input/MaskedInput/variants/FileInput.tsx +94 -0
- package/templates/masked-input/MaskedInput/variants/MaskedInput.tsx +35 -0
- package/templates/masked-input/MaskedInput/variants/PasswordInput.tsx +39 -0
- package/templates/masked-input/MaskedInput/variants/SearchInput.tsx +47 -0
- package/templates/masked-input/MaskedInput/variants/SelectInput.tsx +67 -0
- package/templates/masked-input/MaskedInput/variants/TextInput.tsx +34 -0
- package/templates/masked-input/MaskedInput/variants/TextareaInput.tsx +33 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Image from 'next/image'
|
|
4
|
+
import { useEffect, useRef, useState } from 'react'
|
|
5
|
+
import toast from 'react-hot-toast'
|
|
6
|
+
import { ErrorDiv, FileTrigger, InputLabel, MaskedInputContainer, PreviewImageDiv } from '../MaskedInput.styles'
|
|
7
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
8
|
+
|
|
9
|
+
type Props = { variant: 'file' } & InputVariantMap['file']
|
|
10
|
+
|
|
11
|
+
export function FileInput(props: Props) {
|
|
12
|
+
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
13
|
+
const [previews, setPreviews] = useState<string[]>([])
|
|
14
|
+
const prev = useRef<string[]>([])
|
|
15
|
+
|
|
16
|
+
const hasError = props.touched && props.error
|
|
17
|
+
|
|
18
|
+
function handleFiles(filesList: FileList | null) {
|
|
19
|
+
if (!filesList) return
|
|
20
|
+
|
|
21
|
+
const files = Array.from(filesList)
|
|
22
|
+
const urls = files.map(f => URL.createObjectURL(f))
|
|
23
|
+
|
|
24
|
+
prev.current.forEach(URL.revokeObjectURL)
|
|
25
|
+
prev.current = urls
|
|
26
|
+
|
|
27
|
+
if (props.previewMode !== 'replace') setPreviews(urls)
|
|
28
|
+
|
|
29
|
+
props.onFileChange?.({ files, previews: urls })
|
|
30
|
+
|
|
31
|
+
if (props.fileMode === 'local') {
|
|
32
|
+
props.onChange?.(props.multiple ? files : files[0])
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
uploadToCloudinary(files)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function uploadToCloudinary(files: File[]) {
|
|
40
|
+
if (!props.uploadPreset || !props.cloudName) return
|
|
41
|
+
|
|
42
|
+
props.onUploadingChange?.(true)
|
|
43
|
+
const uploaded: string[] = []
|
|
44
|
+
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const fd = new FormData()
|
|
47
|
+
fd.append('file', file)
|
|
48
|
+
fd.append('upload_preset', props.uploadPreset)
|
|
49
|
+
|
|
50
|
+
const res = await fetch(`https://api.cloudinary.com/v1_1/${props.cloudName}/image/upload`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: fd
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const data = await res.json()
|
|
56
|
+
uploaded.push(data.secure_url)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
props.onChange?.(props.multiple ? uploaded : uploaded[0])
|
|
60
|
+
toast.success('Upload concluído!')
|
|
61
|
+
props.onUploadingChange?.(false)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
return () => prev.current.forEach(URL.revokeObjectURL)
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<MaskedInputContainer $variant="file" $radius={props.radius}>
|
|
70
|
+
<input ref={inputRef} type="file" hidden multiple={props.multiple} onChange={e => handleFiles(e.target.files)} />
|
|
71
|
+
|
|
72
|
+
{props.label && (
|
|
73
|
+
<InputLabel htmlFor={props.id}>
|
|
74
|
+
{props.icon && props.icon}
|
|
75
|
+
<span>{props.label}</span>
|
|
76
|
+
</InputLabel>
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
<FileTrigger type="button" onClick={() => inputRef.current?.click()}>
|
|
80
|
+
{props.multiple ? 'Selecionar arquivos' : 'Selecionar arquivo'}
|
|
81
|
+
</FileTrigger>
|
|
82
|
+
|
|
83
|
+
{props.previewMode !== 'replace' && previews.length > 0 && (
|
|
84
|
+
<PreviewImageDiv>
|
|
85
|
+
{previews.map((src, i) => (
|
|
86
|
+
<Image key={i} src={src} alt="Preview" width={88} height={88} />
|
|
87
|
+
))}
|
|
88
|
+
</PreviewImageDiv>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{props.showError && hasError && <ErrorDiv>{props.error}</ErrorDiv>}
|
|
92
|
+
</MaskedInputContainer>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { IMaskInput } from 'react-imask'
|
|
4
|
+
import { ErrorDiv, InputLabel, MaskedInputContainer } from '../MaskedInput.styles'
|
|
5
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
6
|
+
|
|
7
|
+
type Props = { variant: 'masked' } & InputVariantMap['masked']
|
|
8
|
+
|
|
9
|
+
export function MaskedInput(props: Props) {
|
|
10
|
+
const hasError = props.touched && props.error
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<MaskedInputContainer $variant="masked" $radius={props.radius}>
|
|
14
|
+
{props.label && (
|
|
15
|
+
<InputLabel htmlFor={props.id}>
|
|
16
|
+
{props.icon && props.icon}
|
|
17
|
+
<span>{props.label}</span>
|
|
18
|
+
</InputLabel>
|
|
19
|
+
)}
|
|
20
|
+
|
|
21
|
+
<IMaskInput
|
|
22
|
+
id={props.id}
|
|
23
|
+
mask={props.mask}
|
|
24
|
+
value={props.value ?? ''}
|
|
25
|
+
placeholder={props.placeholder}
|
|
26
|
+
onAccept={val => props.onChange?.(val)}
|
|
27
|
+
className={hasError ? 'error' : ''}
|
|
28
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
29
|
+
aria-describedby={hasError ? `${props.id}-error` : undefined}
|
|
30
|
+
/>
|
|
31
|
+
|
|
32
|
+
{props.showError && hasError && <ErrorDiv id={`${props.id}-error`}>{props.error}</ErrorDiv>}
|
|
33
|
+
</MaskedInputContainer>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai'
|
|
5
|
+
import { ErrorDiv, InputLabel, MaskedInputContainer, PasswordToggle } from '../MaskedInput.styles'
|
|
6
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
7
|
+
|
|
8
|
+
type Props = { variant: 'password' } & InputVariantMap['password']
|
|
9
|
+
|
|
10
|
+
export function PasswordInput(props: Props) {
|
|
11
|
+
const [show, setShow] = useState(false)
|
|
12
|
+
const hasError = props.touched && props.error
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<MaskedInputContainer $variant="password" $radius={props.radius} data-error={hasError}>
|
|
16
|
+
{props.label && (
|
|
17
|
+
<InputLabel htmlFor={props.id}>
|
|
18
|
+
{props.icon && props.icon}
|
|
19
|
+
<span>{props.label}</span>
|
|
20
|
+
</InputLabel>
|
|
21
|
+
)}
|
|
22
|
+
|
|
23
|
+
<input
|
|
24
|
+
id={props.id}
|
|
25
|
+
type={show ? 'text' : 'password'}
|
|
26
|
+
value={props.value ?? ''}
|
|
27
|
+
onChange={e => props.onChange?.(e.target.value)}
|
|
28
|
+
className={hasError ? 'error' : ''}
|
|
29
|
+
placeholder={props.placeholder}
|
|
30
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
31
|
+
aria-describedby={hasError ? `${props.id}-error` : undefined}
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<PasswordToggle onClick={() => setShow(s => !s)}>{show ? <AiOutlineEyeInvisible /> : <AiOutlineEye />}</PasswordToggle>
|
|
35
|
+
|
|
36
|
+
{props.showError && hasError && <ErrorDiv id={`${props.id}-error`}>{props.error}</ErrorDiv>}
|
|
37
|
+
</MaskedInputContainer>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { FiSearch } from 'react-icons/fi'
|
|
4
|
+
import { ErrorDiv, InputLabel, MaskedInputContainer, SearchIcon } from '../MaskedInput.styles'
|
|
5
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
6
|
+
|
|
7
|
+
type Props = { variant: 'search' } & InputVariantMap['search']
|
|
8
|
+
|
|
9
|
+
export function SearchInput(props: Props) {
|
|
10
|
+
const hasError = props.touched && props.error
|
|
11
|
+
|
|
12
|
+
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
|
13
|
+
if (e.key === 'Enter') {
|
|
14
|
+
props.onSearch?.(props.value ?? '')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<MaskedInputContainer $variant="search" $radius={props.radius} data-error={hasError}>
|
|
20
|
+
{props.label && (
|
|
21
|
+
<InputLabel htmlFor={props.id}>
|
|
22
|
+
{props.icon && props.icon}
|
|
23
|
+
<span>{props.label}</span>
|
|
24
|
+
</InputLabel>
|
|
25
|
+
)}
|
|
26
|
+
|
|
27
|
+
<SearchIcon>
|
|
28
|
+
<FiSearch />
|
|
29
|
+
</SearchIcon>
|
|
30
|
+
|
|
31
|
+
<input
|
|
32
|
+
id={props.id}
|
|
33
|
+
type="search"
|
|
34
|
+
value={props.value ?? ''}
|
|
35
|
+
onChange={e => props.onChange?.(e.target.value)}
|
|
36
|
+
onKeyDown={handleKeyDown}
|
|
37
|
+
placeholder={props.placeholder}
|
|
38
|
+
className={hasError ? 'error' : ''}
|
|
39
|
+
style={{ paddingLeft: 40 }}
|
|
40
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
41
|
+
aria-describedby={hasError ? `${props.id}-error` : undefined}
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
{props.showError && hasError && <ErrorDiv id={`${props.id}-error`}>{props.error}</ErrorDiv>}
|
|
45
|
+
</MaskedInputContainer>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { ErrorDiv, InputLabel, MaskedInputContainer, SelectDropdown, SelectOption, SelectTrigger } from '../MaskedInput.styles'
|
|
5
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
6
|
+
|
|
7
|
+
type props = { variant: 'select' } & InputVariantMap['select']
|
|
8
|
+
|
|
9
|
+
export function SelectInput(props: props) {
|
|
10
|
+
const [open, setOpen] = useState(false)
|
|
11
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
12
|
+
|
|
13
|
+
const hasError = props.touched && Boolean(props.error)
|
|
14
|
+
|
|
15
|
+
const selectedOption = props.options.find(opt => opt.value === props.value)
|
|
16
|
+
|
|
17
|
+
/* fecha ao clicar fora */
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
function handleClickOutside(e: MouseEvent) {
|
|
20
|
+
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
|
|
21
|
+
setOpen(false)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
26
|
+
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<MaskedInputContainer $variant="select" ref={containerRef} $radius={props.radius} data-error={hasError}>
|
|
31
|
+
{props.label && (
|
|
32
|
+
<InputLabel>
|
|
33
|
+
{props.icon && props.icon}
|
|
34
|
+
<span>{props.label}</span>
|
|
35
|
+
</InputLabel>
|
|
36
|
+
)}
|
|
37
|
+
|
|
38
|
+
{/* TRIGGER */}
|
|
39
|
+
<SelectTrigger type="button" className={` ${hasError ? 'error' : ''}`} onClick={() => setOpen(o => !o)}>
|
|
40
|
+
{selectedOption?.label ?? 'Selecione uma opção'}
|
|
41
|
+
<span className={`arrow ${open ? 'open' : ''}`} />
|
|
42
|
+
</SelectTrigger>
|
|
43
|
+
|
|
44
|
+
{/* DROPDOWN */}
|
|
45
|
+
{open && (
|
|
46
|
+
<SelectDropdown>
|
|
47
|
+
{props.options.map(option => (
|
|
48
|
+
<SelectOption
|
|
49
|
+
key={option.value}
|
|
50
|
+
className={`${option.value === props.value ? 'selected' : ''}`}
|
|
51
|
+
onClick={() => {
|
|
52
|
+
props.onChange?.(option.value)
|
|
53
|
+
setOpen(false)
|
|
54
|
+
}}
|
|
55
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
56
|
+
aria-describedby={hasError ? `${props.id}-error` : undefined}
|
|
57
|
+
>
|
|
58
|
+
{option.label}
|
|
59
|
+
</SelectOption>
|
|
60
|
+
))}
|
|
61
|
+
</SelectDropdown>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
{props.showError && hasError && <ErrorDiv id={`${props.id}-error`}>{props.error}</ErrorDiv>}
|
|
65
|
+
</MaskedInputContainer>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ErrorDiv, InputLabel, MaskedInputContainer } from '../MaskedInput.styles'
|
|
4
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
5
|
+
|
|
6
|
+
type Props = { variant: 'default' } & InputVariantMap['default']
|
|
7
|
+
|
|
8
|
+
export function TextInput(props: Props) {
|
|
9
|
+
const hasError = props.touched && props.error
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<MaskedInputContainer $variant="default" $radius={props.radius} data-error={hasError}>
|
|
13
|
+
{props.label && (
|
|
14
|
+
<InputLabel htmlFor={props.id}>
|
|
15
|
+
{props.icon && props.icon}
|
|
16
|
+
<span>{props.label}</span>
|
|
17
|
+
</InputLabel>
|
|
18
|
+
)}
|
|
19
|
+
|
|
20
|
+
<input
|
|
21
|
+
id={props.id}
|
|
22
|
+
type={props.type ?? 'text'}
|
|
23
|
+
value={props.value ?? ''}
|
|
24
|
+
onChange={e => props.onChange?.(e.target.value)}
|
|
25
|
+
placeholder={props.placeholder}
|
|
26
|
+
className={hasError ? 'error' : ''}
|
|
27
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
28
|
+
aria-describedby={hasError ? `${props.id}-error` : undefined}
|
|
29
|
+
/>
|
|
30
|
+
|
|
31
|
+
{hasError && <ErrorDiv id={`${props.id}-error`}>{props.error}</ErrorDiv>}
|
|
32
|
+
</MaskedInputContainer>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ErrorDiv, InputLabel, MaskedInputContainer } from '../MaskedInput.styles'
|
|
4
|
+
import { InputVariantMap } from '../MaskedInput.types'
|
|
5
|
+
|
|
6
|
+
type Props = { variant: 'textarea' } & InputVariantMap['textarea']
|
|
7
|
+
|
|
8
|
+
export function TextareaInput(props: Props) {
|
|
9
|
+
const hasError = props.touched && props.error
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<MaskedInputContainer $variant="textarea" $radius={props.radius} data-error={hasError}>
|
|
13
|
+
{props.label && (
|
|
14
|
+
<InputLabel htmlFor={props.id}>
|
|
15
|
+
{props.icon && props.icon}
|
|
16
|
+
<span>{props.label}</span>
|
|
17
|
+
</InputLabel>
|
|
18
|
+
)}
|
|
19
|
+
|
|
20
|
+
<textarea
|
|
21
|
+
id={props.id}
|
|
22
|
+
value={props.value ?? ''}
|
|
23
|
+
onChange={e => props.onChange?.(e.target.value)}
|
|
24
|
+
className={hasError ? 'error' : ''}
|
|
25
|
+
placeholder={props.placeholder}
|
|
26
|
+
aria-invalid={hasError ? 'true' : undefined}
|
|
27
|
+
aria-describedby={hasError ? `${props.id}-error` : undefined}
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
{props.showError && hasError && <ErrorDiv id={`${props.id}-error`}>{props.error}</ErrorDiv>}
|
|
31
|
+
</MaskedInputContainer>
|
|
32
|
+
)
|
|
33
|
+
}
|