@seedgrid/fe-components 0.2.4
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/dist/blocked-email-domains.json +41 -0
- package/dist/buttons/SgButton.d.ts +43 -0
- package/dist/buttons/SgButton.d.ts.map +1 -0
- package/dist/buttons/SgButton.js +123 -0
- package/dist/buttons/SgFloatActionButton.d.ts +60 -0
- package/dist/buttons/SgFloatActionButton.d.ts.map +1 -0
- package/dist/buttons/SgFloatActionButton.js +532 -0
- package/dist/buttons/SgSpeedDial.d.ts +40 -0
- package/dist/buttons/SgSpeedDial.d.ts.map +1 -0
- package/dist/buttons/SgSpeedDial.js +149 -0
- package/dist/buttons/SgSplitButton.d.ts +32 -0
- package/dist/buttons/SgSplitButton.d.ts.map +1 -0
- package/dist/buttons/SgSplitButton.js +81 -0
- package/dist/clock/SgClock.d.ts +28 -0
- package/dist/clock/SgClock.d.ts.map +1 -0
- package/dist/clock/SgClock.js +280 -0
- package/dist/clock/SgTimeProvider.d.ts +13 -0
- package/dist/clock/SgTimeProvider.d.ts.map +1 -0
- package/dist/clock/SgTimeProvider.js +44 -0
- package/dist/clock/themes/SgClockThemePicker.d.ts +14 -0
- package/dist/clock/themes/SgClockThemePicker.d.ts.map +1 -0
- package/dist/clock/themes/SgClockThemePicker.js +71 -0
- package/dist/clock/themes/SgClockThemePreview.d.ts +7 -0
- package/dist/clock/themes/SgClockThemePreview.d.ts.map +1 -0
- package/dist/clock/themes/SgClockThemePreview.js +11 -0
- package/dist/clock/themes/builtins.d.ts +3 -0
- package/dist/clock/themes/builtins.d.ts.map +1 -0
- package/dist/clock/themes/builtins.js +241 -0
- package/dist/clock/themes/index.d.ts +9 -0
- package/dist/clock/themes/index.d.ts.map +1 -0
- package/dist/clock/themes/index.js +7 -0
- package/dist/clock/themes/provider.d.ts +19 -0
- package/dist/clock/themes/provider.d.ts.map +1 -0
- package/dist/clock/themes/provider.js +54 -0
- package/dist/clock/themes/registry.d.ts +9 -0
- package/dist/clock/themes/registry.d.ts.map +1 -0
- package/dist/clock/themes/registry.js +25 -0
- package/dist/clock/themes/renderTheme.d.ts +7 -0
- package/dist/clock/themes/renderTheme.d.ts.map +1 -0
- package/dist/clock/themes/renderTheme.js +41 -0
- package/dist/clock/themes/types.d.ts +21 -0
- package/dist/clock/themes/types.d.ts.map +1 -0
- package/dist/clock/themes/types.js +1 -0
- package/dist/clock/themes/urlThemeCache.d.ts +2 -0
- package/dist/clock/themes/urlThemeCache.d.ts.map +1 -0
- package/dist/clock/themes/urlThemeCache.js +11 -0
- package/dist/clock/themes/useDarkFlag.d.ts +2 -0
- package/dist/clock/themes/useDarkFlag.d.ts.map +1 -0
- package/dist/clock/themes/useDarkFlag.js +14 -0
- package/dist/commons/SgBadge.d.ts +51 -0
- package/dist/commons/SgBadge.d.ts.map +1 -0
- package/dist/commons/SgBadge.js +141 -0
- package/dist/commons/SgBadgeOverlay.d.ts +13 -0
- package/dist/commons/SgBadgeOverlay.d.ts.map +1 -0
- package/dist/commons/SgBadgeOverlay.js +20 -0
- package/dist/commons/SgButton.d.ts +39 -0
- package/dist/commons/SgButton.d.ts.map +1 -0
- package/dist/commons/SgButton.js +116 -0
- package/dist/commons/SgPopup.d.ts +42 -0
- package/dist/commons/SgPopup.d.ts.map +1 -0
- package/dist/commons/SgPopup.js +218 -0
- package/dist/commons/SgToast.d.ts +44 -0
- package/dist/commons/SgToast.d.ts.map +1 -0
- package/dist/commons/SgToast.js +97 -0
- package/dist/commons/SgToaster.d.ts +11 -0
- package/dist/commons/SgToaster.d.ts.map +1 -0
- package/dist/commons/SgToaster.js +85 -0
- package/dist/commons/common-passwords.d.ts +2 -0
- package/dist/commons/common-passwords.d.ts.map +1 -0
- package/dist/commons/common-passwords.js +167 -0
- package/dist/environment/SgEnvironmentProvider.d.ts +31 -0
- package/dist/environment/SgEnvironmentProvider.d.ts.map +1 -0
- package/dist/environment/SgEnvironmentProvider.js +120 -0
- package/dist/environment/persistence.d.ts +44 -0
- package/dist/environment/persistence.d.ts.map +1 -0
- package/dist/environment/persistence.js +149 -0
- package/dist/gadgets/clock/SgClock.d.ts +18 -0
- package/dist/gadgets/clock/SgClock.d.ts.map +1 -0
- package/dist/gadgets/clock/SgClock.js +407 -0
- package/dist/gadgets/clock/SgTimeProvider.d.ts +13 -0
- package/dist/gadgets/clock/SgTimeProvider.d.ts.map +1 -0
- package/dist/gadgets/clock/SgTimeProvider.js +44 -0
- package/dist/gadgets/clock/themes/SgClockThemePicker.d.ts +14 -0
- package/dist/gadgets/clock/themes/SgClockThemePicker.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/SgClockThemePicker.js +71 -0
- package/dist/gadgets/clock/themes/SgClockThemePreview.d.ts +7 -0
- package/dist/gadgets/clock/themes/SgClockThemePreview.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/SgClockThemePreview.js +11 -0
- package/dist/gadgets/clock/themes/builtins.d.ts +3 -0
- package/dist/gadgets/clock/themes/builtins.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/builtins.js +241 -0
- package/dist/gadgets/clock/themes/index.d.ts +9 -0
- package/dist/gadgets/clock/themes/index.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/index.js +7 -0
- package/dist/gadgets/clock/themes/provider.d.ts +19 -0
- package/dist/gadgets/clock/themes/provider.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/provider.js +54 -0
- package/dist/gadgets/clock/themes/registry.d.ts +9 -0
- package/dist/gadgets/clock/themes/registry.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/registry.js +25 -0
- package/dist/gadgets/clock/themes/renderTheme.d.ts +7 -0
- package/dist/gadgets/clock/themes/renderTheme.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/renderTheme.js +41 -0
- package/dist/gadgets/clock/themes/types.d.ts +21 -0
- package/dist/gadgets/clock/themes/types.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/types.js +1 -0
- package/dist/gadgets/clock/themes/urlThemeCache.d.ts +2 -0
- package/dist/gadgets/clock/themes/urlThemeCache.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/urlThemeCache.js +11 -0
- package/dist/gadgets/clock/themes/useDarkFlag.d.ts +2 -0
- package/dist/gadgets/clock/themes/useDarkFlag.d.ts.map +1 -0
- package/dist/gadgets/clock/themes/useDarkFlag.js +14 -0
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts +23 -0
- package/dist/gadgets/flip-digit/SgFlipDigit.d.ts.map +1 -0
- package/dist/gadgets/flip-digit/SgFlipDigit.js +118 -0
- package/dist/gadgets/flip-digit/index.d.ts +3 -0
- package/dist/gadgets/flip-digit/index.d.ts.map +1 -0
- package/dist/gadgets/flip-digit/index.js +1 -0
- package/dist/i18n/en-US.json +76 -0
- package/dist/i18n/es.json +76 -0
- package/dist/i18n/index.d.ts +328 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +87 -0
- package/dist/i18n/pt-BR.json +76 -0
- package/dist/i18n/pt-PT.json +76 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/inputs/FloatingInput.d.ts +13 -0
- package/dist/inputs/FloatingInput.d.ts.map +1 -0
- package/dist/inputs/FloatingInput.js +53 -0
- package/dist/inputs/FloatingSelect.d.ts +15 -0
- package/dist/inputs/FloatingSelect.d.ts.map +1 -0
- package/dist/inputs/FloatingSelect.js +52 -0
- package/dist/inputs/FloatingTextArea.d.ts +11 -0
- package/dist/inputs/FloatingTextArea.d.ts.map +1 -0
- package/dist/inputs/FloatingTextArea.js +34 -0
- package/dist/inputs/InputBirthDate.d.ts +13 -0
- package/dist/inputs/InputBirthDate.d.ts.map +1 -0
- package/dist/inputs/InputBirthDate.js +46 -0
- package/dist/inputs/InputDate.d.ts +8 -0
- package/dist/inputs/InputDate.d.ts.map +1 -0
- package/dist/inputs/InputDate.js +23 -0
- package/dist/inputs/InputEmail.d.ts +14 -0
- package/dist/inputs/InputEmail.d.ts.map +1 -0
- package/dist/inputs/InputEmail.js +43 -0
- package/dist/inputs/InputPassword.d.ts +12 -0
- package/dist/inputs/InputPassword.d.ts.map +1 -0
- package/dist/inputs/InputPassword.js +42 -0
- package/dist/inputs/MaskedInputs.d.ts +27 -0
- package/dist/inputs/MaskedInputs.d.ts.map +1 -0
- package/dist/inputs/MaskedInputs.js +161 -0
- package/dist/inputs/SgAutocomplete.d.ts +42 -0
- package/dist/inputs/SgAutocomplete.d.ts.map +1 -0
- package/dist/inputs/SgAutocomplete.js +241 -0
- package/dist/inputs/SgCurrencyEdit.d.ts +56 -0
- package/dist/inputs/SgCurrencyEdit.d.ts.map +1 -0
- package/dist/inputs/SgCurrencyEdit.js +496 -0
- package/dist/inputs/SgInputBirthDate.d.ts +13 -0
- package/dist/inputs/SgInputBirthDate.d.ts.map +1 -0
- package/dist/inputs/SgInputBirthDate.js +48 -0
- package/dist/inputs/SgInputCEP.d.ts +33 -0
- package/dist/inputs/SgInputCEP.d.ts.map +1 -0
- package/dist/inputs/SgInputCEP.js +117 -0
- package/dist/inputs/SgInputCNPJ.d.ts +20 -0
- package/dist/inputs/SgInputCNPJ.d.ts.map +1 -0
- package/dist/inputs/SgInputCNPJ.js +133 -0
- package/dist/inputs/SgInputCPF.d.ts +15 -0
- package/dist/inputs/SgInputCPF.d.ts.map +1 -0
- package/dist/inputs/SgInputCPF.js +70 -0
- package/dist/inputs/SgInputCPFCNPJ.d.ts +15 -0
- package/dist/inputs/SgInputCPFCNPJ.d.ts.map +1 -0
- package/dist/inputs/SgInputCPFCNPJ.js +92 -0
- package/dist/inputs/SgInputDate.d.ts +8 -0
- package/dist/inputs/SgInputDate.d.ts.map +1 -0
- package/dist/inputs/SgInputDate.js +120 -0
- package/dist/inputs/SgInputEmail.d.ts +16 -0
- package/dist/inputs/SgInputEmail.d.ts.map +1 -0
- package/dist/inputs/SgInputEmail.js +74 -0
- package/dist/inputs/SgInputFone.d.ts +15 -0
- package/dist/inputs/SgInputFone.d.ts.map +1 -0
- package/dist/inputs/SgInputFone.js +60 -0
- package/dist/inputs/SgInputMasked.d.ts +27 -0
- package/dist/inputs/SgInputMasked.d.ts.map +1 -0
- package/dist/inputs/SgInputMasked.js +161 -0
- package/dist/inputs/SgInputNumber.d.ts +49 -0
- package/dist/inputs/SgInputNumber.d.ts.map +1 -0
- package/dist/inputs/SgInputNumber.js +438 -0
- package/dist/inputs/SgInputPassword.d.ts +26 -0
- package/dist/inputs/SgInputPassword.d.ts.map +1 -0
- package/dist/inputs/SgInputPassword.js +278 -0
- package/dist/inputs/SgInputPhone.d.ts +15 -0
- package/dist/inputs/SgInputPhone.d.ts.map +1 -0
- package/dist/inputs/SgInputPhone.js +66 -0
- package/dist/inputs/SgInputPostalCode.d.ts +37 -0
- package/dist/inputs/SgInputPostalCode.d.ts.map +1 -0
- package/dist/inputs/SgInputPostalCode.js +193 -0
- package/dist/inputs/SgInputSelect.d.ts +16 -0
- package/dist/inputs/SgInputSelect.d.ts.map +1 -0
- package/dist/inputs/SgInputSelect.js +104 -0
- package/dist/inputs/SgInputText.d.ts +49 -0
- package/dist/inputs/SgInputText.d.ts.map +1 -0
- package/dist/inputs/SgInputText.js +336 -0
- package/dist/inputs/SgInputTextArea.d.ts +41 -0
- package/dist/inputs/SgInputTextArea.d.ts.map +1 -0
- package/dist/inputs/SgInputTextArea.js +216 -0
- package/dist/inputs/SgTextEditor.d.ts +27 -0
- package/dist/inputs/SgTextEditor.d.ts.map +1 -0
- package/dist/inputs/SgTextEditor.js +201 -0
- package/dist/integration/module.d.ts +39 -0
- package/dist/integration/module.d.ts.map +1 -0
- package/dist/integration/module.js +1 -0
- package/dist/layout/GroupBox.d.ts +10 -0
- package/dist/layout/GroupBox.d.ts.map +1 -0
- package/dist/layout/GroupBox.js +14 -0
- package/dist/layout/SgCard.d.ts +35 -0
- package/dist/layout/SgCard.d.ts.map +1 -0
- package/dist/layout/SgCard.js +106 -0
- package/dist/layout/SgDockLayout.d.ts +37 -0
- package/dist/layout/SgDockLayout.d.ts.map +1 -0
- package/dist/layout/SgDockLayout.js +101 -0
- package/dist/layout/SgDockZone.d.ts +12 -0
- package/dist/layout/SgDockZone.d.ts.map +1 -0
- package/dist/layout/SgDockZone.js +20 -0
- package/dist/layout/SgGrid.d.ts +18 -0
- package/dist/layout/SgGrid.d.ts.map +1 -0
- package/dist/layout/SgGrid.js +101 -0
- package/dist/layout/SgGroupBox.d.ts +10 -0
- package/dist/layout/SgGroupBox.d.ts.map +1 -0
- package/dist/layout/SgGroupBox.js +14 -0
- package/dist/layout/SgMainPanel.d.ts +11 -0
- package/dist/layout/SgMainPanel.d.ts.map +1 -0
- package/dist/layout/SgMainPanel.js +70 -0
- package/dist/layout/SgPanel.d.ts +22 -0
- package/dist/layout/SgPanel.d.ts.map +1 -0
- package/dist/layout/SgPanel.js +33 -0
- package/dist/layout/SgScreen.d.ts +11 -0
- package/dist/layout/SgScreen.d.ts.map +1 -0
- package/dist/layout/SgScreen.js +18 -0
- package/dist/layout/SgStack.d.ts +15 -0
- package/dist/layout/SgStack.d.ts.map +1 -0
- package/dist/layout/SgStack.js +32 -0
- package/dist/layout/SgToolBar.d.ts +46 -0
- package/dist/layout/SgToolBar.d.ts.map +1 -0
- package/dist/layout/SgToolBar.js +199 -0
- package/dist/layout/SgTreeView.d.ts +80 -0
- package/dist/layout/SgTreeView.d.ts.map +1 -0
- package/dist/layout/SgTreeView.js +338 -0
- package/dist/manifest.d.ts +3 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +19 -0
- package/dist/masks.d.ts +14 -0
- package/dist/masks.d.ts.map +1 -0
- package/dist/masks.js +91 -0
- package/dist/overlay/SgDialog.d.ts +39 -0
- package/dist/overlay/SgDialog.d.ts.map +1 -0
- package/dist/overlay/SgDialog.js +177 -0
- package/dist/overlay/SgPopup.d.ts +42 -0
- package/dist/overlay/SgPopup.d.ts.map +1 -0
- package/dist/overlay/SgPopup.js +218 -0
- package/dist/rhf.d.ts +6 -0
- package/dist/rhf.d.ts.map +1 -0
- package/dist/rhf.js +1 -0
- package/dist/validators.d.ts +27 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +218 -0
- package/dist/wizard/SGWizard.d.ts +28 -0
- package/dist/wizard/SGWizard.d.ts.map +1 -0
- package/dist/wizard/SGWizard.js +124 -0
- package/package.json +53 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { SgInputText } from "./SgInputText";
|
|
5
|
+
import { buildCommonPasswordSet } from "../commons/common-passwords";
|
|
6
|
+
import { t, useComponentsI18n } from "../i18n";
|
|
7
|
+
export function SgInputPassword(props) {
|
|
8
|
+
const i18n = useComponentsI18n();
|
|
9
|
+
const { inputProps, onValidation, required, requiredMessage, validateOnBlur, error, onClear, hidePassword, maxLength, validation, createNewPasswordButton = false, showStrengthBar = true, commonPasswordCheck = true, commonPasswordMessage, commonPasswordList, onChange, upperRequired = true, lowerRequired = true, numberRequired = true, specialCharacterRequired = true, prohibitsRepeatedCharactersInSequence = true, prohibitsSequentialAscCharacters = true, prohibitsSequentialDescCharacters = true, minSize = 8, ...rest } = props;
|
|
10
|
+
const resolvedCommonPasswordMessage = commonPasswordMessage ?? t(i18n, "components.password.common");
|
|
11
|
+
const [internalError, setInternalError] = React.useState(null);
|
|
12
|
+
const [isHidden, setIsHidden] = React.useState(hidePassword ?? true);
|
|
13
|
+
const [hasInteracted, setHasInteracted] = React.useState(false);
|
|
14
|
+
const [unmetRequirements, setUnmetRequirements] = React.useState([]);
|
|
15
|
+
const inputRef = React.useRef(null);
|
|
16
|
+
const commonPasswordSet = React.useMemo(() => {
|
|
17
|
+
if (!commonPasswordCheck)
|
|
18
|
+
return null;
|
|
19
|
+
return buildCommonPasswordSet(commonPasswordList);
|
|
20
|
+
}, [commonPasswordCheck, commonPasswordList]);
|
|
21
|
+
const isCommonPassword = React.useCallback((value) => {
|
|
22
|
+
if (!commonPasswordSet)
|
|
23
|
+
return false;
|
|
24
|
+
const normalized = value.trim().toLowerCase();
|
|
25
|
+
if (!normalized)
|
|
26
|
+
return false;
|
|
27
|
+
if (commonPasswordSet.has(normalized))
|
|
28
|
+
return true;
|
|
29
|
+
const simplified = normalized.replace(/[^a-z0-9]/g, "");
|
|
30
|
+
if (!simplified)
|
|
31
|
+
return false;
|
|
32
|
+
return commonPasswordSet.has(simplified);
|
|
33
|
+
}, [commonPasswordSet]);
|
|
34
|
+
const getUnmetRequirements = React.useCallback((value) => {
|
|
35
|
+
const unmet = [];
|
|
36
|
+
if (value.length < minSize) {
|
|
37
|
+
unmet.push(t(i18n, "components.password.minSize", { min: minSize }));
|
|
38
|
+
}
|
|
39
|
+
if (upperRequired && !/[A-Z]/.test(value)) {
|
|
40
|
+
unmet.push(t(i18n, "components.password.upper"));
|
|
41
|
+
}
|
|
42
|
+
if (lowerRequired && !/[a-z]/.test(value)) {
|
|
43
|
+
unmet.push(t(i18n, "components.password.lower"));
|
|
44
|
+
}
|
|
45
|
+
if (numberRequired && !/[0-9]/.test(value)) {
|
|
46
|
+
unmet.push(t(i18n, "components.password.number"));
|
|
47
|
+
}
|
|
48
|
+
if (specialCharacterRequired && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(value)) {
|
|
49
|
+
unmet.push(t(i18n, "components.password.special"));
|
|
50
|
+
}
|
|
51
|
+
if (prohibitsRepeatedCharactersInSequence && /(.)\1/.test(value)) {
|
|
52
|
+
unmet.push(t(i18n, "components.password.noRepeat"));
|
|
53
|
+
}
|
|
54
|
+
if (prohibitsSequentialAscCharacters && hasSequentialCharacters(value, 3, "asc")) {
|
|
55
|
+
unmet.push(t(i18n, "components.password.noSeqAsc"));
|
|
56
|
+
}
|
|
57
|
+
if (prohibitsSequentialDescCharacters && hasSequentialCharacters(value, 3, "desc")) {
|
|
58
|
+
unmet.push(t(i18n, "components.password.noSeqDesc"));
|
|
59
|
+
}
|
|
60
|
+
return unmet;
|
|
61
|
+
}, [
|
|
62
|
+
i18n,
|
|
63
|
+
lowerRequired,
|
|
64
|
+
minSize,
|
|
65
|
+
numberRequired,
|
|
66
|
+
prohibitsRepeatedCharactersInSequence,
|
|
67
|
+
prohibitsSequentialAscCharacters,
|
|
68
|
+
prohibitsSequentialDescCharacters,
|
|
69
|
+
specialCharacterRequired,
|
|
70
|
+
upperRequired
|
|
71
|
+
]);
|
|
72
|
+
const strengthScore = React.useMemo(() => {
|
|
73
|
+
const totalChecks = [
|
|
74
|
+
minSize,
|
|
75
|
+
upperRequired,
|
|
76
|
+
lowerRequired,
|
|
77
|
+
numberRequired,
|
|
78
|
+
specialCharacterRequired,
|
|
79
|
+
prohibitsRepeatedCharactersInSequence,
|
|
80
|
+
prohibitsSequentialAscCharacters,
|
|
81
|
+
prohibitsSequentialDescCharacters
|
|
82
|
+
];
|
|
83
|
+
const activeChecks = totalChecks.filter((v) => v !== false).length;
|
|
84
|
+
if (activeChecks === 0)
|
|
85
|
+
return 0;
|
|
86
|
+
return activeChecks - unmetRequirements.length;
|
|
87
|
+
}, [
|
|
88
|
+
lowerRequired,
|
|
89
|
+
minSize,
|
|
90
|
+
numberRequired,
|
|
91
|
+
prohibitsRepeatedCharactersInSequence,
|
|
92
|
+
specialCharacterRequired,
|
|
93
|
+
upperRequired,
|
|
94
|
+
unmetRequirements.length
|
|
95
|
+
]);
|
|
96
|
+
const getStrengthColor = (score) => {
|
|
97
|
+
if (score === 0)
|
|
98
|
+
return "bg-border";
|
|
99
|
+
if (score <= 1)
|
|
100
|
+
return "bg-destructive";
|
|
101
|
+
if (score <= 2)
|
|
102
|
+
return "bg-orange-500";
|
|
103
|
+
if (score <= 3)
|
|
104
|
+
return "bg-amber-500";
|
|
105
|
+
if (score === 4)
|
|
106
|
+
return "bg-yellow-400";
|
|
107
|
+
return "bg-green-500";
|
|
108
|
+
};
|
|
109
|
+
const generatePassword = React.useCallback(() => {
|
|
110
|
+
const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
111
|
+
const lower = "abcdefghijklmnopqrstuvwxyz";
|
|
112
|
+
const numbers = "0123456789";
|
|
113
|
+
const specials = "!@#$%^&*()_+-=[]{};':\"\\|,.<>/?";
|
|
114
|
+
const requiredGroups = [];
|
|
115
|
+
if (upperRequired)
|
|
116
|
+
requiredGroups.push(upper);
|
|
117
|
+
if (lowerRequired)
|
|
118
|
+
requiredGroups.push(lower);
|
|
119
|
+
if (numberRequired)
|
|
120
|
+
requiredGroups.push(numbers);
|
|
121
|
+
if (specialCharacterRequired)
|
|
122
|
+
requiredGroups.push(specials);
|
|
123
|
+
const pool = (upperRequired ? upper : "") +
|
|
124
|
+
(lowerRequired ? lower : "") +
|
|
125
|
+
(numberRequired ? numbers : "") +
|
|
126
|
+
(specialCharacterRequired ? specials : "") ||
|
|
127
|
+
upper + lower + numbers;
|
|
128
|
+
const targetLength = Math.max(minSize, maxLength ?? minSize);
|
|
129
|
+
const randomInt = (max) => {
|
|
130
|
+
const array = new Uint32Array(1);
|
|
131
|
+
crypto.getRandomValues(array);
|
|
132
|
+
return (array[0] ?? 0) % max;
|
|
133
|
+
};
|
|
134
|
+
const pick = (set) => set[randomInt(set.length)] ?? "";
|
|
135
|
+
const shuffle = (arr) => {
|
|
136
|
+
for (let i = arr.length - 1; i > 0; i -= 1) {
|
|
137
|
+
const j = randomInt(i + 1);
|
|
138
|
+
const left = arr[i] ?? "";
|
|
139
|
+
const right = arr[j] ?? "";
|
|
140
|
+
arr[i] = right;
|
|
141
|
+
arr[j] = left;
|
|
142
|
+
}
|
|
143
|
+
return arr;
|
|
144
|
+
};
|
|
145
|
+
let candidate = "";
|
|
146
|
+
let attempts = 0;
|
|
147
|
+
while (attempts < 50) {
|
|
148
|
+
attempts += 1;
|
|
149
|
+
const chars = [];
|
|
150
|
+
requiredGroups.forEach((group) => {
|
|
151
|
+
chars.push(pick(group));
|
|
152
|
+
});
|
|
153
|
+
while (chars.length < targetLength) {
|
|
154
|
+
chars.push(pick(pool));
|
|
155
|
+
}
|
|
156
|
+
candidate = shuffle(chars).join("");
|
|
157
|
+
const unmet = getUnmetRequirements(candidate);
|
|
158
|
+
if (unmet.length === 0)
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
return candidate;
|
|
162
|
+
}, [
|
|
163
|
+
getUnmetRequirements,
|
|
164
|
+
lowerRequired,
|
|
165
|
+
maxLength,
|
|
166
|
+
minSize,
|
|
167
|
+
numberRequired,
|
|
168
|
+
specialCharacterRequired,
|
|
169
|
+
upperRequired
|
|
170
|
+
]);
|
|
171
|
+
const runValidation = React.useCallback((value) => {
|
|
172
|
+
if (!value && !required) {
|
|
173
|
+
setInternalError(null);
|
|
174
|
+
onValidation?.(null);
|
|
175
|
+
setUnmetRequirements([]);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (!value && required) {
|
|
179
|
+
const message = requiredMessage ?? t(i18n, "components.inputs.required");
|
|
180
|
+
setInternalError(message);
|
|
181
|
+
onValidation?.(message);
|
|
182
|
+
setUnmetRequirements([]);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const unmet = getUnmetRequirements(value);
|
|
186
|
+
const isCommon = commonPasswordCheck ? isCommonPassword(value) : false;
|
|
187
|
+
if (isCommon) {
|
|
188
|
+
unmet.push(resolvedCommonPasswordMessage);
|
|
189
|
+
}
|
|
190
|
+
const customMessage = validation?.(value) ?? null;
|
|
191
|
+
const combined = customMessage ? [...unmet, customMessage] : unmet;
|
|
192
|
+
setUnmetRequirements(combined);
|
|
193
|
+
const message = combined.length > 0 ? (combined[0] ?? null) : null;
|
|
194
|
+
setInternalError(message);
|
|
195
|
+
onValidation?.(message);
|
|
196
|
+
}, [
|
|
197
|
+
commonPasswordCheck,
|
|
198
|
+
getUnmetRequirements,
|
|
199
|
+
i18n,
|
|
200
|
+
isCommonPassword,
|
|
201
|
+
onValidation,
|
|
202
|
+
required,
|
|
203
|
+
requiredMessage,
|
|
204
|
+
resolvedCommonPasswordMessage,
|
|
205
|
+
validation
|
|
206
|
+
]);
|
|
207
|
+
const mergedInputProps = {
|
|
208
|
+
...inputProps,
|
|
209
|
+
"data-sg-password": "true",
|
|
210
|
+
onChange: (event) => {
|
|
211
|
+
setHasInteracted(true);
|
|
212
|
+
runValidation(event.currentTarget.value);
|
|
213
|
+
inputProps?.onChange?.(event);
|
|
214
|
+
},
|
|
215
|
+
onBlur: (event) => {
|
|
216
|
+
if ((validateOnBlur ?? true) || hasInteracted) {
|
|
217
|
+
runValidation(event.currentTarget.value);
|
|
218
|
+
}
|
|
219
|
+
inputProps?.onBlur?.(event);
|
|
220
|
+
},
|
|
221
|
+
ref: undefined
|
|
222
|
+
};
|
|
223
|
+
const setRef = (node) => {
|
|
224
|
+
inputRef.current = node;
|
|
225
|
+
const ref = inputProps?.ref;
|
|
226
|
+
if (!ref)
|
|
227
|
+
return;
|
|
228
|
+
if (typeof ref === "function") {
|
|
229
|
+
ref(node);
|
|
230
|
+
}
|
|
231
|
+
else if (ref && typeof ref === "object" && "current" in ref) {
|
|
232
|
+
ref.current = node;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const applyValue = (value) => {
|
|
236
|
+
if (inputRef.current) {
|
|
237
|
+
inputRef.current.value = value;
|
|
238
|
+
}
|
|
239
|
+
const event = {
|
|
240
|
+
target: inputRef.current ?? { value },
|
|
241
|
+
currentTarget: inputRef.current ?? { value }
|
|
242
|
+
};
|
|
243
|
+
mergedInputProps.onChange?.(event);
|
|
244
|
+
onChange?.(value);
|
|
245
|
+
};
|
|
246
|
+
const toggleIcon = (_jsx("button", { type: "button", onClick: () => setIsHidden((prev) => !prev), className: "rounded p-1 text-foreground/60 hover:text-foreground", "aria-label": isHidden ? t(i18n, "components.password.show") : t(i18n, "components.password.hide"), children: isHidden ? (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] })) : (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M17.94 17.94A10.94 10.94 0 0 1 12 19c-6.5 0-10-7-10-7a20.4 20.4 0 0 1 5.06-6.88" }), _jsx("path", { d: "M1 1l22 22" }), _jsx("path", { d: "M9.9 4.24A10.9 10.9 0 0 1 12 5c6.5 0 10 7 10 7a20.3 20.3 0 0 1-3.44 4.65" }), _jsx("path", { d: "M14.12 14.12a3 3 0 0 1-4.24-4.24" })] })) }));
|
|
247
|
+
const generateIcon = createNewPasswordButton ? (_jsx("button", { type: "button", onClick: () => {
|
|
248
|
+
const next = generatePassword();
|
|
249
|
+
applyValue(next);
|
|
250
|
+
}, className: "rounded p-1 text-foreground/60 hover:text-foreground", "aria-label": t(i18n, "components.password.generate"), title: t(i18n, "components.password.generate"), children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M3 12a9 9 0 1 0 3-6.7" }), _jsx("path", { d: "M3 3v6h6" })] }) })) : null;
|
|
251
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
|
|
252
|
+
[data-sg-password]::-ms-reveal,
|
|
253
|
+
[data-sg-password]::-ms-clear {
|
|
254
|
+
display: none;
|
|
255
|
+
}
|
|
256
|
+
` }), _jsx(SgInputText, { ...rest, type: isHidden ? "password" : "text", maxLength: maxLength ?? 15, error: error ?? internalError ?? undefined, onClear: () => {
|
|
257
|
+
setInternalError(null);
|
|
258
|
+
onValidation?.(null);
|
|
259
|
+
onClear?.();
|
|
260
|
+
}, inputProps: { ...mergedInputProps, ref: setRef }, iconButtons: [toggleIcon, ...(generateIcon ? [generateIcon] : []), ...(props.iconButtons ?? [])] }), _jsxs("div", { className: "mt-2", children: [showStrengthBar ? (_jsx("div", { className: "mb-2 flex h-1 w-full gap-1", children: Array.from({ length: 5 }).map((_, index) => (_jsx("span", { className: `h-full flex-1 rounded-full transition-all duration-300 ease-out ${index < Math.min(5, strengthScore) ? getStrengthColor(strengthScore) : "bg-border"}` }, index))) })) : null, (() => {
|
|
261
|
+
const unique = Array.from(new Set(unmetRequirements.filter((req) => req !== resolvedCommonPasswordMessage && req !== internalError)));
|
|
262
|
+
return unique.length > 0 ? (_jsx("ul", { className: "space-y-1 text-xs text-destructive", children: unique.map((req) => (_jsx("li", { children: req }, req))) })) : null;
|
|
263
|
+
})()] })] }));
|
|
264
|
+
}
|
|
265
|
+
function hasSequentialCharacters(value, minLength, direction) {
|
|
266
|
+
const normalized = value.toLowerCase();
|
|
267
|
+
const sequences = ["abcdefghijklmnopqrstuvwxyz", "0123456789"];
|
|
268
|
+
for (const seq of sequences) {
|
|
269
|
+
for (let i = 0; i <= seq.length - minLength; i += 1) {
|
|
270
|
+
const part = seq.slice(i, i + minLength);
|
|
271
|
+
const pattern = direction === "asc" ? part : part.split("").reverse().join("");
|
|
272
|
+
if (normalized.includes(pattern)) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type SgInputTextProps } from "./SgInputText";
|
|
3
|
+
export type SgInputPhoneProps = Omit<SgInputTextProps, "inputProps" | "error"> & {
|
|
4
|
+
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
|
|
5
|
+
error?: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
requiredMessage?: string;
|
|
8
|
+
lengthMessage?: string;
|
|
9
|
+
invalidMessage?: string;
|
|
10
|
+
validation?: (value: string) => string | null;
|
|
11
|
+
onValidation?: (message: string | null) => void;
|
|
12
|
+
validateOnBlur?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function SgInputPhone(props: Readonly<SgInputPhoneProps>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=SgInputPhone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgInputPhone.d.ts","sourceRoot":"","sources":["../../src/inputs/SgInputPhone.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAInE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG;IAC/E,UAAU,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAMF,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,2CAiF9D"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { SgInputText } from "./SgInputText";
|
|
5
|
+
import { maskPhone } from "../masks";
|
|
6
|
+
import { t, useComponentsI18n } from "../i18n";
|
|
7
|
+
function onlyDigits(value) {
|
|
8
|
+
return value.replace(/\D/g, "");
|
|
9
|
+
}
|
|
10
|
+
export function SgInputPhone(props) {
|
|
11
|
+
const i18n = useComponentsI18n();
|
|
12
|
+
const { required, requiredMessage, lengthMessage, invalidMessage, validateOnBlur, error, validation, onClear, inputProps, ...rest } = props;
|
|
13
|
+
const [internalError, setInternalError] = React.useState(null);
|
|
14
|
+
const [hasInteracted, setHasInteracted] = React.useState(false);
|
|
15
|
+
const runValidation = React.useCallback((value) => {
|
|
16
|
+
const digits = onlyDigits(value);
|
|
17
|
+
if (!digits && !required) {
|
|
18
|
+
setInternalError(null);
|
|
19
|
+
props.onValidation?.(null);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (!digits && required) {
|
|
23
|
+
const message = requiredMessage ?? t(i18n, "components.inputs.required");
|
|
24
|
+
setInternalError(message);
|
|
25
|
+
props.onValidation?.(message);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (validation) {
|
|
29
|
+
const message = validation(value);
|
|
30
|
+
if (message) {
|
|
31
|
+
setInternalError(message);
|
|
32
|
+
props.onValidation?.(message);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (digits.length !== 10 && digits.length !== 11) {
|
|
37
|
+
const message = lengthMessage ?? invalidMessage ?? t(i18n, "components.inputs.phone.invalid");
|
|
38
|
+
setInternalError(message);
|
|
39
|
+
props.onValidation?.(message);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setInternalError(null);
|
|
43
|
+
props.onValidation?.(null);
|
|
44
|
+
}, [i18n, required, requiredMessage, lengthMessage, invalidMessage, validation, props]);
|
|
45
|
+
const mergedInputProps = {
|
|
46
|
+
...inputProps,
|
|
47
|
+
inputMode: inputProps?.inputMode ?? "numeric",
|
|
48
|
+
onChange: (event) => {
|
|
49
|
+
setHasInteracted(true);
|
|
50
|
+
event.target.value = maskPhone(event.target.value);
|
|
51
|
+
runValidation(event.currentTarget.value);
|
|
52
|
+
inputProps?.onChange?.(event);
|
|
53
|
+
},
|
|
54
|
+
onBlur: (event) => {
|
|
55
|
+
if ((validateOnBlur ?? true) || hasInteracted) {
|
|
56
|
+
runValidation(event.currentTarget.value);
|
|
57
|
+
}
|
|
58
|
+
inputProps?.onBlur?.(event);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return (_jsx(SgInputText, { ...rest, error: error ?? internalError ?? undefined, textInputType: props.textInputType ?? "numeric", onClear: () => {
|
|
62
|
+
setInternalError(null);
|
|
63
|
+
props.onValidation?.(null);
|
|
64
|
+
onClear?.();
|
|
65
|
+
}, inputProps: mergedInputProps }));
|
|
66
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type SgInputTextProps } from "./SgInputText";
|
|
3
|
+
export type PostalCodeCountry = "BR" | "PT" | "US" | "ES" | "UY" | "AR" | "PY";
|
|
4
|
+
export type ViaCepResponse = {
|
|
5
|
+
cep?: string;
|
|
6
|
+
logradouro?: string;
|
|
7
|
+
complemento?: string;
|
|
8
|
+
bairro?: string;
|
|
9
|
+
localidade?: string;
|
|
10
|
+
uf?: string;
|
|
11
|
+
ibge?: string;
|
|
12
|
+
gia?: string;
|
|
13
|
+
ddd?: string;
|
|
14
|
+
siafi?: string;
|
|
15
|
+
erro?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type SgInputPostalCodeProps = Omit<SgInputTextProps, "inputProps" | "error"> & {
|
|
18
|
+
inputProps?: (React.InputHTMLAttributes<HTMLInputElement> & {
|
|
19
|
+
ref?: React.Ref<HTMLInputElement>;
|
|
20
|
+
});
|
|
21
|
+
error?: string;
|
|
22
|
+
country?: PostalCodeCountry;
|
|
23
|
+
required?: boolean;
|
|
24
|
+
requiredMessage?: string;
|
|
25
|
+
lengthMessage?: string;
|
|
26
|
+
invalidMessage?: string;
|
|
27
|
+
validation?: (value: string) => string | null;
|
|
28
|
+
onValidation?: (message: string | null) => void;
|
|
29
|
+
validateOnBlur?: boolean;
|
|
30
|
+
validateWithViaCep?: boolean;
|
|
31
|
+
viaCepErrorMessage?: string;
|
|
32
|
+
viaCepFetch?: (cep: string) => Promise<ViaCepResponse>;
|
|
33
|
+
onViaCepResult?: (data: ViaCepResponse) => void;
|
|
34
|
+
onViaCepError?: (error: unknown) => void;
|
|
35
|
+
};
|
|
36
|
+
export declare function SgInputPostalCode(props: Readonly<SgInputPostalCodeProps>): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
//# sourceMappingURL=SgInputPostalCode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgInputPostalCode.d.ts","sourceRoot":"","sources":["../../src/inputs/SgInputPostalCode.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAYnE,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/E,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AA8EF,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG;IACpF,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,GAAG;QAC1D,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;KACnC,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C,CAAC;AAcF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,2CA8IxE"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { SgInputText } from "./SgInputText";
|
|
5
|
+
import { maskPostalCodeBR, maskPostalCodePT, maskPostalCodeUS, maskPostalCodeES, maskPostalCodeUY, maskPostalCodeAR, maskPostalCodePY } from "../masks";
|
|
6
|
+
import { t, useComponentsI18n } from "../i18n";
|
|
7
|
+
const COUNTRY_CONFIGS = {
|
|
8
|
+
BR: {
|
|
9
|
+
mask: maskPostalCodeBR,
|
|
10
|
+
rawLength: 8,
|
|
11
|
+
maxLength: 10,
|
|
12
|
+
inputMode: "numeric",
|
|
13
|
+
placeholder: "00000-000",
|
|
14
|
+
lengthKey: "components.inputs.postalCode.BR.length",
|
|
15
|
+
invalidKey: "components.inputs.postalCode.BR.invalid"
|
|
16
|
+
},
|
|
17
|
+
PT: {
|
|
18
|
+
mask: maskPostalCodePT,
|
|
19
|
+
rawLength: 7,
|
|
20
|
+
maxLength: 8,
|
|
21
|
+
inputMode: "numeric",
|
|
22
|
+
placeholder: "0000-000",
|
|
23
|
+
lengthKey: "components.inputs.postalCode.PT.length",
|
|
24
|
+
invalidKey: "components.inputs.postalCode.PT.invalid"
|
|
25
|
+
},
|
|
26
|
+
US: {
|
|
27
|
+
mask: maskPostalCodeUS,
|
|
28
|
+
rawLength: 5,
|
|
29
|
+
maxLength: 10,
|
|
30
|
+
inputMode: "numeric",
|
|
31
|
+
placeholder: "00000",
|
|
32
|
+
lengthKey: "components.inputs.postalCode.US.length",
|
|
33
|
+
invalidKey: "components.inputs.postalCode.US.invalid"
|
|
34
|
+
},
|
|
35
|
+
ES: {
|
|
36
|
+
mask: maskPostalCodeES,
|
|
37
|
+
rawLength: 5,
|
|
38
|
+
maxLength: 5,
|
|
39
|
+
inputMode: "numeric",
|
|
40
|
+
placeholder: "00000",
|
|
41
|
+
lengthKey: "components.inputs.postalCode.ES.length",
|
|
42
|
+
invalidKey: "components.inputs.postalCode.ES.invalid"
|
|
43
|
+
},
|
|
44
|
+
UY: {
|
|
45
|
+
mask: maskPostalCodeUY,
|
|
46
|
+
rawLength: 5,
|
|
47
|
+
maxLength: 5,
|
|
48
|
+
inputMode: "numeric",
|
|
49
|
+
placeholder: "00000",
|
|
50
|
+
lengthKey: "components.inputs.postalCode.UY.length",
|
|
51
|
+
invalidKey: "components.inputs.postalCode.UY.invalid"
|
|
52
|
+
},
|
|
53
|
+
AR: {
|
|
54
|
+
mask: maskPostalCodeAR,
|
|
55
|
+
rawLength: 4,
|
|
56
|
+
maxLength: 8,
|
|
57
|
+
inputMode: "text",
|
|
58
|
+
placeholder: "A0000AAA",
|
|
59
|
+
lengthKey: "components.inputs.postalCode.AR.length",
|
|
60
|
+
invalidKey: "components.inputs.postalCode.AR.invalid"
|
|
61
|
+
},
|
|
62
|
+
PY: {
|
|
63
|
+
mask: maskPostalCodePY,
|
|
64
|
+
rawLength: 6,
|
|
65
|
+
maxLength: 6,
|
|
66
|
+
inputMode: "numeric",
|
|
67
|
+
placeholder: "000000",
|
|
68
|
+
lengthKey: "components.inputs.postalCode.PY.length",
|
|
69
|
+
invalidKey: "components.inputs.postalCode.PY.invalid"
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
function extractRaw(value, country) {
|
|
73
|
+
if (country === "AR") {
|
|
74
|
+
return value.replaceAll(/[^0-9A-Za-z]/g, "").toUpperCase();
|
|
75
|
+
}
|
|
76
|
+
return value.replaceAll(/\D/g, "");
|
|
77
|
+
}
|
|
78
|
+
const defaultViaCepFetch = async (cep) => {
|
|
79
|
+
const response = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
|
|
80
|
+
return (await response.json());
|
|
81
|
+
};
|
|
82
|
+
export function SgInputPostalCode(props) {
|
|
83
|
+
const i18n = useComponentsI18n();
|
|
84
|
+
const { country = "BR", required, requiredMessage, lengthMessage, invalidMessage, validateOnBlur, error, validation, onClear, inputProps, validateWithViaCep, viaCepErrorMessage, viaCepFetch, onViaCepResult, onViaCepError, ...rest } = props;
|
|
85
|
+
const config = COUNTRY_CONFIGS[country];
|
|
86
|
+
const [internalError, setInternalError] = React.useState(null);
|
|
87
|
+
const [hasInteracted, setHasInteracted] = React.useState(false);
|
|
88
|
+
const lastValidatedRef = React.useRef(null);
|
|
89
|
+
const lastViaCepErroRef = React.useRef(null);
|
|
90
|
+
const runValidation = React.useCallback(async (value) => {
|
|
91
|
+
const raw = extractRaw(value, country);
|
|
92
|
+
if (!raw && !required) {
|
|
93
|
+
setInternalError(null);
|
|
94
|
+
props.onValidation?.(null);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!raw && required) {
|
|
98
|
+
const message = requiredMessage ?? t(i18n, "components.inputs.required");
|
|
99
|
+
setInternalError(message);
|
|
100
|
+
props.onValidation?.(message);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (validation) {
|
|
104
|
+
const message = validation(value);
|
|
105
|
+
if (message) {
|
|
106
|
+
setInternalError(message);
|
|
107
|
+
props.onValidation?.(message);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const expectedLength = country === "US"
|
|
112
|
+
? (raw.length > 5 ? 9 : 5)
|
|
113
|
+
: country === "AR"
|
|
114
|
+
? (raw.length > 4 ? 8 : 4)
|
|
115
|
+
: config.rawLength;
|
|
116
|
+
if (raw.length !== expectedLength) {
|
|
117
|
+
const message = lengthMessage ?? t(i18n, config.lengthKey);
|
|
118
|
+
setInternalError(message);
|
|
119
|
+
props.onValidation?.(message);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (country === "BR" && validateWithViaCep) {
|
|
123
|
+
if (lastValidatedRef.current === raw) {
|
|
124
|
+
const message = viaCepErrorMessage ?? invalidMessage ?? t(i18n, config.invalidKey);
|
|
125
|
+
if (lastViaCepErroRef.current) {
|
|
126
|
+
setInternalError(message);
|
|
127
|
+
props.onValidation?.(message);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
setInternalError(null);
|
|
131
|
+
props.onValidation?.(null);
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const checker = viaCepFetch ?? defaultViaCepFetch;
|
|
137
|
+
const data = await checker(raw);
|
|
138
|
+
lastValidatedRef.current = raw;
|
|
139
|
+
lastViaCepErroRef.current = Boolean(data?.erro);
|
|
140
|
+
onViaCepResult?.(data);
|
|
141
|
+
if (data?.erro) {
|
|
142
|
+
const message = viaCepErrorMessage ?? invalidMessage ?? t(i18n, config.invalidKey);
|
|
143
|
+
setInternalError(message);
|
|
144
|
+
props.onValidation?.(message);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
onViaCepError?.(err);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
setInternalError(null);
|
|
153
|
+
props.onValidation?.(null);
|
|
154
|
+
}, [
|
|
155
|
+
config,
|
|
156
|
+
country,
|
|
157
|
+
i18n,
|
|
158
|
+
invalidMessage,
|
|
159
|
+
lengthMessage,
|
|
160
|
+
onViaCepError,
|
|
161
|
+
onViaCepResult,
|
|
162
|
+
props,
|
|
163
|
+
required,
|
|
164
|
+
requiredMessage,
|
|
165
|
+
validateWithViaCep,
|
|
166
|
+
validation,
|
|
167
|
+
viaCepErrorMessage,
|
|
168
|
+
viaCepFetch
|
|
169
|
+
]);
|
|
170
|
+
const mergedInputProps = {
|
|
171
|
+
...inputProps,
|
|
172
|
+
inputMode: inputProps?.inputMode ?? config.inputMode,
|
|
173
|
+
onChange: (event) => {
|
|
174
|
+
setHasInteracted(true);
|
|
175
|
+
event.target.value = config.mask(event.target.value);
|
|
176
|
+
runValidation(event.currentTarget.value);
|
|
177
|
+
inputProps?.onChange?.(event);
|
|
178
|
+
},
|
|
179
|
+
onBlur: (event) => {
|
|
180
|
+
if ((validateOnBlur ?? true) || hasInteracted) {
|
|
181
|
+
runValidation(event.currentTarget.value);
|
|
182
|
+
}
|
|
183
|
+
inputProps?.onBlur?.(event);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
return (_jsx(SgInputText, { ...rest, maxLength: rest.maxLength ?? config.maxLength, error: error ?? internalError ?? undefined, textInputType: props.textInputType ?? (config.inputMode === "numeric" ? "numeric" : undefined), onClear: () => {
|
|
187
|
+
setInternalError(null);
|
|
188
|
+
props.onValidation?.(null);
|
|
189
|
+
onClear?.();
|
|
190
|
+
lastValidatedRef.current = null;
|
|
191
|
+
lastViaCepErroRef.current = null;
|
|
192
|
+
}, inputProps: mergedInputProps }));
|
|
193
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { RhfFieldProps } from "../rhf";
|
|
3
|
+
export type SgInputSelectProps = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
options: Array<{
|
|
9
|
+
value: string;
|
|
10
|
+
label: string;
|
|
11
|
+
}>;
|
|
12
|
+
selectProps: React.SelectHTMLAttributes<HTMLSelectElement>;
|
|
13
|
+
alwaysFloat?: boolean;
|
|
14
|
+
} & RhfFieldProps;
|
|
15
|
+
export declare function SgInputSelect(props: SgInputSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=SgInputSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgInputSelect.d.ts","sourceRoot":"","sources":["../../src/inputs/SgInputSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,WAAW,EAAE,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,aAAa,CAAC;AAwJlB,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CAwBtD"}
|