periplo-ui 4.4.2 → 4.6.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/dist/components/Checkbox/Checkbox.d.ts +4 -4
- package/dist/components/Checkbox/Checkbox.js +3 -1
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/DataTable/DataTable.d.ts +1 -1
- package/dist/components/DataTable/DataTable.js +49 -18
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/components/DataTableBody.d.ts +12 -3
- package/dist/components/DataTable/components/DataTableBody.js +43 -8
- package/dist/components/DataTable/components/DataTableBody.js.map +1 -1
- package/dist/components/DataTable/components/DataTableGroupRow.d.ts +10 -0
- package/dist/components/DataTable/components/DataTableGroupRow.js +25 -0
- package/dist/components/DataTable/components/DataTableGroupRow.js.map +1 -0
- package/dist/components/DataTable/components/DataTableGroupRowSelector.d.ts +9 -0
- package/dist/components/DataTable/components/DataTableGroupRowSelector.js +32 -0
- package/dist/components/DataTable/components/DataTableGroupRowSelector.js.map +1 -0
- package/dist/components/DataTable/components/DataTableSelectHeader.js +16 -10
- package/dist/components/DataTable/components/DataTableSelectHeader.js.map +1 -1
- package/dist/components/DataTable/hooks/useGroupExpansion.d.ts +19 -0
- package/dist/components/DataTable/hooks/useGroupExpansion.js +34 -0
- package/dist/components/DataTable/hooks/useGroupExpansion.js.map +1 -0
- package/dist/components/DataTable/types.d.ts +27 -6
- package/dist/components/DataTable/utils.d.ts +6 -0
- package/dist/components/DataTable/utils.js +15 -0
- package/dist/components/DataTable/utils.js.map +1 -0
- package/dist/components/InputDate/InputDate.d.ts +9 -11
- package/dist/components/InputDate/InputDate.js +91 -113
- package/dist/components/InputDate/InputDate.js.map +1 -1
- package/package.json +6 -3
- package/dist/components/InputDate/MaskedInput.d.ts +0 -18
- package/dist/components/InputDate/MaskedInput.js +0 -227
- package/dist/components/InputDate/MaskedInput.js.map +0 -1
- package/dist/components/InputDate/manualDateFormat.d.ts +0 -11
- package/dist/components/InputDate/manualDateFormat.js +0 -60
- package/dist/components/InputDate/manualDateFormat.js.map +0 -1
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import React__default from 'react';
|
|
4
|
-
import { normalizeManualInput, parseManualDateString } from './manualDateFormat.js';
|
|
5
|
-
import { cn } from '../../lib/utils.js';
|
|
6
|
-
|
|
7
|
-
const FORMAT_CONFIG = {
|
|
8
|
-
"DD/MM/YYYY": {
|
|
9
|
-
template: "DD/MM/YYYY",
|
|
10
|
-
order: ["day", "month", "year"],
|
|
11
|
-
placeholders: { 0: "D", 1: "D", 3: "M", 4: "M", 6: "Y", 7: "Y", 8: "Y", 9: "Y" },
|
|
12
|
-
separators: [2, 5]
|
|
13
|
-
},
|
|
14
|
-
"MM/DD/YYYY": {
|
|
15
|
-
template: "MM/DD/YYYY",
|
|
16
|
-
order: ["month", "day", "year"],
|
|
17
|
-
placeholders: { 0: "M", 1: "M", 3: "D", 4: "D", 6: "Y", 7: "Y", 8: "Y", 9: "Y" },
|
|
18
|
-
separators: [2, 5]
|
|
19
|
-
},
|
|
20
|
-
"YYYY/MM/DD": {
|
|
21
|
-
template: "YYYY/MM/DD",
|
|
22
|
-
order: ["year", "month", "day"],
|
|
23
|
-
placeholders: { 0: "Y", 1: "Y", 2: "Y", 3: "Y", 5: "M", 6: "M", 8: "D", 9: "D" },
|
|
24
|
-
separators: [4, 7]
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
const getSegmentIndex = (pos, s1, s2) => {
|
|
28
|
-
if (pos < s1) return 0;
|
|
29
|
-
if (pos < s2) return 1;
|
|
30
|
-
return 2;
|
|
31
|
-
};
|
|
32
|
-
const getFirstPosOfSegment = (segmentIndex, separators) => {
|
|
33
|
-
const [sep1, sep2] = separators;
|
|
34
|
-
if (segmentIndex === 0) return 0;
|
|
35
|
-
if (segmentIndex === 1) return sep1 + 1;
|
|
36
|
-
return sep2 + 1;
|
|
37
|
-
};
|
|
38
|
-
const applyFirstDigitAutopad = (newChars, targetPos, digit, segmentType) => {
|
|
39
|
-
const shouldPadDay = segmentType === "day" && Number.parseInt(digit) > 3;
|
|
40
|
-
const shouldPadMonth = segmentType === "month" && Number.parseInt(digit) > 1;
|
|
41
|
-
if (shouldPadDay || shouldPadMonth) {
|
|
42
|
-
newChars[targetPos] = "0";
|
|
43
|
-
newChars[targetPos + 1] = digit;
|
|
44
|
-
return targetPos + 1;
|
|
45
|
-
}
|
|
46
|
-
return targetPos;
|
|
47
|
-
};
|
|
48
|
-
const validateSecondDigit = (newChars, targetPos, segmentType) => {
|
|
49
|
-
const firstPos = targetPos - 1;
|
|
50
|
-
if (segmentType === "day") {
|
|
51
|
-
const day = Number.parseInt(newChars[firstPos] + newChars[targetPos]);
|
|
52
|
-
return newChars[firstPos] === "D" || day <= 31;
|
|
53
|
-
}
|
|
54
|
-
if (segmentType === "month") {
|
|
55
|
-
const month = Number.parseInt(newChars[firstPos] + newChars[targetPos]);
|
|
56
|
-
return newChars[firstPos] === "M" || month <= 12;
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
};
|
|
60
|
-
const validateYearDigit = (newChars, targetPos, digit, yearFirstPos) => {
|
|
61
|
-
const yearDigitIndex = targetPos - yearFirstPos;
|
|
62
|
-
if (yearDigitIndex === 0) {
|
|
63
|
-
return digit === "1" || digit === "2";
|
|
64
|
-
}
|
|
65
|
-
if (yearDigitIndex === 1) {
|
|
66
|
-
const firstDigit = newChars[yearFirstPos];
|
|
67
|
-
if (firstDigit === "1") return digit === "9";
|
|
68
|
-
if (firstDigit === "2") return digit === "0";
|
|
69
|
-
}
|
|
70
|
-
if (yearDigitIndex === 2) {
|
|
71
|
-
const twoDigits = newChars[yearFirstPos] + newChars[yearFirstPos + 1];
|
|
72
|
-
if (twoDigits === "20") return Number.parseInt(digit) <= 5;
|
|
73
|
-
}
|
|
74
|
-
if (yearDigitIndex === 3) {
|
|
75
|
-
const yearStr = newChars.slice(yearFirstPos, yearFirstPos + 3).join("") + digit;
|
|
76
|
-
const year = Number.parseInt(yearStr, 10);
|
|
77
|
-
return !Number.isNaN(year) && year >= 1922 && year <= 2300;
|
|
78
|
-
}
|
|
79
|
-
return true;
|
|
80
|
-
};
|
|
81
|
-
const MaskedInput = React__default.forwardRef(
|
|
82
|
-
({ value, onChange, placeholder, inputFormat = "DD/MM/YYYY", onBlur, onFocus, onKeyDown, className, ...rest }, ref) => {
|
|
83
|
-
const inputRef = React__default.useRef(null);
|
|
84
|
-
React__default.useImperativeHandle(ref, () => inputRef.current);
|
|
85
|
-
const config = FORMAT_CONFIG[inputFormat];
|
|
86
|
-
const [sep1, sep2] = config.separators;
|
|
87
|
-
const maxPos = 9;
|
|
88
|
-
const getNextPos = (pos) => {
|
|
89
|
-
const next = pos + 1;
|
|
90
|
-
if (next === sep1 || next === sep2) return next + 1;
|
|
91
|
-
return Math.min(next, maxPos + 1);
|
|
92
|
-
};
|
|
93
|
-
const handleDelete = (pos, isDelete) => {
|
|
94
|
-
const input = inputRef.current;
|
|
95
|
-
if (!input) return;
|
|
96
|
-
let targetPos = isDelete ? pos : pos - 1;
|
|
97
|
-
if (targetPos === sep1 || targetPos === sep2) {
|
|
98
|
-
targetPos = isDelete ? targetPos + 1 : targetPos - 1;
|
|
99
|
-
}
|
|
100
|
-
if (targetPos < 0 || targetPos > maxPos) return;
|
|
101
|
-
const chars = (value || config.template).split("");
|
|
102
|
-
chars[targetPos] = config.placeholders[targetPos] ?? chars[targetPos];
|
|
103
|
-
const newValue = chars.join("");
|
|
104
|
-
onChange(newValue === config.template ? "" : newValue);
|
|
105
|
-
requestAnimationFrame(() => {
|
|
106
|
-
input.setSelectionRange(targetPos, targetPos);
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
const handleDigit = (pos, digit) => {
|
|
110
|
-
const input = inputRef.current;
|
|
111
|
-
if (!input) return;
|
|
112
|
-
let targetPos = pos;
|
|
113
|
-
if (targetPos === sep1 || targetPos === sep2) targetPos++;
|
|
114
|
-
if (targetPos > maxPos) return;
|
|
115
|
-
const chars = (value || config.template).split("");
|
|
116
|
-
const newChars = [...chars];
|
|
117
|
-
newChars[targetPos] = digit;
|
|
118
|
-
const segmentIndex = getSegmentIndex(targetPos, sep1, sep2);
|
|
119
|
-
const segmentType = config.order[segmentIndex];
|
|
120
|
-
const firstPosOfSegment = getFirstPosOfSegment(segmentIndex, config.separators);
|
|
121
|
-
const isFirstDigit = targetPos === firstPosOfSegment;
|
|
122
|
-
const isSecondDigit = targetPos === firstPosOfSegment + 1;
|
|
123
|
-
if (isFirstDigit) {
|
|
124
|
-
targetPos = applyFirstDigitAutopad(newChars, targetPos, digit, segmentType);
|
|
125
|
-
}
|
|
126
|
-
if (isSecondDigit && !validateSecondDigit(newChars, targetPos, segmentType)) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (segmentType === "year" && !validateYearDigit(newChars, targetPos, digit, firstPosOfSegment)) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
newChars[targetPos] = digit;
|
|
133
|
-
const nextPos = getNextPos(targetPos);
|
|
134
|
-
const result = newChars.join("");
|
|
135
|
-
onChange(result === config.template ? "" : result);
|
|
136
|
-
requestAnimationFrame(() => {
|
|
137
|
-
input.setSelectionRange(nextPos, nextPos);
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
const handleBlur = (event) => {
|
|
141
|
-
const trimmed = (value || "").trim();
|
|
142
|
-
if (trimmed) {
|
|
143
|
-
const normalized = normalizeManualInput(trimmed, inputFormat);
|
|
144
|
-
const parsed = parseManualDateString(normalized, inputFormat);
|
|
145
|
-
if (!parsed) {
|
|
146
|
-
onChange("");
|
|
147
|
-
} else if (normalized !== value) {
|
|
148
|
-
onChange(normalized);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
onBlur?.(event);
|
|
152
|
-
};
|
|
153
|
-
const handleKeyDown = (event) => {
|
|
154
|
-
onKeyDown?.(event);
|
|
155
|
-
const input = inputRef.current;
|
|
156
|
-
if (!input) return;
|
|
157
|
-
const pos = input.selectionStart;
|
|
158
|
-
if (pos === null) return;
|
|
159
|
-
if (event.key === "Tab" || event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "Enter" || event.key === "ArrowUp" || event.key === "ArrowDown" || event.ctrlKey || event.metaKey) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
event.preventDefault();
|
|
163
|
-
if (event.key === "Backspace" || event.key === "Delete") {
|
|
164
|
-
handleDelete(pos, event.key === "Delete");
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (!/^\d$/.test(event.key)) return;
|
|
168
|
-
handleDigit(pos, event.key);
|
|
169
|
-
};
|
|
170
|
-
const handleClick = () => {
|
|
171
|
-
const input = inputRef.current;
|
|
172
|
-
if (!input) return;
|
|
173
|
-
const pos = input.selectionStart ?? 0;
|
|
174
|
-
const getTargetPos = (position) => {
|
|
175
|
-
if (position === sep1) return position - 1;
|
|
176
|
-
if (position === sep2) return position - 1;
|
|
177
|
-
return position;
|
|
178
|
-
};
|
|
179
|
-
requestAnimationFrame(() => {
|
|
180
|
-
input.setSelectionRange(getTargetPos(pos), getTargetPos(pos));
|
|
181
|
-
});
|
|
182
|
-
};
|
|
183
|
-
const renderOverlay = () => {
|
|
184
|
-
if (!value) return null;
|
|
185
|
-
const display = value.padEnd(10, " ");
|
|
186
|
-
const template = config.template;
|
|
187
|
-
return /* @__PURE__ */ jsx(
|
|
188
|
-
"div",
|
|
189
|
-
{
|
|
190
|
-
"aria-hidden": "true",
|
|
191
|
-
className: "pointer-events-none absolute inset-0 flex items-center px-2 text-base select-none",
|
|
192
|
-
children: display.split("").map((char, index) => {
|
|
193
|
-
const isPlaceholderChar = template[index] !== "/" && char === template[index];
|
|
194
|
-
const stableKey = `${template[index]}-${index}`;
|
|
195
|
-
return /* @__PURE__ */ jsx("span", { className: isPlaceholderChar ? "text-neutral-300" : "text-neutral-900", children: char }, stableKey);
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
);
|
|
199
|
-
};
|
|
200
|
-
return /* @__PURE__ */ jsxs("div", { className: "relative flex w-full items-center", children: [
|
|
201
|
-
/* @__PURE__ */ jsx(
|
|
202
|
-
"input",
|
|
203
|
-
{
|
|
204
|
-
ref: inputRef,
|
|
205
|
-
type: "text",
|
|
206
|
-
autoComplete: "off",
|
|
207
|
-
value: value || "",
|
|
208
|
-
placeholder: placeholder ?? inputFormat,
|
|
209
|
-
onKeyDown: handleKeyDown,
|
|
210
|
-
onClick: handleClick,
|
|
211
|
-
onChange: () => {
|
|
212
|
-
},
|
|
213
|
-
onBlur: handleBlur,
|
|
214
|
-
onFocus,
|
|
215
|
-
size: 1,
|
|
216
|
-
className: cn(className, value ? "text-transparent caret-neutral-900" : ""),
|
|
217
|
-
...rest
|
|
218
|
-
}
|
|
219
|
-
),
|
|
220
|
-
renderOverlay()
|
|
221
|
-
] });
|
|
222
|
-
}
|
|
223
|
-
);
|
|
224
|
-
MaskedInput.displayName = "MaskedInput";
|
|
225
|
-
|
|
226
|
-
export { MaskedInput };
|
|
227
|
-
//# sourceMappingURL=MaskedInput.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MaskedInput.js","sources":["../../../src/components/InputDate/MaskedInput.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\n\nimport { normalizeManualInput, parseManualDateString } from './manualDateFormat'\n\nimport { cn } from '@/lib/utils'\n\nexport type DateInputFormat = 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD'\n\ntype Props = {\n value: string\n onChange: (value: string) => void\n onBlur?: React.FocusEventHandler<HTMLInputElement>\n onFocus?: React.FocusEventHandler<HTMLInputElement>\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>\n placeholder?: string\n disabled?: boolean\n id?: string\n name?: string\n className?: string\n 'aria-invalid'?: React.InputHTMLAttributes<HTMLInputElement>['aria-invalid']\n inputFormat?: DateInputFormat\n}\n\ntype SegmentType = 'day' | 'month' | 'year'\n\ntype FormatConfig = {\n template: string\n order: [SegmentType, SegmentType, SegmentType]\n placeholders: Record<number, string>\n separators: [number, number]\n}\n\nconst FORMAT_CONFIG: Record<DateInputFormat, FormatConfig> = {\n 'DD/MM/YYYY': {\n template: 'DD/MM/YYYY',\n order: ['day', 'month', 'year'],\n placeholders: { 0: 'D', 1: 'D', 3: 'M', 4: 'M', 6: 'Y', 7: 'Y', 8: 'Y', 9: 'Y' },\n separators: [2, 5],\n },\n 'MM/DD/YYYY': {\n template: 'MM/DD/YYYY',\n order: ['month', 'day', 'year'],\n placeholders: { 0: 'M', 1: 'M', 3: 'D', 4: 'D', 6: 'Y', 7: 'Y', 8: 'Y', 9: 'Y' },\n separators: [2, 5],\n },\n 'YYYY/MM/DD': {\n template: 'YYYY/MM/DD',\n order: ['year', 'month', 'day'],\n placeholders: { 0: 'Y', 1: 'Y', 2: 'Y', 3: 'Y', 5: 'M', 6: 'M', 8: 'D', 9: 'D' },\n separators: [4, 7],\n },\n}\n\nconst getSegmentIndex = (pos: number, s1: number, s2: number): number => {\n if (pos < s1) return 0\n if (pos < s2) return 1\n return 2\n}\n\nconst getFirstPosOfSegment = (segmentIndex: number, separators: [number, number]): number => {\n const [sep1, sep2] = separators\n if (segmentIndex === 0) return 0\n if (segmentIndex === 1) return sep1 + 1\n return sep2 + 1\n}\n\nconst applyFirstDigitAutopad = (\n newChars: Array<string>,\n targetPos: number,\n digit: string,\n segmentType: SegmentType,\n): number => {\n const shouldPadDay = segmentType === 'day' && Number.parseInt(digit) > 3\n const shouldPadMonth = segmentType === 'month' && Number.parseInt(digit) > 1\n\n if (shouldPadDay || shouldPadMonth) {\n newChars[targetPos] = '0'\n newChars[targetPos + 1] = digit\n return targetPos + 1\n }\n return targetPos\n}\n\nconst validateSecondDigit = (newChars: Array<string>, targetPos: number, segmentType: SegmentType): boolean => {\n const firstPos = targetPos - 1\n if (segmentType === 'day') {\n const day = Number.parseInt(newChars[firstPos] + newChars[targetPos])\n return newChars[firstPos] === 'D' || day <= 31\n }\n if (segmentType === 'month') {\n const month = Number.parseInt(newChars[firstPos] + newChars[targetPos])\n return newChars[firstPos] === 'M' || month <= 12\n }\n return true\n}\n\nconst validateYearDigit = (\n newChars: Array<string>,\n targetPos: number,\n digit: string,\n yearFirstPos: number,\n): boolean => {\n const yearDigitIndex = targetPos - yearFirstPos\n\n if (yearDigitIndex === 0) {\n return digit === '1' || digit === '2'\n }\n\n if (yearDigitIndex === 1) {\n const firstDigit = newChars[yearFirstPos]\n if (firstDigit === '1') return digit === '9'\n if (firstDigit === '2') return digit === '0'\n }\n\n if (yearDigitIndex === 2) {\n const twoDigits = newChars[yearFirstPos] + newChars[yearFirstPos + 1]\n if (twoDigits === '20') return Number.parseInt(digit) <= 5\n }\n\n if (yearDigitIndex === 3) {\n const yearStr = newChars.slice(yearFirstPos, yearFirstPos + 3).join('') + digit\n const year = Number.parseInt(yearStr, 10)\n return !Number.isNaN(year) && year >= 1922 && year <= 2300\n }\n\n return true\n}\n\nexport const MaskedInput = React.forwardRef<HTMLInputElement, Props>(\n (\n { value, onChange, placeholder, inputFormat = 'DD/MM/YYYY', onBlur, onFocus, onKeyDown, className, ...rest },\n ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null)\n React.useImperativeHandle(ref, () => inputRef.current!)\n\n const config = FORMAT_CONFIG[inputFormat]\n const [sep1, sep2] = config.separators\n\n const maxPos = 9\n\n const getNextPos = (pos: number): number => {\n const next = pos + 1\n if (next === sep1 || next === sep2) return next + 1\n return Math.min(next, maxPos + 1)\n }\n\n const handleDelete = (pos: number, isDelete: boolean) => {\n const input = inputRef.current\n if (!input) return\n\n let targetPos = isDelete ? pos : pos - 1\n if (targetPos === sep1 || targetPos === sep2) {\n targetPos = isDelete ? targetPos + 1 : targetPos - 1\n }\n if (targetPos < 0 || targetPos > maxPos) return\n\n const chars = (value || config.template).split('')\n chars[targetPos] = config.placeholders[targetPos] ?? chars[targetPos]\n\n const newValue = chars.join('')\n onChange(newValue === config.template ? '' : newValue)\n\n requestAnimationFrame(() => {\n input.setSelectionRange(targetPos, targetPos)\n })\n }\n\n const handleDigit = (pos: number, digit: string) => {\n const input = inputRef.current\n if (!input) return\n\n let targetPos = pos\n if (targetPos === sep1 || targetPos === sep2) targetPos++\n if (targetPos > maxPos) return\n\n const chars = (value || config.template).split('')\n const newChars = [...chars]\n newChars[targetPos] = digit\n\n const segmentIndex = getSegmentIndex(targetPos, sep1, sep2)\n const segmentType = config.order[segmentIndex]\n const firstPosOfSegment = getFirstPosOfSegment(segmentIndex, config.separators)\n const isFirstDigit = targetPos === firstPosOfSegment\n const isSecondDigit = targetPos === firstPosOfSegment + 1\n\n if (isFirstDigit) {\n targetPos = applyFirstDigitAutopad(newChars, targetPos, digit, segmentType)\n }\n\n if (isSecondDigit && !validateSecondDigit(newChars, targetPos, segmentType)) {\n return\n }\n\n if (segmentType === 'year' && !validateYearDigit(newChars, targetPos, digit, firstPosOfSegment)) {\n return\n }\n\n newChars[targetPos] = digit\n const nextPos = getNextPos(targetPos)\n const result = newChars.join('')\n onChange(result === config.template ? '' : result)\n\n requestAnimationFrame(() => {\n input.setSelectionRange(nextPos, nextPos)\n })\n }\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n const trimmed = (value || '').trim()\n if (trimmed) {\n const normalized = normalizeManualInput(trimmed, inputFormat)\n const parsed = parseManualDateString(normalized, inputFormat)\n if (!parsed) {\n onChange('')\n } else if (normalized !== value) {\n onChange(normalized)\n }\n }\n onBlur?.(event)\n }\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event)\n\n const input = inputRef.current\n if (!input) return\n\n const pos = input.selectionStart\n if (pos === null) return\n\n if (\n event.key === 'Tab' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight' ||\n event.key === 'Enter' ||\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.ctrlKey ||\n event.metaKey\n ) {\n return\n }\n\n event.preventDefault()\n\n if (event.key === 'Backspace' || event.key === 'Delete') {\n handleDelete(pos, event.key === 'Delete')\n return\n }\n\n if (!/^\\d$/.test(event.key)) return\n handleDigit(pos, event.key)\n }\n\n const handleClick = () => {\n const input = inputRef.current\n if (!input) return\n\n const pos = input.selectionStart ?? 0\n\n const getTargetPos = (position: number): number => {\n if (position === sep1) return position - 1\n if (position === sep2) return position - 1\n return position\n }\n\n requestAnimationFrame(() => {\n input.setSelectionRange(getTargetPos(pos), getTargetPos(pos))\n })\n }\n const renderOverlay = () => {\n if (!value) return null\n\n const display = value.padEnd(10, ' ')\n const template = config.template\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 flex items-center px-2 text-base select-none\"\n >\n {display.split('').map((char, index) => {\n const isPlaceholderChar = template[index] !== '/' && char === template[index]\n\n const stableKey = `${template[index]}-${index}`\n return (\n <span key={stableKey} className={isPlaceholderChar ? 'text-neutral-300' : 'text-neutral-900'}>\n {char}\n </span>\n )\n })}\n </div>\n )\n }\n\n return (\n <div className=\"relative flex w-full items-center\">\n <input\n ref={inputRef}\n type=\"text\"\n autoComplete=\"off\"\n value={value || ''}\n placeholder={placeholder ?? inputFormat}\n onKeyDown={handleKeyDown}\n onClick={handleClick}\n onChange={() => {}}\n onBlur={handleBlur}\n onFocus={onFocus}\n size={1}\n className={cn(className, value ? 'text-transparent caret-neutral-900' : '')}\n {...rest}\n />\n {renderOverlay()}\n </div>\n )\n },\n)\n\nMaskedInput.displayName = 'MaskedInput'\n"],"names":[],"mappings":";;;;;;AAkCA;AAA6D;AAC7C;AACF;AACoB;AACiD;AAC9D;AACnB;AACc;AACF;AACoB;AACiD;AAC9D;AACnB;AACc;AACF;AACoB;AACiD;AAC9D;AAErB;AAEA;AACE;AACA;AACA;AACF;AAEA;AACE;AACA;AACA;AACA;AACF;AAEA;AAME;AACA;AAEA;AACE;AACA;AACA;AAAmB;AAErB;AACF;AAEA;AACE;AACA;AACE;AACA;AAA4C;AAE9C;AACE;AACA;AAA8C;AAEhD;AACF;AAEA;AAME;AAEA;AACE;AAAkC;AAGpC;AACE;AACA;AACA;AAAyC;AAG3C;AACE;AACA;AAAyD;AAG3D;AACE;AACA;AACA;AAAsD;AAGxD;AACF;AAEO;AAA0B;AAK7B;AACA;AAEA;AACA;AAEA;AAEA;AACE;AACA;AACA;AAAgC;AAGlC;AACE;AACA;AAEA;AACA;AACE;AAAmD;AAErD;AAEA;AACA;AAEA;AACA;AAEA;AACE;AAA4C;AAC7C;AAGH;AACE;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACE;AAA0E;AAG5E;AACE;AAAA;AAGF;AACE;AAAA;AAGF;AACA;AACA;AACA;AAEA;AACE;AAAwC;AACzC;AAGH;AACE;AACA;AACE;AACA;AACA;AACE;AAAW;AAEX;AAAmB;AACrB;AAEF;AAAc;AAGhB;AACE;AAEA;AACA;AAEA;AACA;AAEA;AAUE;AAAA;AAGF;AAEA;AACE;AACA;AAAA;AAGF;AACA;AAA0B;AAG5B;AACE;AACA;AAEA;AAEA;AACE;AACA;AACA;AAAO;AAGT;AACE;AAA4D;AAC7D;AAEH;AACE;AAEA;AACA;AAEA;AACE;AAAC;AAAA;AACa;AACF;AAGR;AAEA;AACA;AAGE;AAEH;AAAA;AACH;AAIJ;AAEI;AAAA;AAAC;AAAA;AACM;AACA;AACQ;AACG;AACY;AACjB;AACF;AACO;AAAC;AACT;AACR;AACM;AACoE;AACtE;AAAA;AACN;AACe;AACjB;AAGN;AAEA;;"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export type DateInputFormat = 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD';
|
|
2
|
-
type DateSegment = 'day' | 'month' | 'year';
|
|
3
|
-
type FormatRow = {
|
|
4
|
-
dateFns: string;
|
|
5
|
-
segmentOrder: [DateSegment, DateSegment, DateSegment];
|
|
6
|
-
};
|
|
7
|
-
export declare const DATE_INPUT_FORMATS: Record<DateInputFormat, FormatRow>;
|
|
8
|
-
export declare const getDateInputDateFns: (format: DateInputFormat) => string;
|
|
9
|
-
export declare function parseManualDateString(raw: string, format: DateInputFormat): Date | undefined;
|
|
10
|
-
export declare function normalizeManualInput(text: string, format: DateInputFormat): string;
|
|
11
|
-
export {};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { parseISO, isValid } from 'date-fns';
|
|
2
|
-
|
|
3
|
-
const DATE_INPUT_FORMATS = {
|
|
4
|
-
"DD/MM/YYYY": {
|
|
5
|
-
dateFns: "dd/MM/yyyy",
|
|
6
|
-
segmentOrder: ["day", "month", "year"]
|
|
7
|
-
},
|
|
8
|
-
"MM/DD/YYYY": {
|
|
9
|
-
dateFns: "MM/dd/yyyy",
|
|
10
|
-
segmentOrder: ["month", "day", "year"]
|
|
11
|
-
},
|
|
12
|
-
"YYYY/MM/DD": {
|
|
13
|
-
dateFns: "yyyy/MM/dd",
|
|
14
|
-
segmentOrder: ["year", "month", "day"]
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
const getDateInputDateFns = (format) => DATE_INPUT_FORMATS[format].dateFns;
|
|
18
|
-
function parseManualDateString(raw, format) {
|
|
19
|
-
const trimmed = raw.trim();
|
|
20
|
-
if (!trimmed) return void 0;
|
|
21
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
|
22
|
-
const parsed = parseISO(trimmed);
|
|
23
|
-
return isValid(parsed) ? parsed : void 0;
|
|
24
|
-
}
|
|
25
|
-
const parts = trimmed.split("/");
|
|
26
|
-
if (parts.length !== 3) return void 0;
|
|
27
|
-
const { segmentOrder } = DATE_INPUT_FORMATS[format];
|
|
28
|
-
const dayIdx = segmentOrder.indexOf("day");
|
|
29
|
-
const monthIdx = segmentOrder.indexOf("month");
|
|
30
|
-
const yearIdx = segmentOrder.indexOf("year");
|
|
31
|
-
if (dayIdx === -1 || monthIdx === -1 || yearIdx === -1) return void 0;
|
|
32
|
-
const day = Number.parseInt(parts[dayIdx], 10);
|
|
33
|
-
const month = Number.parseInt(parts[monthIdx], 10);
|
|
34
|
-
const year = Number.parseInt(parts[yearIdx], 10);
|
|
35
|
-
if (Number.isNaN(day) || Number.isNaN(month) || Number.isNaN(year)) return void 0;
|
|
36
|
-
const date = new Date(year, month - 1, day);
|
|
37
|
-
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
|
|
38
|
-
return void 0;
|
|
39
|
-
}
|
|
40
|
-
return date;
|
|
41
|
-
}
|
|
42
|
-
function normalizeManualInput(text, format) {
|
|
43
|
-
const parts = text.split("/");
|
|
44
|
-
if (parts.length !== 3) return "";
|
|
45
|
-
const { segmentOrder } = DATE_INPUT_FORMATS[format];
|
|
46
|
-
const parsed = { day: null, month: null, year: null };
|
|
47
|
-
parts.forEach((part, index) => {
|
|
48
|
-
const type = segmentOrder[index];
|
|
49
|
-
const isYear = type === "year";
|
|
50
|
-
const digits = part.replaceAll(/[DMY]/g, "").trim();
|
|
51
|
-
if (!digits) return;
|
|
52
|
-
if (isYear && digits.length < 4) return;
|
|
53
|
-
parsed[type] = digits.padStart(isYear ? 4 : 2, "0");
|
|
54
|
-
});
|
|
55
|
-
if (parsed.day == null || parsed.month == null || parsed.year == null) return "";
|
|
56
|
-
return segmentOrder.map((type) => parsed[type]).join("/");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export { DATE_INPUT_FORMATS, getDateInputDateFns, normalizeManualInput, parseManualDateString };
|
|
60
|
-
//# sourceMappingURL=manualDateFormat.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manualDateFormat.js","sources":["../../../src/components/InputDate/manualDateFormat.ts"],"sourcesContent":["import { isValid, parseISO } from 'date-fns'\n\nexport type DateInputFormat = 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD'\n\ntype DateSegment = 'day' | 'month' | 'year'\n\ntype FormatRow = {\n dateFns: string\n segmentOrder: [DateSegment, DateSegment, DateSegment]\n}\n\nexport const DATE_INPUT_FORMATS: Record<DateInputFormat, FormatRow> = {\n 'DD/MM/YYYY': {\n dateFns: 'dd/MM/yyyy',\n segmentOrder: ['day', 'month', 'year'],\n },\n 'MM/DD/YYYY': {\n dateFns: 'MM/dd/yyyy',\n segmentOrder: ['month', 'day', 'year'],\n },\n 'YYYY/MM/DD': {\n dateFns: 'yyyy/MM/dd',\n segmentOrder: ['year', 'month', 'day'],\n },\n}\n\nexport const getDateInputDateFns = (format: DateInputFormat): string => DATE_INPUT_FORMATS[format].dateFns\n\nexport function parseManualDateString(raw: string, format: DateInputFormat): Date | undefined {\n const trimmed = raw.trim()\n if (!trimmed) return undefined\n\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n const parsed = parseISO(trimmed)\n return isValid(parsed) ? parsed : undefined\n }\n\n const parts = trimmed.split('/')\n if (parts.length !== 3) return undefined\n\n const { segmentOrder } = DATE_INPUT_FORMATS[format]\n const dayIdx = segmentOrder.indexOf('day')\n const monthIdx = segmentOrder.indexOf('month')\n const yearIdx = segmentOrder.indexOf('year')\n if (dayIdx === -1 || monthIdx === -1 || yearIdx === -1) return undefined\n\n const day = Number.parseInt(parts[dayIdx], 10)\n const month = Number.parseInt(parts[monthIdx], 10)\n const year = Number.parseInt(parts[yearIdx], 10)\n if (Number.isNaN(day) || Number.isNaN(month) || Number.isNaN(year)) return undefined\n\n const date = new Date(year, month - 1, day)\n if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {\n return undefined\n }\n return date\n}\n\nexport function normalizeManualInput(text: string, format: DateInputFormat): string {\n const parts = text.split('/')\n if (parts.length !== 3) return ''\n\n const { segmentOrder } = DATE_INPUT_FORMATS[format]\n const parsed: Record<DateSegment, string | null> = { day: null, month: null, year: null }\n\n parts.forEach((part, index) => {\n const type = segmentOrder[index]\n const isYear = type === 'year'\n const digits = part.replaceAll(/[DMY]/g, '').trim()\n if (!digits) return\n if (isYear && digits.length < 4) return\n parsed[type] = digits.padStart(isYear ? 4 : 2, '0')\n })\n\n if (parsed.day == null || parsed.month == null || parsed.year == null) return ''\n\n return segmentOrder.map((type) => parsed[type]).join('/')\n}\n"],"names":[],"mappings":"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"}
|