@panyam/tsutils 0.0.64 → 0.0.66
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/lib/cjs/index.d.ts +2 -0
- package/lib/cjs/index.js +4 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/numberutils.js.map +1 -1
- package/lib/cjs/scrolling.d.ts +49 -0
- package/lib/cjs/scrolling.js +195 -0
- package/lib/cjs/scrolling.js.map +1 -0
- package/lib/cjs/timer.d.ts +12 -0
- package/lib/cjs/timer.js +45 -0
- package/lib/cjs/timer.js.map +1 -0
- package/lib/esm/index.d.ts +2 -0
- package/lib/esm/index.js +3 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/numberutils.js.map +1 -1
- package/lib/esm/scrolling.d.ts +49 -0
- package/lib/esm/scrolling.js +190 -0
- package/lib/esm/scrolling.js.map +1 -0
- package/lib/esm/timer.d.ts +12 -0
- package/lib/esm/timer.js +41 -0
- package/lib/esm/timer.js.map +1 -0
- package/package.json +1 -1
- package/lib/cjs/properties.d.ts +0 -54
- package/lib/cjs/properties.js +0 -158
- package/lib/cjs/properties.js.map +0 -1
- package/lib/esm/properties.d.ts +0 -54
- package/lib/esm/properties.js +0 -149
- package/lib/esm/properties.js.map +0 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Timer } from "./timer";
|
|
2
|
+
export class HTMLElementScrollable {
|
|
3
|
+
constructor(element, vertical = true) {
|
|
4
|
+
this._scrollGroup = null;
|
|
5
|
+
this.vertical = true;
|
|
6
|
+
this.onScrollEventListener = this.onScrollEvent.bind(this);
|
|
7
|
+
this.onMouseEventListener = this.onMouseEvent.bind(this);
|
|
8
|
+
this.onTouchEventListener = this.onTouchEvent.bind(this);
|
|
9
|
+
this.element = element;
|
|
10
|
+
this.vertical = vertical;
|
|
11
|
+
}
|
|
12
|
+
attach(scrollGroup) {
|
|
13
|
+
if (this.element.scrollGroup == scrollGroup) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
else if (this.element.scrollGroup) {
|
|
17
|
+
throw new Error("Detach element from ScrollGroup first.");
|
|
18
|
+
}
|
|
19
|
+
this.element.scrollGroup = scrollGroup;
|
|
20
|
+
this._scrollGroup = scrollGroup;
|
|
21
|
+
this.element.addEventListener("scroll", this.onScrollEventListener);
|
|
22
|
+
this.element.addEventListener("mousedown", this.onMouseEventListener);
|
|
23
|
+
this.element.addEventListener("mouseenter", this.onMouseEventListener);
|
|
24
|
+
this.element.addEventListener("mouseleave", this.onMouseEventListener);
|
|
25
|
+
this.element.addEventListener("touchstart", this.onTouchEventListener);
|
|
26
|
+
}
|
|
27
|
+
detach() {
|
|
28
|
+
this.element.scrollGroup = null;
|
|
29
|
+
this.element.removeEventListener("scroll", this.onScrollEventListener);
|
|
30
|
+
this.element.removeEventListener("mousedown", this.onMouseEventListener);
|
|
31
|
+
this.element.removeEventListener("mouseenter", this.onMouseEventListener);
|
|
32
|
+
this.element.removeEventListener("mouseleave", this.onMouseEventListener);
|
|
33
|
+
this.element.removeEventListener("touchstart", this.onTouchEventListener);
|
|
34
|
+
}
|
|
35
|
+
get scrollGroup() {
|
|
36
|
+
return this._scrollGroup;
|
|
37
|
+
}
|
|
38
|
+
get scrollOffset() {
|
|
39
|
+
if (this.vertical) {
|
|
40
|
+
return this.element.scrollTop;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return this.element.scrollLeft;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
set scrollOffset(value) {
|
|
47
|
+
if (this.vertical) {
|
|
48
|
+
this.element.scrollTop = value;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.element.scrollLeft = value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
get scrollSize() {
|
|
55
|
+
if (this.vertical) {
|
|
56
|
+
return this.element.scrollHeight;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
return this.element.scrollWidth;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
get pageSize() {
|
|
63
|
+
if (this.vertical) {
|
|
64
|
+
return this.element.clientHeight;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return this.element.clientWidth;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
onScrollEvent(event) {
|
|
71
|
+
var _a;
|
|
72
|
+
(_a = this.scrollGroup) === null || _a === void 0 ? void 0 : _a.onScroll(this, event.timeStamp);
|
|
73
|
+
}
|
|
74
|
+
onTouchEvent(event) {
|
|
75
|
+
if (event.type == "touchstart" && this.scrollGroup) {
|
|
76
|
+
this.scrollGroup.focussedScrollable = this;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
onMouseEvent(event) {
|
|
80
|
+
const element = event.target;
|
|
81
|
+
if (this.scrollGroup) {
|
|
82
|
+
if (event.type == "mouseenter") {
|
|
83
|
+
this.scrollGroup.focussedScrollable = this;
|
|
84
|
+
}
|
|
85
|
+
else if (event.type == "mouseleave") {
|
|
86
|
+
this.scrollGroup.focussedScrollable = null;
|
|
87
|
+
}
|
|
88
|
+
else if (event.type == "mousedown") {
|
|
89
|
+
this.scrollGroup.focussedScrollable = this;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export class ScrollGroup {
|
|
95
|
+
constructor(debugLogs = false) {
|
|
96
|
+
this.debugLogs = debugLogs;
|
|
97
|
+
this.scrollables = [];
|
|
98
|
+
this._focussedScrollable = null;
|
|
99
|
+
this.leadScrollable = null;
|
|
100
|
+
this.lastScrolledAt = -1;
|
|
101
|
+
this.lastScrollOffset = 0;
|
|
102
|
+
this.idleThreshold = 300;
|
|
103
|
+
this.offsetDeltaThreshold = 5;
|
|
104
|
+
this.eventDeltaThreshold = 50;
|
|
105
|
+
this.scrollTimer = new Timer(500, this.onTimer.bind(this));
|
|
106
|
+
}
|
|
107
|
+
add(scrollable) {
|
|
108
|
+
const index = this.scrollables.indexOf(scrollable);
|
|
109
|
+
if (index >= 0)
|
|
110
|
+
return;
|
|
111
|
+
scrollable.attach(this);
|
|
112
|
+
this.scrollables.push(scrollable);
|
|
113
|
+
}
|
|
114
|
+
remove(scrollable) {
|
|
115
|
+
const index = this.scrollables.indexOf(scrollable);
|
|
116
|
+
if (index < 0)
|
|
117
|
+
return;
|
|
118
|
+
this.detachAtIndex(index);
|
|
119
|
+
}
|
|
120
|
+
clear() {
|
|
121
|
+
for (let i = this.scrollables.length - 1; i >= 0; i--) {
|
|
122
|
+
this.detachAtIndex(i);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
detachAtIndex(index) {
|
|
126
|
+
const scrollable = this.scrollables[index];
|
|
127
|
+
scrollable.detach();
|
|
128
|
+
this.scrollables.splice(index, 1);
|
|
129
|
+
return scrollable;
|
|
130
|
+
}
|
|
131
|
+
syncFollowersToLeader() {
|
|
132
|
+
const scrollable = this.leadScrollable;
|
|
133
|
+
if (scrollable != null) {
|
|
134
|
+
this.lastScrollOffset = scrollable.scrollOffset;
|
|
135
|
+
const remScroll = Math.max(1, scrollable.scrollSize - scrollable.pageSize);
|
|
136
|
+
for (let i = this.scrollables.length - 1; i >= 0; i--) {
|
|
137
|
+
const other = this.scrollables[i];
|
|
138
|
+
const remOther = Math.max(1, other.scrollSize - other.pageSize);
|
|
139
|
+
if (other != scrollable) {
|
|
140
|
+
other.scrollOffset = (scrollable.scrollOffset * remOther) / remScroll;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
set focussedScrollable(scrollable) {
|
|
146
|
+
this._focussedScrollable = scrollable;
|
|
147
|
+
}
|
|
148
|
+
onScroll(scrollable, timestamp) {
|
|
149
|
+
if (this.leadScrollable == null) {
|
|
150
|
+
this.setLeadScrollable(scrollable);
|
|
151
|
+
}
|
|
152
|
+
const leader = this.leadScrollable;
|
|
153
|
+
if (leader != null) {
|
|
154
|
+
const offsetDelta = Math.abs(leader.scrollOffset - this.lastScrollOffset);
|
|
155
|
+
const timeDelta = Math.abs(timestamp - this.lastScrolledAt);
|
|
156
|
+
if (offsetDelta > this.offsetDeltaThreshold || timeDelta > this.eventDeltaThreshold) {
|
|
157
|
+
this.lastScrolledAt = timestamp;
|
|
158
|
+
this.syncFollowersToLeader();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
setLeadScrollable(scrollable) {
|
|
163
|
+
if (this.leadScrollable == null) {
|
|
164
|
+
this.leadScrollable = scrollable;
|
|
165
|
+
if (this.debugLogs)
|
|
166
|
+
console.log("Scrolling started with: ", scrollable);
|
|
167
|
+
this.scrollTimer.start();
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
throw new Error("This should now happen");
|
|
171
|
+
}
|
|
172
|
+
return this.leadScrollable;
|
|
173
|
+
}
|
|
174
|
+
onTimer(ts) {
|
|
175
|
+
if (this.leadScrollable != null && ts - this.lastScrolledAt > this.idleThreshold) {
|
|
176
|
+
const offsetDelta = Math.abs(this.leadScrollable.scrollOffset - this.lastScrollOffset);
|
|
177
|
+
if (offsetDelta == 0) {
|
|
178
|
+
this.scrollingFinished(ts);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
scrollingFinished(ts) {
|
|
183
|
+
if (this.debugLogs)
|
|
184
|
+
console.log("Scrolling Finished at: ", ts);
|
|
185
|
+
this.syncFollowersToLeader();
|
|
186
|
+
this.leadScrollable = null;
|
|
187
|
+
this.scrollTimer.stop();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=scrolling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrolling.js","sourceRoot":"","sources":["../../src/scrolling.ts"],"names":[],"mappings":"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","sourcesContent":["import { Timer } from \"./timer\";\n\n/**\n * A scroll group allows one to \"connect\" multiple elements to be\n * scrolled synchronously.\n *\n * A typical usecase would be a div showing lines for a code editor\n * and one that shows a line numbers for a code editor.\n *\n * We could also have recursive relationships where elements in\n * a scroll group are connected to other items outside the scroll\n * group and the would form a super scroll group. An example would\n * two UI components A and B that\n * a scroll group is synchronized\n */\n\ntype EventHandler = (evt: any) => void;\nexport interface Scrollable {\n // Set or get the current scroll offset\n scrollOffset: number;\n\n // Get total scroll size\n readonly scrollSize: number;\n\n // Size of the current \"page\".\n // Our scrollOffset + pageSize is always < scrollSize\n readonly pageSize: number;\n\n // Detaches a scrollable from further use\n attach(scrollGroup: ScrollGroup): void;\n detach(): void;\n}\n\n/**\n * A wrapper for html elements that can be scrolled.\n */\nexport class HTMLElementScrollable implements Scrollable {\n private _scrollGroup: ScrollGroup | null = null;\n readonly element: HTMLElement;\n private vertical = true;\n private onScrollEventListener = this.onScrollEvent.bind(this) as EventListener;\n private onMouseEventListener = this.onMouseEvent.bind(this) as EventListener;\n private onTouchEventListener = this.onTouchEvent.bind(this) as EventListener;\n\n constructor(element: HTMLElement, vertical = true) {\n this.element = element;\n this.vertical = vertical;\n }\n\n attach(scrollGroup: ScrollGroup): void {\n if ((this.element as any).scrollGroup == scrollGroup) {\n return;\n } else if ((this.element as any).scrollGroup) {\n throw new Error(\"Detach element from ScrollGroup first.\");\n }\n (this.element as any).scrollGroup = scrollGroup;\n this._scrollGroup = scrollGroup;\n this.element.addEventListener(\"scroll\", this.onScrollEventListener);\n this.element.addEventListener(\"mousedown\", this.onMouseEventListener);\n this.element.addEventListener(\"mouseenter\", this.onMouseEventListener);\n this.element.addEventListener(\"mouseleave\", this.onMouseEventListener);\n this.element.addEventListener(\"touchstart\", this.onTouchEventListener);\n }\n\n detach(): void {\n (this.element as any).scrollGroup = null;\n this.element.removeEventListener(\"scroll\", this.onScrollEventListener);\n this.element.removeEventListener(\"mousedown\", this.onMouseEventListener);\n this.element.removeEventListener(\"mouseenter\", this.onMouseEventListener);\n this.element.removeEventListener(\"mouseleave\", this.onMouseEventListener);\n this.element.removeEventListener(\"touchstart\", this.onTouchEventListener);\n }\n\n get scrollGroup(): ScrollGroup | null {\n return this._scrollGroup;\n }\n\n // Set or get the current scroll offset\n get scrollOffset(): number {\n if (this.vertical) {\n return this.element.scrollTop;\n } else {\n return this.element.scrollLeft;\n }\n }\n\n set scrollOffset(value: number) {\n if (this.vertical) {\n this.element.scrollTop = value;\n } else {\n this.element.scrollLeft = value;\n }\n }\n\n // Get total scroll size\n get scrollSize(): number {\n if (this.vertical) {\n return this.element.scrollHeight;\n } else {\n return this.element.scrollWidth;\n }\n }\n\n // Size of the current \"page\".\n // Our scrollOffset + pageSize is always < scrollSize\n get pageSize(): number {\n if (this.vertical) {\n return this.element.clientHeight;\n } else {\n return this.element.clientWidth;\n }\n }\n\n onScrollEvent(event: Event): void {\n /**\n * Scroll events will be sent for all elements that are scrolling\n * either programatically or invoked via gestures.\n * It is not possible to know which of these it is and the problem\n * with this is that by handling all events it could result in an\n * infinite loop kicking each other off.\n *\n * So we need a way to be able differentiate scroll events between\n * those that were the \"source\" and those that are \"followers\".\n * We can try a few strategies here:\n *\n * 1. Take the first scroll event's target as the source\n * and kick off a timer to check when scroll events stop. As long\n * as scroll events come from this source we update followers.\n */\n this.scrollGroup?.onScroll(this, event.timeStamp);\n }\n\n onTouchEvent(event: TouchEvent): void {\n // console.log(`Touched Eeent(${event.type}): `, event);\n if (event.type == \"touchstart\" && this.scrollGroup) {\n this.scrollGroup.focussedScrollable = this;\n // this.setLeadScrollable(this.focussedElement);\n }\n }\n\n onMouseEvent(event: MouseEvent): void {\n // console.log(`Mouse Event(${event.type}): `, event);\n const element = event.target;\n if (this.scrollGroup) {\n if (event.type == \"mouseenter\") {\n this.scrollGroup.focussedScrollable = this;\n } else if (event.type == \"mouseleave\") {\n this.scrollGroup.focussedScrollable = null;\n } else if (event.type == \"mousedown\") {\n this.scrollGroup.focussedScrollable = this;\n // this.setLeadScrollable(this.focussedElement);\n }\n }\n }\n}\n\nexport class ScrollGroup {\n private scrollables: Scrollable[] = [];\n private _focussedScrollable: Scrollable | null = null;\n private leadScrollable: Scrollable | null = null;\n private lastScrolledAt = -1;\n private lastScrollOffset = 0;\n private scrollTimer: Timer;\n\n // If there has been no change in scroll offset within this\n // time then we can assume scrolling has completed and this\n // can be used to infer that scrolling has finished.\n private idleThreshold = 300;\n\n // Apply sync to followers if we have a scroll distance of atleast\n // this much or time between last even has crossed the\n // `eventDeltaThreshold`.\n private offsetDeltaThreshold = 5;\n private eventDeltaThreshold = 50;\n\n constructor(public debugLogs = false) {\n this.scrollTimer = new Timer(500, this.onTimer.bind(this));\n }\n\n add(scrollable: Scrollable): void {\n // skip if already exists\n const index = this.scrollables.indexOf(scrollable);\n if (index >= 0) return;\n scrollable.attach(this);\n this.scrollables.push(scrollable);\n }\n\n remove(scrollable: Scrollable): void {\n const index = this.scrollables.indexOf(scrollable);\n if (index < 0) return;\n this.detachAtIndex(index);\n }\n\n clear(): void {\n for (let i = this.scrollables.length - 1; i >= 0; i--) {\n this.detachAtIndex(i);\n }\n }\n\n detachAtIndex(index: number): Scrollable {\n const scrollable = this.scrollables[index];\n scrollable.detach();\n this.scrollables.splice(index, 1);\n return scrollable;\n }\n\n syncFollowersToLeader(): void {\n const scrollable = this.leadScrollable;\n if (scrollable != null) {\n this.lastScrollOffset = scrollable.scrollOffset;\n // console.log(\"Scrolled: \", scrollable.scrollOffset, event);\n\n // set the scroll position of all others\n // TODO - should this happen in this handler itself?\n const remScroll = Math.max(1, scrollable.scrollSize - scrollable.pageSize);\n for (let i = this.scrollables.length - 1; i >= 0; i--) {\n const other = this.scrollables[i];\n const remOther = Math.max(1, other.scrollSize - other.pageSize);\n /*\n console.log(\"Scrollable: \", i, other);\n console.log(\n \"scrollOffset, scrollSize, pageSize: \",\n other.scrollOffset,\n other.scrollSize,\n other.pageSize,\n remOther,\n );\n */\n if (other != scrollable) {\n other.scrollOffset = (scrollable.scrollOffset * remOther) / remScroll;\n }\n }\n }\n }\n\n set focussedScrollable(scrollable: Scrollable | null) {\n this._focussedScrollable = scrollable;\n }\n\n onScroll(scrollable: Scrollable, timestamp: number): void {\n if (this.leadScrollable == null) {\n this.setLeadScrollable(scrollable);\n }\n const leader = this.leadScrollable;\n if (leader != null) {\n // update followers\n const offsetDelta = Math.abs(leader.scrollOffset - this.lastScrollOffset);\n const timeDelta = Math.abs(timestamp - this.lastScrolledAt);\n if (offsetDelta > this.offsetDeltaThreshold || timeDelta > this.eventDeltaThreshold) {\n this.lastScrolledAt = timestamp;\n this.syncFollowersToLeader();\n }\n }\n }\n\n /**\n * Sets the active scrollable to the focussed element.\n */\n public setLeadScrollable(scrollable: Scrollable): Scrollable | null {\n if (this.leadScrollable == null) {\n // scrolling has not begun yet so set it as the \"root\" scroller\n this.leadScrollable = scrollable;\n if (this.debugLogs) console.log(\"Scrolling started with: \", scrollable);\n this.scrollTimer.start();\n } else {\n // What if there was an already active scrollable?\n // This can happen if:\n throw new Error(\"This should now happen\");\n }\n return this.leadScrollable;\n }\n\n onTimer(ts: number): void {\n // Called with our timer\n if (this.leadScrollable != null && ts - this.lastScrolledAt > this.idleThreshold) {\n const offsetDelta = Math.abs(this.leadScrollable.scrollOffset - this.lastScrollOffset);\n if (offsetDelta == 0) {\n // No change in delta within a time window\n this.scrollingFinished(ts);\n }\n }\n }\n\n protected scrollingFinished(ts: number): void {\n if (this.debugLogs) console.log(\"Scrolling Finished at: \", ts);\n // TODO - See if this can have a jerking effect\n this.syncFollowersToLeader();\n this.leadScrollable = null;\n this.scrollTimer.stop();\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare type StepFunc = (ts: number) => void;
|
|
2
|
+
export declare class Timer {
|
|
3
|
+
private refreshInterval;
|
|
4
|
+
private lastRefreshAt;
|
|
5
|
+
private updateLoop;
|
|
6
|
+
stepFunc: StepFunc;
|
|
7
|
+
constructor(refreshInterval: number, stepFunc: StepFunc);
|
|
8
|
+
stop(): void;
|
|
9
|
+
start(): void;
|
|
10
|
+
protected kickOffUpdate(): void;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
package/lib/esm/timer.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class Timer {
|
|
2
|
+
constructor(refreshInterval, stepFunc) {
|
|
3
|
+
this.refreshInterval = 1000;
|
|
4
|
+
this.lastRefreshAt = 0;
|
|
5
|
+
this.updateLoop = null;
|
|
6
|
+
this.refreshInterval = refreshInterval;
|
|
7
|
+
this.stepFunc = stepFunc;
|
|
8
|
+
}
|
|
9
|
+
stop() {
|
|
10
|
+
if (this.updateLoop != null) {
|
|
11
|
+
cancelAnimationFrame(this.updateLoop);
|
|
12
|
+
this.updateLoop = null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
start() {
|
|
16
|
+
this.kickOffUpdate();
|
|
17
|
+
}
|
|
18
|
+
kickOffUpdate() {
|
|
19
|
+
if (this.updateLoop != null) {
|
|
20
|
+
cancelAnimationFrame(this.updateLoop);
|
|
21
|
+
}
|
|
22
|
+
this.updateLoop = requestAnimationFrame((timestamp) => {
|
|
23
|
+
if (this.updateLoop != null) {
|
|
24
|
+
if (timestamp - this.lastRefreshAt >= this.refreshInterval) {
|
|
25
|
+
try {
|
|
26
|
+
this.stepFunc(timestamp);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
console.log("Error from Timer Handler: ", err);
|
|
30
|
+
alert("Error from Timer Handler: " + err.message);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.lastRefreshAt = timestamp;
|
|
34
|
+
}
|
|
35
|
+
this.updateLoop = null;
|
|
36
|
+
this.kickOffUpdate();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=timer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timer.js","sourceRoot":"","sources":["../../src/timer.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,KAAK;IAMhB,YAAY,eAAuB,EAAE,QAAkB;QAL/C,oBAAe,GAAG,IAAI,CAAC;QACvB,kBAAa,GAAG,CAAC,CAAC;QAClB,eAAU,GAAkB,IAAI,CAAC;QAIvC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE;YAC3B,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACtC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;SACxB;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAES,aAAa;QACrB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE;YAC3B,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;SACvC;QACD,IAAI,CAAC,UAAU,GAAG,qBAAqB,CAAC,CAAC,SAAS,EAAE,EAAE;YACpD,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,EAAE;gBAC3B,IAAI,SAAS,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,eAAe,EAAE;oBAC1D,IAAI;wBACF,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;qBAC1B;oBAAC,OAAO,GAAQ,EAAE;wBACjB,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;wBAC/C,KAAK,CAAC,4BAA4B,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;wBAClD,OAAO;qBACR;oBACD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;iBAChC;gBACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACvB,IAAI,CAAC,aAAa,EAAE,CAAC;aACtB;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["type StepFunc = (ts: number) => void;\n\nexport class Timer {\n private refreshInterval = 1000;\n private lastRefreshAt = 0;\n private updateLoop: number | null = null;\n stepFunc: StepFunc;\n\n constructor(refreshInterval: number, stepFunc: StepFunc) {\n this.refreshInterval = refreshInterval;\n this.stepFunc = stepFunc;\n }\n\n stop(): void {\n if (this.updateLoop != null) {\n cancelAnimationFrame(this.updateLoop);\n this.updateLoop = null;\n }\n }\n\n start(): void {\n this.kickOffUpdate();\n }\n\n protected kickOffUpdate(): void {\n if (this.updateLoop != null) {\n cancelAnimationFrame(this.updateLoop);\n }\n this.updateLoop = requestAnimationFrame((timestamp) => {\n if (this.updateLoop != null) {\n if (timestamp - this.lastRefreshAt >= this.refreshInterval) {\n try {\n this.stepFunc(timestamp);\n } catch (err: any) {\n console.log(\"Error from Timer Handler: \", err);\n alert(\"Error from Timer Handler: \" + err.message);\n return;\n }\n this.lastRefreshAt = timestamp;\n }\n this.updateLoop = null;\n this.kickOffUpdate();\n }\n });\n }\n}\n"]}
|
package/package.json
CHANGED
package/lib/cjs/properties.d.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Nullable } from "./types";
|
|
2
|
-
export declare class PropertySchema {
|
|
3
|
-
name: string;
|
|
4
|
-
units: string;
|
|
5
|
-
defaultValue: any;
|
|
6
|
-
minValue: Nullable<number>;
|
|
7
|
-
maxValue: Nullable<number>;
|
|
8
|
-
beforeSetterName: Nullable<string>;
|
|
9
|
-
constructor(name: string, config: any);
|
|
10
|
-
validate(newValue: number): boolean;
|
|
11
|
-
}
|
|
12
|
-
export declare class Property {
|
|
13
|
-
readonly schema: PropertySchema;
|
|
14
|
-
currentValue: any;
|
|
15
|
-
beforeSetterFunc: any;
|
|
16
|
-
constructor(schema: PropertySchema);
|
|
17
|
-
clone(): Property;
|
|
18
|
-
get value(): any;
|
|
19
|
-
setValue(instance: any, value: any): boolean;
|
|
20
|
-
}
|
|
21
|
-
export declare class PropertyStore {
|
|
22
|
-
readonly __properties__: {
|
|
23
|
-
[key: string]: Property;
|
|
24
|
-
};
|
|
25
|
-
private eventBus;
|
|
26
|
-
getPropertyNames(): string[];
|
|
27
|
-
getProperty(name: string): Nullable<Property>;
|
|
28
|
-
setProperty(name: string, value: any): void;
|
|
29
|
-
}
|
|
30
|
-
export declare const mixinPropertyStore: (target: any) => {
|
|
31
|
-
new (): {
|
|
32
|
-
[x: string]: any;
|
|
33
|
-
readonly __properties__: {
|
|
34
|
-
[key: string]: Property;
|
|
35
|
-
};
|
|
36
|
-
getProperties(): {
|
|
37
|
-
[key: string]: Property;
|
|
38
|
-
};
|
|
39
|
-
getProperty(name: string): Property;
|
|
40
|
-
setProperty(name: string, value: any): void;
|
|
41
|
-
};
|
|
42
|
-
[x: string]: any;
|
|
43
|
-
};
|
|
44
|
-
interface PropertyParams<T> {
|
|
45
|
-
defaultValue?: Nullable<T>;
|
|
46
|
-
propertiesName?: string;
|
|
47
|
-
beforeSetterName?: Nullable<string>;
|
|
48
|
-
units?: string;
|
|
49
|
-
minValue?: Nullable<number>;
|
|
50
|
-
maxValue?: Nullable<number>;
|
|
51
|
-
}
|
|
52
|
-
export declare function property<T>({ propertiesName, defaultValue, beforeSetterName, units, minValue, maxValue, }?: PropertyParams<T>): (target: any, propertyKey: string) => void;
|
|
53
|
-
export declare function propertyValidator<T>(propertyName: string, propertiesName?: string): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
54
|
-
export {};
|
package/lib/cjs/properties.js
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.propertyValidator = exports.property = exports.mixinPropertyStore = exports.PropertyStore = exports.Property = exports.PropertySchema = void 0;
|
|
4
|
-
const bus_1 = require("./comms/bus");
|
|
5
|
-
const misc_1 = require("./misc");
|
|
6
|
-
class PropertySchema {
|
|
7
|
-
constructor(name, config) {
|
|
8
|
-
this.units = "";
|
|
9
|
-
this.defaultValue = null;
|
|
10
|
-
this.minValue = null;
|
|
11
|
-
this.maxValue = null;
|
|
12
|
-
this.beforeSetterName = null;
|
|
13
|
-
this.name = name;
|
|
14
|
-
this.beforeSetterName = misc_1.ifDefined(config.beforeSetterName);
|
|
15
|
-
this.units = misc_1.ifDefined(config.units);
|
|
16
|
-
this.defaultValue = misc_1.ifDefined(config.defaultValue);
|
|
17
|
-
this.minValue = misc_1.ifDefined(config.minValue);
|
|
18
|
-
this.maxValue = misc_1.ifDefined(config.maxValue);
|
|
19
|
-
}
|
|
20
|
-
validate(newValue) {
|
|
21
|
-
if (this.minValue != null && newValue < this.minValue)
|
|
22
|
-
return false;
|
|
23
|
-
if (this.maxValue != null && newValue > this.maxValue)
|
|
24
|
-
return false;
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
exports.PropertySchema = PropertySchema;
|
|
29
|
-
class Property {
|
|
30
|
-
constructor(schema) {
|
|
31
|
-
this.currentValue = null;
|
|
32
|
-
this.beforeSetterFunc = null;
|
|
33
|
-
this.schema = schema;
|
|
34
|
-
}
|
|
35
|
-
clone() {
|
|
36
|
-
const out = new Property(this.schema);
|
|
37
|
-
out.currentValue = this.currentValue;
|
|
38
|
-
return out;
|
|
39
|
-
}
|
|
40
|
-
get value() {
|
|
41
|
-
return this.currentValue;
|
|
42
|
-
}
|
|
43
|
-
setValue(instance, value) {
|
|
44
|
-
if (this.schema.beforeSetterName != null) {
|
|
45
|
-
if (this.beforeSetterFunc == null) {
|
|
46
|
-
const beforeSetterFunc = instance[this.schema.beforeSetterName];
|
|
47
|
-
if (beforeSetterFunc == null) {
|
|
48
|
-
throw new Error("Cannot find beforeSetter function: " + this.schema.beforeSetterName);
|
|
49
|
-
}
|
|
50
|
-
this.beforeSetterFunc = beforeSetterFunc.bind(instance);
|
|
51
|
-
}
|
|
52
|
-
if (this.beforeSetterFunc(value) == false) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (!this.schema.validate(value)) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
this.currentValue = value;
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
exports.Property = Property;
|
|
64
|
-
class PropertyStore {
|
|
65
|
-
constructor() {
|
|
66
|
-
this.__properties__ = {};
|
|
67
|
-
this.eventBus = bus_1.EventBus.getInstance();
|
|
68
|
-
}
|
|
69
|
-
getPropertyNames() {
|
|
70
|
-
return Object.keys(this.__properties__);
|
|
71
|
-
}
|
|
72
|
-
getProperty(name) {
|
|
73
|
-
this[name];
|
|
74
|
-
return this.__properties__[name] || null;
|
|
75
|
-
}
|
|
76
|
-
setProperty(name, value) {
|
|
77
|
-
if (this.__properties__[name]) {
|
|
78
|
-
const old = this.__properties__[name].value;
|
|
79
|
-
this.__properties__[name].setValue(this, value);
|
|
80
|
-
this.eventBus.emit("PropertyChange", {
|
|
81
|
-
name: name,
|
|
82
|
-
old: old,
|
|
83
|
-
current: value,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
exports.PropertyStore = PropertyStore;
|
|
89
|
-
const mixinPropertyStore = (target) => {
|
|
90
|
-
return class extends target {
|
|
91
|
-
constructor() {
|
|
92
|
-
super(...arguments);
|
|
93
|
-
this.__properties__ = {};
|
|
94
|
-
}
|
|
95
|
-
getProperties() {
|
|
96
|
-
return this.__properties__;
|
|
97
|
-
}
|
|
98
|
-
getProperty(name) {
|
|
99
|
-
return this.__properties__[name];
|
|
100
|
-
}
|
|
101
|
-
setProperty(name, value) {
|
|
102
|
-
if (this.__properties__[name]) {
|
|
103
|
-
this.__properties__[name].setValue(this, value);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
};
|
|
108
|
-
exports.mixinPropertyStore = mixinPropertyStore;
|
|
109
|
-
function ensureProperty(schema, instance, propertyKey, propertiesName = "__properties__") {
|
|
110
|
-
if (!instance[propertiesName]) {
|
|
111
|
-
throw new Error(`Property store (${propertiesName}) does not exist in target.`);
|
|
112
|
-
}
|
|
113
|
-
const propertiesMap = instance[propertiesName];
|
|
114
|
-
if (propertyKey in propertiesMap) {
|
|
115
|
-
return propertiesMap[propertyKey];
|
|
116
|
-
}
|
|
117
|
-
const property = (propertiesMap[propertyKey] = new Property(schema));
|
|
118
|
-
property.setValue(instance, schema.defaultValue);
|
|
119
|
-
return property;
|
|
120
|
-
}
|
|
121
|
-
function property({ propertiesName = "__properties__", defaultValue = null, beforeSetterName = null, units = "", minValue = null, maxValue = null, } = {}) {
|
|
122
|
-
return function (target, propertyKey) {
|
|
123
|
-
const root = target;
|
|
124
|
-
if (!(propertiesName in root)) {
|
|
125
|
-
root[propertiesName] = {};
|
|
126
|
-
}
|
|
127
|
-
const propertySchemas = root[propertiesName];
|
|
128
|
-
const newSchema = new PropertySchema(propertyKey, {
|
|
129
|
-
units: units,
|
|
130
|
-
beforeSetterName: beforeSetterName,
|
|
131
|
-
defaultValue: defaultValue,
|
|
132
|
-
minValue: minValue,
|
|
133
|
-
maxValue: maxValue,
|
|
134
|
-
});
|
|
135
|
-
if (propertyKey in propertySchemas) {
|
|
136
|
-
}
|
|
137
|
-
propertySchemas[propertyKey] = newSchema;
|
|
138
|
-
Object.defineProperty(target, propertyKey, {
|
|
139
|
-
get() {
|
|
140
|
-
const property = ensureProperty(newSchema, this, propertyKey, propertiesName);
|
|
141
|
-
return property.value;
|
|
142
|
-
},
|
|
143
|
-
set(newValue) {
|
|
144
|
-
const property = ensureProperty(newSchema, this, propertyKey, propertiesName);
|
|
145
|
-
property.setValue(this, newValue);
|
|
146
|
-
},
|
|
147
|
-
enumerable: true,
|
|
148
|
-
configurable: false,
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
exports.property = property;
|
|
153
|
-
function propertyValidator(propertyName, propertiesName = "__properties__") {
|
|
154
|
-
return function (target, propertyKey, descriptor) {
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
exports.propertyValidator = propertyValidator;
|
|
158
|
-
//# sourceMappingURL=properties.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"properties.js","sourceRoot":"","sources":["../../src/properties.ts"],"names":[],"mappings":"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","sourcesContent":["import { Nullable } from \"./types\";\nimport { EventBus } from \"./comms/bus\";\nimport { ifDefined } from \"./misc\";\n\nexport class PropertySchema {\n // Name of the property\n public name: string;\n public units = \"\";\n public defaultValue: any = null;\n public minValue: Nullable<number> = null;\n public maxValue: Nullable<number> = null;\n public beforeSetterName: Nullable<string> = null;\n\n constructor(name: string, config: any) {\n this.name = name;\n this.beforeSetterName = ifDefined(config.beforeSetterName);\n this.units = ifDefined(config.units);\n this.defaultValue = ifDefined(config.defaultValue);\n this.minValue = ifDefined(config.minValue);\n this.maxValue = ifDefined(config.maxValue);\n }\n\n validate(newValue: number): boolean {\n if (this.minValue != null && newValue < this.minValue) return false;\n if (this.maxValue != null && newValue > this.maxValue) return false;\n // TODO - other validations\n return true;\n }\n}\n\n/**\n * Proprties are values required to fully resolve a component.\n * Like disk needs seek latency or capacity.\n */\nexport class Property {\n // Value of the property\n readonly schema: PropertySchema;\n public currentValue: any = null;\n public beforeSetterFunc: any = null;\n\n constructor(schema: PropertySchema) {\n this.schema = schema;\n }\n\n clone(): Property {\n const out = new Property(this.schema);\n out.currentValue = this.currentValue;\n return out;\n }\n\n get value() {\n return this.currentValue;\n }\n\n setValue(instance: any, value: any): boolean {\n if (this.schema.beforeSetterName != null) {\n if (this.beforeSetterFunc == null) {\n const beforeSetterFunc = instance[this.schema.beforeSetterName];\n if (beforeSetterFunc == null) {\n throw new Error(\"Cannot find beforeSetter function: \" + this.schema.beforeSetterName);\n }\n this.beforeSetterFunc = beforeSetterFunc.bind(instance);\n }\n if (this.beforeSetterFunc(value) == false) {\n // validation failed\n return false;\n }\n }\n if (!this.schema.validate(value)) {\n return false;\n }\n this.currentValue = value;\n return true;\n }\n}\n\nexport class PropertyStore {\n /**\n * The properties for this Element\n */\n readonly __properties__: { [key: string]: Property } = {};\n\n private eventBus = EventBus.getInstance();\n\n getPropertyNames(): string[] {\n // return Object.keys((this.constructor as any).__properties__);\n return Object.keys(this.__properties__);\n }\n\n getProperty(name: string): Nullable<Property> {\n (this as any)[name];\n return this.__properties__[name] || null;\n }\n\n setProperty(name: string, value: any): void {\n if (this.__properties__[name]) {\n const old = this.__properties__[name].value;\n this.__properties__[name].setValue(this, value);\n // TODO: Maybe need a fully qualified name here.\n this.eventBus.emit(\"PropertyChange\", {\n name: name,\n old: old,\n current: value,\n });\n }\n }\n}\n\nexport const mixinPropertyStore = (target: any) => {\n return class extends target {\n /**\n * The properties for this Element\n */\n readonly __properties__: { [key: string]: Property } = {};\n\n getProperties(): { [key: string]: Property } {\n return this.__properties__;\n }\n\n getProperty(name: string): Property {\n return this.__properties__[name];\n }\n\n setProperty(name: string, value: any): void {\n if (this.__properties__[name]) {\n this.__properties__[name].setValue(this, value);\n }\n }\n };\n};\n\ninterface PropertyParams<T> {\n defaultValue?: Nullable<T>;\n propertiesName?: string;\n beforeSetterName?: Nullable<string>;\n units?: string;\n minValue?: Nullable<number>;\n maxValue?: Nullable<number>;\n}\n\nfunction ensureProperty<T>(\n schema: PropertySchema,\n instance: any,\n propertyKey: string,\n propertiesName = \"__properties__\",\n): Property {\n if (!instance[propertiesName]) {\n throw new Error(`Property store (${propertiesName}) does not exist in target.`);\n }\n const propertiesMap = instance[propertiesName];\n if (propertyKey in propertiesMap) {\n return propertiesMap[propertyKey];\n }\n const property = (propertiesMap[propertyKey] = new Property(schema));\n property.setValue(instance, schema.defaultValue);\n return property;\n}\n\n/**\n * property is a decorator factory. It returns a decorator that can be applied to a property.\n * This decorator allows us to have some properties of an Element be treated specially (so it\n * can be reflected, configured/enumerated via UI). This decorator in a way makes our Element\n * attributes metadata-able so we dont have to duplicate a property object and an attribute\n * manually.\n */\nexport function property<T>({\n propertiesName = \"__properties__\",\n defaultValue = null,\n beforeSetterName = null,\n units = \"\",\n minValue = null,\n maxValue = null,\n}: PropertyParams<T> = {}) {\n return function (target: any, propertyKey: string) {\n const root = target; // .constructor;\n if (!(propertiesName in root)) {\n root[propertiesName] = {};\n }\n const propertySchemas = root[propertiesName];\n const newSchema = new PropertySchema(propertyKey, {\n units: units,\n beforeSetterName: beforeSetterName,\n defaultValue: defaultValue,\n minValue: minValue,\n maxValue: maxValue,\n });\n if (propertyKey in propertySchemas) {\n // throw new Error(\"Duplicate property: \" + propertyKey);\n }\n propertySchemas[propertyKey] = newSchema;\n\n Object.defineProperty(target, propertyKey, {\n get() {\n const property = ensureProperty(newSchema, this, propertyKey, propertiesName);\n return property.value;\n },\n set(newValue: any) {\n const property = ensureProperty(newSchema, this, propertyKey, propertiesName);\n property.setValue(this, newValue);\n },\n enumerable: true,\n configurable: false,\n });\n };\n}\n\nexport function propertyValidator<T>(propertyName: string, propertiesName = \"__properties__\") {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n // const property = ensureProperty(this, propertyKey, null, propertiesName);\n // property.setValue(\n };\n}\n"]}
|
package/lib/esm/properties.d.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Nullable } from "./types";
|
|
2
|
-
export declare class PropertySchema {
|
|
3
|
-
name: string;
|
|
4
|
-
units: string;
|
|
5
|
-
defaultValue: any;
|
|
6
|
-
minValue: Nullable<number>;
|
|
7
|
-
maxValue: Nullable<number>;
|
|
8
|
-
beforeSetterName: Nullable<string>;
|
|
9
|
-
constructor(name: string, config: any);
|
|
10
|
-
validate(newValue: number): boolean;
|
|
11
|
-
}
|
|
12
|
-
export declare class Property {
|
|
13
|
-
readonly schema: PropertySchema;
|
|
14
|
-
currentValue: any;
|
|
15
|
-
beforeSetterFunc: any;
|
|
16
|
-
constructor(schema: PropertySchema);
|
|
17
|
-
clone(): Property;
|
|
18
|
-
get value(): any;
|
|
19
|
-
setValue(instance: any, value: any): boolean;
|
|
20
|
-
}
|
|
21
|
-
export declare class PropertyStore {
|
|
22
|
-
readonly __properties__: {
|
|
23
|
-
[key: string]: Property;
|
|
24
|
-
};
|
|
25
|
-
private eventBus;
|
|
26
|
-
getPropertyNames(): string[];
|
|
27
|
-
getProperty(name: string): Nullable<Property>;
|
|
28
|
-
setProperty(name: string, value: any): void;
|
|
29
|
-
}
|
|
30
|
-
export declare const mixinPropertyStore: (target: any) => {
|
|
31
|
-
new (): {
|
|
32
|
-
[x: string]: any;
|
|
33
|
-
readonly __properties__: {
|
|
34
|
-
[key: string]: Property;
|
|
35
|
-
};
|
|
36
|
-
getProperties(): {
|
|
37
|
-
[key: string]: Property;
|
|
38
|
-
};
|
|
39
|
-
getProperty(name: string): Property;
|
|
40
|
-
setProperty(name: string, value: any): void;
|
|
41
|
-
};
|
|
42
|
-
[x: string]: any;
|
|
43
|
-
};
|
|
44
|
-
interface PropertyParams<T> {
|
|
45
|
-
defaultValue?: Nullable<T>;
|
|
46
|
-
propertiesName?: string;
|
|
47
|
-
beforeSetterName?: Nullable<string>;
|
|
48
|
-
units?: string;
|
|
49
|
-
minValue?: Nullable<number>;
|
|
50
|
-
maxValue?: Nullable<number>;
|
|
51
|
-
}
|
|
52
|
-
export declare function property<T>({ propertiesName, defaultValue, beforeSetterName, units, minValue, maxValue, }?: PropertyParams<T>): (target: any, propertyKey: string) => void;
|
|
53
|
-
export declare function propertyValidator<T>(propertyName: string, propertiesName?: string): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
54
|
-
export {};
|