mirror of https://github.com/sveltejs/svelte
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
parent
b4a70ea38a
commit
f658ca0d54
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
feat: add reactive Date object to svelte/reactivity
|
@ -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…
Reference in new issue