@reatom/timer
Advanced tools
Comparing version 3.3.0 to 3.4.0
import { Action, AtomMut } from '@reatom/core'; | ||
export interface TimerAtom extends AtomMut<number> { | ||
/** (delay - remains) / delay */ | ||
progressAtom: AtomMut<number>; | ||
/** interval in ms */ | ||
@@ -8,9 +10,9 @@ intervalAtom: AtomMut<number> & { | ||
/** start timer by passed interval */ | ||
startTimer: Action<[delayInSeconds: number], Promise<void>>; | ||
startTimer: Action<[delay: number], Promise<void>>; | ||
/** allow to pause timer */ | ||
pauseAtom: AtomMut<boolean>; | ||
/** stop timer manually */ | ||
stopTimer: Action<[], void>; | ||
/** track end of timer, do not call manually */ | ||
/** track end of timer. Do not call manually! */ | ||
endTimer: Action<[], void>; | ||
/** track every interval tick, do not call manually */ | ||
tick: Action<[remains: number], number>; | ||
} | ||
@@ -20,2 +22,4 @@ export declare const reatomTimer: (options?: string | { | ||
interval?: number; | ||
delayMultiplier?: number; | ||
progressPrecision?: number; | ||
}) => TimerAtom; |
@@ -1,2 +0,2 @@ | ||
var t=require("@reatom/core");function n(t,r,i){if(!t.s){if(i instanceof e){if(!i.s)return void(i.o=n.bind(null,t,r));1&r&&(r=i.s),i=i.v}if(i&&i.then)return void i.then(n.bind(null,t,r),n.bind(null,t,2));t.s=r,t.v=i;const o=t.o;o&&o(t)}}const e=/*#__PURE__*/function(){function t(){}return t.prototype.then=function(e,r){const i=new t,o=this.s;if(o){const t=1&o?e:r;if(t){try{n(i,1,t(this.v))}catch(t){n(i,2,t)}return i}return this}return this.o=function(t){try{const o=t.v;1&t.s?n(i,1,e?e(o):o):r?n(i,1,r(o)):n(i,2,o)}catch(t){n(i,2,t)}},i},t}();function r(t){return t instanceof e&&1&t.s}exports.reatomTimer=(i={})=>{const{name:o=t.__count("timerAtom"),interval:c=1e3}="string"==typeof i?{name:i}:i,s=t.atom(0,`${o}Atom`),u=t.atom(c,`${o}.intervalAtom`).pipe((a={setSeconds:(t,n)=>1e3*n},n=>Object.keys(a).reduce((n,e)=>(n[e]=t.action(function(t){return n(t,a[e](t.get(n),...[].slice.call(arguments,1)))},`${n.__reatom.name}._${e}`),n),n)));var a;const h=t.atom(0,`${o}._versionAtom`),f=t.action((t,n)=>n,`${o}.tick`),v=t.action((t,i)=>{const o=h(t,t=>t+1),c=1e3*i,a=c+Date.now();let v=c;return s(t,v),t.schedule(function(){try{let c;function i(n){if(c)return n;d(t)}const m=function(t,i,o){for(var c;;){var s=t();if(r(s)&&(s=s.v),!s)return u;if(s.then){c=0;break}var u=o();if(u&&u.then){if(!r(u)){c=1;break}u=u.s}}var a=new e,h=n.bind(null,a,2);return(0===c?s.then(v):1===c?u.then(f):(void 0).then(function(){(s=t())?s.then?s.then(v).then(void 0,h):v(s):n(a,1,u)})).then(void 0,h),a;function f(e){u=e;do{if(!(s=t())||r(s)&&!s.v)return void n(a,1,u);if(s.then)return void s.then(v).then(void 0,h);r(u=o())&&(u=u.v)}while(!u||!u.then);u.then(f).then(void 0,h)}function v(t){t?(u=o())&&u.then?u.then(f).then(void 0,h):f(u):n(a,1,u)}}(function(){return!c&&v>0},0,function(){return Promise.resolve(((t=0)=>new Promise(n=>setTimeout(n,t)))(Math.min(v,t.get(u)))).then(function(){o===t.get(h)?(v=s(t,a-Date.now()),f(t,v)):c=1})});return Promise.resolve(m&&m.then?m.then(i):i(m))}catch(l){return Promise.reject(l)}})},`${o}.startTimer`),m=t.action(t=>{h(t,t=>t+1),d(t)},`${o}.stopTimer`),d=t.action(t=>{s(t,0)},`${o}.endTimer`);return Object.assign(s,{endTimer:d,intervalAtom:u,startTimer:v,stopTimer:m,tick:f})}; | ||
var t=require("@reatom/core"),e=require("@reatom/primitives"),n=require("@reatom/hooks");const o=()=>{};function r(t,e,n){if(!t.s){if(n instanceof i){if(!n.s)return void(n.o=r.bind(null,t,e));1&e&&(e=n.s),n=n.v}if(n&&n.then)return void n.then(r.bind(null,t,e),r.bind(null,t,2));t.s=e,t.v=n;const o=t.o;o&&o(t)}}const i=/*#__PURE__*/function(){function t(){}return t.prototype.then=function(e,n){const o=new t,i=this.s;if(i){const t=1&i?e:n;if(t){try{r(o,1,t(this.v))}catch(t){r(o,2,t)}return o}return this}return this.o=function(t){try{const i=t.v;1&t.s?r(o,1,e?e(i):i):n?r(o,1,n(i)):r(o,2,i)}catch(t){r(o,2,t)}},o},t}();function s(t){return t instanceof i&&1&t.s}exports.reatomTimer=(u={})=>{const{name:c=t.__count("timerAtom"),interval:a=1e3,delayMultiplier:h=1e3,progressPrecision:m=2}="string"==typeof u?{name:u}:u,f=Math.pow(10,m),v=t.atom(0,`${c}Atom`),l=t.atom(0,`${c}.progressAtom`),d=t.atom(!1,`${c}.pauseAtom`),p=t.atom(a,`${c}.intervalAtom`).pipe(e.withReducers({setSeconds:(t,e)=>1e3*e})),w=t.atom(0,`${c}._versionAtom`),g=t.action((t,e)=>{e*=h;const u=w(t,t=>t+1),c=Date.now(),a=e+c;let m=e,g=Promise.resolve(),A=o;v(t,m),l(t,0),d(t,!1);const P=n.onUpdate(d,(t,e)=>t.schedule(()=>{if(e){const t=a-Date.now();g=new Promise(e=>{A=()=>{m=t,e()}})}else A()}));return t.schedule(function(){try{let o;function n(e){if(o)return e;b(t)}const c=function(t,e,n){for(var o;;){var u=t();if(s(u)&&(u=u.v),!u)return c;if(u.then){o=0;break}var c=n();if(c&&c.then){if(!s(c)){o=1;break}c=c.s}}var a=new i,h=r.bind(null,a,2);return(0===o?u.then(f):1===o?c.then(m):(void 0).then(function(){(u=t())?u.then?u.then(f).then(void 0,h):f(u):r(a,1,c)})).then(void 0,h),a;function m(e){c=e;do{if(!(u=t())||s(u)&&!u.v)return void r(a,1,c);if(u.then)return void u.then(f).then(void 0,h);s(c=n())&&(c=c.v)}while(!c||!c.then);c.then(m).then(void 0,h)}function f(t){t?(c=n())&&c.then?c.then(m).then(void 0,h):m(c):r(a,1,c)}}(function(){return!o&&m>0},0,function(){return Promise.resolve(((t=0)=>new Promise(e=>setTimeout(e,t)))(Math.min(m,t.get(p)))).then(function(){return Promise.resolve(g).then(function(){u===t.get(w)?t.get.bind(t)(()=>{const n=t.get(p);m=v(t,a-Date.now());const o=(e-m)/e;l(t,Math.round((o-o%(n/e))*f)/f)}):o=1})})});return Promise.resolve(c&&c.then?c.then(n):n(c))}catch(h){return Promise.reject(h)}}).finally(P)},`${c}.startTimer`),A=t.action(t=>{w(t,t=>t+1),b(t)},`${c}.stopTimer`),b=t.action(t=>{v(t,0)},`${c}.endTimer`);return Object.assign(v,{progressAtom:l,endTimer:b,intervalAtom:p,startTimer:g,stopTimer:A,pauseAtom:d})}; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import{action as t,__count as n,atom as e}from"@reatom/core";function r(t,n,e){if(!t.s){if(e instanceof i){if(!e.s)return void(e.o=r.bind(null,t,n));1&n&&(n=e.s),e=e.v}if(e&&e.then)return void e.then(r.bind(null,t,n),r.bind(null,t,2));t.s=n,t.v=e;const o=t.o;o&&o(t)}}const i=/*#__PURE__*/function(){function t(){}return t.prototype.then=function(n,e){const i=new t,o=this.s;if(o){const t=1&o?n:e;if(t){try{r(i,1,t(this.v))}catch(t){r(i,2,t)}return i}return this}return this.o=function(t){try{const o=t.v;1&t.s?r(i,1,n?n(o):o):e?r(i,1,e(o)):r(i,2,o)}catch(t){r(i,2,t)}},i},t}();function o(t){return t instanceof i&&1&t.s}const c=(c={})=>{const{name:s=n("timerAtom"),interval:u=1e3}="string"==typeof c?{name:c}:c,f=e(0,`${s}Atom`),h=e(u,`${s}.intervalAtom`).pipe((v={setSeconds:(t,n)=>1e3*n},n=>Object.keys(v).reduce((n,e)=>(n[e]=t(function(t){return n(t,v[e](t.get(n),...[].slice.call(arguments,1)))},`${n.__reatom.name}._${e}`),n),n)));var v;const a=e(0,`${s}._versionAtom`),m=t((t,n)=>n,`${s}.tick`),d=t((t,n)=>{const e=a(t,t=>t+1),c=1e3*n,s=c+Date.now();let u=c;return f(t,u),t.schedule(function(){try{let c;function n(n){if(c)return n;p(t)}const v=function(t,n,e){for(var c;;){var s=t();if(o(s)&&(s=s.v),!s)return u;if(s.then){c=0;break}var u=e();if(u&&u.then){if(!o(u)){c=1;break}u=u.s}}var f=new i,h=r.bind(null,f,2);return(0===c?s.then(a):1===c?u.then(v):(void 0).then(function(){(s=t())?s.then?s.then(a).then(void 0,h):a(s):r(f,1,u)})).then(void 0,h),f;function v(n){u=n;do{if(!(s=t())||o(s)&&!s.v)return void r(f,1,u);if(s.then)return void s.then(a).then(void 0,h);o(u=e())&&(u=u.v)}while(!u||!u.then);u.then(v).then(void 0,h)}function a(t){t?(u=e())&&u.then?u.then(v).then(void 0,h):v(u):r(f,1,u)}}(function(){return!c&&u>0},0,function(){return Promise.resolve(((t=0)=>new Promise(n=>setTimeout(n,t)))(Math.min(u,t.get(h)))).then(function(){e===t.get(a)?(u=f(t,s-Date.now()),m(t,u)):c=1})});return Promise.resolve(v&&v.then?v.then(n):n(v))}catch(d){return Promise.reject(d)}})},`${s}.startTimer`),l=t(t=>{a(t,t=>t+1),p(t)},`${s}.stopTimer`),p=t(t=>{f(t,0)},`${s}.endTimer`);return Object.assign(f,{endTimer:p,intervalAtom:h,startTimer:d,stopTimer:l,tick:m})};export{c as reatomTimer}; | ||
import{__count as t,atom as e,action as n}from"@reatom/core";import{withReducers as o}from"@reatom/primitives";import{onUpdate as r}from"@reatom/hooks";const i=()=>{};function s(t,e,n){if(!t.s){if(n instanceof u){if(!n.s)return void(n.o=s.bind(null,t,e));1&e&&(e=n.s),n=n.v}if(n&&n.then)return void n.then(s.bind(null,t,e),s.bind(null,t,2));t.s=e,t.v=n;const o=t.o;o&&o(t)}}const u=/*#__PURE__*/function(){function t(){}return t.prototype.then=function(e,n){const o=new t,r=this.s;if(r){const t=1&r?e:n;if(t){try{s(o,1,t(this.v))}catch(t){s(o,2,t)}return o}return this}return this.o=function(t){try{const r=t.v;1&t.s?s(o,1,e?e(r):r):n?s(o,1,n(r)):s(o,2,r)}catch(t){s(o,2,t)}},o},t}();function c(t){return t instanceof u&&1&t.s}const h=(h={})=>{const{name:f=t("timerAtom"),interval:m=1e3,delayMultiplier:a=1e3,progressPrecision:v=2}="string"==typeof h?{name:h}:h,l=Math.pow(10,v),d=e(0,`${f}Atom`),p=e(0,`${f}.progressAtom`),g=e(!1,`${f}.pauseAtom`),w=e(m,`${f}.intervalAtom`).pipe(o({setSeconds:(t,e)=>1e3*e})),A=e(0,`${f}._versionAtom`),b=n((t,e)=>{e*=a;const n=A(t,t=>t+1),o=Date.now(),h=e+o;let f=e,m=Promise.resolve(),v=i;d(t,f),p(t,0),g(t,!1);const b=r(g,(t,e)=>t.schedule(()=>{if(e){const t=h-Date.now();m=new Promise(e=>{v=()=>{f=t,e()}})}else v()}));return t.schedule(function(){try{let r;function o(e){if(r)return e;$(t)}const i=function(t,e,n){for(var o;;){var r=t();if(c(r)&&(r=r.v),!r)return i;if(r.then){o=0;break}var i=n();if(i&&i.then){if(!c(i)){o=1;break}i=i.s}}var h=new u,f=s.bind(null,h,2);return(0===o?r.then(a):1===o?i.then(m):(void 0).then(function(){(r=t())?r.then?r.then(a).then(void 0,f):a(r):s(h,1,i)})).then(void 0,f),h;function m(e){i=e;do{if(!(r=t())||c(r)&&!r.v)return void s(h,1,i);if(r.then)return void r.then(a).then(void 0,f);c(i=n())&&(i=i.v)}while(!i||!i.then);i.then(m).then(void 0,f)}function a(t){t?(i=n())&&i.then?i.then(m).then(void 0,f):m(i):s(h,1,i)}}(function(){return!r&&f>0},0,function(){return Promise.resolve(((t=0)=>new Promise(e=>setTimeout(e,t)))(Math.min(f,t.get(w)))).then(function(){return Promise.resolve(m).then(function(){n===t.get(A)?t.get.bind(t)(()=>{const n=t.get(w);f=d(t,h-Date.now());const o=(e-f)/e;p(t,Math.round((o-o%(n/e))*l)/l)}):r=1})})});return Promise.resolve(i&&i.then?i.then(o):o(i))}catch(a){return Promise.reject(a)}}).finally(b)},`${f}.startTimer`),P=n(t=>{A(t,t=>t+1),$(t)},`${f}.stopTimer`),$=n(t=>{d(t,0)},`${f}.endTimer`);return Object.assign(d,{progressAtom:p,endTimer:$,intervalAtom:w,startTimer:b,stopTimer:P,pauseAtom:g})};export{h as reatomTimer}; | ||
//# sourceMappingURL=index.module.js.map |
@@ -1,2 +0,2 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@reatom/core")):"function"==typeof define&&define.amd?define(["exports","@reatom/core"],e):e((t||self).timer={},t.core)}(this,function(t,e){function n(t,e,i){if(!t.s){if(i instanceof o){if(!i.s)return void(i.o=n.bind(null,t,e));1&e&&(e=i.s),i=i.v}if(i&&i.then)return void i.then(n.bind(null,t,e),n.bind(null,t,2));t.s=e,t.v=i;const r=t.o;r&&r(t)}}const o=/*#__PURE__*/function(){function t(){}return t.prototype.then=function(e,o){const i=new t,r=this.s;if(r){const t=1&r?e:o;if(t){try{n(i,1,t(this.v))}catch(t){n(i,2,t)}return i}return this}return this.o=function(t){try{const r=t.v;1&t.s?n(i,1,e?e(r):r):o?n(i,1,o(r)):n(i,2,r)}catch(t){n(i,2,t)}},i},t}();function i(t){return t instanceof o&&1&t.s}t.reatomTimer=(t={})=>{const{name:r=e.__count("timerAtom"),interval:c=1e3}="string"==typeof t?{name:t}:t,s=e.atom(0,`${r}Atom`),u=e.atom(c,`${r}.intervalAtom`).pipe((f={setSeconds:(t,e)=>1e3*e},t=>Object.keys(f).reduce((t,n)=>(t[n]=e.action(function(e){return t(e,f[n](e.get(t),...[].slice.call(arguments,1)))},`${t.__reatom.name}._${n}`),t),t)));var f;const a=e.atom(0,`${r}._versionAtom`),h=e.action((t,e)=>e,`${r}.tick`),m=e.action((t,e)=>{const r=a(t,t=>t+1),c=1e3*e,f=c+Date.now();let m=c;return s(t,m),t.schedule(function(){try{let c;function e(e){if(c)return e;v(t)}const d=function(t,e,r){for(var c;;){var s=t();if(i(s)&&(s=s.v),!s)return u;if(s.then){c=0;break}var u=r();if(u&&u.then){if(!i(u)){c=1;break}u=u.s}}var f=new o,a=n.bind(null,f,2);return(0===c?s.then(m):1===c?u.then(h):(void 0).then(function(){(s=t())?s.then?s.then(m).then(void 0,a):m(s):n(f,1,u)})).then(void 0,a),f;function h(e){u=e;do{if(!(s=t())||i(s)&&!s.v)return void n(f,1,u);if(s.then)return void s.then(m).then(void 0,a);i(u=r())&&(u=u.v)}while(!u||!u.then);u.then(h).then(void 0,a)}function m(t){t?(u=r())&&u.then?u.then(h).then(void 0,a):h(u):n(f,1,u)}}(function(){return!c&&m>0},0,function(){return Promise.resolve(((t=0)=>new Promise(e=>setTimeout(e,t)))(Math.min(m,t.get(u)))).then(function(){r===t.get(a)?(m=s(t,f-Date.now()),h(t,m)):c=1})});return Promise.resolve(d&&d.then?d.then(e):e(d))}catch(l){return Promise.reject(l)}})},`${r}.startTimer`),d=e.action(t=>{a(t,t=>t+1),v(t)},`${r}.stopTimer`),v=e.action(t=>{s(t,0)},`${r}.endTimer`);return Object.assign(s,{endTimer:v,intervalAtom:u,startTimer:m,stopTimer:d,tick:h})}}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@reatom/core"),require("@reatom/primitives"),require("@reatom/hooks")):"function"==typeof define&&define.amd?define(["exports","@reatom/core","@reatom/primitives","@reatom/hooks"],t):t((e||self).timer={},e.core,e.primitives,e.hooks)}(this,function(e,t,n,o){const i=()=>{};function r(e,t,n){if(!e.s){if(n instanceof s){if(!n.s)return void(n.o=r.bind(null,e,t));1&t&&(t=n.s),n=n.v}if(n&&n.then)return void n.then(r.bind(null,e,t),r.bind(null,e,2));e.s=t,e.v=n;const o=e.o;o&&o(e)}}const s=/*#__PURE__*/function(){function e(){}return e.prototype.then=function(t,n){const o=new e,i=this.s;if(i){const e=1&i?t:n;if(e){try{r(o,1,e(this.v))}catch(e){r(o,2,e)}return o}return this}return this.o=function(e){try{const i=e.v;1&e.s?r(o,1,t?t(i):i):n?r(o,1,n(i)):r(o,2,i)}catch(e){r(o,2,e)}},o},e}();function u(e){return e instanceof s&&1&e.s}e.reatomTimer=(e={})=>{const{name:c=t.__count("timerAtom"),interval:a=1e3,delayMultiplier:f=1e3,progressPrecision:m=2}="string"==typeof e?{name:e}:e,h=Math.pow(10,m),d=t.atom(0,`${c}Atom`),l=t.atom(0,`${c}.progressAtom`),v=t.atom(!1,`${c}.pauseAtom`),p=t.atom(a,`${c}.intervalAtom`).pipe(n.withReducers({setSeconds:(e,t)=>1e3*t})),b=t.atom(0,`${c}._versionAtom`),g=t.action((e,t)=>{t*=f;const n=b(e,e=>e+1),c=Date.now(),a=t+c;let m=t,g=Promise.resolve(),y=i;d(e,m),l(e,0),v(e,!1);const T=o.onUpdate(v,(e,t)=>e.schedule(()=>{if(t){const e=a-Date.now();g=new Promise(t=>{y=()=>{m=e,t()}})}else y()}));return e.schedule(function(){try{let i;function o(t){if(i)return t;w(e)}const c=function(e,t,n){for(var o;;){var i=e();if(u(i)&&(i=i.v),!i)return c;if(i.then){o=0;break}var c=n();if(c&&c.then){if(!u(c)){o=1;break}c=c.s}}var a=new s,f=r.bind(null,a,2);return(0===o?i.then(h):1===o?c.then(m):(void 0).then(function(){(i=e())?i.then?i.then(h).then(void 0,f):h(i):r(a,1,c)})).then(void 0,f),a;function m(t){c=t;do{if(!(i=e())||u(i)&&!i.v)return void r(a,1,c);if(i.then)return void i.then(h).then(void 0,f);u(c=n())&&(c=c.v)}while(!c||!c.then);c.then(m).then(void 0,f)}function h(e){e?(c=n())&&c.then?c.then(m).then(void 0,f):m(c):r(a,1,c)}}(function(){return!i&&m>0},0,function(){return Promise.resolve(((e=0)=>new Promise(t=>setTimeout(t,e)))(Math.min(m,e.get(p)))).then(function(){return Promise.resolve(g).then(function(){n===e.get(b)?e.get.bind(e)(()=>{const n=e.get(p);m=d(e,a-Date.now());const o=(t-m)/t;l(e,Math.round((o-o%(n/t))*h)/h)}):i=1})})});return Promise.resolve(c&&c.then?c.then(o):o(c))}catch(f){return Promise.reject(f)}}).finally(T)},`${c}.startTimer`),y=t.action(e=>{b(e,e=>e+1),w(e)},`${c}.stopTimer`),w=t.action(e=>{d(e,0)},`${c}.endTimer`);return Object.assign(d,{progressAtom:l,endTimer:w,intervalAtom:p,startTimer:g,stopTimer:y,pauseAtom:v})}}); | ||
//# sourceMappingURL=index.umd.js.map |
{ | ||
"name": "@reatom/timer", | ||
"version": "3.3.0", | ||
"version": "3.4.0", | ||
"private": false, | ||
@@ -26,3 +26,5 @@ "sideEffects": false, | ||
"dependencies": { | ||
"@reatom/core": ">=3.1.23" | ||
"@reatom/core": "^3.1.23", | ||
"@reatom/hooks": "^3.1.2", | ||
"@reatom/primitives": "^3.1.3" | ||
}, | ||
@@ -29,0 +31,0 @@ "author": "artalar", |
@@ -6,7 +6,17 @@ Simple timer model to manage some countdown. | ||
const timer = reatomTimer({ interval: 1000 }) | ||
// all options are not required | ||
const pomodoroAtom = reatomTimer({ | ||
name: 'pomodoroAtom', | ||
interval: 1000, // tick each second | ||
delayMultiplier: 1000, // allow to pass seconds to startTimer | ||
progressPrecision: 2, // progress will be rounded to 2 digits after dot | ||
}) | ||
``` | ||
Example: https://stackblitz.com/edit/reatom-timer-pomodoro?file=src%2FApp.tsx | ||
```ts | ||
export interface TimerAtom extends AtomMut<number> { | ||
/** (delay - remains) / delay */ | ||
progressAtom: AtomMut<number> | ||
/** interval in ms */ | ||
@@ -17,10 +27,10 @@ intervalAtom: AtomMut<number> & { | ||
/** start timer by passed interval */ | ||
startTimer: Action<[delayInSeconds: number], Promise<void>> | ||
startTimer: Action<[delay: number], Promise<void>> | ||
/** allow to pause timer */ | ||
pauseAtom: AtomMut<boolean> | ||
/** stop timer manually */ | ||
stopTimer: Action<[], void> | ||
/** track end of timer, do not call manually */ | ||
/** track end of timer. Do not call manually! */ | ||
endTimer: Action<[], void> | ||
/** track every interval tick, do not call manually */ | ||
tick: Action<[remains: number], number> | ||
} | ||
``` |
import { test } from 'uvu' | ||
import * as assert from 'uvu/assert' | ||
import { createCtx } from '@reatom/core' | ||
import { getDuration } from '@reatom/testing' | ||
import { createTestCtx, getDuration } from '@reatom/testing' | ||
import { sleep } from '@reatom/utils' | ||
@@ -10,10 +9,10 @@ | ||
test(`base API`, async () => { | ||
const timerModel = reatomTimer(`test`) | ||
const ctx = createCtx() | ||
const timerAtom = reatomTimer(`test`) | ||
const ctx = createTestCtx() | ||
timerModel.intervalAtom.setSeconds(ctx, 0.001) | ||
timerAtom.intervalAtom.setSeconds(ctx, 0.001) | ||
var target = 50 | ||
var duration = await getDuration(() => | ||
timerModel.startTimer(ctx, target / 1000), | ||
timerAtom.startTimer(ctx, target / 1000), | ||
) | ||
@@ -25,4 +24,4 @@ | ||
var [duration] = await Promise.all([ | ||
getDuration(() => timerModel.startTimer(ctx, target / 1000)), | ||
sleep(target / 2).then(() => timerModel.stopTimer(ctx)), | ||
getDuration(() => timerAtom.startTimer(ctx, target / 1000)), | ||
sleep(target / 2).then(() => timerAtom.stopTimer(ctx)), | ||
]) | ||
@@ -33,2 +32,51 @@ assert.ok(duration >= target / 2 && duration < target) | ||
test('progressAtom', async () => { | ||
const timerAtom = reatomTimer({ delayMultiplier: 1 }) | ||
const ctx = createTestCtx() | ||
timerAtom.intervalAtom(ctx, 10) | ||
const track = ctx.subscribeTrack(timerAtom.progressAtom) | ||
track.calls.length = 0 | ||
await timerAtom.startTimer(ctx, 60) | ||
assert.equal( | ||
track.calls.map(({ i }) => i[0]), | ||
[0.17, 0.33, 0.5, 0.67, 0.83, 1], | ||
) | ||
;`👍` //? | ||
}) | ||
test('pauseAtom', async () => { | ||
const timerAtom = reatomTimer({ delayMultiplier: 1 }) | ||
const ctx = createTestCtx() | ||
timerAtom.intervalAtom(ctx, 10) | ||
const track = ctx.subscribeTrack(timerAtom.progressAtom) | ||
track.calls.length = 0 | ||
timerAtom.startTimer(ctx, 60) | ||
await sleep(25) | ||
assert.equal( | ||
track.calls.map(({ i }) => i[0]), | ||
[0.17, 0.33], | ||
) | ||
timerAtom.pauseAtom(ctx, true) | ||
await sleep(20) | ||
assert.equal( | ||
track.calls.map(({ i }) => i[0]), | ||
[0.17, 0.33], | ||
) | ||
timerAtom.pauseAtom(ctx, false) | ||
await sleep(25) | ||
assert.equal( | ||
track.calls.map(({ i }) => i[0]), | ||
[0.17, 0.33, 0.67, 0.83, 1], | ||
) | ||
;`👍` //? | ||
}) | ||
test.run() |
107
src/index.ts
import { Action, action, atom, AtomMut, __count } from '@reatom/core' | ||
import { withReducers } from '@reatom/primitives' | ||
import { sleep } from '@reatom/utils' | ||
import { noop, sleep } from '@reatom/utils' | ||
import { getRootCause, onUpdate } from '@reatom/hooks' | ||
export interface TimerAtom extends AtomMut<number> { | ||
/** (delay - remains) / delay */ | ||
progressAtom: AtomMut<number> | ||
/** interval in ms */ | ||
@@ -11,18 +14,37 @@ intervalAtom: AtomMut<number> & { | ||
/** start timer by passed interval */ | ||
startTimer: Action<[delayInSeconds: number], Promise<void>> | ||
startTimer: Action<[delay: number], Promise<void>> | ||
/** allow to pause timer */ | ||
pauseAtom: AtomMut<boolean> | ||
/** stop timer manually */ | ||
stopTimer: Action<[], void> | ||
/** track end of timer, do not call manually */ | ||
/** track end of timer. Do not call manually! */ | ||
endTimer: Action<[], void> | ||
/** track every interval tick, do not call manually */ | ||
tick: Action<[remains: number], number> | ||
} | ||
export const reatomTimer = ( | ||
options: string | { name?: string; interval?: number } = {}, | ||
options: | ||
| string | ||
| { | ||
name?: string | ||
interval?: number | ||
delayMultiplier?: number | ||
progressPrecision?: number | ||
} = {}, | ||
): TimerAtom => { | ||
const { name = __count('timerAtom'), interval = 1000 } = | ||
typeof options === 'string' ? { name: options } : options | ||
const { | ||
name = __count('timerAtom'), | ||
interval = 1000, | ||
delayMultiplier = 1000, | ||
progressPrecision = 2, | ||
} = typeof options === 'string' ? { name: options } : options | ||
const progressMultiplier = Math.pow(10, progressPrecision) | ||
const timerAtom = atom(0, `${name}Atom`) | ||
const progressAtom: TimerAtom['progressAtom'] = atom( | ||
0, | ||
`${name}.progressAtom`, | ||
) | ||
const pauseAtom: TimerAtom['pauseAtom'] = atom(false, `${name}.pauseAtom`) | ||
const intervalAtom: TimerAtom['intervalAtom'] = atom( | ||
@@ -39,25 +61,56 @@ interval, | ||
const tick: TimerAtom['tick'] = action( | ||
(ctx, remains) => remains, | ||
`${name}.tick`, | ||
) | ||
const startTimer: TimerAtom['startTimer'] = action((ctx, delay: number) => { | ||
delay *= delayMultiplier | ||
const version = _versionAtom(ctx, (s) => s + 1) | ||
const start = Date.now() | ||
const target = delay + start | ||
let remains = delay | ||
let pause = Promise.resolve() | ||
let resolvePause = noop | ||
const startTimer: TimerAtom['startTimer'] = action( | ||
(ctx, delayInSeconds: number) => { | ||
const version = _versionAtom(ctx, (s) => s + 1) | ||
const delay = delayInSeconds * 1000 | ||
const start = Date.now() | ||
const target = delay + start | ||
let remains = delay | ||
timerAtom(ctx, remains) | ||
timerAtom(ctx, remains) | ||
progressAtom(ctx, 0) | ||
return ctx.schedule(async () => { | ||
pauseAtom(ctx, false) | ||
const cleanupPause = onUpdate(pauseAtom, (pauseCtx, value) => | ||
// TODO: circles? | ||
// getRootCause(ctx.cause) === getRootCause(pauseCtx.cause) && | ||
pauseCtx.schedule(() => { | ||
if (value) { | ||
const nextRemains = target - Date.now() | ||
pause = new Promise((resolve) => { | ||
resolvePause = () => { | ||
remains = nextRemains | ||
resolve() | ||
} | ||
}) | ||
} else { | ||
resolvePause() | ||
} | ||
}), | ||
) | ||
return ctx | ||
.schedule(async () => { | ||
while (remains > 0) { | ||
await sleep(Math.min(remains, ctx.get(intervalAtom))) | ||
await pause | ||
if (version !== ctx.get(_versionAtom)) return | ||
remains = timerAtom(ctx, target - Date.now()) | ||
tick(ctx, remains) | ||
const batch = ctx.get.bind(ctx) | ||
batch(() => { | ||
const interval = ctx.get(intervalAtom) | ||
remains = timerAtom(ctx, target - Date.now()) | ||
const progress = (delay - remains) / delay | ||
const roundPart = progress % (interval / delay) | ||
progressAtom( | ||
ctx, | ||
Math.round((progress - roundPart) * progressMultiplier) / | ||
progressMultiplier, | ||
) | ||
}) | ||
} | ||
@@ -67,5 +120,4 @@ | ||
}) | ||
}, | ||
`${name}.startTimer`, | ||
) | ||
.finally(cleanupPause) | ||
}, `${name}.startTimer`) | ||
@@ -82,2 +134,3 @@ const stopTimer: TimerAtom['stopTimer'] = action((ctx) => { | ||
return Object.assign(timerAtom, { | ||
progressAtom, | ||
endTimer, | ||
@@ -87,4 +140,4 @@ intervalAtom, | ||
stopTimer, | ||
tick, | ||
pauseAtom, | ||
}) | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49644
244
35
3
1
+ Added@reatom/hooks@^3.1.2
+ Added@reatom/primitives@^3.1.3
+ Added@reatom/effects@3.10.1(transitive)
+ Added@reatom/hooks@3.6.0(transitive)
+ Added@reatom/primitives@3.7.3(transitive)
+ Added@reatom/utils@3.11.0(transitive)
Updated@reatom/core@^3.1.23