174 lines
4.9 KiB
JavaScript
174 lines
4.9 KiB
JavaScript
#include args.js
|
|
#include uniqstr.js
|
|
|
|
const MamiTabsControl = function(options) {
|
|
options = MamiArgs('options', options, define => {
|
|
define('onAdd').required().type('function').done();
|
|
define('onRemove').required().type('function').done();
|
|
define('onSwitch').required().type('function').done();
|
|
});
|
|
|
|
const onAdd = options.onAdd,
|
|
onRemove = options.onRemove,
|
|
onSwitch = options.onSwitch;
|
|
|
|
const tabs = new Map;
|
|
let currentTab;
|
|
|
|
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';
|
|
|
|
return element;
|
|
};
|
|
|
|
const doTransition = async (transition, ctx) => {
|
|
if(transition === undefined)
|
|
return;
|
|
|
|
if(typeof transition !== 'function')
|
|
return;
|
|
|
|
await transition(ctx);
|
|
};
|
|
|
|
const getTabInfoAndId = tabInfoOrId => {
|
|
const result = {
|
|
id: undefined,
|
|
info: undefined,
|
|
};
|
|
|
|
const type = typeof tabInfoOrId;
|
|
if(type === 'string') {
|
|
result.id = tabInfoOrId;
|
|
result.info = tabs.get(result.id);
|
|
} else if(type === 'object') {
|
|
result.info = tabInfoOrId;
|
|
result.id = getExistingTabId(result.info);
|
|
} else throw 'tabInfoOrId must be an object or a string';
|
|
|
|
if(result.id === undefined || result.info === undefined)
|
|
throw 'tab not found';
|
|
|
|
return result;
|
|
};
|
|
|
|
const switchTab = async (tabInfoOrId, transition) => {
|
|
const result = getTabInfoAndId(tabInfoOrId);
|
|
|
|
const prevTab = currentTab;
|
|
currentTab = result.info;
|
|
|
|
const cbCtx = {
|
|
id: result.id,
|
|
info: result.info,
|
|
elem: extractElement(result.info),
|
|
from: undefined,
|
|
};
|
|
|
|
if(prevTab !== undefined) {
|
|
const prev = getTabInfoAndId(prevTab);
|
|
cbCtx.from = {
|
|
id: prev.id,
|
|
info: prev.info,
|
|
elem: extractElement(prev.info),
|
|
};
|
|
}
|
|
|
|
await onSwitch(cbCtx);
|
|
|
|
if(typeof result.info.onTabForeground === 'function')
|
|
await result.info.onTabForeground();
|
|
|
|
if(prevTab !== undefined) {
|
|
if(typeof prevTab.onTabBackground === 'function')
|
|
await prevTab.onTabBackground();
|
|
|
|
await doTransition(transition, cbCtx);
|
|
}
|
|
};
|
|
|
|
const getExistingTabId = tabInfo => {
|
|
if(tabInfo !== undefined)
|
|
for(const [key, value] of tabs.entries())
|
|
if(value === tabInfo)
|
|
return key;
|
|
|
|
return undefined;
|
|
};
|
|
|
|
return {
|
|
switch: switchTab,
|
|
current: () => currentTab,
|
|
currentId: () => getExistingTabId(currentTab),
|
|
currentElement: () => {
|
|
if(currentTab === undefined)
|
|
return undefined;
|
|
|
|
return extractElement(currentTab);
|
|
},
|
|
count: () => tabs.size,
|
|
has: tabInfo => getExistingTabId(tabInfo) !== undefined,
|
|
add: async (tabInfo, title) => {
|
|
if(typeof tabInfo !== 'object')
|
|
throw 'tabInfo must be an object';
|
|
|
|
let tabId = getExistingTabId(tabInfo);
|
|
if(tabId !== undefined)
|
|
throw 'tabInfo has already been added';
|
|
|
|
tabId = MamiUniqueStr(8);
|
|
tabs.set(tabId, tabInfo);
|
|
|
|
if(title !== 'string' && 'getTitle' in tabInfo)
|
|
title = tabInfo.getTitle();
|
|
|
|
const element = extractElement(tabInfo);
|
|
|
|
await onAdd({
|
|
id: tabId,
|
|
info: tabInfo,
|
|
elem: element,
|
|
title: title,
|
|
onClick: () => switchTab(tabInfo),
|
|
});
|
|
|
|
if(typeof tabInfo.onTabAdd === 'function')
|
|
await tabInfo.onTabAdd();
|
|
|
|
if(currentTab === undefined)
|
|
await switchTab(tabInfo);
|
|
|
|
return tabId;
|
|
},
|
|
remove: async tabInfoOrId => {
|
|
const result = getTabInfoAndId(tabInfoOrId);
|
|
|
|
if(!tabs.has(result.id))
|
|
throw 'this tab does not exist';
|
|
|
|
const element = extractElement(result.info);
|
|
tabs.delete(result.id);
|
|
|
|
await onRemove({
|
|
id: result.id,
|
|
info: result.info,
|
|
elem: element,
|
|
});
|
|
|
|
if(typeof tabInfo.onTabRemove === 'function')
|
|
await tabInfo.onTabRemove();
|
|
|
|
if(currentTab === result.info)
|
|
await switchTab(tabs.size > 0 ? tabs.keys().next().value : undefined);
|
|
},
|
|
};
|
|
};
|