@psytask/core 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +50 -73
- package/dist/index.js +15 -33
- package/dist/index.min.js +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,28 +1,5 @@
|
|
|
1
1
|
type LooseObject = Record<string, unknown>;
|
|
2
2
|
type Merge<T, U> = T extends unknown ? Omit<T, keyof U> & U : never;
|
|
3
|
-
type Split<T extends string, S extends string> = T extends unknown
|
|
4
|
-
? T extends `${infer L}${S}${infer R}`
|
|
5
|
-
? [L, ...Split<R, S>]
|
|
6
|
-
: [T]
|
|
7
|
-
: never;
|
|
8
|
-
|
|
9
|
-
declare global {
|
|
10
|
-
interface ObjectConstructor {
|
|
11
|
-
keys<T>(o: T): (keyof T & string)[];
|
|
12
|
-
}
|
|
13
|
-
interface String {
|
|
14
|
-
split<T extends string, S extends string>(
|
|
15
|
-
this: T,
|
|
16
|
-
separator: S,
|
|
17
|
-
): Split<T, S>;
|
|
18
|
-
}
|
|
19
|
-
interface Array<T> {
|
|
20
|
-
map<R>(fn: <E extends T>(e: E, i: number, arr: this) => R): R[];
|
|
21
|
-
}
|
|
22
|
-
interface Performance {
|
|
23
|
-
getEntriesByType(type: 'resource'): PerformanceResourceTiming[];
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
3
|
|
|
27
4
|
declare const symbol: typeof Symbol.dispose;
|
|
28
5
|
type EventMap<T extends LooseObject> = T & {
|
|
@@ -46,44 +23,65 @@ declare class EventEmitter<T extends LooseObject & {
|
|
|
46
23
|
emit<K extends keyof EventMap<T>>(type: K, ...[evt]: EventMap<T>[K] extends undefined ? [evt?: EventMap<T>[K]] : [evt: EventMap<T>[K]]): this;
|
|
47
24
|
}
|
|
48
25
|
|
|
26
|
+
/** Timestamps of elapsed frames */
|
|
49
27
|
type TimerRecords = number[];
|
|
50
28
|
type Timer = {
|
|
51
|
-
|
|
29
|
+
/**
|
|
30
|
+
* If called in {@link window.requestAnimationFrame rAF} callback or microtasks
|
|
31
|
+
* generated by rAF callback, the start time (the first element of
|
|
32
|
+
* {@link TimerRecords timer records}) will be the current frame time,
|
|
33
|
+
* otherwise the next frame time.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
*
|
|
37
|
+
* Do not repeat
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* timer.start();
|
|
41
|
+
* timer.start(); // error
|
|
42
|
+
*
|
|
43
|
+
* await timer.start();
|
|
44
|
+
* timer.start(); // right
|
|
45
|
+
*
|
|
46
|
+
* timer.start();
|
|
47
|
+
* timer.stop();
|
|
48
|
+
* timer.start(); // right
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
start(onFrame?: (time: number) => void): Promise<TimerRecords>;
|
|
52
52
|
stop(): void;
|
|
53
53
|
};
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* ```text
|
|
58
|
-
* rAF(scene_1.show) -> render -> vsync -> rAF[scene_1.start_time] -> ... ->
|
|
59
|
-
* rAF(scene_2.show) -> ...
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* ## Closing condition
|
|
55
|
+
* Create {@link requestAnimationFrame rAF} timer
|
|
63
56
|
*
|
|
64
|
-
*
|
|
65
|
-
* | :---------------------: | ------------------- |
|
|
66
|
-
* | t | current frame time |
|
|
67
|
-
* | t_0 | start frame time |
|
|
68
|
-
* | D | duration |
|
|
69
|
-
* | \delta | next frame duration |
|
|
70
|
-
* | e = t - t_0 - D | duration error |
|
|
71
|
-
* | \|e\| <= \|e + \delta\| | closing condition |
|
|
57
|
+
* @example Basic usage
|
|
72
58
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* if e + \delta >= 0 then -e <= e + \delta -> e >= -\delta / 2
|
|
80
|
-
* if e + \delta < 0 then -e <= -e - \delta -> false
|
|
59
|
+
* ```ts
|
|
60
|
+
* const frame_ms = 16.67;
|
|
61
|
+
* const duration = 100;
|
|
62
|
+
* const timer = createTimer(
|
|
63
|
+
* (time, records) => time - records[0] >= duration - frame_ms / 2,
|
|
64
|
+
* );
|
|
81
65
|
* ```
|
|
82
66
|
*/
|
|
83
|
-
declare const createTimer: (
|
|
67
|
+
declare const createTimer: (
|
|
68
|
+
/**
|
|
69
|
+
* Return true to stop timer
|
|
70
|
+
*
|
|
71
|
+
* @param time Current frame time
|
|
72
|
+
* @param records History frame times, whose elements represent the start time
|
|
73
|
+
* of each frame that triggers VSync
|
|
74
|
+
*/
|
|
75
|
+
shouldStop: (time: number, records: TimerRecords) => boolean) => Timer;
|
|
84
76
|
type NodeLike = string | Node;
|
|
85
77
|
type BuiltinData = {
|
|
78
|
+
/**
|
|
79
|
+
* History frame times, whose elements represent the start time of each frame
|
|
80
|
+
* that triggers VSync
|
|
81
|
+
*/
|
|
86
82
|
frame_times: TimerRecords;
|
|
83
|
+
/** Real show duration */
|
|
84
|
+
duration: number;
|
|
87
85
|
};
|
|
88
86
|
type ForbiddenData = {
|
|
89
87
|
[K in keyof BuiltinData]?: never;
|
|
@@ -205,33 +203,12 @@ declare class Scene<T extends MaybeGenericComponent> extends EventEmitter<SceneE
|
|
|
205
203
|
*
|
|
206
204
|
* @example
|
|
207
205
|
*
|
|
208
|
-
*
|
|
206
|
+
* Basic usage
|
|
209
207
|
*
|
|
210
208
|
* ```ts
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* using scene = new Scene(
|
|
214
|
-
* (props: { text: string }) => {
|
|
215
|
-
* const node = document.createElement('div');
|
|
216
|
-
* effect(() => {
|
|
217
|
-
* node.textContent = props.text; // auto update when props.text changes
|
|
218
|
-
* });
|
|
219
|
-
* return node;
|
|
220
|
-
* },
|
|
221
|
-
* {
|
|
222
|
-
* root: document.createElement('div'),
|
|
223
|
-
* defaultProps: { text: 'default' },
|
|
224
|
-
* adapter: createComponentAdapter(reactive),
|
|
225
|
-
* timer: () => createTimer((records) => records.length > 100), // show 100 frames
|
|
226
|
-
* },
|
|
227
|
-
* );
|
|
228
|
-
* document.body.appendChild(scene.root);
|
|
229
|
-
*
|
|
230
|
-
* await scene.show({ text: 'new' }); // show `new`
|
|
231
|
-
* await scene.show(); // show `default`
|
|
209
|
+
* await scene.show({ text: 'new' }); // show with new props
|
|
210
|
+
* await scene.show(); // show with default props
|
|
232
211
|
* ```
|
|
233
|
-
*
|
|
234
|
-
* @function
|
|
235
212
|
*/
|
|
236
213
|
show: T extends Component<infer P, infer D> ? SceneShow<P, D> : T;
|
|
237
214
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @psytask/core v1.1.
|
|
1
|
+
/** @psytask/core v1.1.2 cubxx MIT */
|
|
2
2
|
const symbol = Symbol.dispose ?? /* @__PURE__ */ Symbol.for("Symbol.dispose");
|
|
3
3
|
class EventEmitter {
|
|
4
4
|
listeners = {};
|
|
@@ -54,17 +54,21 @@ new Proxy(
|
|
|
54
54
|
);
|
|
55
55
|
const array_normalize = (e) => Array.isArray(e) ? e : [e];
|
|
56
56
|
|
|
57
|
+
let currentFrameTime;
|
|
58
|
+
const setCurrentFrameTime = (time) => (currentFrameTime = time, setTimeout(() => currentFrameTime = 0), // reset after render pipeline
|
|
59
|
+
rAF(setCurrentFrameTime));
|
|
57
60
|
const createTimer = (shouldStop) => {
|
|
61
|
+
currentFrameTime ?? setCurrentFrameTime(0);
|
|
58
62
|
const timer = {
|
|
59
63
|
start: (cb) => new Promise((resolve) => {
|
|
60
|
-
timer.stop = () => (cancelAnimationFrame(handle), resolve(records));
|
|
61
64
|
const records = [];
|
|
65
|
+
const record = (time) => (records.push(time), cb?.(time));
|
|
66
|
+
currentFrameTime && record(currentFrameTime);
|
|
62
67
|
const frame = (time) => {
|
|
63
|
-
records.
|
|
64
|
-
cb?.(records);
|
|
65
|
-
shouldStop(records) ? timer.stop() : handle = rAF(frame);
|
|
68
|
+
shouldStop(time, records) ? timer.stop() : (handle = rAF(frame), record(time));
|
|
66
69
|
};
|
|
67
70
|
let handle = rAF(frame);
|
|
71
|
+
timer.stop = () => (cancelAnimationFrame(handle), resolve(records));
|
|
68
72
|
}),
|
|
69
73
|
stop() {
|
|
70
74
|
}
|
|
@@ -120,33 +124,12 @@ class Scene extends EventEmitter {
|
|
|
120
124
|
*
|
|
121
125
|
* @example
|
|
122
126
|
*
|
|
123
|
-
*
|
|
127
|
+
* Basic usage
|
|
124
128
|
*
|
|
125
129
|
* ```ts
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* using scene = new Scene(
|
|
129
|
-
* (props: { text: string }) => {
|
|
130
|
-
* const node = document.createElement('div');
|
|
131
|
-
* effect(() => {
|
|
132
|
-
* node.textContent = props.text; // auto update when props.text changes
|
|
133
|
-
* });
|
|
134
|
-
* return node;
|
|
135
|
-
* },
|
|
136
|
-
* {
|
|
137
|
-
* root: document.createElement('div'),
|
|
138
|
-
* defaultProps: { text: 'default' },
|
|
139
|
-
* adapter: createComponentAdapter(reactive),
|
|
140
|
-
* timer: () => createTimer((records) => records.length > 100), // show 100 frames
|
|
141
|
-
* },
|
|
142
|
-
* );
|
|
143
|
-
* document.body.appendChild(scene.root);
|
|
144
|
-
*
|
|
145
|
-
* await scene.show({ text: 'new' }); // show `new`
|
|
146
|
-
* await scene.show(); // show `default`
|
|
130
|
+
* await scene.show({ text: 'new' }); // show with new props
|
|
131
|
+
* await scene.show(); // show with default props
|
|
147
132
|
* ```
|
|
148
|
-
*
|
|
149
|
-
* @function
|
|
150
133
|
*/
|
|
151
134
|
//@ts-expect-error impl generic component
|
|
152
135
|
show = this.#show;
|
|
@@ -163,13 +146,12 @@ class Scene extends EventEmitter {
|
|
|
163
146
|
this.emit("show");
|
|
164
147
|
root.style.scale = "1";
|
|
165
148
|
root.focus();
|
|
166
|
-
const records = await this.#timer.start(
|
|
167
|
-
(records2) => this.emit("frame", records2[records2.length - 1])
|
|
168
|
-
);
|
|
149
|
+
const records = await this.#timer.start((time) => this.emit("frame", time));
|
|
169
150
|
root.style.scale = "0";
|
|
170
151
|
return {
|
|
171
152
|
...this.emit("close").data?.(),
|
|
172
|
-
frame_times: records
|
|
153
|
+
frame_times: records,
|
|
154
|
+
duration: currentFrameTime - records[0]
|
|
173
155
|
};
|
|
174
156
|
}
|
|
175
157
|
}
|
package/dist/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const f=Symbol.dispose??Symbol.for("Symbol.dispose");class m{listeners={};[f](){this.emit("dispose")}on(t,s){return(this.listeners[t]??=new Set).add(s),this}off(t,s){const e=this.listeners[t];return e&&(e.delete(s),e.size===0&&delete this.listeners[t]),this}once(t,s){const e=r=>{try{s(r)}finally{this.off(t,e)}};return this.on(t,e),this}emit(t,...[s]){const e=this.listeners[t];if(e)for(const r of[...e])r(s);return this}}const y=o=>{throw Error(o)},h=requestAnimationFrame,w=Object,b=(o,t)=>w.assign(o,t),S=document;new Proxy({},{get:(o,t)=>s=>b(S.createElement(t),s)});const g=o=>Array.isArray(o)?o:[o];let d;const p=o=>(d=o,setTimeout(()=>d=0),h(p)),A=o=>{d??p(0);const t={start:s=>new Promise(e=>{const r=[],n=c=>(r.push(c),s?.(c));d&&n(d);const i=c=>{o(c,r)?t.stop():(a=h(i),n(c))};let a=h(i);t.stop=()=>(cancelAnimationFrame(a),e(r))}),stop(){}};return t},E=o=>o,F=o=>({wrap:t=>(s=>t(o(s))),render:(t,s,e)=>{const r=o(s);e&&l.push(e);const n=t(r);e&&l.pop();let i;const a=g(typeof n!="string"&&"node"in n?(i=n.data,n.node):n);return{props:r,nodes:a,data:i}}}),l=[],P=()=>l[l.length-1]??y("Not found current scene");class O extends m{constructor(t,s){super(),this.options=s;const{root:e,adapter:r,defaultProps:n,timer:i}=s;(this.root=e).tabIndex=-1,e.style.scale="0";const{props:a,nodes:c,data:u}=r.render(t,{...n},this.on("dispose",()=>e.remove()));this.#t=i(),this.#s=a,this.data=u,e.append(...c)}root;#t;#s;data;show=this.#e;async close(){await 0,this.#t.stop()}async#e(t){const{root:s,defaultProps:e}=this.options,r={...e,...t};for(const i of Object.keys({...this.#s,...r}))this.#s[i]=r[i];this.emit("show"),s.style.scale="1",s.focus();const n=await this.#t.start(i=>this.emit("frame",i));return s.style.scale="0",{...this.emit("close").data?.(),frame_times:n,duration:d-n[0]}}}export{m as EventEmitter,O as Scene,F as createComponentAdapter,A as createTimer,E as generic,P as getCurrentScene};
|