@yomologic/react-ui 0.6.2 → 0.6.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/index.d.mts CHANGED
@@ -20,6 +20,42 @@ type ButtonAsLink = ButtonBaseProps & Omit<React.AnchorHTMLAttributes<HTMLAnchor
20
20
  type ButtonProps = ButtonAsButton | ButtonAsLink;
21
21
  declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
22
22
 
23
+ /**
24
+ * Input formatting utilities
25
+ */
26
+ type FormatType = "phone" | "phone-intl" | "credit-card" | "date" | "datetime";
27
+ /**
28
+ * Format US phone number: (123) 456-7890
29
+ */
30
+ declare const formatPhoneUS: (value: string) => string;
31
+ /**
32
+ * Format international phone number: +1 (123) 456-7890
33
+ */
34
+ declare const formatPhoneIntl: (value: string) => string;
35
+ /**
36
+ * Format credit card number: 1234 5678 9012 3456
37
+ */
38
+ declare const formatCreditCard: (value: string) => string;
39
+ /**
40
+ * Format date: MM/DD/YYYY
41
+ */
42
+ declare const formatDate: (value: string) => string;
43
+ /**
44
+ * Format datetime: MM/DD/YYYY HH:MM AM/PM
45
+ */
46
+ declare const formatDateTime: (value: string) => string;
47
+ /**
48
+ * Get raw value from formatted string
49
+ * - For phone/credit-card: returns digits only
50
+ * - For date: returns YYYY-MM-DD format (ISO 8601)
51
+ * - For datetime: returns ISO 8601 with timezone offset
52
+ */
53
+ declare const getRawValue: (value: string, format?: FormatType) => string;
54
+ /**
55
+ * Apply format based on format type
56
+ */
57
+ declare const applyFormat: (value: string, format: FormatType) => string;
58
+
23
59
  interface FormState {
24
60
  values: Record<string, any>;
25
61
  errors: Record<string, string>;
@@ -62,6 +98,8 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
62
98
  onValidationError?: (error: string | undefined) => void;
63
99
  /** Regex pattern for validation (string or RegExp) */
64
100
  pattern?: RegExp | string;
101
+ /** Format type for auto-formatting input value (phone, credit-card, date, etc.) */
102
+ format?: FormatType | ((value: string) => string);
65
103
  /** Custom error messages for built-in validations */
66
104
  errorMessages?: {
67
105
  required?: string;
@@ -72,6 +110,7 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
72
110
  pattern?: string;
73
111
  email?: string;
74
112
  url?: string;
113
+ date?: string;
75
114
  };
76
115
  }
77
116
  declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
@@ -1107,4 +1146,47 @@ type ThemeId = keyof typeof themes;
1107
1146
 
1108
1147
  declare function cn(...inputs: ClassValue[]): string;
1109
1148
 
1110
- export { Alert, Badge, Button, Card, CardActionArea, CardActions, CardContent, CardDescription, CardFooter, CardHeader, CardMedia, CardTitle, Checkbox, CheckboxGroup, CodeSnippet, Container, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Divider, Drawer, type NavItem$1 as DrawerNavItem, type NavSection$1 as DrawerNavSection, type DrawerProps, EmptyState, Form, type FormContextValue, FormControl, type FormControlContextValue, FormControlLabel, type FormControlProps, type FormControlState, type FormFieldProps, FormHelperText, type FormState, Input, NativeSelect, type NativeSelectProps, Nav, type NavItem$2 as NavItem, type NavProps, type NavSection, RadioGroup, Rating, SectionLayout, Select, type SelectOption, type SelectProps, SidebarNav, type NavItem as SidebarNavItem, Slider, type SliderMark, type SliderProps, Spinner, Switch, type SwitchProps, Textarea, type TextareaProps, type ThemeId, ThemeProvider, type ValidationFunction, type ValidationRule, cn, themes, useForm, useFormContext, useFormControl, useFormControlContext, useFormField, useTheme };
1149
+ /**
1150
+ * Validation constants and regex patterns
1151
+ */
1152
+ /**
1153
+ * Email validation regex
1154
+ * Matches standard email formats like: user@example.com
1155
+ * Requires @ symbol, domain name, and TLD (like .com, .org, etc.)
1156
+ */
1157
+ declare const EMAIL_REGEX: RegExp;
1158
+ /**
1159
+ * URL validation regex
1160
+ * Matches http(s) URLs
1161
+ */
1162
+ declare const URL_REGEX: RegExp;
1163
+ /**
1164
+ * Phone number validation regex (US format)
1165
+ * Matches formats like: (123) 456-7890, 123-456-7890, 1234567890
1166
+ */
1167
+ declare const PHONE_REGEX: RegExp;
1168
+ /**
1169
+ * Validate email format
1170
+ */
1171
+ declare const isValidEmail: (email: string) => boolean;
1172
+ /**
1173
+ * Validate URL format and security
1174
+ * Checks for valid http/https protocol and blocks dangerous protocols
1175
+ */
1176
+ declare const isValidUrl: (url: string) => boolean;
1177
+ /**
1178
+ * Date validation regex (MM/DD/YYYY)
1179
+ * Matches formats like: 12/31/2024, 01/01/2024
1180
+ */
1181
+ declare const DATE_REGEX: RegExp;
1182
+ /**
1183
+ * Validate phone number format
1184
+ */
1185
+ declare const isValidPhone: (phone: string) => boolean;
1186
+ /**
1187
+ * Validate date format and value
1188
+ * Checks if date is in MM/DD/YYYY format and is a valid calendar date
1189
+ */
1190
+ declare const isValidDate: (date: string) => boolean;
1191
+
1192
+ export { Alert, Badge, Button, Card, CardActionArea, CardActions, CardContent, CardDescription, CardFooter, CardHeader, CardMedia, CardTitle, Checkbox, CheckboxGroup, CodeSnippet, Container, DATE_REGEX, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Divider, Drawer, type NavItem$1 as DrawerNavItem, type NavSection$1 as DrawerNavSection, type DrawerProps, EMAIL_REGEX, EmptyState, Form, type FormContextValue, FormControl, type FormControlContextValue, FormControlLabel, type FormControlProps, type FormControlState, type FormFieldProps, FormHelperText, type FormState, type FormatType, Input, NativeSelect, type NativeSelectProps, Nav, type NavItem$2 as NavItem, type NavProps, type NavSection, PHONE_REGEX, RadioGroup, Rating, SectionLayout, Select, type SelectOption, type SelectProps, SidebarNav, type NavItem as SidebarNavItem, Slider, type SliderMark, type SliderProps, Spinner, Switch, type SwitchProps, Textarea, type TextareaProps, type ThemeId, ThemeProvider, URL_REGEX, type ValidationFunction, type ValidationRule, applyFormat, cn, formatCreditCard, formatDate, formatDateTime, formatPhoneIntl, formatPhoneUS, getRawValue, isValidDate, isValidEmail, isValidPhone, isValidUrl, themes, useForm, useFormContext, useFormControl, useFormControlContext, useFormField, useTheme };
package/dist/index.d.ts CHANGED
@@ -20,6 +20,42 @@ type ButtonAsLink = ButtonBaseProps & Omit<React.AnchorHTMLAttributes<HTMLAnchor
20
20
  type ButtonProps = ButtonAsButton | ButtonAsLink;
21
21
  declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
22
22
 
23
+ /**
24
+ * Input formatting utilities
25
+ */
26
+ type FormatType = "phone" | "phone-intl" | "credit-card" | "date" | "datetime";
27
+ /**
28
+ * Format US phone number: (123) 456-7890
29
+ */
30
+ declare const formatPhoneUS: (value: string) => string;
31
+ /**
32
+ * Format international phone number: +1 (123) 456-7890
33
+ */
34
+ declare const formatPhoneIntl: (value: string) => string;
35
+ /**
36
+ * Format credit card number: 1234 5678 9012 3456
37
+ */
38
+ declare const formatCreditCard: (value: string) => string;
39
+ /**
40
+ * Format date: MM/DD/YYYY
41
+ */
42
+ declare const formatDate: (value: string) => string;
43
+ /**
44
+ * Format datetime: MM/DD/YYYY HH:MM AM/PM
45
+ */
46
+ declare const formatDateTime: (value: string) => string;
47
+ /**
48
+ * Get raw value from formatted string
49
+ * - For phone/credit-card: returns digits only
50
+ * - For date: returns YYYY-MM-DD format (ISO 8601)
51
+ * - For datetime: returns ISO 8601 with timezone offset
52
+ */
53
+ declare const getRawValue: (value: string, format?: FormatType) => string;
54
+ /**
55
+ * Apply format based on format type
56
+ */
57
+ declare const applyFormat: (value: string, format: FormatType) => string;
58
+
23
59
  interface FormState {
24
60
  values: Record<string, any>;
25
61
  errors: Record<string, string>;
@@ -62,6 +98,8 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
62
98
  onValidationError?: (error: string | undefined) => void;
63
99
  /** Regex pattern for validation (string or RegExp) */
64
100
  pattern?: RegExp | string;
101
+ /** Format type for auto-formatting input value (phone, credit-card, date, etc.) */
102
+ format?: FormatType | ((value: string) => string);
65
103
  /** Custom error messages for built-in validations */
66
104
  errorMessages?: {
67
105
  required?: string;
@@ -72,6 +110,7 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
72
110
  pattern?: string;
73
111
  email?: string;
74
112
  url?: string;
113
+ date?: string;
75
114
  };
76
115
  }
77
116
  declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
@@ -1107,4 +1146,47 @@ type ThemeId = keyof typeof themes;
1107
1146
 
1108
1147
  declare function cn(...inputs: ClassValue[]): string;
1109
1148
 
1110
- export { Alert, Badge, Button, Card, CardActionArea, CardActions, CardContent, CardDescription, CardFooter, CardHeader, CardMedia, CardTitle, Checkbox, CheckboxGroup, CodeSnippet, Container, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Divider, Drawer, type NavItem$1 as DrawerNavItem, type NavSection$1 as DrawerNavSection, type DrawerProps, EmptyState, Form, type FormContextValue, FormControl, type FormControlContextValue, FormControlLabel, type FormControlProps, type FormControlState, type FormFieldProps, FormHelperText, type FormState, Input, NativeSelect, type NativeSelectProps, Nav, type NavItem$2 as NavItem, type NavProps, type NavSection, RadioGroup, Rating, SectionLayout, Select, type SelectOption, type SelectProps, SidebarNav, type NavItem as SidebarNavItem, Slider, type SliderMark, type SliderProps, Spinner, Switch, type SwitchProps, Textarea, type TextareaProps, type ThemeId, ThemeProvider, type ValidationFunction, type ValidationRule, cn, themes, useForm, useFormContext, useFormControl, useFormControlContext, useFormField, useTheme };
1149
+ /**
1150
+ * Validation constants and regex patterns
1151
+ */
1152
+ /**
1153
+ * Email validation regex
1154
+ * Matches standard email formats like: user@example.com
1155
+ * Requires @ symbol, domain name, and TLD (like .com, .org, etc.)
1156
+ */
1157
+ declare const EMAIL_REGEX: RegExp;
1158
+ /**
1159
+ * URL validation regex
1160
+ * Matches http(s) URLs
1161
+ */
1162
+ declare const URL_REGEX: RegExp;
1163
+ /**
1164
+ * Phone number validation regex (US format)
1165
+ * Matches formats like: (123) 456-7890, 123-456-7890, 1234567890
1166
+ */
1167
+ declare const PHONE_REGEX: RegExp;
1168
+ /**
1169
+ * Validate email format
1170
+ */
1171
+ declare const isValidEmail: (email: string) => boolean;
1172
+ /**
1173
+ * Validate URL format and security
1174
+ * Checks for valid http/https protocol and blocks dangerous protocols
1175
+ */
1176
+ declare const isValidUrl: (url: string) => boolean;
1177
+ /**
1178
+ * Date validation regex (MM/DD/YYYY)
1179
+ * Matches formats like: 12/31/2024, 01/01/2024
1180
+ */
1181
+ declare const DATE_REGEX: RegExp;
1182
+ /**
1183
+ * Validate phone number format
1184
+ */
1185
+ declare const isValidPhone: (phone: string) => boolean;
1186
+ /**
1187
+ * Validate date format and value
1188
+ * Checks if date is in MM/DD/YYYY format and is a valid calendar date
1189
+ */
1190
+ declare const isValidDate: (date: string) => boolean;
1191
+
1192
+ export { Alert, Badge, Button, Card, CardActionArea, CardActions, CardContent, CardDescription, CardFooter, CardHeader, CardMedia, CardTitle, Checkbox, CheckboxGroup, CodeSnippet, Container, DATE_REGEX, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Divider, Drawer, type NavItem$1 as DrawerNavItem, type NavSection$1 as DrawerNavSection, type DrawerProps, EMAIL_REGEX, EmptyState, Form, type FormContextValue, FormControl, type FormControlContextValue, FormControlLabel, type FormControlProps, type FormControlState, type FormFieldProps, FormHelperText, type FormState, type FormatType, Input, NativeSelect, type NativeSelectProps, Nav, type NavItem$2 as NavItem, type NavProps, type NavSection, PHONE_REGEX, RadioGroup, Rating, SectionLayout, Select, type SelectOption, type SelectProps, SidebarNav, type NavItem as SidebarNavItem, Slider, type SliderMark, type SliderProps, Spinner, Switch, type SwitchProps, Textarea, type TextareaProps, type ThemeId, ThemeProvider, URL_REGEX, type ValidationFunction, type ValidationRule, applyFormat, cn, formatCreditCard, formatDate, formatDateTime, formatPhoneIntl, formatPhoneUS, getRawValue, isValidDate, isValidEmail, isValidPhone, isValidUrl, themes, useForm, useFormContext, useFormControl, useFormControlContext, useFormField, useTheme };
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __export = (target, all) => {
10
13
  for (var name in all)
11
14
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -28,6 +31,62 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
31
  ));
29
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
33
 
34
+ // src/constants/validation.ts
35
+ var validation_exports = {};
36
+ __export(validation_exports, {
37
+ DATE_REGEX: () => DATE_REGEX,
38
+ EMAIL_REGEX: () => EMAIL_REGEX,
39
+ PHONE_REGEX: () => PHONE_REGEX,
40
+ URL_REGEX: () => URL_REGEX,
41
+ isValidDate: () => isValidDate,
42
+ isValidEmail: () => isValidEmail,
43
+ isValidPhone: () => isValidPhone,
44
+ isValidUrl: () => isValidUrl
45
+ });
46
+ var EMAIL_REGEX, URL_REGEX, PHONE_REGEX, isValidEmail, isValidUrl, DATE_REGEX, isValidPhone, isValidDate;
47
+ var init_validation = __esm({
48
+ "src/constants/validation.ts"() {
49
+ "use strict";
50
+ EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}$/;
51
+ URL_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
52
+ PHONE_REGEX = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;
53
+ isValidEmail = (email) => {
54
+ return EMAIL_REGEX.test(email);
55
+ };
56
+ isValidUrl = (url) => {
57
+ if (/^(javascript|data|vbscript|file|about):/i.test(url)) {
58
+ return false;
59
+ }
60
+ return URL_REGEX.test(url);
61
+ };
62
+ DATE_REGEX = /^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/;
63
+ isValidPhone = (phone) => {
64
+ return PHONE_REGEX.test(phone);
65
+ };
66
+ isValidDate = (date) => {
67
+ if (!DATE_REGEX.test(date)) {
68
+ return false;
69
+ }
70
+ const [month, day, year] = date.split("/").map(Number);
71
+ if (year < 1900 || year > 2100) {
72
+ return false;
73
+ }
74
+ if (month < 1 || month > 12) {
75
+ return false;
76
+ }
77
+ const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
78
+ const isLeapYear = year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
79
+ if (isLeapYear && month === 2) {
80
+ daysInMonth[1] = 29;
81
+ }
82
+ if (day < 1 || day > daysInMonth[month - 1]) {
83
+ return false;
84
+ }
85
+ return true;
86
+ };
87
+ }
88
+ });
89
+
31
90
  // src/index.ts
32
91
  var index_exports = {};
33
92
  __export(index_exports, {
@@ -47,6 +106,7 @@ __export(index_exports, {
47
106
  CheckboxGroup: () => CheckboxGroup,
48
107
  CodeSnippet: () => CodeSnippet,
49
108
  Container: () => Container,
109
+ DATE_REGEX: () => DATE_REGEX,
50
110
  Dialog: () => Dialog,
51
111
  DialogContent: () => DialogContent,
52
112
  DialogDescription: () => DialogDescription,
@@ -55,6 +115,7 @@ __export(index_exports, {
55
115
  DialogTitle: () => DialogTitle,
56
116
  Divider: () => Divider,
57
117
  Drawer: () => Drawer,
118
+ EMAIL_REGEX: () => EMAIL_REGEX,
58
119
  EmptyState: () => EmptyState,
59
120
  Form: () => Form,
60
121
  FormControl: () => FormControl,
@@ -63,6 +124,7 @@ __export(index_exports, {
63
124
  Input: () => Input,
64
125
  NativeSelect: () => NativeSelect,
65
126
  Nav: () => Nav,
127
+ PHONE_REGEX: () => PHONE_REGEX,
66
128
  RadioGroup: () => RadioGroup,
67
129
  Rating: () => Rating,
68
130
  SectionLayout: () => SectionLayout,
@@ -73,7 +135,19 @@ __export(index_exports, {
73
135
  Switch: () => Switch,
74
136
  Textarea: () => Textarea,
75
137
  ThemeProvider: () => ThemeProvider,
138
+ URL_REGEX: () => URL_REGEX,
139
+ applyFormat: () => applyFormat,
76
140
  cn: () => cn,
141
+ formatCreditCard: () => formatCreditCard,
142
+ formatDate: () => formatDate,
143
+ formatDateTime: () => formatDateTime,
144
+ formatPhoneIntl: () => formatPhoneIntl,
145
+ formatPhoneUS: () => formatPhoneUS,
146
+ getRawValue: () => getRawValue,
147
+ isValidDate: () => isValidDate,
148
+ isValidEmail: () => isValidEmail,
149
+ isValidPhone: () => isValidPhone,
150
+ isValidUrl: () => isValidUrl,
77
151
  themes: () => themes_default,
78
152
  useForm: () => useForm,
79
153
  useFormContext: () => useFormContext,
@@ -234,8 +308,113 @@ Button.displayName = "Button";
234
308
  // src/ui/input.tsx
235
309
  var import_react5 = __toESM(require("react"));
236
310
 
311
+ // src/lib/formatting.ts
312
+ var formatPhoneUS = (value) => {
313
+ const digits = value.replace(/\D/g, "");
314
+ const limited = digits.slice(0, 10);
315
+ if (limited.length === 0) return "";
316
+ if (limited.length <= 3) return `(${limited}`;
317
+ if (limited.length <= 6)
318
+ return `(${limited.slice(0, 3)}) ${limited.slice(3)}`;
319
+ return `(${limited.slice(0, 3)}) ${limited.slice(3, 6)}-${limited.slice(6)}`;
320
+ };
321
+ var formatPhoneIntl = (value) => {
322
+ let cleaned = value.replace(/[^\d+]/g, "");
323
+ if (!cleaned.startsWith("+")) {
324
+ cleaned = "+" + cleaned;
325
+ }
326
+ cleaned = cleaned.slice(0, 16);
327
+ const digits = cleaned.slice(1);
328
+ if (digits.length === 0) return "+";
329
+ if (digits.length <= 1) return `+${digits}`;
330
+ if (digits.length <= 4) return `+${digits.slice(0, 1)} (${digits.slice(1)}`;
331
+ if (digits.length <= 7)
332
+ return `+${digits.slice(0, 1)} (${digits.slice(1, 4)}) ${digits.slice(4)}`;
333
+ return `+${digits.slice(0, 1)} (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;
334
+ };
335
+ var formatCreditCard = (value) => {
336
+ const digits = value.replace(/\D/g, "");
337
+ const limited = digits.slice(0, 16);
338
+ return limited.replace(/(\d{4})/g, "$1 ").trim();
339
+ };
340
+ var formatDate = (value) => {
341
+ const digits = value.replace(/\D/g, "");
342
+ const limited = digits.slice(0, 8);
343
+ if (limited.length === 0) return "";
344
+ if (limited.length <= 2) return limited;
345
+ if (limited.length <= 4)
346
+ return `${limited.slice(0, 2)}/${limited.slice(2)}`;
347
+ return `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4)}`;
348
+ };
349
+ var formatDateTime = (value) => {
350
+ const digits = value.replace(/\D/g, "");
351
+ const limited = digits.slice(0, 12);
352
+ if (limited.length === 0) return "";
353
+ if (limited.length <= 2) return limited;
354
+ if (limited.length <= 4)
355
+ return `${limited.slice(0, 2)}/${limited.slice(2)}`;
356
+ if (limited.length <= 8)
357
+ return `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4)}`;
358
+ const datePart = `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4, 8)}`;
359
+ if (limited.length <= 10) return `${datePart} ${limited.slice(8)}`;
360
+ let hours = parseInt(limited.slice(8, 10));
361
+ const minutes = limited.slice(10, 12);
362
+ const ampm = hours >= 12 ? "PM" : "AM";
363
+ hours = hours % 12 || 12;
364
+ return `${datePart} ${hours}:${minutes} ${ampm}`;
365
+ };
366
+ var getRawValue = (value, format) => {
367
+ if (format === "date") {
368
+ const digits = value.replace(/\D/g, "");
369
+ if (digits.length === 8) {
370
+ const month = digits.slice(0, 2);
371
+ const day = digits.slice(2, 4);
372
+ const year = digits.slice(4, 8);
373
+ return `${year}-${month}-${day}`;
374
+ }
375
+ return digits;
376
+ }
377
+ if (format === "datetime") {
378
+ const digits = value.replace(/\D/g, "");
379
+ if (digits.length === 12) {
380
+ const month = digits.slice(0, 2);
381
+ const day = digits.slice(2, 4);
382
+ const year = digits.slice(4, 8);
383
+ const hours = digits.slice(8, 10);
384
+ const minutes = digits.slice(10, 12);
385
+ const date = new Date(
386
+ parseInt(year),
387
+ parseInt(month) - 1,
388
+ parseInt(day),
389
+ parseInt(hours),
390
+ parseInt(minutes)
391
+ );
392
+ return date.toISOString();
393
+ }
394
+ return digits;
395
+ }
396
+ return value.replace(/\D/g, "");
397
+ };
398
+ var applyFormat = (value, format) => {
399
+ switch (format) {
400
+ case "phone":
401
+ return formatPhoneUS(value);
402
+ case "phone-intl":
403
+ return formatPhoneIntl(value);
404
+ case "credit-card":
405
+ return formatCreditCard(value);
406
+ case "date":
407
+ return formatDate(value);
408
+ case "datetime":
409
+ return formatDateTime(value);
410
+ default:
411
+ return value;
412
+ }
413
+ };
414
+
237
415
  // src/ui/hooks/useFormField.ts
238
416
  var import_react4 = require("react");
417
+ init_validation();
239
418
 
240
419
  // src/ui/form.tsx
241
420
  var import_react2 = require("react");
@@ -807,10 +986,13 @@ function useFormField2(options) {
807
986
  return errorMessages?.required || "This field is required";
808
987
  }
809
988
  if (value) {
810
- if (type === "email" && !value.includes("@")) {
989
+ if (type === "email" && !EMAIL_REGEX.test(value)) {
811
990
  return errorMessages?.email || "Please enter a valid email address";
812
991
  }
813
992
  if (type === "url") {
993
+ if (/^(javascript|data|vbscript|file|about):/i.test(value)) {
994
+ return errorMessages?.url || "Invalid URL protocol";
995
+ }
814
996
  try {
815
997
  new URL(value);
816
998
  } catch {
@@ -864,10 +1046,15 @@ function useFormField2(options) {
864
1046
  return errorMessages?.required || "This field is required";
865
1047
  }
866
1048
  if (value) {
867
- if (type === "email" && !value.includes("@")) {
1049
+ if (type === "email" && !EMAIL_REGEX.test(value)) {
868
1050
  return errorMessages?.email || "Please enter a valid email address";
869
1051
  }
870
1052
  if (type === "url") {
1053
+ if (/^(javascript|data|vbscript|file|about):/i.test(
1054
+ value
1055
+ )) {
1056
+ return errorMessages?.url || "Invalid URL protocol";
1057
+ }
871
1058
  try {
872
1059
  new URL(value);
873
1060
  } catch {
@@ -981,12 +1168,31 @@ var Input = import_react5.default.forwardRef(
981
1168
  validate,
982
1169
  onValidationError,
983
1170
  pattern,
1171
+ format,
984
1172
  errorMessages,
985
1173
  ...props
986
1174
  }, ref) => {
1175
+ const [internalValue, setInternalValue] = import_react5.default.useState("");
1176
+ const dateValidate = import_react5.default.useCallback(
1177
+ async (value) => {
1178
+ if (validate) {
1179
+ const customError = await validate(value);
1180
+ if (customError) return customError;
1181
+ }
1182
+ if (format === "date" && value && value.length === 8) {
1183
+ const { isValidDate: isValidDate2 } = await Promise.resolve().then(() => (init_validation(), validation_exports));
1184
+ const formatted = `${value.slice(0, 2)}/${value.slice(2, 4)}/${value.slice(4, 8)}`;
1185
+ if (!isValidDate2(formatted)) {
1186
+ return errorMessages?.date || "Please enter a valid date";
1187
+ }
1188
+ }
1189
+ return void 0;
1190
+ },
1191
+ [validate, format, errorMessages]
1192
+ );
987
1193
  const {
988
1194
  fieldId,
989
- value: inputValue,
1195
+ value: hookValue,
990
1196
  error: inputError,
991
1197
  isDisabled,
992
1198
  isRequired,
@@ -1008,19 +1214,71 @@ var Input = import_react5.default.forwardRef(
1008
1214
  min: props.min,
1009
1215
  max: props.max,
1010
1216
  pattern,
1011
- validate,
1217
+ validate: format === "date" ? dateValidate : validate,
1012
1218
  onValidationError,
1013
1219
  errorMessages,
1014
1220
  idPrefix: "input"
1015
1221
  });
1222
+ const inputValue = hookValue !== void 0 ? hookValue : internalValue;
1223
+ const [cursorPosition, setCursorPosition] = import_react5.default.useState(null);
1016
1224
  const handleChange = (e) => {
1017
- hookHandleChange(e.target.value);
1225
+ const input = e.target;
1226
+ const newValue = input.value;
1227
+ const cursorPos = input.selectionStart || 0;
1228
+ if (format && typeof format === "string") {
1229
+ const cleaned = newValue.replace(/\D/g, "");
1230
+ const formatted = applyFormat(cleaned, format);
1231
+ if (hookValue !== void 0) {
1232
+ hookHandleChange(cleaned);
1233
+ } else {
1234
+ setInternalValue(cleaned);
1235
+ }
1236
+ let formattedPos = 0;
1237
+ let digitCount = 0;
1238
+ const targetDigits = cleaned.slice(
1239
+ 0,
1240
+ Math.min(cleaned.length, cursorPos)
1241
+ );
1242
+ for (let i = 0; i < formatted.length && digitCount < targetDigits.length; i++) {
1243
+ if (/\d/.test(formatted[i])) {
1244
+ digitCount++;
1245
+ }
1246
+ formattedPos = i + 1;
1247
+ }
1248
+ setCursorPosition(formattedPos);
1249
+ } else {
1250
+ if (hookValue !== void 0) {
1251
+ hookHandleChange(newValue);
1252
+ } else {
1253
+ setInternalValue(newValue);
1254
+ }
1255
+ }
1018
1256
  onChange?.(e);
1019
1257
  };
1020
1258
  const handleBlur = (e) => {
1021
- hookHandleBlur(e.target.value);
1259
+ const value = e.target.value;
1260
+ const blurValue = format && typeof format === "string" ? value.replace(/\D/g, "") : value;
1261
+ hookHandleBlur(blurValue);
1022
1262
  onBlur?.(e);
1023
1263
  };
1264
+ const displayValue = import_react5.default.useMemo(() => {
1265
+ if (format && inputValue) {
1266
+ if (typeof format === "function") {
1267
+ return format(inputValue);
1268
+ }
1269
+ return applyFormat(inputValue, format);
1270
+ }
1271
+ return inputValue;
1272
+ }, [format, inputValue]);
1273
+ import_react5.default.useEffect(() => {
1274
+ if (cursorPosition !== null && internalRef.current) {
1275
+ internalRef.current.setSelectionRange(
1276
+ cursorPosition,
1277
+ cursorPosition
1278
+ );
1279
+ setCursorPosition(null);
1280
+ }
1281
+ }, [cursorPosition, displayValue]);
1024
1282
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1025
1283
  "div",
1026
1284
  {
@@ -1053,7 +1311,7 @@ var Input = import_react5.default.forwardRef(
1053
1311
  },
1054
1312
  type,
1055
1313
  id: fieldId,
1056
- value: inputValue,
1314
+ value: displayValue,
1057
1315
  onChange: handleChange,
1058
1316
  onBlur: handleBlur,
1059
1317
  disabled: isDisabled,
@@ -5247,6 +5505,9 @@ function useTheme() {
5247
5505
  }
5248
5506
  return context;
5249
5507
  }
5508
+
5509
+ // src/index.ts
5510
+ init_validation();
5250
5511
  // Annotate the CommonJS export names for ESM import in node:
5251
5512
  0 && (module.exports = {
5252
5513
  Alert,
@@ -5265,6 +5526,7 @@ function useTheme() {
5265
5526
  CheckboxGroup,
5266
5527
  CodeSnippet,
5267
5528
  Container,
5529
+ DATE_REGEX,
5268
5530
  Dialog,
5269
5531
  DialogContent,
5270
5532
  DialogDescription,
@@ -5273,6 +5535,7 @@ function useTheme() {
5273
5535
  DialogTitle,
5274
5536
  Divider,
5275
5537
  Drawer,
5538
+ EMAIL_REGEX,
5276
5539
  EmptyState,
5277
5540
  Form,
5278
5541
  FormControl,
@@ -5281,6 +5544,7 @@ function useTheme() {
5281
5544
  Input,
5282
5545
  NativeSelect,
5283
5546
  Nav,
5547
+ PHONE_REGEX,
5284
5548
  RadioGroup,
5285
5549
  Rating,
5286
5550
  SectionLayout,
@@ -5291,7 +5555,19 @@ function useTheme() {
5291
5555
  Switch,
5292
5556
  Textarea,
5293
5557
  ThemeProvider,
5558
+ URL_REGEX,
5559
+ applyFormat,
5294
5560
  cn,
5561
+ formatCreditCard,
5562
+ formatDate,
5563
+ formatDateTime,
5564
+ formatPhoneIntl,
5565
+ formatPhoneUS,
5566
+ getRawValue,
5567
+ isValidDate,
5568
+ isValidEmail,
5569
+ isValidPhone,
5570
+ isValidUrl,
5295
5571
  themes,
5296
5572
  useForm,
5297
5573
  useFormContext,