200 lines
5.7 KiB
JavaScript
200 lines
5.7 KiB
JavaScript
#include args.js
|
|
#include utility.js
|
|
|
|
const MamiViewsControl = function(options) {
|
|
options = MamiArguments.verify(options, [
|
|
MamiArguments.check('body', undefined, value => value instanceof Element, true),
|
|
]);
|
|
|
|
const targetBody = options.body;
|
|
targetBody.classList.toggle('views', true);
|
|
|
|
const views = [];
|
|
|
|
const extractElement = elementInfo => {
|
|
let element;
|
|
if(elementInfo instanceof Element) {
|
|
element = elementInfo;
|
|
} else if('getElement' in elementInfo) {
|
|
element = elementInfo.getElement();
|
|
} else throw 'elementInfo is not a valid type';
|
|
|
|
if(!(element instanceof Element))
|
|
throw 'element is not an instance of Element';
|
|
if(element === targetBody)
|
|
throw 'element may not be the same as targetBody';
|
|
|
|
return element;
|
|
};
|
|
|
|
const doTransition = async (transition, ctx) => {
|
|
if(transition === undefined)
|
|
return;
|
|
|
|
if(typeof transition !== 'function')
|
|
return;
|
|
|
|
await transition(ctx);
|
|
};
|
|
|
|
const updateZIncides = () => {
|
|
let index = 0;
|
|
for(const view of views)
|
|
extractElement(view).style.zIndex = (++index).toString();
|
|
};
|
|
|
|
const push = async (elementInfo, transition) => {
|
|
if(typeof elementInfo !== 'object')
|
|
throw 'elementInfo must be an object';
|
|
|
|
if(!views.includes(elementInfo))
|
|
views.push(elementInfo);
|
|
if(typeof elementInfo.onViewPush === 'function')
|
|
await elementInfo.onViewPush();
|
|
|
|
const element = extractElement(elementInfo);
|
|
element.classList.toggle('views-item', true);
|
|
element.classList.toggle('views-background', false);
|
|
|
|
if(!targetBody.contains(element))
|
|
targetBody.appendChild(element);
|
|
|
|
if(typeof elementInfo.onViewForeground === 'function')
|
|
await elementInfo.onViewForeground();
|
|
|
|
updateZIncides();
|
|
|
|
if(views.length > 1) {
|
|
const prevElemInfo = views[views.length - 2];
|
|
const prevElem = extractElement(prevElemInfo);
|
|
|
|
prevElem.classList.toggle('views-background', true);
|
|
|
|
if(typeof prevElemInfo.onViewBackground === 'function')
|
|
await prevElemInfo.onViewBackground();
|
|
|
|
await doTransition(transition, {
|
|
toInfo: elementInfo,
|
|
toElem: element,
|
|
fromInfo: prevElemInfo,
|
|
fromElem: prevElem,
|
|
});
|
|
}
|
|
};
|
|
|
|
const pop = async transition => {
|
|
const elementInfo = views.pop();
|
|
if(elementInfo === undefined)
|
|
throw 'views is empty';
|
|
|
|
const element = extractElement(elementInfo);
|
|
|
|
element.classList.toggle('views-background', true);
|
|
|
|
if(views.length > 0) {
|
|
const nextElemInfo = views[views.length - 1];
|
|
const nextElem = extractElement(nextElemInfo);
|
|
|
|
nextElem.classList.toggle('views-background', false);
|
|
|
|
if(typeof nextElemInfo.onViewForeground === 'function')
|
|
await nextElemInfo.onViewForeground();
|
|
|
|
await doTransition(transition, {
|
|
toInfo: nextElemInfo,
|
|
toElem: nextElem,
|
|
fromInfo: elementInfo,
|
|
fromElem: element,
|
|
});
|
|
}
|
|
|
|
if(typeof elementInfo.onViewBackground === 'function')
|
|
await elementInfo.onViewBackground();
|
|
|
|
if(targetBody.contains(element))
|
|
targetBody.removeChild(element);
|
|
|
|
if(typeof elementInfo.onViewPop === 'function')
|
|
await elementInfo.onViewPop();
|
|
|
|
updateZIncides();
|
|
|
|
return elementInfo;
|
|
};
|
|
|
|
const current = () => {
|
|
if(views.length < 1)
|
|
return undefined;
|
|
|
|
return views[views.length - 1];
|
|
};
|
|
|
|
const raise = async (elementInfo, transition) => {
|
|
if(typeof elementInfo !== 'object')
|
|
throw 'elementInfo must be an object';
|
|
|
|
if(current() === elementInfo)
|
|
return;
|
|
|
|
const index = views.indexOf(elementInfo);
|
|
if(index >= 0)
|
|
views.splice(index, 1);
|
|
|
|
return await push(elementInfo, transition);
|
|
};
|
|
|
|
return {
|
|
push: push,
|
|
pop: pop,
|
|
raise: raise,
|
|
count: () => views.length,
|
|
current: current,
|
|
currentElement: () => {
|
|
const currentInfo = current();
|
|
if(currentInfo === undefined)
|
|
return undefined;
|
|
|
|
return extractElement(currentInfo);
|
|
},
|
|
includes: elementInfo => views.includes(elementInfo),
|
|
isCurrent: elementInfo => current() === elementInfo,
|
|
unshift: async elementInfo => {
|
|
if(views.length < 1)
|
|
return await push(elementInfo, null);
|
|
|
|
if(typeof elementInfo !== 'object')
|
|
throw 'elementInfo must be an object';
|
|
|
|
const element = extractElement(elementInfo);
|
|
|
|
if(!views.includes(elementInfo))
|
|
views.unshift(elementInfo);
|
|
|
|
element.classList.toggle('views-item', true);
|
|
element.classList.toggle('views-background', true);
|
|
|
|
if(!targetBody.contains(element))
|
|
targetBody.appendChild(element);
|
|
|
|
updateZIncides();
|
|
},
|
|
shift: async () => {
|
|
if(views.length < 2)
|
|
return await pop(null);
|
|
|
|
const elementInfo = views.shift();
|
|
const element = extractElement(elementInfo);
|
|
|
|
if(targetBody.contains(element))
|
|
targetBody.removeChild(element);
|
|
|
|
if(typeof elementInfo.onViewPop === 'function')
|
|
await elementInfo.onViewPop();
|
|
|
|
updateZIncides();
|
|
|
|
return elementInfo;
|
|
},
|
|
};
|
|
};
|