@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,218 @@
|
|
|
1
|
+
import blockedEmailDomainsConfig from "./blocked-email-domains.json";
|
|
2
|
+
import { resolveComponentsI18n, t } from "./i18n";
|
|
3
|
+
export const DEFAULT_BLOCKED_EMAIL_DOMAINS = blockedEmailDomainsConfig.blockedEmailDomains ?? [];
|
|
4
|
+
function getRuntimeBlockedEmailDomains() {
|
|
5
|
+
if (typeof globalThis === "undefined")
|
|
6
|
+
return [];
|
|
7
|
+
const globalConfig = globalThis;
|
|
8
|
+
if (!Array.isArray(globalConfig.__seedgridBlockedEmailDomains))
|
|
9
|
+
return [];
|
|
10
|
+
return globalConfig.__seedgridBlockedEmailDomains;
|
|
11
|
+
}
|
|
12
|
+
export function getBlockedEmailDomains(extra) {
|
|
13
|
+
const merged = [
|
|
14
|
+
...DEFAULT_BLOCKED_EMAIL_DOMAINS,
|
|
15
|
+
...getRuntimeBlockedEmailDomains(),
|
|
16
|
+
...(extra ?? [])
|
|
17
|
+
];
|
|
18
|
+
const normalized = merged
|
|
19
|
+
.map((domain) => domain.trim().toLowerCase())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
return Array.from(new Set(normalized));
|
|
22
|
+
}
|
|
23
|
+
export function isBlockedEmailDomain(email, extra) {
|
|
24
|
+
const parts = email.trim().toLowerCase().split("@");
|
|
25
|
+
const domain = parts.length > 1 ? parts[parts.length - 1] : "";
|
|
26
|
+
if (!domain)
|
|
27
|
+
return false;
|
|
28
|
+
const blocked = new Set(getBlockedEmailDomains(extra));
|
|
29
|
+
return blocked.has(domain);
|
|
30
|
+
}
|
|
31
|
+
export function onlyDigits(value) {
|
|
32
|
+
return value.replace(/\D/g, "");
|
|
33
|
+
}
|
|
34
|
+
function onlyAlnumUpper(value) {
|
|
35
|
+
return value.replace(/[^0-9A-Za-z]/g, "").toUpperCase();
|
|
36
|
+
}
|
|
37
|
+
export function isValidCpf(value) {
|
|
38
|
+
const digits = onlyDigits(value);
|
|
39
|
+
if (digits.length !== 11)
|
|
40
|
+
return false;
|
|
41
|
+
if (/^(\d)\1+$/.test(digits))
|
|
42
|
+
return false;
|
|
43
|
+
const calc = (factor) => {
|
|
44
|
+
let total = 0;
|
|
45
|
+
for (let i = 0; i < factor - 1; i++) {
|
|
46
|
+
total += parseInt(digits.charAt(i), 10) * (factor - i);
|
|
47
|
+
}
|
|
48
|
+
const mod = total % 11;
|
|
49
|
+
return mod < 2 ? 0 : 11 - mod;
|
|
50
|
+
};
|
|
51
|
+
const d1 = calc(10);
|
|
52
|
+
const d2 = calc(11);
|
|
53
|
+
return d1 === parseInt(digits.charAt(9), 10) && d2 === parseInt(digits.charAt(10), 10);
|
|
54
|
+
}
|
|
55
|
+
export function isValidCnpj(value) {
|
|
56
|
+
const raw = onlyAlnumUpper(value);
|
|
57
|
+
if (raw.length !== 14)
|
|
58
|
+
return false;
|
|
59
|
+
// opcional: eu manteria so para CNPJ numerico.
|
|
60
|
+
// Para alfanumerico, isso pode bloquear casos "ok" (embora improvaveis).
|
|
61
|
+
// if (/^([0-9A-Z])\1+$/.test(raw)) return false;
|
|
62
|
+
if (/[A-Z]/.test(raw.slice(12)))
|
|
63
|
+
return false; // DV precisa ser numerico
|
|
64
|
+
const toValue = (char) => {
|
|
65
|
+
if (char >= "0" && char <= "9")
|
|
66
|
+
return char.charCodeAt(0) - 48;
|
|
67
|
+
if (char >= "A" && char <= "Z")
|
|
68
|
+
return char.charCodeAt(0) - 55; // A=10..Z=35
|
|
69
|
+
return Number.NaN;
|
|
70
|
+
};
|
|
71
|
+
const calc = (base, weights) => {
|
|
72
|
+
let total = 0;
|
|
73
|
+
for (let i = 0; i < weights.length; i++) {
|
|
74
|
+
const weight = weights[i];
|
|
75
|
+
const v = toValue(base.charAt(i));
|
|
76
|
+
if (weight === undefined || Number.isNaN(v))
|
|
77
|
+
return Number.NaN;
|
|
78
|
+
total += v * weight;
|
|
79
|
+
}
|
|
80
|
+
const mod = total % 11;
|
|
81
|
+
return mod < 2 ? 0 : 11 - mod;
|
|
82
|
+
};
|
|
83
|
+
const base12 = raw.slice(0, 12);
|
|
84
|
+
const d1 = calc(base12, [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
|
|
85
|
+
if (!Number.isFinite(d1))
|
|
86
|
+
return false;
|
|
87
|
+
const d2 = calc(base12 + String(d1), [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
|
|
88
|
+
if (!Number.isFinite(d2))
|
|
89
|
+
return false;
|
|
90
|
+
return d1 === Number(raw.charAt(12)) && d2 === Number(raw.charAt(13));
|
|
91
|
+
}
|
|
92
|
+
export function isValidEmail(value) {
|
|
93
|
+
return /.+@.+\..+/.test(value.trim());
|
|
94
|
+
}
|
|
95
|
+
function hasSequentialRun(value, minRun) {
|
|
96
|
+
if (value.length < minRun)
|
|
97
|
+
return false;
|
|
98
|
+
let asc = 1;
|
|
99
|
+
let desc = 1;
|
|
100
|
+
for (let i = 1; i < value.length; i++) {
|
|
101
|
+
const prev = value.charAt(i - 1).toLowerCase();
|
|
102
|
+
const curr = value.charAt(i).toLowerCase();
|
|
103
|
+
const letters = /[a-z]/.test(prev) && /[a-z]/.test(curr);
|
|
104
|
+
const digits = /\d/.test(prev) && /\d/.test(curr);
|
|
105
|
+
if (letters || digits) {
|
|
106
|
+
if (curr.charCodeAt(0) - prev.charCodeAt(0) === 1) {
|
|
107
|
+
asc += 1;
|
|
108
|
+
desc = 1;
|
|
109
|
+
}
|
|
110
|
+
else if (prev.charCodeAt(0) - curr.charCodeAt(0) === 1) {
|
|
111
|
+
desc += 1;
|
|
112
|
+
asc = 1;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
asc = 1;
|
|
116
|
+
desc = 1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
asc = 1;
|
|
121
|
+
desc = 1;
|
|
122
|
+
}
|
|
123
|
+
if (asc >= minRun || desc >= minRun)
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
function hasRepeatedSequence(value) {
|
|
129
|
+
if (/(.)\1{2,}/.test(value))
|
|
130
|
+
return true;
|
|
131
|
+
return /([A-Za-z0-9])([^A-Za-z0-9])\1(?:\2\1){2,}/.test(value);
|
|
132
|
+
}
|
|
133
|
+
export function validatePassword(value, policy = {}, i18n) {
|
|
134
|
+
const resolvedI18n = resolveComponentsI18n(i18n);
|
|
135
|
+
if (!value || !value.trim())
|
|
136
|
+
return t(resolvedI18n, "components.validators.password.empty");
|
|
137
|
+
const minSize = Math.max(1, policy.minSize ?? 8);
|
|
138
|
+
const upper = policy.upper ?? true;
|
|
139
|
+
const lower = policy.lower ?? true;
|
|
140
|
+
const number = policy.number ?? true;
|
|
141
|
+
const special = policy.special ?? true;
|
|
142
|
+
const noRepeat = policy.noRepeat ?? true;
|
|
143
|
+
const problems = [];
|
|
144
|
+
if (value.length < minSize) {
|
|
145
|
+
problems.push(t(resolvedI18n, "components.validators.password.minSize", { min: minSize }));
|
|
146
|
+
}
|
|
147
|
+
if (upper && !/[A-Z]/.test(value))
|
|
148
|
+
problems.push(t(resolvedI18n, "components.validators.password.upper"));
|
|
149
|
+
if (lower && !/[a-z]/.test(value))
|
|
150
|
+
problems.push(t(resolvedI18n, "components.validators.password.lower"));
|
|
151
|
+
if (number && !/\d/.test(value))
|
|
152
|
+
problems.push(t(resolvedI18n, "components.validators.password.number"));
|
|
153
|
+
if (special && !/[^A-Za-z0-9]/.test(value)) {
|
|
154
|
+
problems.push(t(resolvedI18n, "components.validators.password.special"));
|
|
155
|
+
}
|
|
156
|
+
if (noRepeat && hasSequentialRun(value, 3)) {
|
|
157
|
+
problems.push(t(resolvedI18n, "components.validators.password.sequence"));
|
|
158
|
+
}
|
|
159
|
+
if (noRepeat && hasRepeatedSequence(value)) {
|
|
160
|
+
problems.push(t(resolvedI18n, "components.validators.password.repeated"));
|
|
161
|
+
}
|
|
162
|
+
if (problems.length) {
|
|
163
|
+
return t(resolvedI18n, "components.validators.password.invalid", { reasons: problems.join("; ") });
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
export function isDateAfter(value, minDate) {
|
|
168
|
+
if (!value || !minDate)
|
|
169
|
+
return true;
|
|
170
|
+
return new Date(value).getTime() >= new Date(minDate).getTime();
|
|
171
|
+
}
|
|
172
|
+
export function isDateBefore(value, maxDate) {
|
|
173
|
+
if (!value || !maxDate)
|
|
174
|
+
return true;
|
|
175
|
+
return new Date(value).getTime() <= new Date(maxDate).getTime();
|
|
176
|
+
}
|
|
177
|
+
function calculateAge(birthDate, today) {
|
|
178
|
+
let age = today.getFullYear() - birthDate.getFullYear();
|
|
179
|
+
const m = today.getMonth() - birthDate.getMonth();
|
|
180
|
+
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
|
|
181
|
+
age -= 1;
|
|
182
|
+
}
|
|
183
|
+
return age;
|
|
184
|
+
}
|
|
185
|
+
export function validateBirthDate(value, policy, i18n) {
|
|
186
|
+
const resolvedI18n = resolveComponentsI18n(i18n);
|
|
187
|
+
const minAge = policy.minAge;
|
|
188
|
+
const maxAge = policy.maxAge;
|
|
189
|
+
const hasMin = typeof minAge === "number" && minAge >= 0;
|
|
190
|
+
const hasMax = typeof maxAge === "number" && maxAge >= 0;
|
|
191
|
+
if (!hasMin && !hasMax) {
|
|
192
|
+
return t(resolvedI18n, "components.validators.birthdate.configMissing");
|
|
193
|
+
}
|
|
194
|
+
if (hasMin && hasMax && minAge > maxAge) {
|
|
195
|
+
return t(resolvedI18n, "components.validators.birthdate.configRange");
|
|
196
|
+
}
|
|
197
|
+
if (!value)
|
|
198
|
+
return null;
|
|
199
|
+
const birth = new Date(value);
|
|
200
|
+
if (Number.isNaN(birth.getTime()))
|
|
201
|
+
return t(resolvedI18n, "components.validators.birthdate.invalidDate");
|
|
202
|
+
const today = new Date();
|
|
203
|
+
const age = calculateAge(birth, today);
|
|
204
|
+
const ok = (!hasMin || age >= minAge) && (!hasMax || age <= maxAge);
|
|
205
|
+
if (ok)
|
|
206
|
+
return null;
|
|
207
|
+
if (hasMin && hasMax) {
|
|
208
|
+
const min = minAge ?? 0;
|
|
209
|
+
const max = maxAge ?? 0;
|
|
210
|
+
return t(resolvedI18n, "components.validators.birthdate.ageBetween", { min, max });
|
|
211
|
+
}
|
|
212
|
+
if (hasMin) {
|
|
213
|
+
const min = minAge ?? 0;
|
|
214
|
+
return t(resolvedI18n, "components.validators.birthdate.ageMin", { min });
|
|
215
|
+
}
|
|
216
|
+
const max = maxAge ?? 0;
|
|
217
|
+
return t(resolvedI18n, "components.validators.birthdate.ageMax", { max });
|
|
218
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type SgWizardPageProps = {
|
|
3
|
+
title?: string;
|
|
4
|
+
icon?: React.ReactNode;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function SgWizardPage(props: SgWizardPageProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export type SgWizardLabels = {
|
|
10
|
+
next: string;
|
|
11
|
+
previous: string;
|
|
12
|
+
finish: string;
|
|
13
|
+
};
|
|
14
|
+
export type SgWizardStepper = "numbered" | "icons" | "none";
|
|
15
|
+
export type SgWizardProps = {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
onFinish: () => void | Promise<void>;
|
|
18
|
+
onStepChange?: (index: number) => void;
|
|
19
|
+
onBeforeNext?: (index: number) => boolean | Promise<boolean>;
|
|
20
|
+
onBeforeFinish?: (index: number) => boolean | Promise<boolean>;
|
|
21
|
+
validateStep?: (index: number) => boolean | Promise<boolean>;
|
|
22
|
+
initialStep?: number;
|
|
23
|
+
labels?: Partial<SgWizardLabels>;
|
|
24
|
+
stepper?: SgWizardStepper;
|
|
25
|
+
className?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function SgWizard(props: SgWizardProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=SgWizard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SgWizard.d.ts","sourceRoot":"","sources":["../../src/wizard/SgWizard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,2CAEpD;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5D,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA+EF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAwI5C"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { t, useComponentsI18n } from "../i18n";
|
|
5
|
+
export function SgWizardPage(props) {
|
|
6
|
+
return _jsx("div", { className: props.className, children: props.children });
|
|
7
|
+
}
|
|
8
|
+
function CheckIcon({ className }) {
|
|
9
|
+
return (_jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) }));
|
|
10
|
+
}
|
|
11
|
+
function StepperBar({ pages, currentStep, mode }) {
|
|
12
|
+
return (_jsx("nav", { "aria-label": "Progress", className: "mb-8", children: _jsx("ol", { className: "flex items-center", children: pages.map((page, i) => {
|
|
13
|
+
const isCompleted = i < currentStep;
|
|
14
|
+
const isCurrent = i === currentStep;
|
|
15
|
+
const title = page.props.title ?? `Step ${i + 1}`;
|
|
16
|
+
const icon = page.props.icon;
|
|
17
|
+
const isLast = i === pages.length - 1;
|
|
18
|
+
return (_jsxs("li", { className: `flex items-center ${isLast ? "" : "flex-1"}`, children: [_jsxs("div", { className: "flex flex-col items-center", children: [_jsx("div", { className: `flex items-center justify-center rounded-full border-2 transition-colors duration-200 ${isCompleted
|
|
19
|
+
? "border-[hsl(var(--primary))] bg-[hsl(var(--primary))] text-white"
|
|
20
|
+
: isCurrent
|
|
21
|
+
? "border-[hsl(var(--primary))] bg-white text-[hsl(var(--primary))]"
|
|
22
|
+
: "border-gray-300 bg-white text-gray-400"}`, style: { width: 40, height: 40 }, children: isCompleted ? (_jsx(CheckIcon, { className: "size-5" })) : mode === "icons" && icon ? (_jsx("span", { className: "flex items-center justify-center size-5", children: icon })) : (_jsx("span", { className: "text-sm font-semibold", children: i + 1 })) }), _jsx("span", { className: `mt-2 text-xs font-medium text-center max-w-[80px] leading-tight ${isCompleted || isCurrent ? "text-[hsl(var(--primary))]" : "text-gray-400"}`, children: title })] }), !isLast ? (_jsx("div", { className: "flex-1 mx-2 self-start", style: { marginTop: 19 }, children: _jsx("div", { className: `h-0.5 w-full transition-colors duration-200 ${isCompleted ? "bg-[hsl(var(--primary))]" : "bg-gray-200"}` }) })) : null] }, i));
|
|
23
|
+
}) }) }));
|
|
24
|
+
}
|
|
25
|
+
export function SgWizard(props) {
|
|
26
|
+
const i18n = useComponentsI18n();
|
|
27
|
+
const pages = React.Children.toArray(props.children).filter((child) => React.isValidElement(child) && child.type === SgWizardPage);
|
|
28
|
+
const stepper = props.stepper ?? "none";
|
|
29
|
+
const [step, setStep] = React.useState(() => {
|
|
30
|
+
const idx = props.initialStep ?? 0;
|
|
31
|
+
return Math.min(Math.max(idx, 0), Math.max(pages.length - 1, 0));
|
|
32
|
+
});
|
|
33
|
+
const [isFinishing, setIsFinishing] = React.useState(false);
|
|
34
|
+
const [isValidating, setIsValidating] = React.useState(false);
|
|
35
|
+
const pageRef = React.useRef(null);
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
props.onStepChange?.(step);
|
|
38
|
+
}, [props, step]);
|
|
39
|
+
const labels = {
|
|
40
|
+
next: t(i18n, "components.wizard.next"),
|
|
41
|
+
previous: t(i18n, "components.wizard.previous"),
|
|
42
|
+
finish: t(i18n, "components.wizard.finish"),
|
|
43
|
+
...(props.labels ?? {})
|
|
44
|
+
};
|
|
45
|
+
const isFirst = step <= 0;
|
|
46
|
+
const isLast = step >= pages.length - 1;
|
|
47
|
+
const validateCurrentPage = async () => {
|
|
48
|
+
const container = pageRef.current;
|
|
49
|
+
if (!container)
|
|
50
|
+
return true;
|
|
51
|
+
const inputs = container.querySelectorAll("input, select, textarea");
|
|
52
|
+
for (const input of Array.from(inputs)) {
|
|
53
|
+
input.focus({ preventScroll: true });
|
|
54
|
+
input.blur();
|
|
55
|
+
}
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
57
|
+
const errors = container.querySelectorAll("[data-sg-error]");
|
|
58
|
+
return errors.length === 0;
|
|
59
|
+
};
|
|
60
|
+
const goNext = async () => {
|
|
61
|
+
if (isLast)
|
|
62
|
+
return;
|
|
63
|
+
if (isValidating)
|
|
64
|
+
return;
|
|
65
|
+
setIsValidating(true);
|
|
66
|
+
try {
|
|
67
|
+
const pageValid = await validateCurrentPage();
|
|
68
|
+
if (!pageValid)
|
|
69
|
+
return;
|
|
70
|
+
if (props.validateStep) {
|
|
71
|
+
const ok = await props.validateStep(step);
|
|
72
|
+
if (!ok)
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (props.onBeforeNext) {
|
|
76
|
+
const ok = await props.onBeforeNext(step);
|
|
77
|
+
if (!ok)
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setStep((prev) => Math.min(prev + 1, pages.length - 1));
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
setIsValidating(false);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const goPrevious = () => {
|
|
87
|
+
if (isFirst)
|
|
88
|
+
return;
|
|
89
|
+
setStep((prev) => Math.max(prev - 1, 0));
|
|
90
|
+
};
|
|
91
|
+
const handleFinish = async () => {
|
|
92
|
+
if (isFinishing)
|
|
93
|
+
return;
|
|
94
|
+
if (isValidating)
|
|
95
|
+
return;
|
|
96
|
+
setIsValidating(true);
|
|
97
|
+
try {
|
|
98
|
+
const pageValid = await validateCurrentPage();
|
|
99
|
+
if (!pageValid)
|
|
100
|
+
return;
|
|
101
|
+
if (props.validateStep) {
|
|
102
|
+
const ok = await props.validateStep(step);
|
|
103
|
+
if (!ok)
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (props.onBeforeFinish) {
|
|
107
|
+
const ok = await props.onBeforeFinish(step);
|
|
108
|
+
if (!ok)
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
setIsValidating(false);
|
|
114
|
+
}
|
|
115
|
+
setIsFinishing(true);
|
|
116
|
+
try {
|
|
117
|
+
await props.onFinish();
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
setIsFinishing(false);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
return (_jsxs("div", { className: props.className, children: [stepper !== "none" ? (_jsx(StepperBar, { pages: pages, currentStep: step, mode: stepper })) : null, _jsx("div", { ref: pageRef, children: pages[step] }), _jsxs("div", { className: "mt-6 flex flex-wrap items-center justify-between gap-3", children: [_jsx("div", { children: !isFirst ? (_jsx("button", { type: "button", onClick: goPrevious, className: "inline-flex h-10 items-center justify-center rounded-full border border-border px-5 text-sm font-semibold text-foreground/80 transition hover:bg-black/5", children: labels.previous })) : null }), _jsx("div", { className: "flex items-center gap-3", children: !isLast ? (_jsx("button", { type: "button", onClick: goNext, disabled: isValidating, className: "inline-flex h-10 items-center justify-center rounded-full bg-[hsl(var(--primary))] px-5 text-sm font-semibold text-white shadow-lg shadow-[hsl(var(--primary)/0.35)] transition hover:brightness-95 disabled:cursor-not-allowed disabled:opacity-60", children: labels.next })) : (_jsx("button", { type: "button", onClick: handleFinish, disabled: isFinishing || isValidating, className: "inline-flex h-10 items-center justify-center rounded-full bg-[hsl(var(--primary))] px-5 text-sm font-semibold text-white shadow-lg shadow-[hsl(var(--primary)/0.35)] transition hover:brightness-95 disabled:cursor-not-allowed disabled:opacity-60", children: labels.finish })) })] })] }));
|
|
124
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seedgrid/fe-components",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
22
|
+
"react-hook-form": "^7.0.0",
|
|
23
|
+
"lucide-react": "^0.468.0",
|
|
24
|
+
"@tiptap/react": "^2.9.1",
|
|
25
|
+
"@tiptap/starter-kit": "^2.9.1",
|
|
26
|
+
"@tiptap/extension-underline": "^2.9.1",
|
|
27
|
+
"@tiptap/extension-link": "^2.9.1",
|
|
28
|
+
"@tiptap/extension-image": "^2.9.1",
|
|
29
|
+
"@tiptap/extension-text-align": "^2.9.1",
|
|
30
|
+
"@tiptap/extension-text-style": "^2.9.1",
|
|
31
|
+
"@tiptap/extension-color": "^2.9.1",
|
|
32
|
+
"@tiptap/extension-highlight": "^2.9.1",
|
|
33
|
+
"@tiptap/extension-subscript": "^2.9.1",
|
|
34
|
+
"@tiptap/extension-superscript": "^2.9.1",
|
|
35
|
+
"@tiptap/extension-font-family": "^2.9.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"react-hook-form": "^7.0.0",
|
|
39
|
+
"lucide-react": "^0.468.0",
|
|
40
|
+
"@tiptap/react": "^2.9.1",
|
|
41
|
+
"@tiptap/starter-kit": "^2.9.1",
|
|
42
|
+
"@tiptap/extension-underline": "^2.9.1",
|
|
43
|
+
"@tiptap/extension-link": "^2.9.1",
|
|
44
|
+
"@tiptap/extension-image": "^2.9.1",
|
|
45
|
+
"@tiptap/extension-text-align": "^2.9.1",
|
|
46
|
+
"@tiptap/extension-text-style": "^2.9.1",
|
|
47
|
+
"@tiptap/extension-color": "^2.9.1",
|
|
48
|
+
"@tiptap/extension-highlight": "^2.9.1",
|
|
49
|
+
"@tiptap/extension-subscript": "^2.9.1",
|
|
50
|
+
"@tiptap/extension-superscript": "^2.9.1",
|
|
51
|
+
"@tiptap/extension-font-family": "^2.9.1"
|
|
52
|
+
}
|
|
53
|
+
}
|