@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 +83 -1
- package/dist/index.d.ts +83 -1
- package/dist/index.js +283 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +274 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,69 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/constants/validation.ts
|
|
13
|
+
var validation_exports = {};
|
|
14
|
+
__export(validation_exports, {
|
|
15
|
+
DATE_REGEX: () => DATE_REGEX,
|
|
16
|
+
EMAIL_REGEX: () => EMAIL_REGEX,
|
|
17
|
+
PHONE_REGEX: () => PHONE_REGEX,
|
|
18
|
+
URL_REGEX: () => URL_REGEX,
|
|
19
|
+
isValidDate: () => isValidDate,
|
|
20
|
+
isValidEmail: () => isValidEmail,
|
|
21
|
+
isValidPhone: () => isValidPhone,
|
|
22
|
+
isValidUrl: () => isValidUrl
|
|
23
|
+
});
|
|
24
|
+
var EMAIL_REGEX, URL_REGEX, PHONE_REGEX, isValidEmail, isValidUrl, DATE_REGEX, isValidPhone, isValidDate;
|
|
25
|
+
var init_validation = __esm({
|
|
26
|
+
"src/constants/validation.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}$/;
|
|
29
|
+
URL_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
|
|
30
|
+
PHONE_REGEX = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;
|
|
31
|
+
isValidEmail = (email) => {
|
|
32
|
+
return EMAIL_REGEX.test(email);
|
|
33
|
+
};
|
|
34
|
+
isValidUrl = (url) => {
|
|
35
|
+
if (/^(javascript|data|vbscript|file|about):/i.test(url)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return URL_REGEX.test(url);
|
|
39
|
+
};
|
|
40
|
+
DATE_REGEX = /^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/;
|
|
41
|
+
isValidPhone = (phone) => {
|
|
42
|
+
return PHONE_REGEX.test(phone);
|
|
43
|
+
};
|
|
44
|
+
isValidDate = (date) => {
|
|
45
|
+
if (!DATE_REGEX.test(date)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const [month, day, year] = date.split("/").map(Number);
|
|
49
|
+
if (year < 1900 || year > 2100) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (month < 1 || month > 12) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
56
|
+
const isLeapYear = year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
57
|
+
if (isLeapYear && month === 2) {
|
|
58
|
+
daysInMonth[1] = 29;
|
|
59
|
+
}
|
|
60
|
+
if (day < 1 || day > daysInMonth[month - 1]) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
});
|
|
2
67
|
|
|
3
68
|
// src/ui/button.tsx
|
|
4
69
|
import React from "react";
|
|
@@ -150,7 +215,112 @@ Button.displayName = "Button";
|
|
|
150
215
|
// src/ui/input.tsx
|
|
151
216
|
import React2 from "react";
|
|
152
217
|
|
|
218
|
+
// src/lib/formatting.ts
|
|
219
|
+
var formatPhoneUS = (value) => {
|
|
220
|
+
const digits = value.replace(/\D/g, "");
|
|
221
|
+
const limited = digits.slice(0, 10);
|
|
222
|
+
if (limited.length === 0) return "";
|
|
223
|
+
if (limited.length <= 3) return `(${limited}`;
|
|
224
|
+
if (limited.length <= 6)
|
|
225
|
+
return `(${limited.slice(0, 3)}) ${limited.slice(3)}`;
|
|
226
|
+
return `(${limited.slice(0, 3)}) ${limited.slice(3, 6)}-${limited.slice(6)}`;
|
|
227
|
+
};
|
|
228
|
+
var formatPhoneIntl = (value) => {
|
|
229
|
+
let cleaned = value.replace(/[^\d+]/g, "");
|
|
230
|
+
if (!cleaned.startsWith("+")) {
|
|
231
|
+
cleaned = "+" + cleaned;
|
|
232
|
+
}
|
|
233
|
+
cleaned = cleaned.slice(0, 16);
|
|
234
|
+
const digits = cleaned.slice(1);
|
|
235
|
+
if (digits.length === 0) return "+";
|
|
236
|
+
if (digits.length <= 1) return `+${digits}`;
|
|
237
|
+
if (digits.length <= 4) return `+${digits.slice(0, 1)} (${digits.slice(1)}`;
|
|
238
|
+
if (digits.length <= 7)
|
|
239
|
+
return `+${digits.slice(0, 1)} (${digits.slice(1, 4)}) ${digits.slice(4)}`;
|
|
240
|
+
return `+${digits.slice(0, 1)} (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;
|
|
241
|
+
};
|
|
242
|
+
var formatCreditCard = (value) => {
|
|
243
|
+
const digits = value.replace(/\D/g, "");
|
|
244
|
+
const limited = digits.slice(0, 16);
|
|
245
|
+
return limited.replace(/(\d{4})/g, "$1 ").trim();
|
|
246
|
+
};
|
|
247
|
+
var formatDate = (value) => {
|
|
248
|
+
const digits = value.replace(/\D/g, "");
|
|
249
|
+
const limited = digits.slice(0, 8);
|
|
250
|
+
if (limited.length === 0) return "";
|
|
251
|
+
if (limited.length <= 2) return limited;
|
|
252
|
+
if (limited.length <= 4)
|
|
253
|
+
return `${limited.slice(0, 2)}/${limited.slice(2)}`;
|
|
254
|
+
return `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4)}`;
|
|
255
|
+
};
|
|
256
|
+
var formatDateTime = (value) => {
|
|
257
|
+
const digits = value.replace(/\D/g, "");
|
|
258
|
+
const limited = digits.slice(0, 12);
|
|
259
|
+
if (limited.length === 0) return "";
|
|
260
|
+
if (limited.length <= 2) return limited;
|
|
261
|
+
if (limited.length <= 4)
|
|
262
|
+
return `${limited.slice(0, 2)}/${limited.slice(2)}`;
|
|
263
|
+
if (limited.length <= 8)
|
|
264
|
+
return `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4)}`;
|
|
265
|
+
const datePart = `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4, 8)}`;
|
|
266
|
+
if (limited.length <= 10) return `${datePart} ${limited.slice(8)}`;
|
|
267
|
+
let hours = parseInt(limited.slice(8, 10));
|
|
268
|
+
const minutes = limited.slice(10, 12);
|
|
269
|
+
const ampm = hours >= 12 ? "PM" : "AM";
|
|
270
|
+
hours = hours % 12 || 12;
|
|
271
|
+
return `${datePart} ${hours}:${minutes} ${ampm}`;
|
|
272
|
+
};
|
|
273
|
+
var getRawValue = (value, format) => {
|
|
274
|
+
if (format === "date") {
|
|
275
|
+
const digits = value.replace(/\D/g, "");
|
|
276
|
+
if (digits.length === 8) {
|
|
277
|
+
const month = digits.slice(0, 2);
|
|
278
|
+
const day = digits.slice(2, 4);
|
|
279
|
+
const year = digits.slice(4, 8);
|
|
280
|
+
return `${year}-${month}-${day}`;
|
|
281
|
+
}
|
|
282
|
+
return digits;
|
|
283
|
+
}
|
|
284
|
+
if (format === "datetime") {
|
|
285
|
+
const digits = value.replace(/\D/g, "");
|
|
286
|
+
if (digits.length === 12) {
|
|
287
|
+
const month = digits.slice(0, 2);
|
|
288
|
+
const day = digits.slice(2, 4);
|
|
289
|
+
const year = digits.slice(4, 8);
|
|
290
|
+
const hours = digits.slice(8, 10);
|
|
291
|
+
const minutes = digits.slice(10, 12);
|
|
292
|
+
const date = new Date(
|
|
293
|
+
parseInt(year),
|
|
294
|
+
parseInt(month) - 1,
|
|
295
|
+
parseInt(day),
|
|
296
|
+
parseInt(hours),
|
|
297
|
+
parseInt(minutes)
|
|
298
|
+
);
|
|
299
|
+
return date.toISOString();
|
|
300
|
+
}
|
|
301
|
+
return digits;
|
|
302
|
+
}
|
|
303
|
+
return value.replace(/\D/g, "");
|
|
304
|
+
};
|
|
305
|
+
var applyFormat = (value, format) => {
|
|
306
|
+
switch (format) {
|
|
307
|
+
case "phone":
|
|
308
|
+
return formatPhoneUS(value);
|
|
309
|
+
case "phone-intl":
|
|
310
|
+
return formatPhoneIntl(value);
|
|
311
|
+
case "credit-card":
|
|
312
|
+
return formatCreditCard(value);
|
|
313
|
+
case "date":
|
|
314
|
+
return formatDate(value);
|
|
315
|
+
case "datetime":
|
|
316
|
+
return formatDateTime(value);
|
|
317
|
+
default:
|
|
318
|
+
return value;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
153
322
|
// src/ui/hooks/useFormField.ts
|
|
323
|
+
init_validation();
|
|
154
324
|
import { useEffect, useId as useId2, useRef as useRef2, useState as useState3 } from "react";
|
|
155
325
|
|
|
156
326
|
// src/ui/form.tsx
|
|
@@ -738,10 +908,13 @@ function useFormField2(options) {
|
|
|
738
908
|
return errorMessages?.required || "This field is required";
|
|
739
909
|
}
|
|
740
910
|
if (value) {
|
|
741
|
-
if (type === "email" && !
|
|
911
|
+
if (type === "email" && !EMAIL_REGEX.test(value)) {
|
|
742
912
|
return errorMessages?.email || "Please enter a valid email address";
|
|
743
913
|
}
|
|
744
914
|
if (type === "url") {
|
|
915
|
+
if (/^(javascript|data|vbscript|file|about):/i.test(value)) {
|
|
916
|
+
return errorMessages?.url || "Invalid URL protocol";
|
|
917
|
+
}
|
|
745
918
|
try {
|
|
746
919
|
new URL(value);
|
|
747
920
|
} catch {
|
|
@@ -795,10 +968,15 @@ function useFormField2(options) {
|
|
|
795
968
|
return errorMessages?.required || "This field is required";
|
|
796
969
|
}
|
|
797
970
|
if (value) {
|
|
798
|
-
if (type === "email" && !
|
|
971
|
+
if (type === "email" && !EMAIL_REGEX.test(value)) {
|
|
799
972
|
return errorMessages?.email || "Please enter a valid email address";
|
|
800
973
|
}
|
|
801
974
|
if (type === "url") {
|
|
975
|
+
if (/^(javascript|data|vbscript|file|about):/i.test(
|
|
976
|
+
value
|
|
977
|
+
)) {
|
|
978
|
+
return errorMessages?.url || "Invalid URL protocol";
|
|
979
|
+
}
|
|
802
980
|
try {
|
|
803
981
|
new URL(value);
|
|
804
982
|
} catch {
|
|
@@ -912,12 +1090,31 @@ var Input = React2.forwardRef(
|
|
|
912
1090
|
validate,
|
|
913
1091
|
onValidationError,
|
|
914
1092
|
pattern,
|
|
1093
|
+
format,
|
|
915
1094
|
errorMessages,
|
|
916
1095
|
...props
|
|
917
1096
|
}, ref) => {
|
|
1097
|
+
const [internalValue, setInternalValue] = React2.useState("");
|
|
1098
|
+
const dateValidate = React2.useCallback(
|
|
1099
|
+
async (value) => {
|
|
1100
|
+
if (validate) {
|
|
1101
|
+
const customError = await validate(value);
|
|
1102
|
+
if (customError) return customError;
|
|
1103
|
+
}
|
|
1104
|
+
if (format === "date" && value && value.length === 8) {
|
|
1105
|
+
const { isValidDate: isValidDate2 } = await Promise.resolve().then(() => (init_validation(), validation_exports));
|
|
1106
|
+
const formatted = `${value.slice(0, 2)}/${value.slice(2, 4)}/${value.slice(4, 8)}`;
|
|
1107
|
+
if (!isValidDate2(formatted)) {
|
|
1108
|
+
return errorMessages?.date || "Please enter a valid date";
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return void 0;
|
|
1112
|
+
},
|
|
1113
|
+
[validate, format, errorMessages]
|
|
1114
|
+
);
|
|
918
1115
|
const {
|
|
919
1116
|
fieldId,
|
|
920
|
-
value:
|
|
1117
|
+
value: hookValue,
|
|
921
1118
|
error: inputError,
|
|
922
1119
|
isDisabled,
|
|
923
1120
|
isRequired,
|
|
@@ -939,19 +1136,71 @@ var Input = React2.forwardRef(
|
|
|
939
1136
|
min: props.min,
|
|
940
1137
|
max: props.max,
|
|
941
1138
|
pattern,
|
|
942
|
-
validate,
|
|
1139
|
+
validate: format === "date" ? dateValidate : validate,
|
|
943
1140
|
onValidationError,
|
|
944
1141
|
errorMessages,
|
|
945
1142
|
idPrefix: "input"
|
|
946
1143
|
});
|
|
1144
|
+
const inputValue = hookValue !== void 0 ? hookValue : internalValue;
|
|
1145
|
+
const [cursorPosition, setCursorPosition] = React2.useState(null);
|
|
947
1146
|
const handleChange = (e) => {
|
|
948
|
-
|
|
1147
|
+
const input = e.target;
|
|
1148
|
+
const newValue = input.value;
|
|
1149
|
+
const cursorPos = input.selectionStart || 0;
|
|
1150
|
+
if (format && typeof format === "string") {
|
|
1151
|
+
const cleaned = newValue.replace(/\D/g, "");
|
|
1152
|
+
const formatted = applyFormat(cleaned, format);
|
|
1153
|
+
if (hookValue !== void 0) {
|
|
1154
|
+
hookHandleChange(cleaned);
|
|
1155
|
+
} else {
|
|
1156
|
+
setInternalValue(cleaned);
|
|
1157
|
+
}
|
|
1158
|
+
let formattedPos = 0;
|
|
1159
|
+
let digitCount = 0;
|
|
1160
|
+
const targetDigits = cleaned.slice(
|
|
1161
|
+
0,
|
|
1162
|
+
Math.min(cleaned.length, cursorPos)
|
|
1163
|
+
);
|
|
1164
|
+
for (let i = 0; i < formatted.length && digitCount < targetDigits.length; i++) {
|
|
1165
|
+
if (/\d/.test(formatted[i])) {
|
|
1166
|
+
digitCount++;
|
|
1167
|
+
}
|
|
1168
|
+
formattedPos = i + 1;
|
|
1169
|
+
}
|
|
1170
|
+
setCursorPosition(formattedPos);
|
|
1171
|
+
} else {
|
|
1172
|
+
if (hookValue !== void 0) {
|
|
1173
|
+
hookHandleChange(newValue);
|
|
1174
|
+
} else {
|
|
1175
|
+
setInternalValue(newValue);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
949
1178
|
onChange?.(e);
|
|
950
1179
|
};
|
|
951
1180
|
const handleBlur = (e) => {
|
|
952
|
-
|
|
1181
|
+
const value = e.target.value;
|
|
1182
|
+
const blurValue = format && typeof format === "string" ? value.replace(/\D/g, "") : value;
|
|
1183
|
+
hookHandleBlur(blurValue);
|
|
953
1184
|
onBlur?.(e);
|
|
954
1185
|
};
|
|
1186
|
+
const displayValue = React2.useMemo(() => {
|
|
1187
|
+
if (format && inputValue) {
|
|
1188
|
+
if (typeof format === "function") {
|
|
1189
|
+
return format(inputValue);
|
|
1190
|
+
}
|
|
1191
|
+
return applyFormat(inputValue, format);
|
|
1192
|
+
}
|
|
1193
|
+
return inputValue;
|
|
1194
|
+
}, [format, inputValue]);
|
|
1195
|
+
React2.useEffect(() => {
|
|
1196
|
+
if (cursorPosition !== null && internalRef.current) {
|
|
1197
|
+
internalRef.current.setSelectionRange(
|
|
1198
|
+
cursorPosition,
|
|
1199
|
+
cursorPosition
|
|
1200
|
+
);
|
|
1201
|
+
setCursorPosition(null);
|
|
1202
|
+
}
|
|
1203
|
+
}, [cursorPosition, displayValue]);
|
|
955
1204
|
return /* @__PURE__ */ jsxs3(
|
|
956
1205
|
"div",
|
|
957
1206
|
{
|
|
@@ -984,7 +1233,7 @@ var Input = React2.forwardRef(
|
|
|
984
1233
|
},
|
|
985
1234
|
type,
|
|
986
1235
|
id: fieldId,
|
|
987
|
-
value:
|
|
1236
|
+
value: displayValue,
|
|
988
1237
|
onChange: handleChange,
|
|
989
1238
|
onBlur: handleBlur,
|
|
990
1239
|
disabled: isDisabled,
|
|
@@ -5183,6 +5432,9 @@ function useTheme() {
|
|
|
5183
5432
|
}
|
|
5184
5433
|
return context;
|
|
5185
5434
|
}
|
|
5435
|
+
|
|
5436
|
+
// src/index.ts
|
|
5437
|
+
init_validation();
|
|
5186
5438
|
export {
|
|
5187
5439
|
Alert,
|
|
5188
5440
|
Badge,
|
|
@@ -5200,6 +5452,7 @@ export {
|
|
|
5200
5452
|
CheckboxGroup,
|
|
5201
5453
|
CodeSnippet,
|
|
5202
5454
|
Container,
|
|
5455
|
+
DATE_REGEX,
|
|
5203
5456
|
Dialog,
|
|
5204
5457
|
DialogContent,
|
|
5205
5458
|
DialogDescription,
|
|
@@ -5208,6 +5461,7 @@ export {
|
|
|
5208
5461
|
DialogTitle,
|
|
5209
5462
|
Divider,
|
|
5210
5463
|
Drawer,
|
|
5464
|
+
EMAIL_REGEX,
|
|
5211
5465
|
EmptyState,
|
|
5212
5466
|
Form,
|
|
5213
5467
|
FormControl,
|
|
@@ -5216,6 +5470,7 @@ export {
|
|
|
5216
5470
|
Input,
|
|
5217
5471
|
NativeSelect,
|
|
5218
5472
|
Nav,
|
|
5473
|
+
PHONE_REGEX,
|
|
5219
5474
|
RadioGroup,
|
|
5220
5475
|
Rating,
|
|
5221
5476
|
SectionLayout,
|
|
@@ -5226,7 +5481,19 @@ export {
|
|
|
5226
5481
|
Switch,
|
|
5227
5482
|
Textarea,
|
|
5228
5483
|
ThemeProvider,
|
|
5484
|
+
URL_REGEX,
|
|
5485
|
+
applyFormat,
|
|
5229
5486
|
cn,
|
|
5487
|
+
formatCreditCard,
|
|
5488
|
+
formatDate,
|
|
5489
|
+
formatDateTime,
|
|
5490
|
+
formatPhoneIntl,
|
|
5491
|
+
formatPhoneUS,
|
|
5492
|
+
getRawValue,
|
|
5493
|
+
isValidDate,
|
|
5494
|
+
isValidEmail,
|
|
5495
|
+
isValidPhone,
|
|
5496
|
+
isValidUrl,
|
|
5230
5497
|
themes_default as themes,
|
|
5231
5498
|
useForm,
|
|
5232
5499
|
useFormContext,
|