Skip to content

Commit f658ca0

Browse files
trueadmRich-Harris
andauthored
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 <[email protected]>
1 parent b4a70ea commit f658ca0

File tree

5 files changed

+170
-0
lines changed

5 files changed

+170
-0
lines changed

.changeset/new-rabbits-flow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
feat: add reactive Date object to svelte/reactivity

packages/svelte/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
"types": "./types/index.d.ts",
6363
"default": "./src/motion/index.js"
6464
},
65+
"./reactivity": {
66+
"types": "./types/index.d.ts",
67+
"default": "./src/reactivity/index.js"
68+
},
6569
"./server": {
6670
"types": "./types/index.d.ts",
6771
"default": "./src/server/index.js"
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { source } from '../internal/client/reactivity/sources';
2+
import { get, set } from '../internal/client/runtime';
3+
4+
/** @type {Array<keyof Date>} */
5+
const read = [
6+
'getDate',
7+
'getDay',
8+
'getFullYear',
9+
'getHours',
10+
'getMilliseconds',
11+
'getMinutes',
12+
'getMonth',
13+
'getSeconds',
14+
'getTime',
15+
'getTimezoneOffset',
16+
'getUTCDate',
17+
'getUTCDay',
18+
'getUTCFullYear',
19+
'getUTCHours',
20+
'getUTCMilliseconds',
21+
'getUTCMinutes',
22+
'getUTCMonth',
23+
'getUTCSeconds',
24+
// @ts-expect-error this is deprecated
25+
'getYear',
26+
'toDateString',
27+
'toISOString',
28+
'toJSON',
29+
'toLocaleDateString',
30+
'toLocaleString',
31+
'toLocaleTimeString',
32+
'toString',
33+
'toTimeString',
34+
'toUTCString'
35+
];
36+
37+
/** @type {Array<keyof Date>} */
38+
const write = [
39+
'setDate',
40+
'setFullYear',
41+
'setHours',
42+
'setMilliseconds',
43+
'setMinutes',
44+
'setMonth',
45+
'setSeconds',
46+
'setTime',
47+
'setUTCDate',
48+
'setUTCFullYear',
49+
'setUTCHours',
50+
'setUTCMilliseconds',
51+
'setUTCMinutes',
52+
'setUTCMonth',
53+
'setUTCSeconds',
54+
// @ts-expect-error this is deprecated
55+
'setYear'
56+
];
57+
58+
class ReactiveDate extends Date {
59+
#raw_time = source(super.getTime());
60+
static #inited = false;
61+
62+
// We init as part of the first instance so that we can treeshake this class
63+
#init() {
64+
if (!ReactiveDate.#inited) {
65+
ReactiveDate.#inited = true;
66+
const proto = ReactiveDate.prototype;
67+
const date_proto = Date.prototype;
68+
69+
for (const method of read) {
70+
// @ts-ignore
71+
proto[method] = function () {
72+
get(this.#raw_time);
73+
// @ts-ignore
74+
return date_proto[method].call(this);
75+
};
76+
}
77+
78+
for (const method of write) {
79+
// @ts-ignore
80+
proto[method] = function (/** @type {any} */ ...args) {
81+
// @ts-ignore
82+
const v = date_proto[method].apply(this, args);
83+
const time = date_proto.getTime.call(this);
84+
if (time !== this.#raw_time.v) {
85+
set(this.#raw_time, time);
86+
}
87+
return v;
88+
};
89+
}
90+
}
91+
}
92+
93+
/**
94+
* @param {any[]} values
95+
*/
96+
constructor(...values) {
97+
// @ts-ignore
98+
super(...values);
99+
this.#init();
100+
}
101+
}
102+
103+
export { ReactiveDate as Date };
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { flushSync } from '../../../../src/main/main-client';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
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>`,
6+
7+
test({ assert, target }) {
8+
const [btn, btn2, btn3] = target.querySelectorAll('button');
9+
10+
flushSync(() => {
11+
btn?.click();
12+
});
13+
14+
assert.htmlEqual(
15+
target.innerHTML,
16+
`<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>`
17+
);
18+
19+
flushSync(() => {
20+
btn2?.click();
21+
});
22+
23+
assert.htmlEqual(
24+
target.innerHTML,
25+
`<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>`
26+
);
27+
28+
flushSync(() => {
29+
btn3?.click();
30+
});
31+
32+
assert.htmlEqual(
33+
target.innerHTML,
34+
`<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>`
35+
);
36+
}
37+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
import { Date } from 'svelte/reactivity';
3+
4+
let date = new Date('2024/02/23 15:00:00');
5+
</script>
6+
7+
<div>getSeconds: {date.getSeconds()}</div>
8+
<div>getMinutes: {date.getMinutes()}</div>
9+
<div>getHours: {date.getHours()}</div>
10+
<div>getTime: {date.getTime()}</div>
11+
<div>toDateString: {date.toDateString()}</div>
12+
13+
<button onclick={() => {
14+
date.setSeconds(date.getSeconds() + 1);
15+
}}>1 second</button>
16+
<button onclick={() => {
17+
date.setMinutes(date.getMinutes() + 1);
18+
}}>1 minute</button>
19+
<button onclick={() => {
20+
date.setHours(date.getHours() + 1);
21+
}}>1 hour</button>

0 commit comments

Comments
 (0)