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