@zcomponent/core 1.14.1-beta → 1.14.3-beta
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/animation/animation.d.ts +3 -5
- package/lib/animation/animation.js +24 -75
- package/lib/animation/layerclip.js +1 -1
- package/lib/animation/tracks/propertytrack.d.ts +2 -0
- package/lib/animation/tracks/propertytrack.js +23 -5
- package/lib/contexts/gesturecontext.d.ts +58 -0
- package/lib/contexts/gesturecontext.js +140 -0
- package/lib/contexts/orientationcontext.d.ts +51 -0
- package/lib/contexts/orientationcontext.js +124 -0
- package/lib/observable.d.ts +2 -0
- package/lib/observable.js +15 -0
- package/lib/types.d.ts +3 -1
- package/lib/types.js +2 -0
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@ export declare class Animation {
|
|
|
34
34
|
private _spotlightLayer?;
|
|
35
35
|
timeSource: (() => number) | undefined;
|
|
36
36
|
private _entityPaths;
|
|
37
|
-
private
|
|
37
|
+
private _defaultValuesByPath;
|
|
38
38
|
/** @internal */
|
|
39
39
|
_pendingEvents: {
|
|
40
40
|
evt: AnimationEvents;
|
|
@@ -49,17 +49,15 @@ export declare class Animation {
|
|
|
49
49
|
removeLayer(l: Layer): void;
|
|
50
50
|
initialize(): void;
|
|
51
51
|
tick(): void;
|
|
52
|
-
|
|
52
|
+
registerEntityProperty(path: string, entity: any, property: string | number): void;
|
|
53
53
|
evaluateTouchedPaths(paths: string[], t?: number): void;
|
|
54
|
-
private _evaluateLayer;
|
|
55
54
|
get spotlightLayer(): Layer | undefined;
|
|
56
55
|
set spotlightLayer(l: Layer | undefined);
|
|
57
56
|
clearCachedDefault(entityID: string, prop: string): void;
|
|
58
57
|
private _emitPendingEvents;
|
|
59
|
-
private _resolvePath;
|
|
60
58
|
start(): void;
|
|
61
59
|
}
|
|
62
60
|
export interface EntityPath {
|
|
63
61
|
entity: any;
|
|
64
|
-
|
|
62
|
+
property: string | number;
|
|
65
63
|
}
|
|
@@ -22,7 +22,7 @@ export class Animation {
|
|
|
22
22
|
this.hasLayers = new Observable(false);
|
|
23
23
|
this._layers = [];
|
|
24
24
|
this._entityPaths = new Map();
|
|
25
|
-
this.
|
|
25
|
+
this._defaultValuesByPath = new Map();
|
|
26
26
|
/** @internal */
|
|
27
27
|
this._pendingEvents = [];
|
|
28
28
|
}
|
|
@@ -87,7 +87,7 @@ export class Animation {
|
|
|
87
87
|
tick() {
|
|
88
88
|
if (!this._initialize)
|
|
89
89
|
return;
|
|
90
|
-
const t = this.timeSource?.() ?? performance.now();
|
|
90
|
+
const t = Math.floor(this.timeSource?.() ?? performance.now());
|
|
91
91
|
const touchedPaths = [];
|
|
92
92
|
if (this._spotlightLayer) {
|
|
93
93
|
this._spotlightLayer.tick(t, touchedPaths);
|
|
@@ -100,66 +100,40 @@ export class Animation {
|
|
|
100
100
|
this.evaluateTouchedPaths(touchedPaths, t);
|
|
101
101
|
this.onTick.emit();
|
|
102
102
|
}
|
|
103
|
-
|
|
104
|
-
this._entityPaths.set(path, { entity,
|
|
103
|
+
registerEntityProperty(path, entity, property) {
|
|
104
|
+
this._entityPaths.set(path, { entity, property });
|
|
105
105
|
}
|
|
106
106
|
evaluateTouchedPaths(paths, t) {
|
|
107
107
|
if (!this._initialize)
|
|
108
108
|
return;
|
|
109
|
-
t = t ?? this.timeSource?.() ?? performance.now();
|
|
109
|
+
t = t ?? Math.floor(this.timeSource?.() ?? performance.now());
|
|
110
110
|
const unique = new Set([...paths]);
|
|
111
111
|
const uniqueAsArray = unique.values();
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
if (!
|
|
112
|
+
for (const entry of uniqueAsArray) {
|
|
113
|
+
const entityPath = this._entityPaths.get(entry);
|
|
114
|
+
if (!entityPath)
|
|
115
115
|
continue;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const prop = entityPath.entity[entityPath.property];
|
|
117
|
+
const propIsObservable = prop instanceof Observable;
|
|
118
|
+
let def = this._defaultValuesByPath.get(entry);
|
|
119
|
+
if (!def) {
|
|
120
|
+
const val = propIsObservable ? prop.value : prop;
|
|
121
|
+
def = new Observable(val);
|
|
122
|
+
this._defaultValuesByPath.set(entry, def);
|
|
120
123
|
}
|
|
121
|
-
let
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
observable = new Observable(val);
|
|
127
|
-
byEntity.set(entry[1][0], observable);
|
|
124
|
+
let val = def.value;
|
|
125
|
+
for (const layer of this._layers) {
|
|
126
|
+
if (!layer.influencedPaths.has(entry))
|
|
127
|
+
continue;
|
|
128
|
+
val = layer.computePathProperty(t, entry, val);
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// Reset our working values to default
|
|
132
|
-
for (const p of resolvedPaths) {
|
|
133
|
-
if (p?.[2])
|
|
134
|
-
p[2].value = undefined;
|
|
135
|
-
}
|
|
136
|
-
if (this._spotlightLayer)
|
|
137
|
-
this._evaluateLayer(t, this._spotlightLayer, resolvedPaths);
|
|
138
|
-
else {
|
|
139
|
-
for (const layer of this._layers)
|
|
140
|
-
this._evaluateLayer(t, layer, resolvedPaths);
|
|
141
|
-
}
|
|
142
|
-
for (const entry of resolvedPaths) {
|
|
143
|
-
if (!entry?.[2])
|
|
144
|
-
continue;
|
|
145
|
-
if (entry[0][entry[1][0]] instanceof Observable)
|
|
146
|
-
entry[0][entry[1][0]].value = entry[2].value;
|
|
130
|
+
if (propIsObservable)
|
|
131
|
+
prop.value = val;
|
|
147
132
|
else
|
|
148
|
-
|
|
133
|
+
entityPath.entity[entityPath.property] = val;
|
|
149
134
|
}
|
|
150
135
|
this._emitPendingEvents();
|
|
151
136
|
}
|
|
152
|
-
_evaluateLayer(t, layer, paths) {
|
|
153
|
-
for (const p of paths) {
|
|
154
|
-
if (!p?.[2])
|
|
155
|
-
continue;
|
|
156
|
-
if (!layer.influencedPaths.has(p[3]))
|
|
157
|
-
continue;
|
|
158
|
-
let val = getResolved(p[2], p[1]);
|
|
159
|
-
val = layer.computePathProperty(t, p[3], val);
|
|
160
|
-
setResolved(p[2], p[1], val);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
137
|
get spotlightLayer() {
|
|
164
138
|
return this._spotlightLayer;
|
|
165
139
|
}
|
|
@@ -181,11 +155,7 @@ export class Animation {
|
|
|
181
155
|
this.hasLayers.value = hasLayers;
|
|
182
156
|
}
|
|
183
157
|
clearCachedDefault(entityID, prop) {
|
|
184
|
-
|
|
185
|
-
for (const entityPath of this._defaultValues.keys()) {
|
|
186
|
-
if (entityPath.indexOf(prefix) === 0)
|
|
187
|
-
this._defaultValues.delete(entityPath);
|
|
188
|
-
}
|
|
158
|
+
this._defaultValuesByPath.delete(entityID + '.' + prop);
|
|
189
159
|
}
|
|
190
160
|
_emitPendingEvents() {
|
|
191
161
|
while (this._pendingEvents.length > 0) {
|
|
@@ -195,12 +165,6 @@ export class Animation {
|
|
|
195
165
|
this[entry.evt].emit(...entry.args);
|
|
196
166
|
}
|
|
197
167
|
}
|
|
198
|
-
_resolvePath(p) {
|
|
199
|
-
const path = this._entityPaths.get(p);
|
|
200
|
-
if (!path || path.pathParts.length === 0)
|
|
201
|
-
return;
|
|
202
|
-
return [path.entity, path.pathParts, undefined, p];
|
|
203
|
-
}
|
|
204
168
|
start() {
|
|
205
169
|
for (const layer of this._layers) {
|
|
206
170
|
for (const clip of Object.values(layer.clips)) {
|
|
@@ -210,18 +174,3 @@ export class Animation {
|
|
|
210
174
|
}
|
|
211
175
|
}
|
|
212
176
|
}
|
|
213
|
-
function getResolved(o, pathParts) {
|
|
214
|
-
let current = o.value;
|
|
215
|
-
for (let i = 1; i < pathParts.length; i++)
|
|
216
|
-
current = current[pathParts[i]];
|
|
217
|
-
return current;
|
|
218
|
-
}
|
|
219
|
-
function setResolved(o, pathParts, val) {
|
|
220
|
-
let target = o;
|
|
221
|
-
let key = 'value';
|
|
222
|
-
for (let i = 1; i < pathParts.length; i++) {
|
|
223
|
-
target = target[key];
|
|
224
|
-
key = pathParts[i];
|
|
225
|
-
}
|
|
226
|
-
target[key] = val;
|
|
227
|
-
}
|
|
@@ -99,7 +99,7 @@ export class LayerClip {
|
|
|
99
99
|
return t1 - t;
|
|
100
100
|
}
|
|
101
101
|
_getTime() {
|
|
102
|
-
return this.timeSource?.() ?? this.animation
|
|
102
|
+
return Math.floor(this.timeSource?.() ?? this.animation.timeSource?.() ?? performance.now());
|
|
103
103
|
}
|
|
104
104
|
queue(opts) {
|
|
105
105
|
this.layer.queue(this, opts);
|
|
@@ -7,6 +7,7 @@ export declare class PropertyTrack extends Track {
|
|
|
7
7
|
private _entity;
|
|
8
8
|
path: string;
|
|
9
9
|
private _property;
|
|
10
|
+
private _path;
|
|
10
11
|
private _keyframes;
|
|
11
12
|
private _keyframesById;
|
|
12
13
|
private _keyframesDirty;
|
|
@@ -30,4 +31,5 @@ export declare class PropertyTrack extends Track {
|
|
|
30
31
|
get sortedWeights(): Keyframe[];
|
|
31
32
|
getFadeLengthForPath(path: string): number;
|
|
32
33
|
computePathProperty(t: number, p: string, valueBefore: any, parentWeight?: number): any;
|
|
34
|
+
private _computePathProperty;
|
|
33
35
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Observable } from '../../observable';
|
|
1
2
|
import { addBlend, interpolate } from '../interpolate';
|
|
2
3
|
import { resolveKeyframes, resolveKeyframeValue, Track } from './track';
|
|
4
|
+
const _scratch = new Observable(undefined);
|
|
3
5
|
export class PropertyTrack extends Track {
|
|
4
6
|
constructor(anim, _entityID, _entity, property, id) {
|
|
5
7
|
super(anim, id);
|
|
@@ -12,13 +14,17 @@ export class PropertyTrack extends Track {
|
|
|
12
14
|
this._weightsById = {};
|
|
13
15
|
this._weightsDirty = false;
|
|
14
16
|
this.blend = 'overlay';
|
|
15
|
-
if (Array.isArray(property))
|
|
17
|
+
if (Array.isArray(property)) {
|
|
18
|
+
this._property = property[0];
|
|
19
|
+
this._path = property.slice(1);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
16
22
|
this._property = property;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.path =
|
|
23
|
+
this._path = [];
|
|
24
|
+
}
|
|
25
|
+
this.path = this._entityID + '.' + this._property;
|
|
20
26
|
this.influencedPaths = [this.path];
|
|
21
|
-
anim.
|
|
27
|
+
anim.registerEntityProperty(this.path, _entity, this._property);
|
|
22
28
|
}
|
|
23
29
|
addKeyframe(k) {
|
|
24
30
|
this._keyframesById[k.id] = k;
|
|
@@ -59,6 +65,18 @@ export class PropertyTrack extends Track {
|
|
|
59
65
|
return this.fadeParameters?.time ?? 0;
|
|
60
66
|
}
|
|
61
67
|
computePathProperty(t, p, valueBefore, parentWeight) {
|
|
68
|
+
if (this._path.length === 0) {
|
|
69
|
+
return this._computePathProperty(t, p, valueBefore, parentWeight);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
_scratch.value = valueBefore;
|
|
73
|
+
const v = _scratch.getValueAtPath(this._path);
|
|
74
|
+
const computed = this._computePathProperty(t, p, v, parentWeight);
|
|
75
|
+
_scratch.setValueAtPath(this._path, computed);
|
|
76
|
+
return _scratch.value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
_computePathProperty(t, p, valueBefore, parentWeight) {
|
|
62
80
|
const keyframes = this.sortedKeyframes;
|
|
63
81
|
const [before, after] = resolveKeyframes(keyframes, t);
|
|
64
82
|
if (!before)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Context, ContextManager, Event, Observable } from '../index';
|
|
2
|
+
export declare enum GestureMode {
|
|
3
|
+
ScaleOnly = "Scale Only",
|
|
4
|
+
RotateOnly = "Rotate Only",
|
|
5
|
+
ScaleRotate = "Scale Rotate"
|
|
6
|
+
}
|
|
7
|
+
interface ConstructionProps {
|
|
8
|
+
/**
|
|
9
|
+
* The HTML element to which touch event listeners are attached.
|
|
10
|
+
* Defaults to canvas.
|
|
11
|
+
*/
|
|
12
|
+
element?: HTMLElement;
|
|
13
|
+
}
|
|
14
|
+
/** @zcontext */
|
|
15
|
+
export declare class GestureContext extends Context<ConstructionProps> {
|
|
16
|
+
private element;
|
|
17
|
+
private canScale;
|
|
18
|
+
private gestureMode;
|
|
19
|
+
private canRotate;
|
|
20
|
+
private initialDistance;
|
|
21
|
+
private initialAngle;
|
|
22
|
+
private oneFingerRotateMode;
|
|
23
|
+
private initialTouchX;
|
|
24
|
+
rotation: Observable<number, never>;
|
|
25
|
+
scale: Observable<number, never>;
|
|
26
|
+
onRotate: Event<[number]>;
|
|
27
|
+
onScale: Event<[number]>;
|
|
28
|
+
/**
|
|
29
|
+
* Defaults to true.
|
|
30
|
+
*/
|
|
31
|
+
enabled: Observable<boolean, never>;
|
|
32
|
+
/**
|
|
33
|
+
* Defaults to 0.5;
|
|
34
|
+
*/
|
|
35
|
+
minScale: number;
|
|
36
|
+
/**
|
|
37
|
+
* Defaults to 2.
|
|
38
|
+
*/
|
|
39
|
+
maxScale: number;
|
|
40
|
+
/**
|
|
41
|
+
* Defaults to 1.
|
|
42
|
+
*/
|
|
43
|
+
rotationSensitivity: number;
|
|
44
|
+
constructor(contextManager: ContextManager, constructorProps: ConstructionProps);
|
|
45
|
+
private onTouchStart;
|
|
46
|
+
private onTouchMove;
|
|
47
|
+
private handleOneFingerRotate;
|
|
48
|
+
private handleTwoFingerGestures;
|
|
49
|
+
private handleScale;
|
|
50
|
+
private handleRotate;
|
|
51
|
+
private onTouchEnd;
|
|
52
|
+
private getDistanceBetweenTouches;
|
|
53
|
+
private getAngleBetweenTouches;
|
|
54
|
+
private attachEventListeners;
|
|
55
|
+
private detachEventListeners;
|
|
56
|
+
dispose(): never;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Context, Event, Observable, useCanvas } from '../index';
|
|
2
|
+
export var GestureMode;
|
|
3
|
+
(function (GestureMode) {
|
|
4
|
+
GestureMode["ScaleOnly"] = "Scale Only";
|
|
5
|
+
GestureMode["RotateOnly"] = "Rotate Only";
|
|
6
|
+
GestureMode["ScaleRotate"] = "Scale Rotate";
|
|
7
|
+
})(GestureMode || (GestureMode = {}));
|
|
8
|
+
/** @zcontext */
|
|
9
|
+
export class GestureContext extends Context {
|
|
10
|
+
constructor(contextManager, constructorProps) {
|
|
11
|
+
super(contextManager, constructorProps);
|
|
12
|
+
this.canScale = true;
|
|
13
|
+
this.gestureMode = GestureMode.ScaleRotate;
|
|
14
|
+
this.canRotate = true;
|
|
15
|
+
this.initialDistance = null;
|
|
16
|
+
this.initialAngle = null;
|
|
17
|
+
this.oneFingerRotateMode = true;
|
|
18
|
+
this.initialTouchX = null;
|
|
19
|
+
this.rotation = new Observable(0);
|
|
20
|
+
this.scale = new Observable(0);
|
|
21
|
+
this.onRotate = new Event();
|
|
22
|
+
this.onScale = new Event();
|
|
23
|
+
/**
|
|
24
|
+
* Defaults to true.
|
|
25
|
+
*/
|
|
26
|
+
this.enabled = new Observable(true);
|
|
27
|
+
/**
|
|
28
|
+
* Defaults to 0.5;
|
|
29
|
+
*/
|
|
30
|
+
this.minScale = 0.5;
|
|
31
|
+
/**
|
|
32
|
+
* Defaults to 2.
|
|
33
|
+
*/
|
|
34
|
+
this.maxScale = 2;
|
|
35
|
+
/**
|
|
36
|
+
* Defaults to 1.
|
|
37
|
+
*/
|
|
38
|
+
this.rotationSensitivity = 1;
|
|
39
|
+
this.onTouchStart = (event) => {
|
|
40
|
+
if (this.oneFingerRotateMode && event.touches.length === 1) {
|
|
41
|
+
this.initialTouchX = event.touches[0].pageX;
|
|
42
|
+
}
|
|
43
|
+
else if (event.touches.length === 2) {
|
|
44
|
+
this.initialDistance = this.getDistanceBetweenTouches(event.touches);
|
|
45
|
+
this.initialAngle = this.getAngleBetweenTouches(event.touches);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
this.onTouchMove = (event) => {
|
|
49
|
+
switch (this.gestureMode) {
|
|
50
|
+
case GestureMode.ScaleOnly:
|
|
51
|
+
if (event.touches.length === 2) {
|
|
52
|
+
this.handleScale(event);
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case GestureMode.RotateOnly:
|
|
56
|
+
if (this.oneFingerRotateMode && event.touches.length === 1) {
|
|
57
|
+
this.handleOneFingerRotate(event);
|
|
58
|
+
}
|
|
59
|
+
else if (event.touches.length === 2) {
|
|
60
|
+
this.handleRotate(event);
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
case GestureMode.ScaleRotate:
|
|
64
|
+
if (this.oneFingerRotateMode && event.touches.length === 1) {
|
|
65
|
+
this.handleOneFingerRotate(event);
|
|
66
|
+
}
|
|
67
|
+
else if (event.touches.length === 2) {
|
|
68
|
+
this.handleTwoFingerGestures(event);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
this.onTouchEnd = (event) => {
|
|
74
|
+
this.initialDistance = null;
|
|
75
|
+
this.initialAngle = null;
|
|
76
|
+
};
|
|
77
|
+
this.element = constructorProps.element ?? useCanvas(contextManager);
|
|
78
|
+
this.enabled.addListener(enabled => {
|
|
79
|
+
if (enabled) {
|
|
80
|
+
this.attachEventListeners();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.detachEventListeners();
|
|
84
|
+
}
|
|
85
|
+
}, undefined, true);
|
|
86
|
+
}
|
|
87
|
+
handleOneFingerRotate(event) {
|
|
88
|
+
if (this.initialTouchX !== null) {
|
|
89
|
+
const currentTouchX = event.touches[0].pageX;
|
|
90
|
+
const rotateAmount = (currentTouchX - this.initialTouchX) * this.rotationSensitivity;
|
|
91
|
+
this.onRotate.emit(rotateAmount);
|
|
92
|
+
this.rotation.value = rotateAmount;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
handleTwoFingerGestures(event) {
|
|
96
|
+
this.handleScale(event);
|
|
97
|
+
this.handleRotate(event);
|
|
98
|
+
}
|
|
99
|
+
handleScale(event) {
|
|
100
|
+
if (this.canScale && this.initialDistance !== null) {
|
|
101
|
+
const currentDistance = this.getDistanceBetweenTouches(event.touches);
|
|
102
|
+
let scaleAmount = currentDistance / this.initialDistance;
|
|
103
|
+
scaleAmount = Math.max(this.minScale, Math.min(scaleAmount, this.maxScale));
|
|
104
|
+
this.onScale.emit(scaleAmount);
|
|
105
|
+
this.scale.value = scaleAmount;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
handleRotate(event) {
|
|
109
|
+
if (this.canRotate && this.initialAngle !== null) {
|
|
110
|
+
const currentAngle = this.getAngleBetweenTouches(event.touches);
|
|
111
|
+
const rotateAmount = currentAngle - this.initialAngle;
|
|
112
|
+
this.onRotate.emit(-rotateAmount);
|
|
113
|
+
this.rotation.value = -rotateAmount;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
getDistanceBetweenTouches(touches) {
|
|
117
|
+
const dx = touches[0].pageX - touches[1].pageX;
|
|
118
|
+
const dy = touches[0].pageY - touches[1].pageY;
|
|
119
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
120
|
+
}
|
|
121
|
+
getAngleBetweenTouches(touches) {
|
|
122
|
+
const dx = touches[1].pageX - touches[0].pageX;
|
|
123
|
+
const dy = touches[1].pageY - touches[0].pageY;
|
|
124
|
+
return (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
125
|
+
}
|
|
126
|
+
attachEventListeners() {
|
|
127
|
+
this.element.addEventListener('touchstart', this.onTouchStart, { passive: true });
|
|
128
|
+
this.element.addEventListener('touchmove', this.onTouchMove, { passive: true });
|
|
129
|
+
this.element.addEventListener('touchend', this.onTouchEnd);
|
|
130
|
+
}
|
|
131
|
+
detachEventListeners() {
|
|
132
|
+
this.element.removeEventListener('touchstart', this.onTouchStart);
|
|
133
|
+
this.element.removeEventListener('touchmove', this.onTouchMove);
|
|
134
|
+
this.element.removeEventListener('touchend', this.onTouchEnd);
|
|
135
|
+
}
|
|
136
|
+
dispose() {
|
|
137
|
+
this.detachEventListeners();
|
|
138
|
+
return super.dispose();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ContextManager, Context } from '../context';
|
|
2
|
+
import { Event } from '../event';
|
|
3
|
+
import { Observable } from '../observable';
|
|
4
|
+
export declare enum Orientation {
|
|
5
|
+
Portrait = "portrait",
|
|
6
|
+
Landscape = "landscape",
|
|
7
|
+
PortraitSecondary = "portrait-secondary",
|
|
8
|
+
LandscapeSecondary = "landscape-secondary"
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* @zcontext
|
|
12
|
+
*/
|
|
13
|
+
export declare class OrientationContext extends Context {
|
|
14
|
+
/**
|
|
15
|
+
* @zui
|
|
16
|
+
*/
|
|
17
|
+
readonly isPortrait: Observable<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* @zui
|
|
20
|
+
*/
|
|
21
|
+
readonly isLandscape: Observable<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* @zui
|
|
24
|
+
*/
|
|
25
|
+
readonly isPortraitSecondary: Observable<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* @zui
|
|
28
|
+
*/
|
|
29
|
+
readonly isLandscapeSecondary: Observable<boolean>;
|
|
30
|
+
readonly onOrientationChange: Event<[Orientation]>;
|
|
31
|
+
readonly orientation: Observable<Orientation>;
|
|
32
|
+
readonly onPortrait: Event<[void]>;
|
|
33
|
+
readonly onLandscape: Event<[void]>;
|
|
34
|
+
readonly onPortraitSecondary: Event<[void]>;
|
|
35
|
+
readonly onLandscapeSecondary: Event<[void]>;
|
|
36
|
+
constructor(contextManager: ContextManager);
|
|
37
|
+
private attachListeners;
|
|
38
|
+
private _onOrientationChange;
|
|
39
|
+
private orientationChangeHandler;
|
|
40
|
+
private getOrientation;
|
|
41
|
+
private getOrientationFromScreen;
|
|
42
|
+
private getOrientationFromWindow;
|
|
43
|
+
private getOrientationFromDimensions;
|
|
44
|
+
dispose: () => never;
|
|
45
|
+
}
|
|
46
|
+
export declare function useOnOrientationChange(mgr: ContextManager): Event<[Orientation]>;
|
|
47
|
+
export declare function getOrientation(mgr: ContextManager): Orientation;
|
|
48
|
+
export declare function isPortrait(mgr: ContextManager): boolean;
|
|
49
|
+
export declare function isLandscape(mgr: ContextManager): boolean;
|
|
50
|
+
export declare function isPortraitSecondary(mgr: ContextManager): boolean;
|
|
51
|
+
export declare function isLandscapeSecondary(mgr: ContextManager): boolean;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Context } from '../context';
|
|
2
|
+
import { Event } from '../event';
|
|
3
|
+
import { Observable } from '../observable';
|
|
4
|
+
export var Orientation;
|
|
5
|
+
(function (Orientation) {
|
|
6
|
+
Orientation["Portrait"] = "portrait";
|
|
7
|
+
Orientation["Landscape"] = "landscape";
|
|
8
|
+
Orientation["PortraitSecondary"] = "portrait-secondary";
|
|
9
|
+
Orientation["LandscapeSecondary"] = "landscape-secondary";
|
|
10
|
+
})(Orientation || (Orientation = {}));
|
|
11
|
+
/**
|
|
12
|
+
* @zcontext
|
|
13
|
+
*/
|
|
14
|
+
export class OrientationContext extends Context {
|
|
15
|
+
constructor(contextManager) {
|
|
16
|
+
super(contextManager, {});
|
|
17
|
+
this.onOrientationChange = new Event();
|
|
18
|
+
this.onPortrait = new Event();
|
|
19
|
+
this.onLandscape = new Event();
|
|
20
|
+
this.onPortraitSecondary = new Event();
|
|
21
|
+
this.onLandscapeSecondary = new Event();
|
|
22
|
+
this.attachListeners = () => {
|
|
23
|
+
this.orientation.addListener(this._onOrientationChange);
|
|
24
|
+
window.addEventListener('resize', this.orientationChangeHandler);
|
|
25
|
+
if ('orientation' in screen && screen.orientation) {
|
|
26
|
+
screen.orientation.addEventListener('change', this.orientationChangeHandler);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
this._onOrientationChange = (orientation) => {
|
|
30
|
+
this.onOrientationChange.emit(orientation);
|
|
31
|
+
this.isPortrait.value = orientation === Orientation.Portrait;
|
|
32
|
+
this.isLandscape.value = orientation === Orientation.Landscape;
|
|
33
|
+
this.isPortraitSecondary.value = orientation === Orientation.PortraitSecondary;
|
|
34
|
+
this.isLandscapeSecondary.value = orientation === Orientation.LandscapeSecondary;
|
|
35
|
+
switch (orientation) {
|
|
36
|
+
case Orientation.Portrait:
|
|
37
|
+
this.onPortrait.emit();
|
|
38
|
+
break;
|
|
39
|
+
case Orientation.Landscape:
|
|
40
|
+
this.onLandscape.emit();
|
|
41
|
+
break;
|
|
42
|
+
case Orientation.PortraitSecondary:
|
|
43
|
+
this.onPortraitSecondary.emit();
|
|
44
|
+
break;
|
|
45
|
+
case Orientation.LandscapeSecondary:
|
|
46
|
+
this.onLandscapeSecondary.emit();
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
this.orientationChangeHandler = () => {
|
|
51
|
+
const newOrientation = this.getOrientation();
|
|
52
|
+
if (this.orientation.value === newOrientation)
|
|
53
|
+
return;
|
|
54
|
+
this.orientation.value = newOrientation;
|
|
55
|
+
};
|
|
56
|
+
this.dispose = () => {
|
|
57
|
+
window.removeEventListener('resize', this.orientationChangeHandler);
|
|
58
|
+
if ('orientation' in screen && screen.orientation)
|
|
59
|
+
screen.orientation.removeEventListener('change', this.orientationChangeHandler);
|
|
60
|
+
this.orientation.removeListener(this._onOrientationChange);
|
|
61
|
+
return super.dispose();
|
|
62
|
+
};
|
|
63
|
+
const currentOrientation = this.getOrientation();
|
|
64
|
+
this.orientation = new Observable(currentOrientation);
|
|
65
|
+
this.isPortrait = new Observable(currentOrientation === Orientation.Portrait);
|
|
66
|
+
this.isLandscape = new Observable(currentOrientation === Orientation.Landscape);
|
|
67
|
+
this.isPortraitSecondary = new Observable(currentOrientation === Orientation.PortraitSecondary);
|
|
68
|
+
this.isLandscapeSecondary = new Observable(currentOrientation === Orientation.LandscapeSecondary);
|
|
69
|
+
this.attachListeners();
|
|
70
|
+
}
|
|
71
|
+
getOrientation() {
|
|
72
|
+
const orientation = this.getOrientationFromScreen() ?? this.getOrientationFromWindow() ?? this.getOrientationFromDimensions();
|
|
73
|
+
return orientation;
|
|
74
|
+
}
|
|
75
|
+
getOrientationFromScreen() {
|
|
76
|
+
const orientationType = screen?.orientation?.type;
|
|
77
|
+
if (orientationType) {
|
|
78
|
+
if (orientationType.includes('portrait')) {
|
|
79
|
+
return orientationType.includes('secondary') ? Orientation.PortraitSecondary : Orientation.Portrait;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return orientationType.includes('secondary') ? Orientation.LandscapeSecondary : Orientation.Landscape;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
getOrientationFromWindow() {
|
|
88
|
+
const windowOrientation = window?.orientation;
|
|
89
|
+
if (typeof windowOrientation === 'number') {
|
|
90
|
+
switch (windowOrientation) {
|
|
91
|
+
case 0:
|
|
92
|
+
return Orientation.Portrait;
|
|
93
|
+
case 90:
|
|
94
|
+
return Orientation.Landscape;
|
|
95
|
+
case 180:
|
|
96
|
+
return Orientation.PortraitSecondary;
|
|
97
|
+
case -90:
|
|
98
|
+
return Orientation.LandscapeSecondary;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
getOrientationFromDimensions() {
|
|
104
|
+
return window.innerWidth > window.innerHeight ? Orientation.Landscape : Orientation.Portrait;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function useOnOrientationChange(mgr) {
|
|
108
|
+
return mgr.get(OrientationContext).onOrientationChange;
|
|
109
|
+
}
|
|
110
|
+
export function getOrientation(mgr) {
|
|
111
|
+
return mgr.get(OrientationContext).orientation.value;
|
|
112
|
+
}
|
|
113
|
+
export function isPortrait(mgr) {
|
|
114
|
+
return mgr.get(OrientationContext).isPortrait.value;
|
|
115
|
+
}
|
|
116
|
+
export function isLandscape(mgr) {
|
|
117
|
+
return mgr.get(OrientationContext).isLandscape.value;
|
|
118
|
+
}
|
|
119
|
+
export function isPortraitSecondary(mgr) {
|
|
120
|
+
return mgr.get(OrientationContext).isPortraitSecondary.value;
|
|
121
|
+
}
|
|
122
|
+
export function isLandscapeSecondary(mgr) {
|
|
123
|
+
return mgr.get(OrientationContext).isLandscapeSecondary.value;
|
|
124
|
+
}
|
package/lib/observable.d.ts
CHANGED
|
@@ -56,5 +56,7 @@ export declare class Observable<Type = never, TypeInternal extends any[] | [] |
|
|
|
56
56
|
set value(v: Resolved<Type, TypeInternal> | undefined);
|
|
57
57
|
addListener(fn: (v: Resolved<Type, TypeInternal>) => void, priority?: number, callImmediately?: boolean): void;
|
|
58
58
|
private _emitValue;
|
|
59
|
+
setValueAtPath(path: (string | number)[], val: any): void;
|
|
60
|
+
getValueAtPath(path: (string | number)[]): any;
|
|
59
61
|
}
|
|
60
62
|
export {};
|
package/lib/observable.js
CHANGED
|
@@ -113,6 +113,21 @@ export class Observable extends Emitter {
|
|
|
113
113
|
_emitValue() {
|
|
114
114
|
this._emit(this._value.proxy);
|
|
115
115
|
}
|
|
116
|
+
setValueAtPath(path, val) {
|
|
117
|
+
let target = this;
|
|
118
|
+
let key = 'value';
|
|
119
|
+
for (let i = 0; i < path.length; i++) {
|
|
120
|
+
target = target[key];
|
|
121
|
+
key = path[i];
|
|
122
|
+
}
|
|
123
|
+
target[key] = val;
|
|
124
|
+
}
|
|
125
|
+
getValueAtPath(path) {
|
|
126
|
+
let current = this.value;
|
|
127
|
+
for (let i = 0; i < path.length; i++)
|
|
128
|
+
current = current[path[i]];
|
|
129
|
+
return current;
|
|
130
|
+
}
|
|
116
131
|
}
|
|
117
132
|
function clone(a, deep) {
|
|
118
133
|
if (!deep) {
|
package/lib/types.d.ts
CHANGED
|
@@ -83,7 +83,9 @@ export declare enum TypeHint {
|
|
|
83
83
|
'angle-radians' = "angle-radians",
|
|
84
84
|
'time-seconds' = "time-seconds",
|
|
85
85
|
'time-milliseconds' = "time-milliseconds",
|
|
86
|
-
'time-hertz' = "time-hertz"
|
|
86
|
+
'time-hertz' = "time-hertz",
|
|
87
|
+
'emailaddress' = "emailaddress",
|
|
88
|
+
'url' = "url"
|
|
87
89
|
}
|
|
88
90
|
export declare enum ValuesType {
|
|
89
91
|
'files' = "files",
|
package/lib/types.js
CHANGED
|
@@ -31,6 +31,8 @@ export var TypeHint;
|
|
|
31
31
|
TypeHint["time-seconds"] = "time-seconds";
|
|
32
32
|
TypeHint["time-milliseconds"] = "time-milliseconds";
|
|
33
33
|
TypeHint["time-hertz"] = "time-hertz";
|
|
34
|
+
TypeHint["emailaddress"] = "emailaddress";
|
|
35
|
+
TypeHint["url"] = "url";
|
|
34
36
|
})(TypeHint || (TypeHint = {}));
|
|
35
37
|
export var ValuesType;
|
|
36
38
|
(function (ValuesType) {
|