feat: add reactive Date object to svelte/reactivity (#10622)

* feat: add reactive Date object to svelte/reactivity

* add type safety, fix revealed typos

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/10621/head
Dominic Gannaway 10 months ago committed by GitHub
parent b4a70ea38a
commit f658ca0d54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
feat: add reactive Date object to svelte/reactivity

@ -62,6 +62,10 @@
"types": "./types/index.d.ts",
"default": "./src/motion/index.js"
},
"./reactivity": {
"types": "./types/index.d.ts",
"default": "./src/reactivity/index.js"
},
"./server": {
"types": "./types/index.d.ts",
"default": "./src/server/index.js"

@ -0,0 +1,103 @@
import { source } from '../internal/client/reactivity/sources';
import { get, set } from '../internal/client/runtime';
/** @type {Array<keyof Date>} */
const read = [
'getDate',
'getDay',
'getFullYear',
'getHours',
'getMilliseconds',
'getMinutes',
'getMonth',
'getSeconds',
'getTime',
'getTimezoneOffset',
'getUTCDate',
'getUTCDay',
'getUTCFullYear',
'getUTCHours',
'getUTCMilliseconds',
'getUTCMinutes',
'getUTCMonth',
'getUTCSeconds',
// @ts-expect-error this is deprecated
'getYear',
'toDateString',
'toISOString',
'toJSON',
'toLocaleDateString',
'toLocaleString',
'toLocaleTimeString',
'toString',
'toTimeString',
'toUTCString'
];
/** @type {Array<keyof Date>} */
const write = [
'setDate',
'setFullYear',
'setHours',
'setMilliseconds',
'setMinutes',
'setMonth',
'setSeconds',
'setTime',
'setUTCDate',
'setUTCFullYear',
'setUTCHours',
'setUTCMilliseconds',
'setUTCMinutes',
'setUTCMonth',
'setUTCSeconds',
// @ts-expect-error this is deprecated
'setYear'
];
class ReactiveDate extends Date {
#raw_time = source(super.getTime());
static #inited = false;
// We init as part of the first instance so that we can treeshake this class
#init() {
if (!ReactiveDate.#inited) {
ReactiveDate.#inited = true;
const proto = ReactiveDate.prototype;
const date_proto = Date.prototype;
for (const method of read) {
// @ts-ignore
proto[method] = function () {
get(this.#raw_time);
// @ts-ignore
return date_proto[method].call(this);
};
}
for (const method of write) {
// @ts-ignore
proto[method] = function (/** @type {any} */ ...args) {
// @ts-ignore
const v = date_proto[method].apply(this, args);
const time = date_proto.getTime.call(this);
if (time !== this.#raw_time.v) {
set(this.#raw_time, time);
}
return v;
};
}
}
}
/**
* @param {any[]} values
*/
constructor(...values) {
// @ts-ignore
super(...values);
this.#init();
}
}
export { ReactiveDate as Date };

@ -0,0 +1,37 @@
import { flushSync } from '../../../../src/main/main-client';
import { test } from '../../test';
export default test({
html: `<div>getSeconds: 0</div><div>getMinutes: 0</div><div>getHours: 15</div><div>getTime: 1708700400000</div><div>toDateString: Fri Feb 23 2024</div><button>1 second</button><button>1 minute</button><button>1 hour</button>`,
test({ assert, target }) {
const [btn, btn2, btn3] = target.querySelectorAll('button');
flushSync(() => {
btn?.click();
});
assert.htmlEqual(
target.innerHTML,
`<div>getSeconds: 1</div><div>getMinutes: 0</div><div>getHours: 15</div><div>getTime: 1708700401000</div><div>toDateString: Fri Feb 23 2024</div><button>1 second</button><button>1 minute</button><button>1 hour</button>`
);
flushSync(() => {
btn2?.click();
});
assert.htmlEqual(
target.innerHTML,
`<div>getSeconds: 1</div><div>getMinutes: 1</div><div>getHours: 15</div><div>getTime: 1708700461000</div><div>toDateString: Fri Feb 23 2024</div><button>1 second</button><button>1 minute</button><button>1 hour</button>`
);
flushSync(() => {
btn3?.click();
});
assert.htmlEqual(
target.innerHTML,
`<div>getSeconds: 1</div><div>getMinutes: 1</div><div>getHours: 16</div><div>getTime: 1708704061000</div><div>toDateString: Fri Feb 23 2024</div><button>1 second</button><button>1 minute</button><button>1 hour</button>`
);
}
});

@ -0,0 +1,21 @@
<script>
import { Date } from 'svelte/reactivity';
let date = new Date('2024/02/23 15:00:00');
</script>
<div>getSeconds: {date.getSeconds()}</div>
<div>getMinutes: {date.getMinutes()}</div>
<div>getHours: {date.getHours()}</div>
<div>getTime: {date.getTime()}</div>
<div>toDateString: {date.toDateString()}</div>
<button onclick={() => {
date.setSeconds(date.getSeconds() + 1);
}}>1 second</button>
<button onclick={() => {
date.setMinutes(date.getMinutes() + 1);
}}>1 minute</button>
<button onclick={() => {
date.setHours(date.getHours() + 1);
}}>1 hour</button>
Loading…
Cancel
Save