1 | import { CoreTypes } from '../../../core-types';
|
2 | import { Trace } from '../../../trace';
|
3 | import { CSSType, View } from '../../core/view';
|
4 | import { GridLayout } from '../grid-layout';
|
5 | import { Animation } from '../../animation';
|
6 | import { isNumber } from '../../../utils/types';
|
7 | let RootLayoutBase = class RootLayoutBase extends GridLayout {
|
8 | constructor() {
|
9 | super();
|
10 | this.popupViews = [];
|
11 | global.rootLayout = this;
|
12 | }
|
13 | onLoaded() {
|
14 |
|
15 |
|
16 | this.staticChildCount = this.getChildrenCount();
|
17 | super.onLoaded();
|
18 | }
|
19 | _onLivesync(context) {
|
20 | let handled = false;
|
21 | if (this.popupViews.length > 0) {
|
22 | this.closeAll();
|
23 | handled = true;
|
24 | }
|
25 | if (super._onLivesync(context)) {
|
26 | handled = true;
|
27 | }
|
28 | return handled;
|
29 | }
|
30 | |
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | open(view, options = {}) {
|
38 | return new Promise((resolve, reject) => {
|
39 | if (!(view instanceof View)) {
|
40 | return reject(new Error(`Invalid open view: ${view}`));
|
41 | }
|
42 | if (this.hasChild(view)) {
|
43 | return reject(new Error(`${view} has already been added`));
|
44 | }
|
45 | const toOpen = [];
|
46 | const enterAnimationDefinition = options.animation ? options.animation.enterFrom : null;
|
47 |
|
48 | this.popupViews.push({ view: view, options: options });
|
49 | if (options.shadeCover) {
|
50 |
|
51 |
|
52 | if (this.shadeCover) {
|
53 |
|
54 | toOpen.push(this.updateShadeCover(this.shadeCover, options.shadeCover));
|
55 | }
|
56 | else {
|
57 | toOpen.push(this.openShadeCover(options.shadeCover));
|
58 | }
|
59 | }
|
60 | view.opacity = 0;
|
61 | this.insertChild(view, this.getChildrenCount() + 1);
|
62 | toOpen.push(new Promise((res, rej) => {
|
63 | setTimeout(() => {
|
64 |
|
65 | this.applyInitialState(view, enterAnimationDefinition);
|
66 | this.getEnterAnimation(view, enterAnimationDefinition)
|
67 | .play()
|
68 | .then(() => {
|
69 | this.applyDefaultState(view);
|
70 | view.notify({ eventName: 'opened', object: view });
|
71 | res();
|
72 | }, (err) => {
|
73 | rej(new Error(`Error playing enter animation: ${err}`));
|
74 | });
|
75 | });
|
76 | }));
|
77 | Promise.all(toOpen).then(() => {
|
78 | resolve();
|
79 | }, (err) => {
|
80 | reject(err);
|
81 | });
|
82 | });
|
83 | }
|
84 | |
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | close(view, exitTo) {
|
93 | return new Promise((resolve, reject) => {
|
94 | if (!(view instanceof View)) {
|
95 | return reject(new Error(`Invalid close view: ${view}`));
|
96 | }
|
97 | if (!this.hasChild(view)) {
|
98 | return reject(new Error(`Unable to close popup. ${view} not found`));
|
99 | }
|
100 | const toClose = [];
|
101 | const popupIndex = this.getPopupIndex(view);
|
102 | const poppedView = this.popupViews[popupIndex];
|
103 | const cleanupAndFinish = () => {
|
104 | view.notify({ eventName: 'closed', object: view });
|
105 | this.removeChild(view);
|
106 | resolve();
|
107 | };
|
108 |
|
109 | const exitAnimationDefinition = exitTo || poppedView?.options?.animation?.exitTo;
|
110 |
|
111 | this.popupViews.splice(popupIndex, 1);
|
112 | toClose.push(new Promise((res, rej) => {
|
113 | if (exitAnimationDefinition) {
|
114 | this.getExitAnimation(view, exitAnimationDefinition)
|
115 | .play()
|
116 | .then(res, (err) => {
|
117 | rej(new Error(`Error playing exit animation: ${err}`));
|
118 | });
|
119 | }
|
120 | else {
|
121 | res();
|
122 | }
|
123 | }));
|
124 | if (this.shadeCover) {
|
125 |
|
126 | if (this.popupViews.length) {
|
127 | if (!poppedView?.options?.shadeCover?.ignoreShadeRestore) {
|
128 | const shadeCoverOptions = this.popupViews[this.popupViews.length - 1].options?.shadeCover;
|
129 | if (shadeCoverOptions) {
|
130 | toClose.push(this.updateShadeCover(this.shadeCover, shadeCoverOptions));
|
131 | }
|
132 | }
|
133 | }
|
134 | else {
|
135 |
|
136 | toClose.push(this.closeShadeCover(poppedView?.options?.shadeCover));
|
137 | }
|
138 | }
|
139 | Promise.all(toClose).then(() => {
|
140 | cleanupAndFinish();
|
141 | }, (err) => {
|
142 | reject(err);
|
143 | });
|
144 | });
|
145 | }
|
146 | closeAll() {
|
147 | const toClose = [];
|
148 | const views = this.popupViews.map((popupView) => popupView.view);
|
149 |
|
150 | for (const view of views) {
|
151 | toClose.push(this.close(view));
|
152 | }
|
153 | return Promise.all(toClose);
|
154 | }
|
155 | getShadeCover() {
|
156 | return this.shadeCover;
|
157 | }
|
158 | openShadeCover(options = {}) {
|
159 | return new Promise((resolve) => {
|
160 | if (this.shadeCover) {
|
161 | if (Trace.isEnabled()) {
|
162 | Trace.write(`RootLayout shadeCover already open.`, Trace.categories.Layout, Trace.messageType.warn);
|
163 | }
|
164 | resolve();
|
165 | }
|
166 | else {
|
167 |
|
168 | const shadeCover = this.createShadeCover();
|
169 | shadeCover.on('loaded', () => {
|
170 | this._initShadeCover(shadeCover, options);
|
171 | this.updateShadeCover(shadeCover, options).then(() => {
|
172 | resolve();
|
173 | });
|
174 | });
|
175 | this.shadeCover = shadeCover;
|
176 |
|
177 | this.insertChild(this.shadeCover, this.staticChildCount + 1);
|
178 | }
|
179 | });
|
180 | }
|
181 | closeShadeCover(shadeCoverOptions = {}) {
|
182 | return new Promise((resolve) => {
|
183 |
|
184 | if (this.shadeCover) {
|
185 | return this._closeShadeCover(this.shadeCover, shadeCoverOptions).then(() => {
|
186 | if (this.shadeCover) {
|
187 | this.shadeCover.off('loaded');
|
188 | if (this.shadeCover.parent) {
|
189 | this.removeChild(this.shadeCover);
|
190 | }
|
191 | }
|
192 | this.shadeCover = null;
|
193 |
|
194 | this._cleanupPlatformShadeCover();
|
195 | resolve();
|
196 | });
|
197 | }
|
198 | resolve();
|
199 | });
|
200 | }
|
201 | topmost() {
|
202 | return this.popupViews.length ? this.popupViews[this.popupViews.length - 1].view : null;
|
203 | }
|
204 |
|
205 | bringToFront(view, animated = false) {
|
206 | return new Promise((resolve, reject) => {
|
207 | if (!(view instanceof View)) {
|
208 | return reject(new Error(`Invalid bringToFront view: ${view}`));
|
209 | }
|
210 | if (!this.hasChild(view)) {
|
211 | return reject(new Error(`${view} not found or already at topmost`));
|
212 | }
|
213 | const popupIndex = this.getPopupIndex(view);
|
214 |
|
215 | if (popupIndex < 0 || popupIndex == this.popupViews.length - 1) {
|
216 | return reject(new Error(`${view} not found or already at topmost`));
|
217 | }
|
218 |
|
219 | const currentView = this.popupViews[this.getPopupIndex(view)];
|
220 | this.popupViews.splice(this.getPopupIndex(view), 1);
|
221 | this.popupViews.push(currentView);
|
222 | const exitAnimation = this.getViewExitState(view);
|
223 | if (animated && exitAnimation) {
|
224 | this.getExitAnimation(view, exitAnimation)
|
225 | .play()
|
226 | .then(() => {
|
227 | this._bringToFront(view);
|
228 | const initialState = this.getViewInitialState(currentView.view);
|
229 | if (initialState) {
|
230 | this.applyInitialState(view, initialState);
|
231 | this.getEnterAnimation(view, initialState)
|
232 | .play()
|
233 | .then(() => {
|
234 | this.applyDefaultState(view);
|
235 | })
|
236 | .catch((ex) => {
|
237 | reject(new Error(`Error playing enter animation: ${ex}`));
|
238 | });
|
239 | }
|
240 | else {
|
241 | this.applyDefaultState(view);
|
242 | }
|
243 | })
|
244 | .catch((ex) => {
|
245 | this._bringToFront(view);
|
246 | reject(new Error(`Error playing exit animation: ${ex}`));
|
247 | });
|
248 | }
|
249 | else {
|
250 | this._bringToFront(view);
|
251 | }
|
252 |
|
253 | const shadeCoverOptions = currentView?.options?.shadeCover;
|
254 | if (shadeCoverOptions) {
|
255 | this.updateShadeCover(this.shadeCover, shadeCoverOptions);
|
256 | }
|
257 | resolve();
|
258 | });
|
259 | }
|
260 | getPopupIndex(view) {
|
261 | return this.popupViews.findIndex((popupView) => popupView.view === view);
|
262 | }
|
263 | getViewInitialState(view) {
|
264 | const popupIndex = this.getPopupIndex(view);
|
265 | if (popupIndex === -1) {
|
266 | return;
|
267 | }
|
268 | const initialState = this.popupViews[popupIndex]?.options?.animation?.enterFrom;
|
269 | if (!initialState) {
|
270 | return;
|
271 | }
|
272 | return initialState;
|
273 | }
|
274 | getViewExitState(view) {
|
275 | const popupIndex = this.getPopupIndex(view);
|
276 | if (popupIndex === -1) {
|
277 | return;
|
278 | }
|
279 | const exitAnimation = this.popupViews[popupIndex]?.options?.animation?.exitTo;
|
280 | if (!exitAnimation) {
|
281 | return;
|
282 | }
|
283 | return exitAnimation;
|
284 | }
|
285 | applyInitialState(targetView, enterFrom) {
|
286 | const animationOptions = {
|
287 | ...defaultTransitionAnimation,
|
288 | ...(enterFrom || {}),
|
289 | };
|
290 | targetView.translateX = animationOptions.translateX;
|
291 | targetView.translateY = animationOptions.translateY;
|
292 | targetView.scaleX = animationOptions.scaleX;
|
293 | targetView.scaleY = animationOptions.scaleY;
|
294 | targetView.rotate = animationOptions.rotate;
|
295 | targetView.opacity = animationOptions.opacity;
|
296 | }
|
297 | applyDefaultState(targetView) {
|
298 | targetView.translateX = 0;
|
299 | targetView.translateY = 0;
|
300 | targetView.scaleX = 1;
|
301 | targetView.scaleY = 1;
|
302 | targetView.rotate = 0;
|
303 | targetView.opacity = 1;
|
304 | }
|
305 | getEnterAnimation(targetView, enterFrom) {
|
306 | const animationOptions = {
|
307 | ...defaultTransitionAnimation,
|
308 | ...(enterFrom || {}),
|
309 | };
|
310 | return new Animation([
|
311 | {
|
312 | target: targetView,
|
313 | translate: { x: 0, y: 0 },
|
314 | scale: { x: 1, y: 1 },
|
315 | rotate: 0,
|
316 | opacity: 1,
|
317 | duration: animationOptions.duration,
|
318 | curve: animationOptions.curve,
|
319 | },
|
320 | ]);
|
321 | }
|
322 | getExitAnimation(targetView, exitTo) {
|
323 | return new Animation([this.getExitAnimationDefinition(targetView, exitTo)]);
|
324 | }
|
325 | getExitAnimationDefinition(targetView, exitTo) {
|
326 | return {
|
327 | target: targetView,
|
328 | ...defaultTransitionAnimation,
|
329 | ...(exitTo || {}),
|
330 | translate: { x: isNumber(exitTo.translateX) ? exitTo.translateX : defaultTransitionAnimation.translateX, y: isNumber(exitTo.translateY) ? exitTo.translateY : defaultTransitionAnimation.translateY },
|
331 | scale: { x: isNumber(exitTo.scaleX) ? exitTo.scaleX : defaultTransitionAnimation.scaleX, y: isNumber(exitTo.scaleY) ? exitTo.scaleY : defaultTransitionAnimation.scaleY },
|
332 | };
|
333 | }
|
334 | createShadeCover() {
|
335 | const shadeCover = new GridLayout();
|
336 | shadeCover.verticalAlignment = 'bottom';
|
337 | return shadeCover;
|
338 | }
|
339 | updateShadeCover(shade, shadeOptions = {}) {
|
340 | if (shadeOptions.tapToClose !== undefined && shadeOptions.tapToClose !== null) {
|
341 | shade.off('tap');
|
342 | if (shadeOptions.tapToClose) {
|
343 | shade.on('tap', () => {
|
344 | this.closeAll();
|
345 | });
|
346 | }
|
347 | }
|
348 | return this._updateShadeCover(shade, shadeOptions);
|
349 | }
|
350 | hasChild(view) {
|
351 | return this.getChildIndex(view) >= 0;
|
352 | }
|
353 | _bringToFront(view) { }
|
354 | _initShadeCover(view, shadeOption) { }
|
355 | _updateShadeCover(view, shadeOption) {
|
356 | return new Promise(() => { });
|
357 | }
|
358 | _closeShadeCover(view, shadeOptions) {
|
359 | return new Promise(() => { });
|
360 | }
|
361 | _cleanupPlatformShadeCover() { }
|
362 | };
|
363 | RootLayoutBase = __decorate([
|
364 | CSSType('RootLayout'),
|
365 | __metadata("design:paramtypes", [])
|
366 | ], RootLayoutBase);
|
367 | export { RootLayoutBase };
|
368 | export function getRootLayout() {
|
369 | return global.rootLayout;
|
370 | }
|
371 | export const defaultTransitionAnimation = {
|
372 | translateX: 0,
|
373 | translateY: 0,
|
374 | scaleX: 1,
|
375 | scaleY: 1,
|
376 | rotate: 0,
|
377 | opacity: 1,
|
378 | duration: 300,
|
379 | curve: CoreTypes.AnimationCurve.easeIn,
|
380 | };
|
381 | export const defaultShadeCoverTransitionAnimation = {
|
382 | ...defaultTransitionAnimation,
|
383 | opacity: 0,
|
384 | };
|
385 | export const defaultShadeCoverOptions = {
|
386 | opacity: 0.5,
|
387 | color: '#000000',
|
388 | tapToClose: true,
|
389 | animation: {
|
390 | enterFrom: defaultShadeCoverTransitionAnimation,
|
391 | exitTo: defaultShadeCoverTransitionAnimation,
|
392 | },
|
393 | ignoreShadeRestore: false,
|
394 | };
|
395 |
|
\ | No newline at end of file |