react-dyn-tabs
Version:
React dynamic tabs with full API
256 lines (255 loc) • 9.03 kB
JavaScript
import Helper from '../helper.js';
const {throwMissingParam: missingParamEr, isArray, thorwInvalidParam} = Helper;
export const apiConstructor = function (getDeps, param = {options: {}}, modules = [], Components) {
const {optionsManager, helper, activedTabsHistory} = getDeps.call(this, param.options);
helper.setNoneEnumProps(this, {optionsManager, helper, activedTabsHistory, userProxy: {}});
this._setUserProxy()
._subscribeOnReadyEvent()
._createReadyFunction()
._subscribeSelectedTabsHistory()
._subscribeCallbacksOptions();
modules.forEach((module) => module(this, Components));
};
const _apiProps = {
_setUserProxy: function () {
const userProxy = {};
for (const prop in this) {
if (prop[0] !== '_' && prop !== 'constructor') {
(function (that) {
const propValue = that[prop];
if (typeof propValue === 'function') {
userProxy[prop] = function () {
const result = propValue.apply(that, arguments);
return result === that ? userProxy : result;
};
} else {
userProxy[prop] = propValue;
}
})(this);
}
}
this.userProxy = userProxy;
return this;
},
_subscribeOnReadyEvent: function () {
this.one('_onReady', () => {
this._isReady = true;
});
return this;
},
_createReadyFunction: function () {
let ready = (fn) => {
if (this._isReady === true) {
fn.call(this.userProxy, this.userProxy);
} else {
this.one('_onReady', () => {
fn.call(this.userProxy, this.userProxy);
});
}
};
ready = ready.bind(this);
this.helper.setNoneEnumProps(this, {ready});
return this;
},
_subscribeSelectedTabsHistory: function () {
this.on('onChange', ({currentData, previousData, closedTabIDs}) => {
for (let i = 0, l = closedTabIDs.length; i < l; i++) {
this.activedTabsHistory.remove(closedTabIDs[i]);
}
const isSwitched = previousData.selectedTabID !== currentData.selectedTabID;
if (isSwitched && this.isOpen(previousData.selectedTabID) && !this.isSelected(previousData.selectedTabID))
this.activedTabsHistory.add(previousData.selectedTabID);
});
return this;
},
_subscribeCallbacksOptions: function () {
const op = this.optionsManager.options;
Object.keys(this._publishers).forEach((eventName) => {
if (eventName[0] !== '_')
this.on(eventName, function () {
op[eventName].apply(this, arguments);
});
});
return this;
},
getOption: function (name) {
return this.optionsManager.getOption(name);
},
setOption: function (name, value) {
this.optionsManager.setOption(name, value);
return this;
},
getPreviousData: function () {
return this.helper.getCopyState(this.previousState);
},
getCopyPerviousData: function () {
return this.getPreviousData();
},
getData: function () {
return this.helper.getCopyState(this.stateRef);
},
getCopyData: function () {
return this.getData();
},
isSelected: function (id = missingParamEr('isSelected')) {
return this.stateRef.selectedTabID == id;
},
isOpen: function (id = missingParamEr('isOpen')) {
return this.stateRef.openTabIDs.indexOf(id) >= 0;
},
_getFlushEffectsPromise: function () {
return new Promise((resolve) => {
this.one('_onFlushEffects', function () {
resolve.apply(this, arguments);
});
});
},
select: function (id = missingParamEr('select')) {
if (id) id = id + ''; //make sure id is string
const result = this._getFlushEffectsPromise();
this._select(id);
return result;
},
_getPreSelectedTabId: function () {
const selectedTabHistory = [...this.activedTabsHistory.tabsId];
let tabID = '';
while (!tabID && selectedTabHistory.length) {
const _tabID = selectedTabHistory.pop();
if (_tabID) {
const _tabData = this.getTab(_tabID);
if (_tabData && !_tabData.disable && this.isOpen(_tabID) && !this.isSelected(_tabID)) tabID = _tabID;
}
}
return tabID;
},
_getPreSiblingTabId: function () {
const {selectedTabID, openTabIDs} = this.stateRef;
const isRightToLeft = true;
const arr = openTabIDs.slice(0, openTabIDs.indexOf(selectedTabID));
return this.helper.filterArrayUntilFirstValue(arr, (id) => !this.getTab(id).disable, isRightToLeft);
},
_getNextSiblingTabId: function () {
const {selectedTabID, openTabIDs} = this.stateRef;
const isRightToLeft = false;
const arr = openTabIDs.slice(openTabIDs.indexOf(selectedTabID) + 1);
return this.helper.filterArrayUntilFirstValue(arr, (id) => !this.getTab(id).disable, isRightToLeft);
},
_findTabIdForSwitching: function () {
let tabId = '';
tabId = this._getPreSelectedTabId();
tabId = tabId || this._getPreSiblingTabId();
tabId = tabId || this._getNextSiblingTabId();
return tabId || '';
},
setTab: function (id, newData = {}) {
this.optionsManager.validateObjectiveTabData(newData).validatePanelComponent(newData);
this._setTab(id, newData);
return this;
},
open: function (tabObj = missingParamEr('open')) {
const newTabObj = this.optionsManager.validateTabData(tabObj);
const result = this._getFlushEffectsPromise();
this._addTab(newTabObj);
this._open(newTabObj.id);
return result;
},
sort: function (tabIDs = missingParamEr('sort')) {
if (!isArray(tabIDs)) {
thorwInvalidParam('sort');
}
const result = this._getFlushEffectsPromise();
this._sort(tabIDs);
return result;
},
__close: function (id) {
const result = this._getFlushEffectsPromise();
this._close(id);
this._removeTab(id);
return result;
},
close: function (id = missingParamEr('close'), switching = true) {
if (id) id = id + ''; //make sure id is string
if (switching && this.isOpen(id) && this.isSelected(id)) {
const _openTabsId = [...this.stateRef.openTabIDs];
_openTabsId.splice(_openTabsId.indexOf(id), 1);
this.select(this._findTabIdForSwitching());
return this.__close(id);
} else return this.__close(id);
},
refresh: function () {
const result = this._getFlushEffectsPromise();
this._refresh();
return result;
},
};
Helper.setNoneEnumProps(_apiProps, {
onChange: function ({newState, oldState, closedTabIDs, openedTabIDs, isSwitched}) {
if (isSwitched || openedTabIDs.length || closedTabIDs.length) {
this.trigger('onChange', this.userProxy, () => {
return [
{
currentData: this.helper.getCopyState(newState),
previousData: this.helper.getCopyState(oldState),
closedTabIDs: closedTabIDs.slice(),
openedTabIDs: openedTabIDs.slice(),
},
];
});
openedTabIDs.length &&
this.trigger('onOpen', this.userProxy, () => {
return [openedTabIDs.slice()];
});
closedTabIDs.length &&
this.trigger('onClose', this.userProxy, () => {
return [closedTabIDs.slice()];
});
if (isSwitched) {
if (newState.selectedTabID && this.activedTabsHistory.tabsId.indexOf(newState.selectedTabID) === -1) {
this.trigger('onFirstSelect', this.userProxy, () => {
return [
{
currentSelectedTabId: newState.selectedTabID,
previousSelectedTabId: oldState.selectedTabID,
},
];
});
}
this.trigger('onSelect', this.userProxy, () => {
return [
{
currentSelectedTabId: newState.selectedTabID,
previousSelectedTabId: oldState.selectedTabID,
},
];
});
}
}
return this;
},
eventHandlerFactory: function ({e, id}) {
const el = e.target,
parentEl = el.parentElement,
{closeClass, tabClass} = this.optionsManager.setting;
if (
el.className.includes(closeClass) &&
parentEl &&
parentEl.lastChild &&
parentEl.lastChild == el &&
parentEl.className.includes(tabClass)
) {
this.getOption('beforeClose').call(this.userProxy, e, id) !== false && this.close(id, true);
} else {
this.getOption('beforeSelect').call(this.userProxy, e, id) !== false && this.select(id);
}
},
getSetting: function (settingName) {
const st = this.optionsManager.setting;
if (Object.prototype.hasOwnProperty.call(st, settingName)) {
if (typeof st[settingName] === 'function') {
return st[settingName].apply(st, Array.prototype.slice.call(arguments, 1));
}
return st[settingName];
}
},
});
export const apiProps = _apiProps;