diff --git a/src/components/viewManager/package.json b/src/components/viewManager/package.json new file mode 100644 index 0000000000..819371af71 --- /dev/null +++ b/src/components/viewManager/package.json @@ -0,0 +1,3 @@ +{ + "main": "viewManager.js" +} \ No newline at end of file diff --git a/src/components/viewManager/viewContainer.css b/src/components/viewManager/viewContainer.css new file mode 100644 index 0000000000..edfa101976 --- /dev/null +++ b/src/components/viewManager/viewContainer.css @@ -0,0 +1,69 @@ +.mainAnimatedPage { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + contain: layout style size; + /* Can't use will-change because it causes the alpha picker to move when the page scrolls*/ + /*will-change: transform;*/ +} + +@keyframes view-fadeout { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} +@keyframes view-fadein { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes view-slideleft { + from { + transform: translate3d(100%, 0, 0); + } + + to { + transform: none; + } +} + +@keyframes view-slideleft-r { + from { + transform: none; + } + + to { + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes view-slideright { + from { + transform: translate3d(-100%, 0, 0); + } + + to { + transform: none; + } +} + +@keyframes view-slideright-r { + from { + transform: none; + } + + to { + transform: translate3d(100%, 0, 0); + } +} \ No newline at end of file diff --git a/src/components/viewmanager/viewmanager.js b/src/components/viewManager/viewManager.js similarity index 100% rename from src/components/viewmanager/viewmanager.js rename to src/components/viewManager/viewManager.js diff --git a/src/components/viewmanager/viewManager.js b/src/components/viewmanager/viewManager.js new file mode 100644 index 0000000000..606c7e98c1 --- /dev/null +++ b/src/components/viewmanager/viewManager.js @@ -0,0 +1,183 @@ +define(['viewcontainer', 'focusManager', 'queryString', 'layoutManager'], function (viewcontainer, focusManager, queryString, layoutManager) { + 'use strict'; + + var currentView; + var dispatchPageEvents; + + viewcontainer.setOnBeforeChange(function (newView, isRestored, options) { + + var lastView = currentView; + if (lastView) { + + var beforeHideResult = dispatchViewEvent(lastView, null, 'viewbeforehide', true); + + if (!beforeHideResult) { + // todo: cancel + } + } + + var eventDetail = getViewEventDetail(newView, options, isRestored); + + if (!newView.initComplete) { + newView.initComplete = true; + + if (options.controllerFactory) { + + // Use controller method + var controller = new options.controllerFactory(newView, eventDetail.detail.params); + } + + if (!options.controllerFactory || dispatchPageEvents) { + dispatchViewEvent(newView, eventDetail, 'viewinit'); + } + } + + dispatchViewEvent(newView, eventDetail, 'viewbeforeshow'); + }); + + function onViewChange(view, options, isRestore) { + + var lastView = currentView; + if (lastView) { + dispatchViewEvent(lastView, null, 'viewhide'); + } + + currentView = view; + + var eventDetail = getViewEventDetail(view, options, isRestore); + + if (!isRestore) { + if (options.autoFocus !== false) { + focusManager.autoFocus(view); + } + } + else if (!layoutManager.mobile) { + if (view.activeElement && document.body.contains(view.activeElement) && focusManager.isCurrentlyFocusable(view.activeElement)) { + focusManager.focus(view.activeElement); + } else { + focusManager.autoFocus(view); + } + } + + view.dispatchEvent(new CustomEvent('viewshow', eventDetail)); + + if (dispatchPageEvents) { + view.dispatchEvent(new CustomEvent('pageshow', eventDetail)); + } + } + + function getProperties(view) { + var props = view.getAttribute('data-properties'); + + if (props) { + return props.split(','); + } + + return []; + } + + function dispatchViewEvent(view, eventInfo, eventName, isCancellable) { + + if (!eventInfo) { + eventInfo = { + detail: { + type: view.getAttribute('data-type'), + properties: getProperties(view) + }, + bubbles: true, + cancelable: isCancellable + }; + } + + eventInfo.cancelable = isCancellable || false; + + var eventResult = view.dispatchEvent(new CustomEvent(eventName, eventInfo)); + + if (dispatchPageEvents) { + eventInfo.cancelable = false; + view.dispatchEvent(new CustomEvent(eventName.replace('view', 'page'), eventInfo)); + } + + return eventResult; + } + + function getViewEventDetail(view, options, isRestore) { + + var url = options.url; + var index = url.indexOf('?'); + var params = index === -1 ? {} : queryString.parse(url.substring(index + 1)); + + return { + detail: { + type: view.getAttribute('data-type'), + properties: getProperties(view), + params: params, + isRestored: isRestore, + state: options.state, + + // The route options + options: options.options || {} + }, + bubbles: true, + cancelable: false + }; + } + + function resetCachedViews() { + // Reset all cached views whenever the skin changes + viewcontainer.reset(); + } + + document.addEventListener('skinunload', resetCachedViews); + + function ViewManager() { + } + + ViewManager.prototype.loadView = function (options) { + + var lastView = currentView; + + // Record the element that has focus + if (lastView) { + lastView.activeElement = document.activeElement; + } + + if (options.cancel) { + return; + } + + viewcontainer.loadView(options).then(function (view) { + + onViewChange(view, options); + }); + }; + + ViewManager.prototype.tryRestoreView = function (options, onViewChanging) { + + if (options.cancel) { + return Promise.reject({ cancelled: true }); + } + + // Record the element that has focus + if (currentView) { + currentView.activeElement = document.activeElement; + } + + return viewcontainer.tryRestoreView(options).then(function (view) { + + onViewChanging(); + onViewChange(view, options, true); + + }); + }; + + ViewManager.prototype.currentView = function () { + return currentView; + }; + + ViewManager.prototype.dispatchPageEvents = function (value) { + dispatchPageEvents = value; + }; + + return new ViewManager(); +});