used the new utility to create a fine-grained ReactiveDate

pull/11774/head
godzylinux 2 years ago
parent 853d28efe5
commit ab837f24b2

@ -1,145 +1,234 @@
import { make_reactive } from './utils.js'; import { make_reactive } from './utils.js';
/** /**
* @type {(keyof Date)[]} * we have to create a new Date to compare, because setting `X` might or might not affect `Y`
* for instance calling `date.setMonth(55)` will also change the `date.getYear()`
* but calling `date.setMonth(1)` (assuming its not 12) will not increase the year.
* we could check all of these edge-cases but I think that might become complicated very soon and introduce more bugs
* also there is the possibility of these behaviors to change as well,
* so creating a new date and applying the change is a better idea I guess
* @param {Date} current_datetime
* @param {Date} new_datetime
* @param {import("./utils.js").InterceptorOptions<Date, (keyof Date)[], (keyof Date)[]>["notify_read_properties"]} notify_read_properties
* @return {boolean} - returns true if any changes happened
*/ */
const write_properties = /** @type {const} */ ([ const notify_datetime_changes = (current_datetime, new_datetime, notify_read_properties) => {
'setDate', let had_time_changes = false;
'setFullYear', let had_date_changes = false;
'setHours',
'setMilliseconds',
'setMinutes',
'setMonth',
'setSeconds',
'setTime',
'setUTCDate',
'setUTCFullYear',
'setUTCHours',
'setUTCMilliseconds',
'setUTCMinutes',
'setUTCMonth',
'setUTCSeconds',
// @ts-expect-error this is deprecated
'setYear'
]);
/** if (current_datetime.getFullYear() !== new_datetime.getFullYear()) {
* @type {(keyof Date)[]} notify_read_properties(['getFullYear', 'getUTCFullYear']);
*/ had_date_changes = true;
const read_properties = /** @type {const} */ ([ }
'getDate',
'getDay',
'getFullYear',
'getHours',
'getMilliseconds',
'getMinutes',
'getMonth',
'getSeconds',
'getTimezoneOffset',
'getUTCDate',
'getUTCDay',
'getUTCFullYear',
'getUTCHours',
'getUTCMilliseconds',
'getUTCMinutes',
'getUTCMonth',
'getUTCSeconds',
// @ts-expect-error this is deprecated
'getYear',
'toDateString',
'toLocaleDateString',
'toLocaleTimeString',
'toTimeString'
]);
/** // @ts-expect-error
* @type {Record<(typeof write_properties)[number], (typeof read_properties)[number][]>} if (current_datetime.getYear && current_datetime.getYear() !== new_datetime.getYear()) {
*/ // @ts-expect-error
const affected_changes = { notify_read_properties(['getYear']);
setDate: ['getDate', 'getUTCDate', 'toDateString', 'toLocaleDateString'], had_date_changes = true;
setFullYear: [ }
'getFullYear',
'getUTCFullYear', if (current_datetime.getMonth() !== new_datetime.getMonth()) {
'getDate', notify_read_properties(['getMonth', 'getUTCMonth']);
'getUTCDate', had_date_changes = true;
'toDateString', }
'toLocaleDateString'
], if (current_datetime.getDay() !== new_datetime.getDay()) {
setHours: ['getHours', 'getUTCHours', 'toTimeString', 'toLocaleTimeString'], notify_read_properties(['getDay', 'getUTCDay']);
setMilliseconds: ['getMilliseconds', 'getUTCMilliseconds', 'toTimeString', 'toLocaleTimeString'], had_date_changes = true;
setMinutes: [ }
'getMinutes',
'getUTCMinutes', if (current_datetime.getHours() !== new_datetime.getHours()) {
'getHours', notify_read_properties(['getHours', 'getUTCHours']);
'getUTCHours', had_time_changes = true;
'toTimeString', }
'toLocaleTimeString'
if (current_datetime.getMinutes() !== new_datetime.getMinutes()) {
notify_read_properties(['getMinutes', 'getUTCMinutes']);
had_time_changes = true;
}
if (current_datetime.getSeconds() !== new_datetime.getSeconds()) {
notify_read_properties(['getSeconds', 'getUTCSeconds']);
had_time_changes = true;
}
if (current_datetime.getMilliseconds() !== new_datetime.getMilliseconds()) {
notify_read_properties(['getMilliseconds', 'getUTCMilliseconds']);
had_time_changes = true;
}
if (had_time_changes) {
notify_read_properties(['toTimeString', 'toLocaleTimeString']);
}
if (had_date_changes) {
notify_read_properties(['toDateString', 'toLocaleDateString']);
}
return had_date_changes || had_time_changes;
};
export const ReactiveDate = make_reactive(Date, {
write_properties: [
'setDate',
'setFullYear',
'setHours',
'setMilliseconds',
'setMinutes',
'setMonth',
'setSeconds',
'setTime',
'setUTCDate',
'setUTCFullYear',
'setUTCHours',
'setUTCMilliseconds',
'setUTCMinutes',
'setUTCMonth',
'setUTCSeconds',
// @ts-expect-error this is deprecated
'setYear'
], ],
setMonth: [ read_properties: [
'getMonth',
'getUTCMonth',
'getDate', 'getDate',
'getUTCDate', 'getDay',
'toDateString',
'toLocaleDateString'
],
setSeconds: ['getSeconds', 'getUTCSeconds', 'toTimeString', 'toLocaleTimeString'],
setTime: [
'toDateString',
'toTimeString',
'toLocaleDateString',
'toLocaleTimeString',
'getFullYear', 'getFullYear',
'getMonth',
'getDate',
'getHours', 'getHours',
'getMilliseconds',
'getMinutes', 'getMinutes',
'getMonth',
'getSeconds', 'getSeconds',
'getMilliseconds', 'getTimezoneOffset',
'getUTCFullYear',
'getUTCMonth',
'getUTCDate', 'getUTCDate',
'getUTCDay',
'getUTCFullYear',
'getUTCHours', 'getUTCHours',
'getUTCMilliseconds',
'getUTCMinutes', 'getUTCMinutes',
'getUTCMonth',
'getUTCSeconds', 'getUTCSeconds',
'getUTCMilliseconds' // @ts-expect-error this is deprecated
'getYear',
'toDateString',
'toLocaleDateString',
'toTimeString',
'toLocaleTimeString'
], ],
setUTCDate: ['getUTCDate', 'getDate'],
setUTCFullYear: ['getUTCFullYear', 'getFullYear', 'getUTCDate', 'getDate'],
setUTCHours: ['getUTCHours', 'getHours'],
setUTCMilliseconds: ['getUTCMilliseconds', 'getMilliseconds'],
setUTCMinutes: ['getUTCMinutes', 'getMinutes', 'getUTCHours', 'getHours'],
setUTCMonth: ['getUTCMonth', 'getMonth', 'getUTCDate', 'getDate'],
setUTCSeconds: ['getUTCSeconds', 'getSeconds'],
// @ts-expect-error
setYear: ['getYear', 'getFullYear', 'toDateString']
};
/**
* @typedef {import("./utils.js").Interceptors<InstanceType<Date>, typeof write_properties, typeof read_properties>} ReactiveDateInterceptor
*/
export const ReactiveDate = make_reactive(Date, {
write_properties: write_properties,
read_properties: read_properties,
// @ts-expect-error - because of `setYear` which deprecated all types are screwed so have to compromise
interceptors: { interceptors: {
...write_properties.map((write_property) => { setDate: (options, ...params) => {
return /** @type {import("./utils.js").Interceptors<InstanceType<Date>, typeof write_properties, typeof read_properties>} */ ({ const tmp = new Date(options.value);
/** tmp.setDate(/**@type {number}*/ (params[0]));
* @param {import("./utils.js").InterceptorOptions<InstanceType<Date>, typeof write_properties, typeof read_properties>} options return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
* @param {unknown[]} params },
**/ setFullYear: (options, ...params) => {
[write_property]: (options, ...params) => { const tmp = new Date(options.value);
if (options.value[write_property]() === params[0]) { tmp.setFullYear(
return false; /**@type {number}*/ (params[0]),
} /**@type {number | undefined}*/ (params[1]),
affected_changes[write_property].forEach((affected) => { /**@type {number | undefined}*/ (params[2])
options.notify_read_properties([affected]); );
}); return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
return true; },
} setHours: (options, ...params) => {
}); const tmp = new Date(options.value);
}) tmp.setHours(
/**@type {number}*/ (params[0]),
/**@type {number | undefined}*/ (params[1]),
/**@type {number | undefined}*/ (params[2]),
/**@type {number | undefined}*/ (params[3])
);
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setMilliseconds: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setMilliseconds(/**@type {number}*/ (params[0]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setMinutes: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setMinutes(
/**@type {number}*/ (params[0]),
/**@type {number | undefined}*/ (params[1]),
/**@type {number | undefined}*/ (params[2])
);
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setMonth: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setMonth(/**@type {number}*/ (params[0]), /**@type {number | undefined}*/ (params[1]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setSeconds: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setSeconds(/**@type {number}*/ (params[0]), /**@type {number | undefined}*/ (params[1]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setTime: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setTime(/**@type {number}*/ (params[0]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCDate: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCDate(/**@type {number}*/ (params[0]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCFullYear: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCFullYear(
/**@type {number}*/ (params[0]),
/**@type {number | undefined}*/ (params[1]),
/**@type {number | undefined}*/ (params[2])
);
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCHours: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCHours(
/**@type {number}*/ (params[0]),
/**@type {number | undefined}*/ (params[1]),
/**@type {number | undefined}*/ (params[2]),
/**@type {number | undefined}*/ (params[3])
);
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCMilliseconds: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCMilliseconds(/**@type {number}*/ (params[0]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCMinutes: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCMinutes(
/**@type {number}*/ (params[0]),
/**@type {number | undefined}*/ (params[1]),
/**@type {number | undefined}*/ (params[2])
);
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCMonth: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCMonth(/**@type {number}*/ (params[0]), /**@type {number | undefined}*/ (params[1]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
setUTCSeconds: (options, ...params) => {
const tmp = new Date(options.value);
tmp.setUTCSeconds(
/**@type {number}*/ (params[0]),
/**@type {number | undefined}*/ (params[1])
);
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
},
// @ts-expect-error - deprecated method
setYear: (options, ...params) => {
// it might be removed from browsers
if (!options.value.getYear) {
return false;
}
const tmp = new Date(options.value);
// @ts-expect-error
tmp.setYear(/**@type {number}*/ (params[0]));
return notify_datetime_changes(options.value, tmp, options.notify_read_properties);
}
} }
}); });

Loading…
Cancel
Save