@yomologic/react-ui 0.6.2 → 0.6.3
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 +81 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.js +272 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +263 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,66 @@
|
|
|
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-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
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
|
+
return URL_REGEX.test(url);
|
|
36
|
+
};
|
|
37
|
+
DATE_REGEX = /^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/;
|
|
38
|
+
isValidPhone = (phone) => {
|
|
39
|
+
return PHONE_REGEX.test(phone);
|
|
40
|
+
};
|
|
41
|
+
isValidDate = (date) => {
|
|
42
|
+
if (!DATE_REGEX.test(date)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const [month, day, year] = date.split("/").map(Number);
|
|
46
|
+
if (year < 1900 || year > 2100) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (month < 1 || month > 12) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
53
|
+
const isLeapYear = year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
54
|
+
if (isLeapYear && month === 2) {
|
|
55
|
+
daysInMonth[1] = 29;
|
|
56
|
+
}
|
|
57
|
+
if (day < 1 || day > daysInMonth[month - 1]) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
2
64
|
|
|
3
65
|
// src/ui/button.tsx
|
|
4
66
|
import React from "react";
|
|
@@ -150,7 +212,112 @@ Button.displayName = "Button";
|
|
|
150
212
|
// src/ui/input.tsx
|
|
151
213
|
import React2 from "react";
|
|
152
214
|
|
|
215
|
+
// src/lib/formatting.ts
|
|
216
|
+
var formatPhoneUS = (value) => {
|
|
217
|
+
const digits = value.replace(/\D/g, "");
|
|
218
|
+
const limited = digits.slice(0, 10);
|
|
219
|
+
if (limited.length === 0) return "";
|
|
220
|
+
if (limited.length <= 3) return `(${limited}`;
|
|
221
|
+
if (limited.length <= 6)
|
|
222
|
+
return `(${limited.slice(0, 3)}) ${limited.slice(3)}`;
|
|
223
|
+
return `(${limited.slice(0, 3)}) ${limited.slice(3, 6)}-${limited.slice(6)}`;
|
|
224
|
+
};
|
|
225
|
+
var formatPhoneIntl = (value) => {
|
|
226
|
+
let cleaned = value.replace(/[^\d+]/g, "");
|
|
227
|
+
if (!cleaned.startsWith("+")) {
|
|
228
|
+
cleaned = "+" + cleaned;
|
|
229
|
+
}
|
|
230
|
+
cleaned = cleaned.slice(0, 16);
|
|
231
|
+
const digits = cleaned.slice(1);
|
|
232
|
+
if (digits.length === 0) return "+";
|
|
233
|
+
if (digits.length <= 1) return `+${digits}`;
|
|
234
|
+
if (digits.length <= 4) return `+${digits.slice(0, 1)} (${digits.slice(1)}`;
|
|
235
|
+
if (digits.length <= 7)
|
|
236
|
+
return `+${digits.slice(0, 1)} (${digits.slice(1, 4)}) ${digits.slice(4)}`;
|
|
237
|
+
return `+${digits.slice(0, 1)} (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;
|
|
238
|
+
};
|
|
239
|
+
var formatCreditCard = (value) => {
|
|
240
|
+
const digits = value.replace(/\D/g, "");
|
|
241
|
+
const limited = digits.slice(0, 16);
|
|
242
|
+
return limited.replace(/(\d{4})/g, "$1 ").trim();
|
|
243
|
+
};
|
|
244
|
+
var formatDate = (value) => {
|
|
245
|
+
const digits = value.replace(/\D/g, "");
|
|
246
|
+
const limited = digits.slice(0, 8);
|
|
247
|
+
if (limited.length === 0) return "";
|
|
248
|
+
if (limited.length <= 2) return limited;
|
|
249
|
+
if (limited.length <= 4)
|
|
250
|
+
return `${limited.slice(0, 2)}/${limited.slice(2)}`;
|
|
251
|
+
return `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4)}`;
|
|
252
|
+
};
|
|
253
|
+
var formatDateTime = (value) => {
|
|
254
|
+
const digits = value.replace(/\D/g, "");
|
|
255
|
+
const limited = digits.slice(0, 12);
|
|
256
|
+
if (limited.length === 0) return "";
|
|
257
|
+
if (limited.length <= 2) return limited;
|
|
258
|
+
if (limited.length <= 4)
|
|
259
|
+
return `${limited.slice(0, 2)}/${limited.slice(2)}`;
|
|
260
|
+
if (limited.length <= 8)
|
|
261
|
+
return `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4)}`;
|
|
262
|
+
const datePart = `${limited.slice(0, 2)}/${limited.slice(2, 4)}/${limited.slice(4, 8)}`;
|
|
263
|
+
if (limited.length <= 10) return `${datePart} ${limited.slice(8)}`;
|
|
264
|
+
let hours = parseInt(limited.slice(8, 10));
|
|
265
|
+
const minutes = limited.slice(10, 12);
|
|
266
|
+
const ampm = hours >= 12 ? "PM" : "AM";
|
|
267
|
+
hours = hours % 12 || 12;
|
|
268
|
+
return `${datePart} ${hours}:${minutes} ${ampm}`;
|
|
269
|
+
};
|
|
270
|
+
var getRawValue = (value, format) => {
|
|
271
|
+
if (format === "date") {
|
|
272
|
+
const digits = value.replace(/\D/g, "");
|
|
273
|
+
if (digits.length === 8) {
|
|
274
|
+
const month = digits.slice(0, 2);
|
|
275
|
+
const day = digits.slice(2, 4);
|
|
276
|
+
const year = digits.slice(4, 8);
|
|
277
|
+
return `${year}-${month}-${day}`;
|
|
278
|
+
}
|
|
279
|
+
return digits;
|
|
280
|
+
}
|
|
281
|
+
if (format === "datetime") {
|
|
282
|
+
const digits = value.replace(/\D/g, "");
|
|
283
|
+
if (digits.length === 12) {
|
|
284
|
+
const month = digits.slice(0, 2);
|
|
285
|
+
const day = digits.slice(2, 4);
|
|
286
|
+
const year = digits.slice(4, 8);
|
|
287
|
+
const hours = digits.slice(8, 10);
|
|
288
|
+
const minutes = digits.slice(10, 12);
|
|
289
|
+
const date = new Date(
|
|
290
|
+
parseInt(year),
|
|
291
|
+
parseInt(month) - 1,
|
|
292
|
+
parseInt(day),
|
|
293
|
+
parseInt(hours),
|
|
294
|
+
parseInt(minutes)
|
|
295
|
+
);
|
|
296
|
+
return date.toISOString();
|
|
297
|
+
}
|
|
298
|
+
return digits;
|
|
299
|
+
}
|
|
300
|
+
return value.replace(/\D/g, "");
|
|
301
|
+
};
|
|
302
|
+
var applyFormat = (value, format) => {
|
|
303
|
+
switch (format) {
|
|
304
|
+
case "phone":
|
|
305
|
+
return formatPhoneUS(value);
|
|
306
|
+
case "phone-intl":
|
|
307
|
+
return formatPhoneIntl(value);
|
|
308
|
+
case "credit-card":
|
|
309
|
+
return formatCreditCard(value);
|
|
310
|
+
case "date":
|
|
311
|
+
return formatDate(value);
|
|
312
|
+
case "datetime":
|
|
313
|
+
return formatDateTime(value);
|
|
314
|
+
default:
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
153
319
|
// src/ui/hooks/useFormField.ts
|
|
320
|
+
init_validation();
|
|
154
321
|
import { useEffect, useId as useId2, useRef as useRef2, useState as useState3 } from "react";
|
|
155
322
|
|
|
156
323
|
// src/ui/form.tsx
|
|
@@ -738,7 +905,7 @@ function useFormField2(options) {
|
|
|
738
905
|
return errorMessages?.required || "This field is required";
|
|
739
906
|
}
|
|
740
907
|
if (value) {
|
|
741
|
-
if (type === "email" && !
|
|
908
|
+
if (type === "email" && !EMAIL_REGEX.test(value)) {
|
|
742
909
|
return errorMessages?.email || "Please enter a valid email address";
|
|
743
910
|
}
|
|
744
911
|
if (type === "url") {
|
|
@@ -795,7 +962,7 @@ function useFormField2(options) {
|
|
|
795
962
|
return errorMessages?.required || "This field is required";
|
|
796
963
|
}
|
|
797
964
|
if (value) {
|
|
798
|
-
if (type === "email" && !
|
|
965
|
+
if (type === "email" && !EMAIL_REGEX.test(value)) {
|
|
799
966
|
return errorMessages?.email || "Please enter a valid email address";
|
|
800
967
|
}
|
|
801
968
|
if (type === "url") {
|
|
@@ -912,12 +1079,31 @@ var Input = React2.forwardRef(
|
|
|
912
1079
|
validate,
|
|
913
1080
|
onValidationError,
|
|
914
1081
|
pattern,
|
|
1082
|
+
format,
|
|
915
1083
|
errorMessages,
|
|
916
1084
|
...props
|
|
917
1085
|
}, ref) => {
|
|
1086
|
+
const [internalValue, setInternalValue] = React2.useState("");
|
|
1087
|
+
const dateValidate = React2.useCallback(
|
|
1088
|
+
async (value) => {
|
|
1089
|
+
if (validate) {
|
|
1090
|
+
const customError = await validate(value);
|
|
1091
|
+
if (customError) return customError;
|
|
1092
|
+
}
|
|
1093
|
+
if (format === "date" && value && value.length === 8) {
|
|
1094
|
+
const { isValidDate: isValidDate2 } = await Promise.resolve().then(() => (init_validation(), validation_exports));
|
|
1095
|
+
const formatted = `${value.slice(0, 2)}/${value.slice(2, 4)}/${value.slice(4, 8)}`;
|
|
1096
|
+
if (!isValidDate2(formatted)) {
|
|
1097
|
+
return errorMessages?.date || "Please enter a valid date";
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return void 0;
|
|
1101
|
+
},
|
|
1102
|
+
[validate, format, errorMessages]
|
|
1103
|
+
);
|
|
918
1104
|
const {
|
|
919
1105
|
fieldId,
|
|
920
|
-
value:
|
|
1106
|
+
value: hookValue,
|
|
921
1107
|
error: inputError,
|
|
922
1108
|
isDisabled,
|
|
923
1109
|
isRequired,
|
|
@@ -939,19 +1125,71 @@ var Input = React2.forwardRef(
|
|
|
939
1125
|
min: props.min,
|
|
940
1126
|
max: props.max,
|
|
941
1127
|
pattern,
|
|
942
|
-
validate,
|
|
1128
|
+
validate: format === "date" ? dateValidate : validate,
|
|
943
1129
|
onValidationError,
|
|
944
1130
|
errorMessages,
|
|
945
1131
|
idPrefix: "input"
|
|
946
1132
|
});
|
|
1133
|
+
const inputValue = hookValue !== void 0 ? hookValue : internalValue;
|
|
1134
|
+
const [cursorPosition, setCursorPosition] = React2.useState(null);
|
|
947
1135
|
const handleChange = (e) => {
|
|
948
|
-
|
|
1136
|
+
const input = e.target;
|
|
1137
|
+
const newValue = input.value;
|
|
1138
|
+
const cursorPos = input.selectionStart || 0;
|
|
1139
|
+
if (format && typeof format === "string") {
|
|
1140
|
+
const cleaned = newValue.replace(/\D/g, "");
|
|
1141
|
+
const formatted = applyFormat(cleaned, format);
|
|
1142
|
+
if (hookValue !== void 0) {
|
|
1143
|
+
hookHandleChange(cleaned);
|
|
1144
|
+
} else {
|
|
1145
|
+
setInternalValue(cleaned);
|
|
1146
|
+
}
|
|
1147
|
+
let formattedPos = 0;
|
|
1148
|
+
let digitCount = 0;
|
|
1149
|
+
const targetDigits = cleaned.slice(
|
|
1150
|
+
0,
|
|
1151
|
+
Math.min(cleaned.length, cursorPos)
|
|
1152
|
+
);
|
|
1153
|
+
for (let i = 0; i < formatted.length && digitCount < targetDigits.length; i++) {
|
|
1154
|
+
if (/\d/.test(formatted[i])) {
|
|
1155
|
+
digitCount++;
|
|
1156
|
+
}
|
|
1157
|
+
formattedPos = i + 1;
|
|
1158
|
+
}
|
|
1159
|
+
setCursorPosition(formattedPos);
|
|
1160
|
+
} else {
|
|
1161
|
+
if (hookValue !== void 0) {
|
|
1162
|
+
hookHandleChange(newValue);
|
|
1163
|
+
} else {
|
|
1164
|
+
setInternalValue(newValue);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
949
1167
|
onChange?.(e);
|
|
950
1168
|
};
|
|
951
1169
|
const handleBlur = (e) => {
|
|
952
|
-
|
|
1170
|
+
const value = e.target.value;
|
|
1171
|
+
const blurValue = format && typeof format === "string" ? value.replace(/\D/g, "") : value;
|
|
1172
|
+
hookHandleBlur(blurValue);
|
|
953
1173
|
onBlur?.(e);
|
|
954
1174
|
};
|
|
1175
|
+
const displayValue = React2.useMemo(() => {
|
|
1176
|
+
if (format && inputValue) {
|
|
1177
|
+
if (typeof format === "function") {
|
|
1178
|
+
return format(inputValue);
|
|
1179
|
+
}
|
|
1180
|
+
return applyFormat(inputValue, format);
|
|
1181
|
+
}
|
|
1182
|
+
return inputValue;
|
|
1183
|
+
}, [format, inputValue]);
|
|
1184
|
+
React2.useEffect(() => {
|
|
1185
|
+
if (cursorPosition !== null && internalRef.current) {
|
|
1186
|
+
internalRef.current.setSelectionRange(
|
|
1187
|
+
cursorPosition,
|
|
1188
|
+
cursorPosition
|
|
1189
|
+
);
|
|
1190
|
+
setCursorPosition(null);
|
|
1191
|
+
}
|
|
1192
|
+
}, [cursorPosition, displayValue]);
|
|
955
1193
|
return /* @__PURE__ */ jsxs3(
|
|
956
1194
|
"div",
|
|
957
1195
|
{
|
|
@@ -984,7 +1222,7 @@ var Input = React2.forwardRef(
|
|
|
984
1222
|
},
|
|
985
1223
|
type,
|
|
986
1224
|
id: fieldId,
|
|
987
|
-
value:
|
|
1225
|
+
value: displayValue,
|
|
988
1226
|
onChange: handleChange,
|
|
989
1227
|
onBlur: handleBlur,
|
|
990
1228
|
disabled: isDisabled,
|
|
@@ -5183,6 +5421,9 @@ function useTheme() {
|
|
|
5183
5421
|
}
|
|
5184
5422
|
return context;
|
|
5185
5423
|
}
|
|
5424
|
+
|
|
5425
|
+
// src/index.ts
|
|
5426
|
+
init_validation();
|
|
5186
5427
|
export {
|
|
5187
5428
|
Alert,
|
|
5188
5429
|
Badge,
|
|
@@ -5200,6 +5441,7 @@ export {
|
|
|
5200
5441
|
CheckboxGroup,
|
|
5201
5442
|
CodeSnippet,
|
|
5202
5443
|
Container,
|
|
5444
|
+
DATE_REGEX,
|
|
5203
5445
|
Dialog,
|
|
5204
5446
|
DialogContent,
|
|
5205
5447
|
DialogDescription,
|
|
@@ -5208,6 +5450,7 @@ export {
|
|
|
5208
5450
|
DialogTitle,
|
|
5209
5451
|
Divider,
|
|
5210
5452
|
Drawer,
|
|
5453
|
+
EMAIL_REGEX,
|
|
5211
5454
|
EmptyState,
|
|
5212
5455
|
Form,
|
|
5213
5456
|
FormControl,
|
|
@@ -5216,6 +5459,7 @@ export {
|
|
|
5216
5459
|
Input,
|
|
5217
5460
|
NativeSelect,
|
|
5218
5461
|
Nav,
|
|
5462
|
+
PHONE_REGEX,
|
|
5219
5463
|
RadioGroup,
|
|
5220
5464
|
Rating,
|
|
5221
5465
|
SectionLayout,
|
|
@@ -5226,7 +5470,19 @@ export {
|
|
|
5226
5470
|
Switch,
|
|
5227
5471
|
Textarea,
|
|
5228
5472
|
ThemeProvider,
|
|
5473
|
+
URL_REGEX,
|
|
5474
|
+
applyFormat,
|
|
5229
5475
|
cn,
|
|
5476
|
+
formatCreditCard,
|
|
5477
|
+
formatDate,
|
|
5478
|
+
formatDateTime,
|
|
5479
|
+
formatPhoneIntl,
|
|
5480
|
+
formatPhoneUS,
|
|
5481
|
+
getRawValue,
|
|
5482
|
+
isValidDate,
|
|
5483
|
+
isValidEmail,
|
|
5484
|
+
isValidPhone,
|
|
5485
|
+
isValidUrl,
|
|
5230
5486
|
themes_default as themes,
|
|
5231
5487
|
useForm,
|
|
5232
5488
|
useFormContext,
|