Skip to content

Commit afd4027

Browse files
committed
fix: improved handling of type
1 parent 636b355 commit afd4027

File tree

2 files changed

+195
-144
lines changed

2 files changed

+195
-144
lines changed

src/getValue.js

Lines changed: 159 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ const getValue = (element, valueType) => {
4343
let suffix = element.getAttribute("value-suffix") || "";
4444

4545
// Determine elementType using type first, fallback to tagName (both lowercase and uppercase respectively)
46-
const elementType = element.type ? element.type.toLowerCase() : null;
46+
// const elementType = element.type ? element.type.toLowerCase() : null;
47+
const elementType = element.getAttribute("type") || element.type
4748
const tagName = element.tagName.toUpperCase();
4849

4950
// Switch statement to handle different element types and tagNames
@@ -196,139 +197,174 @@ const handleCheckbox = (element, prefix = "", suffix = "") => {
196197
* @param {string} valueType - Specifies the type of transformation to apply to the date/time value.
197198
* @returns {any} - The transformed or processed date/time value.
198199
*/
200+
/**
201+
* Handles and transforms a date/time value with a 3-step pipeline:
202+
* 1. SNAP: Adjusts date to start/end of periods (Month, Week, Year).
203+
* 2. MATH: Adds/Subtracts Years, Months, Weeks, Days, Hours, Minutes, Seconds.
204+
* 3. FORMAT: Returns the final string/number representation.
205+
*/
199206
const handleDateTime = (element, value, valueType) => {
200-
// Convert special string '$now' to current date
207+
const inputType = (element.getAttribute("type") || element.type || "").toLowerCase();
208+
let date;
201209
if (value === "$now") {
202-
value = new Date();
210+
date = new Date();
211+
} else if (value instanceof Date) {
212+
// If it's already a date object, clone it to avoid mutating references
213+
date = new Date(value.getTime());
203214
} else if (value) {
204-
// Initialize a new Date from the string or object
205-
value = new Date(value);
215+
if (
216+
typeof value === "string" &&
217+
/^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2}(?:\.\d{3})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/.test(
218+
value
219+
)
220+
) {
221+
// Normalize datetime-local only when seconds are absent; preserve provided seconds.
222+
if (inputType === "datetime-local" && /^[0-9T:-]{16}$/.test(value)) {
223+
value = `${value}:00`;
224+
}
225+
date = new Date(value);
226+
} else {
227+
date = new Date(value);
228+
}
206229
} else {
207-
value = new Date();
230+
console.warn("Provided date is invalid:", value);
231+
return "";
208232
}
209233

210-
// Check if value is a valid date
211-
if (value instanceof Date && !isNaN(value.getTime())) {
212-
// Default behavior if no specific valueType provided
213-
if (!valueType) {
214-
value = value.toISOString();
215-
}
216-
217-
// Format for 'time' type elements
218-
if (element.type === "time" && !valueType) {
219-
value = value.substring(11, 19) + "Z";
234+
if (date instanceof Date && !isNaN(date.getTime())) {
235+
// These operations "floor" or "ceil" the date to a specific boundary.
236+
switch (valueType) {
237+
case "startOfDay":
238+
date.setHours(0, 0, 0, 0);
239+
break;
240+
case "startOfWeek":
241+
const startWkOff = parseInt(
242+
element.getAttribute("week-start-day") || 0,
243+
10
244+
);
245+
date.setDate(date.getDate() - date.getDay() + startWkOff);
246+
date.setHours(0, 0, 0, 0);
247+
break;
248+
case "endOfWeek":
249+
const endWkOff = parseInt(
250+
element.getAttribute("week-start-day") || 0,
251+
10
252+
);
253+
date.setDate(date.getDate() - date.getDay() + 6 + endWkOff);
254+
date.setHours(23, 59, 59, 999);
255+
break;
256+
case "startOfMonth":
257+
date.setDate(1);
258+
date.setHours(0, 0, 0, 0);
259+
break;
260+
case "endOfMonth":
261+
date.setMonth(date.getMonth() + 1, 0);
262+
date.setHours(23, 59, 59, 999);
263+
break;
264+
case "startOfYear":
265+
date.setMonth(0, 1);
266+
date.setHours(0, 0, 0, 0);
267+
break;
268+
case "endOfYear":
269+
date.setMonth(11, 31);
270+
date.setHours(23, 59, 59, 999);
271+
break;
220272
}
221273

222-
if (valueType) {
223-
switch (valueType) {
224-
case "getDayName":
225-
const days = [
226-
"Sunday",
227-
"Monday",
228-
"Tuesday",
229-
"Wednesday",
230-
"Thursday",
231-
"Friday",
232-
"Saturday"
233-
];
234-
value = days[value.getDay()];
235-
break;
236-
case "getMonthName":
237-
const months = [
238-
"January",
239-
"February",
240-
"March",
241-
"April",
242-
"May",
243-
"June",
244-
"July",
245-
"August",
246-
"September",
247-
"October",
248-
"November",
249-
"December"
250-
];
251-
value = months[value.getMonth()];
252-
break;
253-
case "getYear":
254-
case "getFullYear":
255-
// Returns the full 4-digit year (e.g., 2025)
256-
value = value.getFullYear();
257-
break;
258-
case "toUnixTimestamp":
259-
value = Math.floor(value.getTime() / 1000);
260-
break;
261-
case "toLocaleString":
262-
let locale = element.getAttribute("locale") || "en-US";
263-
value = value.toLocaleString(locale);
264-
break;
265-
case "addDays":
266-
// Add days to the current date
267-
const addDays = parseInt(element.getAttribute("add-days") || 0, 10);
268-
value.setDate(value.getDate() + addDays);
269-
value = value.toISOString();
270-
break;
271-
case "subtractDays":
272-
// Subtract days from the current date
273-
const subtractDays = parseInt(element.getAttribute("subtract-days") || 0, 10);
274-
value.setDate(value.getDate() - subtractDays);
275-
value = value.toISOString();
276-
break;
277-
case "startOfDay":
278-
// Get the start of the current day (12:00 midnight)
279-
const startOfDay = new Date(value);
280-
startOfDay.setHours(0, 0, 0, 0); // Set time to midnight
281-
value = startOfDay.toISOString();
282-
break;
283-
case "startOfWeek":
284-
// Get the start of the current week (Sunday by default)
285-
const startOfWeekOffset = parseInt(element.getAttribute("week-start-day") || 0, 10); // Default to Sunday (0)
286-
const startOfWeek = new Date(value);
287-
startOfWeek.setDate(value.getDate() - value.getDay() + startOfWeekOffset);
288-
startOfWeek.setHours(0, 0, 0, 0); // Set to midnight
289-
value = startOfWeek.toISOString();
290-
break;
291-
case "endOfWeek":
292-
// Get the end of the current week (Saturday by default)
293-
const endOfWeekOffset = parseInt(element.getAttribute("week-start-day") || 0, 10); // Default to Sunday (0)
294-
const endOfWeek = new Date(value);
295-
endOfWeek.setDate(value.getDate() - value.getDay() + 6 + endOfWeekOffset);
296-
endOfWeek.setHours(23, 59, 59, 999); // Set to the end of the day
297-
value = endOfWeek.toISOString();
298-
break;
299-
case "startOfMonth":
300-
// Get the start of the month
301-
value = new Date(value.getFullYear(), value.getMonth(), 1).toISOString();
302-
break;
303-
case "endOfMonth":
304-
// Get the end of the month
305-
value = new Date(value.getFullYear(), value.getMonth() + 1, 0).toISOString();
306-
break;
307-
case "startOfYear":
308-
// Get the start of the year
309-
value = new Date(value.getFullYear(), 0, 1).toISOString();
310-
break;
311-
case "endOfYear":
312-
// Get the end of the year
313-
value = new Date(value.getFullYear(), 11, 31).toISOString();
314-
break;
315-
274+
// --- PHASE 3: MATH (Modify the Date)
275+
// Helper to get integer value from attribute safely
276+
const getVal = (attr) =>
277+
parseInt(element.getAttribute(attr) || "0", 10);
278+
279+
// 1. Years
280+
const addYears = getVal("add-years") - getVal("subtract-years");
281+
if (addYears) date.setFullYear(date.getFullYear() + addYears);
282+
283+
// 2. Months
284+
const addMonths = getVal("add-months") - getVal("subtract-months");
285+
if (addMonths) date.setMonth(date.getMonth() + addMonths);
286+
287+
// 3. Weeks
288+
const addWeeks = getVal("add-weeks") - getVal("subtract-weeks");
289+
if (addWeeks) date.setDate(date.getDate() + addWeeks * 7);
290+
291+
// 4. Days
292+
const addDays = getVal("add-days") - getVal("subtract-days");
293+
if (addDays) date.setDate(date.getDate() + addDays);
294+
295+
// 5. Hours
296+
const addHours = getVal("add-hours") - getVal("subtract-hours");
297+
if (addHours) date.setHours(date.getHours() + addHours);
298+
299+
// 6. Minutes
300+
const addMinutes = getVal("add-minutes") - getVal("subtract-minutes");
301+
if (addMinutes) date.setMinutes(date.getMinutes() + addMinutes);
302+
303+
// 7. Seconds
304+
const addSeconds = getVal("add-seconds") - getVal("subtract-seconds");
305+
if (addSeconds) date.setSeconds(date.getSeconds() + addSeconds);
306+
307+
308+
// --- PHASE 4: FORMATTING (Output the Result) ---
309+
switch (valueType) {
310+
case "getDayName":
311+
const days = [
312+
"Sunday",
313+
"Monday",
314+
"Tuesday",
315+
"Wednesday",
316+
"Thursday",
317+
"Friday",
318+
"Saturday",
319+
];
320+
return days[date.getDay()];
321+
322+
case "getMonthName":
323+
const months = [
324+
"January",
325+
"February",
326+
"March",
327+
"April",
328+
"May",
329+
"June",
330+
"July",
331+
"August",
332+
"September",
333+
"October",
334+
"November",
335+
"December",
336+
];
337+
return months[date.getMonth()];
338+
339+
case "getYear":
340+
case "getFullYear":
341+
return date.getFullYear();
342+
343+
case "toUnixTimestamp":
344+
return Math.floor(date.getTime() / 1000);
345+
346+
case "toLocaleString":
347+
const locale = element.getAttribute("locale") || "en-US";
348+
return date.toLocaleString(locale);
349+
350+
default:
351+
// Handle generic methods if specified
352+
if (valueType && typeof date[valueType] === "function") {
353+
return date[valueType]();
354+
}
316355

317-
default:
318-
if (typeof value[valueType] === "function") {
319-
value = value[valueType]();
320-
} else {
321-
console.warn(
322-
`The method ${valueType} is not a function of Date object.`
323-
);
324-
}
325-
break;
326-
}
356+
const pad = (n) => String(n).padStart(2, "0");
357+
if (inputType === "datetime-local") {
358+
// Return a datetime-local compatible string with seconds to avoid invalid values
359+
return `${date.getFullYear()}-${pad(
360+
date.getMonth() + 1
361+
)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(
362+
date.getMinutes()
363+
)}:${pad(date.getSeconds())}`;
364+
}
365+
return date.toISOString();
327366
}
328-
} else {
329-
console.warn("Provided date is invalid or could not be parsed:", value);
330367
}
331-
return value;
332368
};
333369

334370
/**

src/setValue.js

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,44 @@ const setValue = (el, value, dispatch) => {
1818
return storage.set(el, value);
1919
else if (typeof value === "object") value = JSON.stringify(value, null, 2);
2020

21-
if (["time", "datetime", "datetime-local"].includes(el.type)) {
22-
if (value) {
23-
const date = new Date(value);
24-
if (el.type === "time") {
25-
// Format time as "HH:MM"
26-
const hours = String(date.getHours()).padStart(2, "0");
27-
const minutes = String(date.getMinutes()).padStart(2, "0");
28-
el.value = `${hours}:${minutes}`;
29-
} else if (el.type === "datetime-local") {
30-
// Format datetime-local as "YYYY-MM-DDTHH:MM"
31-
const year = date.getFullYear();
32-
const month = String(date.getMonth() + 1).padStart(2, "0");
33-
const day = String(date.getDate()).padStart(2, "0");
34-
const hours = String(date.getHours()).padStart(2, "0");
35-
const minutes = String(date.getMinutes()).padStart(2, "0");
36-
el.value = `${year}-${month}-${day}T${hours}:${minutes}`;
21+
if (["date", "time", "datetime", "datetime-local"].includes(el.getAttribute("type") || el.type )) {
22+
if (value) {
23+
const date = new Date(value);
24+
// If 'use-utc' is present, we shift the time forward by the timezone offset.
25+
if (el.hasAttribute("use-utc")) {
26+
date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
3727
}
38-
} else {
39-
el.value = "";
40-
}
41-
return dispatchEvents(el, bubbles, dispatch);
42-
}
4328

29+
if (el.tagName === "INPUT") {
30+
if (!isNaN(date.getTime())) {
31+
// We no longer need ternary operators or if/else blocks here.
32+
const year = date.getFullYear();
33+
const month = String(date.getMonth() + 1).padStart(2, "0");
34+
const day = String(date.getDate()).padStart(2, "0");
35+
const hours = String(date.getHours()).padStart(2, "0");
36+
const minutes = String(date.getMinutes()).padStart(2, "0");
37+
38+
// 5. Set Value
39+
if (el.type === "time") {
40+
el.value = `${hours}:${minutes}`;
41+
} else if (el.type === "date") {
42+
el.value = `${year}-${month}-${day}`;
43+
} else {
44+
el.value = `${year}-${month}-${day}T${hours}:${minutes}`;
45+
}
46+
} else {
47+
console.warn(`Invalid date for input ${el.name}:`, value);
48+
el.value = "";
49+
}
50+
return dispatchEvents(el, bubbles, dispatch);
51+
} else if (!isNaN(date.getTime())) {
52+
value = date.toLocaleString()
53+
}
54+
} else {
55+
el.value = "";
56+
}
57+
}
58+
4459
let valueType = el.getAttribute("value-type");
4560
let prefix = el.getAttribute("value-prefix") || "";
4661
if (prefix) value = value.toString().replace(prefix, "");

0 commit comments

Comments
 (0)