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.
Files changed (44) hide show
  1. package/README.md +189 -0
  2. package/dist/cli.js +103 -0
  3. package/package.json +40 -0
  4. package/templates/masked-buttons/MaskedButton/Base/BaseButton.styles.ts +155 -0
  5. package/templates/masked-buttons/MaskedButton/Base/BaseButton.tsx +75 -0
  6. package/templates/masked-buttons/MaskedButton/MaskedButton.tsx +38 -0
  7. package/templates/masked-buttons/MaskedButton/MaskedButton.types.ts +67 -0
  8. package/templates/masked-buttons/MaskedButton/variants/Default/DefaultButton.styles.ts +53 -0
  9. package/templates/masked-buttons/MaskedButton/variants/Default/DefaultButton.tsx +10 -0
  10. package/templates/masked-buttons/MaskedButton/variants/Ghost/GhostButton.styles.ts +52 -0
  11. package/templates/masked-buttons/MaskedButton/variants/Ghost/GhostButton.tsx +10 -0
  12. package/templates/masked-buttons/MaskedButton/variants/Gradient/GradientButton.styles.ts +52 -0
  13. package/templates/masked-buttons/MaskedButton/variants/Gradient/GradientButton.tsx +10 -0
  14. package/templates/masked-buttons/MaskedButton/variants/Link/LinkButton.styles.ts +55 -0
  15. package/templates/masked-buttons/MaskedButton/variants/Link/LinkButton.tsx +10 -0
  16. package/templates/masked-buttons/MaskedButton/variants/Neon/NeonButton.styles.ts +57 -0
  17. package/templates/masked-buttons/MaskedButton/variants/Neon/NeonButton.tsx +10 -0
  18. package/templates/masked-buttons/MaskedButton/variants/Outline/OutlineButton.styles.ts +53 -0
  19. package/templates/masked-buttons/MaskedButton/variants/Outline/OutlineButton.tsx +10 -0
  20. package/templates/masked-buttons/MaskedButton/variants/Toggle/ToggleButton.styles.ts +70 -0
  21. package/templates/masked-buttons/MaskedButton/variants/Toggle/ToggleButton.tsx +15 -0
  22. package/templates/masked-cards/MaskedCards/MaskedCards.styles.ts +0 -0
  23. package/templates/masked-cards/MaskedCards/MaskedCards.tsx +0 -0
  24. package/templates/masked-cards/MaskedCards/MaskedCardsStyles.ts +0 -0
  25. package/templates/masked-input/MaskedInput/FormikMaskedInput.tsx +44 -0
  26. package/templates/masked-input/MaskedInput/MaskedInput.styles.ts +298 -0
  27. package/templates/masked-input/MaskedInput/MaskedInput.tsx +42 -0
  28. package/templates/masked-input/MaskedInput/MaskedInput.types.ts +79 -0
  29. package/templates/masked-input/MaskedInput/formikVariants/FormikCurrencyInput.tsx +13 -0
  30. package/templates/masked-input/MaskedInput/formikVariants/FormikFileInput.tsx +13 -0
  31. package/templates/masked-input/MaskedInput/formikVariants/FormikMaskedTextInput.tsx +13 -0
  32. package/templates/masked-input/MaskedInput/formikVariants/FormikPasswordInput.tsx +13 -0
  33. package/templates/masked-input/MaskedInput/formikVariants/FormikSearchInput.tsx +13 -0
  34. package/templates/masked-input/MaskedInput/formikVariants/FormikSelectInput.tsx +13 -0
  35. package/templates/masked-input/MaskedInput/formikVariants/FormikTextInput.tsx +13 -0
  36. package/templates/masked-input/MaskedInput/formikVariants/FormikTextareaInput.tsx +13 -0
  37. package/templates/masked-input/MaskedInput/variants/CurrencyInput.tsx +100 -0
  38. package/templates/masked-input/MaskedInput/variants/FileInput.tsx +94 -0
  39. package/templates/masked-input/MaskedInput/variants/MaskedInput.tsx +35 -0
  40. package/templates/masked-input/MaskedInput/variants/PasswordInput.tsx +39 -0
  41. package/templates/masked-input/MaskedInput/variants/SearchInput.tsx +47 -0
  42. package/templates/masked-input/MaskedInput/variants/SelectInput.tsx +67 -0
  43. package/templates/masked-input/MaskedInput/variants/TextInput.tsx +34 -0
  44. 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
+ }