@samline/formatter 1.0.0
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/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/browser/global.d.ts +84 -0
- package/dist/browser/global.global.js +4098 -0
- package/dist/index.cjs +308 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +158 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.js +295 -0
- package/dist/index.js.map +1 -0
- package/dist/vanilla/index.cjs +308 -0
- package/dist/vanilla/index.cjs.map +1 -0
- package/dist/vanilla/index.d.cts +158 -0
- package/dist/vanilla/index.d.ts +158 -0
- package/dist/vanilla/index.js +295 -0
- package/dist/vanilla/index.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var cleaveZen = require('cleave-zen');
|
|
4
|
+
var libphonenumberJs = require('libphonenumber-js');
|
|
5
|
+
|
|
6
|
+
// src/core/format.ts
|
|
7
|
+
var formatPhone = (value, country = "MX", delimiter = " ") => {
|
|
8
|
+
if (!value) return "";
|
|
9
|
+
const digits = value.replace(/[^\d+]/g, "");
|
|
10
|
+
const formatter = new libphonenumberJs.AsYouType(country);
|
|
11
|
+
const formatted = formatter.input(digits);
|
|
12
|
+
if (delimiter !== " ") {
|
|
13
|
+
return formatted.replace(/ /g, delimiter);
|
|
14
|
+
}
|
|
15
|
+
return formatted;
|
|
16
|
+
};
|
|
17
|
+
var DEFAULT_DATE_PATTERN = ["d", "m", "Y"];
|
|
18
|
+
var DEFAULT_DATE_RAW_PATTERN = ["Y", "m", "d"];
|
|
19
|
+
var DEFAULT_DATE_RAW_PATTERN_DELIMITER = "-";
|
|
20
|
+
var DEFAULT_TIME_PATTERN = ["h", "m", "s"];
|
|
21
|
+
var DEFAULT_TIME_RAW_PATTERN = ["h", "m"];
|
|
22
|
+
var DEFAULT_TIME_RAW_PATTERN_DELIMITER = ":";
|
|
23
|
+
var getDateSegmentKey = (unit) => {
|
|
24
|
+
switch (unit.toLowerCase()) {
|
|
25
|
+
case "d":
|
|
26
|
+
return "day";
|
|
27
|
+
case "m":
|
|
28
|
+
return "month";
|
|
29
|
+
default:
|
|
30
|
+
return "year";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var getDateUnitLength = (unit) => unit === "Y" ? 4 : 2;
|
|
34
|
+
var normalizeDateSegment = (segment, unit) => {
|
|
35
|
+
if (!segment) return "";
|
|
36
|
+
switch (unit) {
|
|
37
|
+
case "y":
|
|
38
|
+
return segment.slice(-2);
|
|
39
|
+
case "Y":
|
|
40
|
+
return segment.slice(0, 4);
|
|
41
|
+
default:
|
|
42
|
+
return segment.slice(0, 2);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var getDateSegments = (value, pattern) => {
|
|
46
|
+
const digits = value.replace(/[^\d]/g, "");
|
|
47
|
+
const segments = {};
|
|
48
|
+
let start = 0;
|
|
49
|
+
for (const unit of pattern) {
|
|
50
|
+
const end = start + getDateUnitLength(unit);
|
|
51
|
+
const segment = digits.slice(start, end);
|
|
52
|
+
if (segment) {
|
|
53
|
+
segments[getDateSegmentKey(unit)] = segment;
|
|
54
|
+
}
|
|
55
|
+
start = end;
|
|
56
|
+
}
|
|
57
|
+
return segments;
|
|
58
|
+
};
|
|
59
|
+
var formatDateSegments = (segments, pattern, delimiter = "") => pattern.map(
|
|
60
|
+
(unit) => normalizeDateSegment(segments[getDateSegmentKey(unit)] ?? "", unit)
|
|
61
|
+
).filter(Boolean).join(delimiter);
|
|
62
|
+
var getTimeSegmentKey = (unit) => {
|
|
63
|
+
switch (unit) {
|
|
64
|
+
case "h":
|
|
65
|
+
return "hours";
|
|
66
|
+
case "m":
|
|
67
|
+
return "minutes";
|
|
68
|
+
default:
|
|
69
|
+
return "seconds";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var getTimeSegments = (value, pattern) => {
|
|
73
|
+
const digits = value.replace(/[^\d]/g, "");
|
|
74
|
+
const segments = {};
|
|
75
|
+
let start = 0;
|
|
76
|
+
for (const unit of pattern) {
|
|
77
|
+
const end = start + 2;
|
|
78
|
+
const segment = digits.slice(start, end);
|
|
79
|
+
if (segment) {
|
|
80
|
+
segments[getTimeSegmentKey(unit)] = segment;
|
|
81
|
+
}
|
|
82
|
+
start = end;
|
|
83
|
+
}
|
|
84
|
+
return segments;
|
|
85
|
+
};
|
|
86
|
+
var formatTimeSegments = (segments, pattern, delimiter = "") => pattern.map((unit) => (segments[getTimeSegmentKey(unit)] ?? "").slice(0, 2)).filter(Boolean).join(delimiter);
|
|
87
|
+
var getDateValueFromRaw = (value, options = {}) => {
|
|
88
|
+
const sourcePattern = options.dateRawPattern ?? DEFAULT_DATE_RAW_PATTERN;
|
|
89
|
+
const targetPattern = options.datePattern ?? DEFAULT_DATE_PATTERN;
|
|
90
|
+
return formatDateSegments(
|
|
91
|
+
getDateSegments(value, sourcePattern),
|
|
92
|
+
targetPattern
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
var getTimeValueFromRaw = (value, options = {}) => {
|
|
96
|
+
const sourcePattern = options.timeRawPattern ?? options.timePattern ?? DEFAULT_TIME_RAW_PATTERN;
|
|
97
|
+
const targetPattern = options.timePattern ?? DEFAULT_TIME_PATTERN;
|
|
98
|
+
return formatTimeSegments(
|
|
99
|
+
getTimeSegments(value, sourcePattern),
|
|
100
|
+
targetPattern
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
var getDateRawValue = (value, options = {}) => {
|
|
104
|
+
const sourcePattern = options.datePattern ?? DEFAULT_DATE_PATTERN;
|
|
105
|
+
const targetPattern = options.dateRawPattern ?? DEFAULT_DATE_RAW_PATTERN;
|
|
106
|
+
const delimiter = options.dateRawPatternDelimiter ?? DEFAULT_DATE_RAW_PATTERN_DELIMITER;
|
|
107
|
+
return formatDateSegments(
|
|
108
|
+
getDateSegments(value, sourcePattern),
|
|
109
|
+
targetPattern,
|
|
110
|
+
delimiter
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
var getTimeRawValue = (value, options = {}) => {
|
|
114
|
+
const sourcePattern = options.timePattern ?? DEFAULT_TIME_PATTERN;
|
|
115
|
+
const targetPattern = options.timeRawPattern ?? options.timePattern ?? DEFAULT_TIME_RAW_PATTERN;
|
|
116
|
+
const delimiter = options.timeRawPatternDelimiter ?? DEFAULT_TIME_RAW_PATTERN_DELIMITER;
|
|
117
|
+
return formatTimeSegments(
|
|
118
|
+
getTimeSegments(value, sourcePattern),
|
|
119
|
+
targetPattern,
|
|
120
|
+
delimiter
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
var stripPrefixAndSuffix = (value, options = {}) => {
|
|
124
|
+
if (!value || !options.prefix) return value;
|
|
125
|
+
const { prefix } = options;
|
|
126
|
+
const tailPrefix = options.tailPrefix ?? false;
|
|
127
|
+
if (tailPrefix && value.endsWith(prefix)) {
|
|
128
|
+
return value.slice(0, -prefix.length);
|
|
129
|
+
}
|
|
130
|
+
if (!tailPrefix && value.startsWith(prefix)) {
|
|
131
|
+
return value.slice(prefix.length);
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
};
|
|
135
|
+
var getRawValue = (value, formatType, options = {}) => {
|
|
136
|
+
if (!value) return "";
|
|
137
|
+
const cleanValue = stripPrefixAndSuffix(value, options);
|
|
138
|
+
switch (formatType) {
|
|
139
|
+
case "numeral": {
|
|
140
|
+
const numeralOpts = {};
|
|
141
|
+
if (options.numeralDecimalMark !== void 0) {
|
|
142
|
+
numeralOpts.numeralDecimalMark = options.numeralDecimalMark;
|
|
143
|
+
}
|
|
144
|
+
return cleaveZen.unformatNumeral(cleanValue, numeralOpts);
|
|
145
|
+
}
|
|
146
|
+
case "creditCard":
|
|
147
|
+
case "creditCardType":
|
|
148
|
+
return cleaveZen.unformatCreditCard(cleanValue);
|
|
149
|
+
case "general": {
|
|
150
|
+
const generalOpts = {};
|
|
151
|
+
if (options.delimiter !== void 0) generalOpts.delimiter = options.delimiter;
|
|
152
|
+
if (options.delimiters !== void 0) generalOpts.delimiters = options.delimiters;
|
|
153
|
+
return cleaveZen.unformatGeneral(cleanValue, generalOpts);
|
|
154
|
+
}
|
|
155
|
+
case "date":
|
|
156
|
+
return getDateRawValue(cleanValue, options);
|
|
157
|
+
case "time":
|
|
158
|
+
return getTimeRawValue(cleanValue, options);
|
|
159
|
+
case "phone":
|
|
160
|
+
return cleanValue.replace(/[^\d+]/g, "");
|
|
161
|
+
default:
|
|
162
|
+
return cleanValue.replace(/\s+/g, "");
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/core/format.ts
|
|
167
|
+
var FORMAT_TYPES = [
|
|
168
|
+
"general",
|
|
169
|
+
"phone",
|
|
170
|
+
"numeral",
|
|
171
|
+
"date",
|
|
172
|
+
"time",
|
|
173
|
+
"creditCard",
|
|
174
|
+
"creditCardType"
|
|
175
|
+
];
|
|
176
|
+
var isFormatType = (value) => typeof value === "string" && FORMAT_TYPES.includes(value);
|
|
177
|
+
var PHONE_DEFAULT_DELIMITER = " ";
|
|
178
|
+
var PHONE_DEFAULT_COUNTRY = "MX";
|
|
179
|
+
var resolveRuntimeOptions = (formatType, options = {}) => {
|
|
180
|
+
const isPhone = formatType === "phone";
|
|
181
|
+
const country = options.country ?? (isPhone ? PHONE_DEFAULT_COUNTRY : "");
|
|
182
|
+
const runtime = {
|
|
183
|
+
...options,
|
|
184
|
+
country
|
|
185
|
+
};
|
|
186
|
+
if (options.delimiter !== void 0) {
|
|
187
|
+
runtime.delimiter = options.delimiter;
|
|
188
|
+
} else if (isPhone) {
|
|
189
|
+
runtime.delimiter = PHONE_DEFAULT_DELIMITER;
|
|
190
|
+
}
|
|
191
|
+
return runtime;
|
|
192
|
+
};
|
|
193
|
+
var formatValue = (value, formatType, options) => {
|
|
194
|
+
switch (formatType) {
|
|
195
|
+
case "phone":
|
|
196
|
+
return formatPhone(value, options.country, options.delimiter);
|
|
197
|
+
case "numeral":
|
|
198
|
+
return cleaveZen.formatNumeral(value, options);
|
|
199
|
+
case "date":
|
|
200
|
+
return cleaveZen.formatDate(value, options);
|
|
201
|
+
case "time":
|
|
202
|
+
return cleaveZen.formatTime(value, options);
|
|
203
|
+
case "creditCard":
|
|
204
|
+
return cleaveZen.formatCreditCard(value, options);
|
|
205
|
+
case "creditCardType":
|
|
206
|
+
return cleaveZen.getCreditCardType(value, options.delimiter);
|
|
207
|
+
case "general":
|
|
208
|
+
return cleaveZen.formatGeneral(
|
|
209
|
+
value,
|
|
210
|
+
options
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var getValueForFormatting = (value, formatType, options) => {
|
|
215
|
+
if (formatType === "date") {
|
|
216
|
+
return getDateValueFromRaw(value, options);
|
|
217
|
+
}
|
|
218
|
+
if (formatType === "time") {
|
|
219
|
+
return getTimeValueFromRaw(value, options);
|
|
220
|
+
}
|
|
221
|
+
return value;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/core/formatter.ts
|
|
225
|
+
var format = (value, formatType, options = {}) => {
|
|
226
|
+
if (!isFormatType(formatType)) {
|
|
227
|
+
throw new TypeError(
|
|
228
|
+
`Invalid formatType: ${String(formatType)}. Expected one of: ${FORMAT_TYPES.join(", ")}`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
if (value === null || value === void 0 || value === "") {
|
|
232
|
+
return { formatted: "", raw: "", type: formatType };
|
|
233
|
+
}
|
|
234
|
+
const runtime = resolveRuntimeOptions(formatType, options);
|
|
235
|
+
const stringValue = typeof value === "string" ? value : String(value);
|
|
236
|
+
const cleanValue = stripPrefixAndSuffix(stringValue, runtime);
|
|
237
|
+
const preValue = getValueForFormatting(cleanValue, formatType, runtime);
|
|
238
|
+
const formatted = formatValue(preValue, formatType, runtime);
|
|
239
|
+
const raw = formatType === "creditCardType" ? getRawValue(cleanValue, formatType, runtime) : getRawValue(formatted, formatType, runtime);
|
|
240
|
+
return { formatted, raw, type: formatType };
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/core/regex.ts
|
|
244
|
+
var regex = {
|
|
245
|
+
phone: {
|
|
246
|
+
// 10 digit phone numbers, allowing for optional country code and delimiters
|
|
247
|
+
pattern: /^(?:\D*\d){10}\D*$/,
|
|
248
|
+
errorMessage: "Please enter a valid 10-digit phone number."
|
|
249
|
+
},
|
|
250
|
+
email: {
|
|
251
|
+
pattern: /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/,
|
|
252
|
+
errorMessage: "Please enter a valid email address."
|
|
253
|
+
},
|
|
254
|
+
rfc: {
|
|
255
|
+
// Mexican RFC format (simplified)
|
|
256
|
+
pattern: /^([A-ZÑ&]{3,4})\d{6}([A-Z0-9]{0,3})$/i,
|
|
257
|
+
errorMessage: "Please enter a valid RFC."
|
|
258
|
+
},
|
|
259
|
+
numeral: {
|
|
260
|
+
// Numbers with optional thousand separators and decimal points
|
|
261
|
+
pattern: /\d{1,3}(,\d{3})*(\.\d+)?/,
|
|
262
|
+
errorMessage: "Please enter a valid number."
|
|
263
|
+
},
|
|
264
|
+
onlyNumbers: {
|
|
265
|
+
pattern: /^\d+$/,
|
|
266
|
+
errorMessage: "Please enter only numbers."
|
|
267
|
+
},
|
|
268
|
+
creditCard: {
|
|
269
|
+
// 15 or 16 digits, allowing spaces or delimiters
|
|
270
|
+
pattern: /^(?:\D*\d){15,16}\D*$/,
|
|
271
|
+
errorMessage: "Please enter a valid card number."
|
|
272
|
+
},
|
|
273
|
+
expirationDate: {
|
|
274
|
+
// MM/YY format
|
|
275
|
+
pattern: /^(0[1-9]|1[0-2])\/\d{2}$/,
|
|
276
|
+
errorMessage: "Please enter a valid expiration date."
|
|
277
|
+
},
|
|
278
|
+
cardCvc: {
|
|
279
|
+
// 3 or 4 digit card security codes
|
|
280
|
+
pattern: /^\d{3,4}$/,
|
|
281
|
+
errorMessage: "Please enter a valid CVC."
|
|
282
|
+
},
|
|
283
|
+
onlyLetters: {
|
|
284
|
+
// Letters only (including accented characters and spaces)
|
|
285
|
+
pattern: /^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$/,
|
|
286
|
+
errorMessage: "Please enter only letters."
|
|
287
|
+
},
|
|
288
|
+
onlyAlphanumeric: {
|
|
289
|
+
// Letters and numbers (including accented characters and spaces)
|
|
290
|
+
pattern: /^[A-Za-zÁÉÍÓÚáéíóúÑñ0-9\s]+$/,
|
|
291
|
+
errorMessage: "Please enter only letters and numbers."
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
exports.FORMAT_TYPES = FORMAT_TYPES;
|
|
296
|
+
exports.format = format;
|
|
297
|
+
exports.formatPhone = formatPhone;
|
|
298
|
+
exports.formatValue = formatValue;
|
|
299
|
+
exports.getDateValueFromRaw = getDateValueFromRaw;
|
|
300
|
+
exports.getRawValue = getRawValue;
|
|
301
|
+
exports.getTimeValueFromRaw = getTimeValueFromRaw;
|
|
302
|
+
exports.getValueForFormatting = getValueForFormatting;
|
|
303
|
+
exports.isFormatType = isFormatType;
|
|
304
|
+
exports.regex = regex;
|
|
305
|
+
exports.resolveRuntimeOptions = resolveRuntimeOptions;
|
|
306
|
+
exports.stripPrefixAndSuffix = stripPrefixAndSuffix;
|
|
307
|
+
//# sourceMappingURL=index.cjs.map
|
|
308
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/phone.ts","../../src/core/raw.ts","../../src/core/format.ts","../../src/core/formatter.ts","../../src/core/regex.ts"],"names":["AsYouType","unformatNumeral","unformatCreditCard","unformatGeneral","formatNumeral","formatDate","formatTime","formatCreditCard","getCreditCardType","formatGeneral"],"mappings":"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","file":"index.cjs","sourcesContent":["import { AsYouType, type CountryCode } from 'libphonenumber-js'\n\n/**\n * Format a phone number string using `libphonenumber-js`'s `AsYouType`.\n *\n * Non-digit characters are stripped before formatting (the leading `+` is\n * preserved so international numbers keep their country prefix). After\n * formatting, spaces produced by `AsYouType` are swapped for `delimiter` when\n * a custom delimiter is provided.\n *\n * @param value - The raw input (digits, `+`, spaces, dashes, parentheses, etc.).\n * @param country - ISO 3166-1 alpha-2 country code (e.g. `'MX'`, `'US'`).\n * @param delimiter - Character used to separate groups in the output.\n * @returns The formatted phone string, or `''` when `value` is empty.\n */\nexport const formatPhone = (\n value: string,\n country: string = 'MX',\n delimiter: string = ' '\n): string => {\n if (!value) return ''\n\n // Preserve '+' so international numbers keep their prefix.\n const digits = value.replace(/[^\\d+]/g, '')\n const formatter = new AsYouType(country as CountryCode)\n const formatted = formatter.input(digits)\n\n if (delimiter !== ' ') {\n return formatted.replace(/ /g, delimiter)\n }\n\n return formatted\n}","import {\n unformatCreditCard,\n unformatGeneral,\n unformatNumeral,\n type DatePatternType,\n type DateUnit,\n type FormatCreditCardOptions,\n type FormatDateOptions,\n type FormatGeneralOptions,\n type FormatNumeralOptions,\n type FormatTimeOptions,\n type TimePatternType,\n type TimeUnit\n} from 'cleave-zen'\n\nexport type FormatType =\n | 'general'\n | 'phone'\n | 'numeral'\n | 'date'\n | 'time'\n | 'creditCard'\n | 'creditCardType'\n\ntype ExtraFormatOptions = {\n country?: string\n dateRawPattern?: DatePatternType\n dateRawPatternDelimiter?: string\n timeRawPattern?: TimePatternType\n timeRawPatternDelimiter?: string\n tailPrefix?: boolean\n}\n\nexport type FormatOptions = Partial<\nFormatGeneralOptions &\nFormatNumeralOptions &\nFormatDateOptions &\nFormatTimeOptions &\nFormatCreditCardOptions\n> &\nExtraFormatOptions\n\ntype DateSegmentKey = 'day' | 'month' | 'year'\ntype TimeSegmentKey = 'hours' | 'minutes' | 'seconds'\n\nconst DEFAULT_DATE_PATTERN: DatePatternType = ['d', 'm', 'Y']\nconst DEFAULT_DATE_RAW_PATTERN: DatePatternType = ['Y', 'm', 'd']\nconst DEFAULT_DATE_RAW_PATTERN_DELIMITER = '-'\nconst DEFAULT_TIME_PATTERN: TimePatternType = ['h', 'm', 's']\nconst DEFAULT_TIME_RAW_PATTERN: TimePatternType = ['h', 'm']\nconst DEFAULT_TIME_RAW_PATTERN_DELIMITER = ':'\n\nconst getDateSegmentKey = (unit: DateUnit): DateSegmentKey => {\n switch (unit.toLowerCase()) {\n case 'd':\n return 'day'\n case 'm':\n return 'month'\n default:\n return 'year'\n }\n}\n\nconst getDateUnitLength = (unit: DateUnit): 2 | 4 =>\n unit === 'Y' ? 4 : 2\n\nconst normalizeDateSegment = (segment: string, unit: DateUnit): string => {\n if (!segment) return ''\n\n switch (unit) {\n case 'y':\n return segment.slice(-2)\n case 'Y':\n return segment.slice(0, 4)\n default:\n return segment.slice(0, 2)\n }\n}\n\nconst getDateSegments = (\n value: string,\n pattern: DatePatternType\n): Partial<Record<DateSegmentKey, string>> => {\n const digits = value.replace(/[^\\d]/g, '')\n const segments: Partial<Record<DateSegmentKey, string>> = {}\n let start = 0\n\n for (const unit of pattern) {\n const end = start + getDateUnitLength(unit)\n const segment = digits.slice(start, end)\n\n if (segment) {\n segments[getDateSegmentKey(unit)] = segment\n }\n\n start = end\n }\n\n return segments\n}\n\nconst formatDateSegments = (\n segments: Partial<Record<DateSegmentKey, string>>,\n pattern: DatePatternType,\n delimiter = ''\n): string =>\n pattern\n .map((unit) =>\n normalizeDateSegment(segments[getDateSegmentKey(unit)] ?? '', unit)\n )\n .filter(Boolean)\n .join(delimiter)\n\nconst getTimeSegmentKey = (unit: TimeUnit): TimeSegmentKey => {\n switch (unit) {\n case 'h':\n return 'hours'\n case 'm':\n return 'minutes'\n default:\n return 'seconds'\n }\n}\n\nconst getTimeSegments = (\n value: string,\n pattern: TimePatternType\n): Partial<Record<TimeSegmentKey, string>> => {\n const digits = value.replace(/[^\\d]/g, '')\n const segments: Partial<Record<TimeSegmentKey, string>> = {}\n let start = 0\n\n for (const unit of pattern) {\n const end = start + 2\n const segment = digits.slice(start, end)\n\n if (segment) {\n segments[getTimeSegmentKey(unit)] = segment\n }\n\n start = end\n }\n\n return segments\n}\n\nconst formatTimeSegments = (\n segments: Partial<Record<TimeSegmentKey, string>>,\n pattern: TimePatternType,\n delimiter = ''\n): string =>\n pattern\n .map((unit) => (segments[getTimeSegmentKey(unit)] ?? '').slice(0, 2))\n .filter(Boolean)\n .join(delimiter)\n\n/**\n * Convert a raw date string (e.g. `2026-05-12`) into the digit order of the\n * display pattern (e.g. `12/05/2026` → `12052026`).\n *\n * The output is intentionally digits-only and un-delimited — the caller is\n * expected to pass the result through `cleave-zen`'s `formatDate` (or an\n * equivalent) to add the configured delimiter. The source pattern is\n * `dateRawPattern`; the target pattern is `datePattern`.\n */\nexport const getDateValueFromRaw = (\n value: string,\n options: FormatOptions = {}\n): string => {\n const sourcePattern = options.dateRawPattern ?? DEFAULT_DATE_RAW_PATTERN\n const targetPattern = options.datePattern ?? DEFAULT_DATE_PATTERN\n\n return formatDateSegments(\n getDateSegments(value, sourcePattern),\n targetPattern\n )\n}\n\n/**\n * Convert a raw time string (e.g. `14:30`) into the digit order of the\n * display pattern (e.g. `14:30:00` → `1430`).\n *\n * The output is intentionally digits-only and un-delimited — the caller is\n * expected to pass the result through `cleave-zen`'s `formatTime` (or an\n * equivalent) to add the configured delimiter and pad missing segments.\n * The source pattern is `timeRawPattern`; the target pattern is\n * `timePattern`.\n */\nexport const getTimeValueFromRaw = (\n value: string,\n options: FormatOptions = {}\n): string => {\n const sourcePattern =\n options.timeRawPattern ?? options.timePattern ?? DEFAULT_TIME_RAW_PATTERN\n const targetPattern = options.timePattern ?? DEFAULT_TIME_PATTERN\n\n return formatTimeSegments(\n getTimeSegments(value, sourcePattern),\n targetPattern\n )\n}\n\nconst getDateRawValue = (\n value: string,\n options: FormatOptions = {}\n): string => {\n const sourcePattern = options.datePattern ?? DEFAULT_DATE_PATTERN\n const targetPattern = options.dateRawPattern ?? DEFAULT_DATE_RAW_PATTERN\n const delimiter =\n options.dateRawPatternDelimiter ?? DEFAULT_DATE_RAW_PATTERN_DELIMITER\n\n return formatDateSegments(\n getDateSegments(value, sourcePattern),\n targetPattern,\n delimiter\n )\n}\n\nconst getTimeRawValue = (\n value: string,\n options: FormatOptions = {}\n): string => {\n const sourcePattern = options.timePattern ?? DEFAULT_TIME_PATTERN\n const targetPattern =\n options.timeRawPattern ?? options.timePattern ?? DEFAULT_TIME_RAW_PATTERN\n const delimiter =\n options.timeRawPatternDelimiter ?? DEFAULT_TIME_RAW_PATTERN_DELIMITER\n\n return formatTimeSegments(\n getTimeSegments(value, sourcePattern),\n targetPattern,\n delimiter\n )\n}\n\n/**\n * Strip a configured `prefix` from either the start or the end of `value`.\n * `tailPrefix: true` treats the prefix as a suffix.\n */\nexport const stripPrefixAndSuffix = (\n value: string,\n options: FormatOptions = {}\n): string => {\n if (!value || !options.prefix) return value\n\n const { prefix } = options\n const tailPrefix = options.tailPrefix ?? false\n\n if (tailPrefix && value.endsWith(prefix)) {\n return value.slice(0, -prefix.length)\n }\n\n if (!tailPrefix && value.startsWith(prefix)) {\n return value.slice(prefix.length)\n }\n\n return value\n}\n\n/**\n * Convert a formatted value back into its raw counterpart (digits only,\n * canonical structure ready to ship to a backend).\n */\nexport const getRawValue = (\n value: string,\n formatType: FormatType,\n options: FormatOptions = {}\n): string => {\n if (!value) return ''\n\n const cleanValue = stripPrefixAndSuffix(value, options)\n\n switch (formatType) {\n case 'numeral': {\n const numeralOpts: { numeralDecimalMark?: string } = {}\n if (options.numeralDecimalMark !== undefined) {\n numeralOpts.numeralDecimalMark = options.numeralDecimalMark\n }\n return unformatNumeral(cleanValue, numeralOpts)\n }\n\n case 'creditCard':\n case 'creditCardType':\n return unformatCreditCard(cleanValue)\n\n case 'general': {\n const generalOpts: {\n delimiter?: string\n delimiters?: string[]\n } = {}\n if (options.delimiter !== undefined) generalOpts.delimiter = options.delimiter\n if (options.delimiters !== undefined) generalOpts.delimiters = options.delimiters\n return unformatGeneral(cleanValue, generalOpts)\n }\n\n case 'date':\n return getDateRawValue(cleanValue, options)\n\n case 'time':\n return getTimeRawValue(cleanValue, options)\n\n case 'phone':\n return cleanValue.replace(/[^\\d+]/g, '')\n\n default:\n return cleanValue.replace(/\\s+/g, '')\n }\n}","import {\n formatCreditCard,\n formatDate,\n formatGeneral,\n formatNumeral,\n formatTime,\n getCreditCardType,\n type FormatGeneralOptions\n} from 'cleave-zen'\n\nimport { formatPhone } from './phone.js'\nimport {\n getDateValueFromRaw,\n getTimeValueFromRaw,\n type FormatOptions,\n type FormatType\n} from './raw.js'\n\nexport type { FormatOptions, FormatType }\n\nexport const FORMAT_TYPES = [\n 'general',\n 'phone',\n 'numeral',\n 'date',\n 'time',\n 'creditCard',\n 'creditCardType'\n] as const satisfies readonly FormatType[]\n\nexport const isFormatType = (value: unknown): value is FormatType =>\n typeof value === 'string' &&\n (FORMAT_TYPES as readonly string[]).includes(value)\n\ntype RuntimeOptions = FormatOptions & {\n country: string\n delimiter?: string\n}\n\n// Per-type defaults applied only when the caller does not pass their own.\n// `phone` overrides cleave-zen because libphonenumber emits spaces natively;\n// every other type lets cleave-zen pick its own idiomatic delimiter\n// (`/`, `:`, `,`, etc.) by leaving `delimiter` undefined on the runtime\n// options object passed downstream.\nconst PHONE_DEFAULT_DELIMITER = ' '\nconst PHONE_DEFAULT_COUNTRY = 'MX'\n\n/**\n * Internal: pick a country/delimiter-aware runtime options object with\n * phone-specific defaults applied. For non-phone formats the `delimiter`\n * is left as the user supplied it (or absent) so cleave-zen falls back to\n * its idiomatic defaults.\n */\nconst resolveRuntimeOptions = (\n formatType: FormatType,\n options: FormatOptions = {}\n): RuntimeOptions => {\n const isPhone = formatType === 'phone'\n const country = options.country ?? (isPhone ? PHONE_DEFAULT_COUNTRY : '')\n\n // Build the runtime options without ever assigning `undefined` to optional\n // fields (so `exactOptionalPropertyTypes` stays happy) and without ever\n // adding an explicit empty-string `delimiter` for non-phone types (so\n // cleave-zen falls back to its own idiomatic default).\n const runtime: RuntimeOptions = {\n ...options,\n country\n }\n\n if (options.delimiter !== undefined) {\n runtime.delimiter = options.delimiter\n } else if (isPhone) {\n runtime.delimiter = PHONE_DEFAULT_DELIMITER\n }\n\n return runtime\n}\n\n/**\n * Internal: format a pre-processed value into its display representation\n * by delegating to the matching `cleave-zen` (or `formatPhone`) helper.\n */\nconst formatValue = (\n value: string,\n formatType: FormatType,\n options: RuntimeOptions\n): string => {\n switch (formatType) {\n case 'phone':\n return formatPhone(value, options.country, options.delimiter)\n\n case 'numeral':\n return formatNumeral(value, options)\n\n case 'date':\n return formatDate(value, options)\n\n case 'time':\n return formatTime(value, options)\n\n case 'creditCard':\n return formatCreditCard(value, options)\n\n case 'creditCardType':\n return getCreditCardType(value, options.delimiter)\n\n case 'general':\n // cleave-zen's `formatGeneral` requires `blocks` in its options;\n // `FormatOptions` does not declare it (consumers pick blocks via\n // their own configuration). This cast is the documented seam.\n return formatGeneral(\n value,\n options as unknown as FormatGeneralOptions\n )\n }\n}\n\n/**\n * Internal: pre-process `value` before passing it to the per-type formatter.\n * Date and time inputs are first converted from raw to the display pattern\n * (so users can type either formatted or raw and still get sensible output).\n */\nconst getValueForFormatting = (\n value: string,\n formatType: FormatType,\n options: RuntimeOptions\n): string => {\n if (formatType === 'date') {\n return getDateValueFromRaw(value, options)\n }\n\n if (formatType === 'time') {\n return getTimeValueFromRaw(value, options)\n }\n\n return value\n}\n\nexport { formatValue, getValueForFormatting, resolveRuntimeOptions }","import {\n formatValue,\n getValueForFormatting,\n resolveRuntimeOptions,\n FORMAT_TYPES,\n isFormatType,\n type FormatType\n} from './format.js'\n\nimport { getRawValue, stripPrefixAndSuffix } from './raw.js'\n\nimport type { FormatterResult, FormatOptions } from './types.js'\n\nexport type { FormatOptions, FormatType, FormatterResult }\nexport { FORMAT_TYPES, isFormatType, stripPrefixAndSuffix }\n\n/**\n * Format `value` according to `formatType`, returning both a presentable\n * `formatted` string and a backend-ready `raw` string.\n *\n * The function is pure: it never touches the DOM and never reads from the\n * network. Pair it with your own input wiring (React, Vue, Svelte, vanilla)\n * to drive the visible field; mirror `result.raw` into a hidden input when\n * shipping to a server that expects clean digits.\n *\n * @param value - The raw value typed by the user (`null`/`undefined`/`''` return an empty result).\n * @param formatType - One of the supported `FormatType` values.\n * @param options - Per-type options (see `FormatOptions`).\n * @returns A `FormatterResult` with `formatted`, `raw`, and the echoed `type`.\n * @throws {TypeError} When `formatType` is not one of the supported types.\n */\nexport const format = (\n value: unknown,\n formatType: FormatType,\n options: FormatOptions = {}\n): FormatterResult => {\n if (!isFormatType(formatType)) {\n throw new TypeError(\n `Invalid formatType: ${String(formatType)}. Expected one of: ${FORMAT_TYPES.join(', ')}`\n )\n }\n\n if (value === null || value === undefined || value === '') {\n return { formatted: '', raw: '', type: formatType }\n }\n\n const runtime = resolveRuntimeOptions(formatType, options)\n const stringValue = typeof value === 'string' ? value : String(value)\n const cleanValue = stripPrefixAndSuffix(stringValue, runtime)\n const preValue = getValueForFormatting(cleanValue, formatType, runtime)\n const formatted = formatValue(preValue, formatType, runtime)\n // `getRawValue` normally consumes the *formatted* display string (mirrors\n // the original `syncRawInputValue` pipeline). The one exception is\n // `creditCardType`, where `formatted` is the card brand name (e.g. \"visa\")\n // rather than the card number — the raw needs to come from the cleaned\n // input directly.\n const raw =\n formatType === 'creditCardType'\n ? getRawValue(cleanValue, formatType, runtime)\n : getRawValue(formatted, formatType, runtime)\n\n return { formatted, raw, type: formatType }\n}","// Validation patterns exposed as public API for form consumers that pair\n// formatting with validation. Each entry bundles the regex with a ready-to-use\n// error message so consumers do not have to keep them in sync.\n\nexport const regex = {\n phone: {\n // 10 digit phone numbers, allowing for optional country code and delimiters\n pattern: /^(?:\\D*\\d){10}\\D*$/,\n errorMessage: 'Please enter a valid 10-digit phone number.'\n },\n email: {\n pattern: /^[^\\s@]+@[^\\s@]+\\.[a-zA-Z]{2,}$/,\n errorMessage: 'Please enter a valid email address.'\n },\n rfc: {\n // Mexican RFC format (simplified)\n pattern: /^([A-ZÑ&]{3,4})\\d{6}([A-Z0-9]{0,3})$/i,\n errorMessage: 'Please enter a valid RFC.'\n },\n numeral: {\n // Numbers with optional thousand separators and decimal points\n pattern: /\\d{1,3}(,\\d{3})*(\\.\\d+)?/,\n errorMessage: 'Please enter a valid number.'\n },\n onlyNumbers: {\n pattern: /^\\d+$/,\n errorMessage: 'Please enter only numbers.'\n },\n creditCard: {\n // 15 or 16 digits, allowing spaces or delimiters\n pattern: /^(?:\\D*\\d){15,16}\\D*$/,\n errorMessage: 'Please enter a valid card number.'\n },\n expirationDate: {\n // MM/YY format\n pattern: /^(0[1-9]|1[0-2])\\/\\d{2}$/,\n errorMessage: 'Please enter a valid expiration date.'\n },\n cardCvc: {\n // 3 or 4 digit card security codes\n pattern: /^\\d{3,4}$/,\n errorMessage: 'Please enter a valid CVC.'\n },\n onlyLetters: {\n // Letters only (including accented characters and spaces)\n pattern: /^[A-Za-zÁÉÍÓÚáéíóúÑñ\\s]+$/,\n errorMessage: 'Please enter only letters.'\n },\n onlyAlphanumeric: {\n // Letters and numbers (including accented characters and spaces)\n pattern: /^[A-Za-zÁÉÍÓÚáéíóúÑñ0-9\\s]+$/,\n errorMessage: 'Please enter only letters and numbers.'\n }\n} as const\n\nexport type RegexKey = keyof typeof regex"]}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { FormatGeneralOptions, FormatNumeralOptions, FormatDateOptions, FormatTimeOptions, FormatCreditCardOptions, DatePatternType, TimePatternType } from 'cleave-zen';
|
|
2
|
+
|
|
3
|
+
type FormatType = 'general' | 'phone' | 'numeral' | 'date' | 'time' | 'creditCard' | 'creditCardType';
|
|
4
|
+
type ExtraFormatOptions = {
|
|
5
|
+
country?: string;
|
|
6
|
+
dateRawPattern?: DatePatternType;
|
|
7
|
+
dateRawPatternDelimiter?: string;
|
|
8
|
+
timeRawPattern?: TimePatternType;
|
|
9
|
+
timeRawPatternDelimiter?: string;
|
|
10
|
+
tailPrefix?: boolean;
|
|
11
|
+
};
|
|
12
|
+
type FormatOptions = Partial<FormatGeneralOptions & FormatNumeralOptions & FormatDateOptions & FormatTimeOptions & FormatCreditCardOptions> & ExtraFormatOptions;
|
|
13
|
+
/**
|
|
14
|
+
* Convert a raw date string (e.g. `2026-05-12`) into the digit order of the
|
|
15
|
+
* display pattern (e.g. `12/05/2026` → `12052026`).
|
|
16
|
+
*
|
|
17
|
+
* The output is intentionally digits-only and un-delimited — the caller is
|
|
18
|
+
* expected to pass the result through `cleave-zen`'s `formatDate` (or an
|
|
19
|
+
* equivalent) to add the configured delimiter. The source pattern is
|
|
20
|
+
* `dateRawPattern`; the target pattern is `datePattern`.
|
|
21
|
+
*/
|
|
22
|
+
declare const getDateValueFromRaw: (value: string, options?: FormatOptions) => string;
|
|
23
|
+
/**
|
|
24
|
+
* Convert a raw time string (e.g. `14:30`) into the digit order of the
|
|
25
|
+
* display pattern (e.g. `14:30:00` → `1430`).
|
|
26
|
+
*
|
|
27
|
+
* The output is intentionally digits-only and un-delimited — the caller is
|
|
28
|
+
* expected to pass the result through `cleave-zen`'s `formatTime` (or an
|
|
29
|
+
* equivalent) to add the configured delimiter and pad missing segments.
|
|
30
|
+
* The source pattern is `timeRawPattern`; the target pattern is
|
|
31
|
+
* `timePattern`.
|
|
32
|
+
*/
|
|
33
|
+
declare const getTimeValueFromRaw: (value: string, options?: FormatOptions) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Strip a configured `prefix` from either the start or the end of `value`.
|
|
36
|
+
* `tailPrefix: true` treats the prefix as a suffix.
|
|
37
|
+
*/
|
|
38
|
+
declare const stripPrefixAndSuffix: (value: string, options?: FormatOptions) => string;
|
|
39
|
+
/**
|
|
40
|
+
* Convert a formatted value back into its raw counterpart (digits only,
|
|
41
|
+
* canonical structure ready to ship to a backend).
|
|
42
|
+
*/
|
|
43
|
+
declare const getRawValue: (value: string, formatType: FormatType, options?: FormatOptions) => string;
|
|
44
|
+
|
|
45
|
+
declare const FORMAT_TYPES: readonly ["general", "phone", "numeral", "date", "time", "creditCard", "creditCardType"];
|
|
46
|
+
declare const isFormatType: (value: unknown) => value is FormatType;
|
|
47
|
+
type RuntimeOptions = FormatOptions & {
|
|
48
|
+
country: string;
|
|
49
|
+
delimiter?: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Internal: pick a country/delimiter-aware runtime options object with
|
|
53
|
+
* phone-specific defaults applied. For non-phone formats the `delimiter`
|
|
54
|
+
* is left as the user supplied it (or absent) so cleave-zen falls back to
|
|
55
|
+
* its idiomatic defaults.
|
|
56
|
+
*/
|
|
57
|
+
declare const resolveRuntimeOptions: (formatType: FormatType, options?: FormatOptions) => RuntimeOptions;
|
|
58
|
+
/**
|
|
59
|
+
* Internal: format a pre-processed value into its display representation
|
|
60
|
+
* by delegating to the matching `cleave-zen` (or `formatPhone`) helper.
|
|
61
|
+
*/
|
|
62
|
+
declare const formatValue: (value: string, formatType: FormatType, options: RuntimeOptions) => string;
|
|
63
|
+
/**
|
|
64
|
+
* Internal: pre-process `value` before passing it to the per-type formatter.
|
|
65
|
+
* Date and time inputs are first converted from raw to the display pattern
|
|
66
|
+
* (so users can type either formatted or raw and still get sensible output).
|
|
67
|
+
*/
|
|
68
|
+
declare const getValueForFormatting: (value: string, formatType: FormatType, options: RuntimeOptions) => string;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Result returned by `format()`. `formatted` is the display string
|
|
72
|
+
* (with delimiters, prefix, etc.); `raw` is the canonical backend-ready
|
|
73
|
+
* representation (digits only, no separators); `type` echoes the requested
|
|
74
|
+
* format type for chaining or debugging.
|
|
75
|
+
*/
|
|
76
|
+
interface FormatterResult {
|
|
77
|
+
readonly formatted: string;
|
|
78
|
+
readonly raw: string;
|
|
79
|
+
readonly type: FormatType;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Format `value` according to `formatType`, returning both a presentable
|
|
84
|
+
* `formatted` string and a backend-ready `raw` string.
|
|
85
|
+
*
|
|
86
|
+
* The function is pure: it never touches the DOM and never reads from the
|
|
87
|
+
* network. Pair it with your own input wiring (React, Vue, Svelte, vanilla)
|
|
88
|
+
* to drive the visible field; mirror `result.raw` into a hidden input when
|
|
89
|
+
* shipping to a server that expects clean digits.
|
|
90
|
+
*
|
|
91
|
+
* @param value - The raw value typed by the user (`null`/`undefined`/`''` return an empty result).
|
|
92
|
+
* @param formatType - One of the supported `FormatType` values.
|
|
93
|
+
* @param options - Per-type options (see `FormatOptions`).
|
|
94
|
+
* @returns A `FormatterResult` with `formatted`, `raw`, and the echoed `type`.
|
|
95
|
+
* @throws {TypeError} When `formatType` is not one of the supported types.
|
|
96
|
+
*/
|
|
97
|
+
declare const format: (value: unknown, formatType: FormatType, options?: FormatOptions) => FormatterResult;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Format a phone number string using `libphonenumber-js`'s `AsYouType`.
|
|
101
|
+
*
|
|
102
|
+
* Non-digit characters are stripped before formatting (the leading `+` is
|
|
103
|
+
* preserved so international numbers keep their country prefix). After
|
|
104
|
+
* formatting, spaces produced by `AsYouType` are swapped for `delimiter` when
|
|
105
|
+
* a custom delimiter is provided.
|
|
106
|
+
*
|
|
107
|
+
* @param value - The raw input (digits, `+`, spaces, dashes, parentheses, etc.).
|
|
108
|
+
* @param country - ISO 3166-1 alpha-2 country code (e.g. `'MX'`, `'US'`).
|
|
109
|
+
* @param delimiter - Character used to separate groups in the output.
|
|
110
|
+
* @returns The formatted phone string, or `''` when `value` is empty.
|
|
111
|
+
*/
|
|
112
|
+
declare const formatPhone: (value: string, country?: string, delimiter?: string) => string;
|
|
113
|
+
|
|
114
|
+
declare const regex: {
|
|
115
|
+
readonly phone: {
|
|
116
|
+
readonly pattern: RegExp;
|
|
117
|
+
readonly errorMessage: "Please enter a valid 10-digit phone number.";
|
|
118
|
+
};
|
|
119
|
+
readonly email: {
|
|
120
|
+
readonly pattern: RegExp;
|
|
121
|
+
readonly errorMessage: "Please enter a valid email address.";
|
|
122
|
+
};
|
|
123
|
+
readonly rfc: {
|
|
124
|
+
readonly pattern: RegExp;
|
|
125
|
+
readonly errorMessage: "Please enter a valid RFC.";
|
|
126
|
+
};
|
|
127
|
+
readonly numeral: {
|
|
128
|
+
readonly pattern: RegExp;
|
|
129
|
+
readonly errorMessage: "Please enter a valid number.";
|
|
130
|
+
};
|
|
131
|
+
readonly onlyNumbers: {
|
|
132
|
+
readonly pattern: RegExp;
|
|
133
|
+
readonly errorMessage: "Please enter only numbers.";
|
|
134
|
+
};
|
|
135
|
+
readonly creditCard: {
|
|
136
|
+
readonly pattern: RegExp;
|
|
137
|
+
readonly errorMessage: "Please enter a valid card number.";
|
|
138
|
+
};
|
|
139
|
+
readonly expirationDate: {
|
|
140
|
+
readonly pattern: RegExp;
|
|
141
|
+
readonly errorMessage: "Please enter a valid expiration date.";
|
|
142
|
+
};
|
|
143
|
+
readonly cardCvc: {
|
|
144
|
+
readonly pattern: RegExp;
|
|
145
|
+
readonly errorMessage: "Please enter a valid CVC.";
|
|
146
|
+
};
|
|
147
|
+
readonly onlyLetters: {
|
|
148
|
+
readonly pattern: RegExp;
|
|
149
|
+
readonly errorMessage: "Please enter only letters.";
|
|
150
|
+
};
|
|
151
|
+
readonly onlyAlphanumeric: {
|
|
152
|
+
readonly pattern: RegExp;
|
|
153
|
+
readonly errorMessage: "Please enter only letters and numbers.";
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
type RegexKey = keyof typeof regex;
|
|
157
|
+
|
|
158
|
+
export { FORMAT_TYPES, type FormatOptions, type FormatType, type FormatterResult, type RegexKey, format, formatPhone, formatValue, getDateValueFromRaw, getRawValue, getTimeValueFromRaw, getValueForFormatting, isFormatType, regex, resolveRuntimeOptions, stripPrefixAndSuffix };
|