From ab837f24b2f6a18ba38bba69c338d0cd11706825 Mon Sep 17 00:00:00 2001 From: godzylinux Date: Fri, 24 May 2024 21:56:57 +0330 Subject: [PATCH] used the new utility to create a fine-grained ReactiveDate --- packages/svelte/src/reactivity/date.js | 339 ++++++++++++++++--------- 1 file changed, 214 insertions(+), 125 deletions(-) diff --git a/packages/svelte/src/reactivity/date.js b/packages/svelte/src/reactivity/date.js index cb4ce79b96..3283ae71a8 100644 --- a/packages/svelte/src/reactivity/date.js +++ b/packages/svelte/src/reactivity/date.js @@ -1,145 +1,234 @@ 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["notify_read_properties"]} notify_read_properties + * @return {boolean} - returns true if any changes happened */ -const write_properties = /** @type {const} */ ([ - 'setDate', - 'setFullYear', - 'setHours', - 'setMilliseconds', - 'setMinutes', - 'setMonth', - 'setSeconds', - 'setTime', - 'setUTCDate', - 'setUTCFullYear', - 'setUTCHours', - 'setUTCMilliseconds', - 'setUTCMinutes', - 'setUTCMonth', - 'setUTCSeconds', - // @ts-expect-error this is deprecated - 'setYear' -]); +const notify_datetime_changes = (current_datetime, new_datetime, notify_read_properties) => { + let had_time_changes = false; + let had_date_changes = false; -/** - * @type {(keyof Date)[]} - */ -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' -]); + if (current_datetime.getFullYear() !== new_datetime.getFullYear()) { + notify_read_properties(['getFullYear', 'getUTCFullYear']); + had_date_changes = true; + } -/** - * @type {Record<(typeof write_properties)[number], (typeof read_properties)[number][]>} - */ -const affected_changes = { - setDate: ['getDate', 'getUTCDate', 'toDateString', 'toLocaleDateString'], - setFullYear: [ - 'getFullYear', - 'getUTCFullYear', - 'getDate', - 'getUTCDate', - 'toDateString', - 'toLocaleDateString' - ], - setHours: ['getHours', 'getUTCHours', 'toTimeString', 'toLocaleTimeString'], - setMilliseconds: ['getMilliseconds', 'getUTCMilliseconds', 'toTimeString', 'toLocaleTimeString'], - setMinutes: [ - 'getMinutes', - 'getUTCMinutes', - 'getHours', - 'getUTCHours', - 'toTimeString', - 'toLocaleTimeString' + // @ts-expect-error + if (current_datetime.getYear && current_datetime.getYear() !== new_datetime.getYear()) { + // @ts-expect-error + notify_read_properties(['getYear']); + had_date_changes = true; + } + + if (current_datetime.getMonth() !== new_datetime.getMonth()) { + notify_read_properties(['getMonth', 'getUTCMonth']); + had_date_changes = true; + } + + if (current_datetime.getDay() !== new_datetime.getDay()) { + notify_read_properties(['getDay', 'getUTCDay']); + had_date_changes = true; + } + + if (current_datetime.getHours() !== new_datetime.getHours()) { + notify_read_properties(['getHours', 'getUTCHours']); + had_time_changes = true; + } + + 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: [ - 'getMonth', - 'getUTCMonth', + read_properties: [ 'getDate', - 'getUTCDate', - 'toDateString', - 'toLocaleDateString' - ], - setSeconds: ['getSeconds', 'getUTCSeconds', 'toTimeString', 'toLocaleTimeString'], - setTime: [ - 'toDateString', - 'toTimeString', - 'toLocaleDateString', - 'toLocaleTimeString', + 'getDay', 'getFullYear', - 'getMonth', - 'getDate', 'getHours', + 'getMilliseconds', 'getMinutes', + 'getMonth', 'getSeconds', - 'getMilliseconds', - 'getUTCFullYear', - 'getUTCMonth', + 'getTimezoneOffset', 'getUTCDate', + 'getUTCDay', + 'getUTCFullYear', 'getUTCHours', + 'getUTCMilliseconds', 'getUTCMinutes', + 'getUTCMonth', '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, 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: { - ...write_properties.map((write_property) => { - return /** @type {import("./utils.js").Interceptors, typeof write_properties, typeof read_properties>} */ ({ - /** - * @param {import("./utils.js").InterceptorOptions, typeof write_properties, typeof read_properties>} options - * @param {unknown[]} params - **/ - [write_property]: (options, ...params) => { - if (options.value[write_property]() === params[0]) { - return false; - } - affected_changes[write_property].forEach((affected) => { - options.notify_read_properties([affected]); - }); - return true; - } - }); - }) + setDate: (options, ...params) => { + const tmp = new Date(options.value); + tmp.setDate(/**@type {number}*/ (params[0])); + return notify_datetime_changes(options.value, tmp, options.notify_read_properties); + }, + setFullYear: (options, ...params) => { + const tmp = new Date(options.value); + tmp.setFullYear( + /**@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); + }, + 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); + } } });