solid-ui
Version:
UI library for writing Solid read-write-web applications
396 lines (315 loc) • 11.9 kB
JavaScript
"use strict";
// SOLID-compaible Tabs widget
//
// - Any Orientation = top, left, bottom, right
// - Selected bodies are hidden not deleted
// - Multiple tab select with Alt key
//
// written 2016-05-27
var tabs = {};
module.exports = tabs;
var UI = {
icons: require('./iconBase'),
log: require('./log'),
ns: require('./ns'),
store: require('./store'),
tabs: tabs,
widgets: require('./widgets')
};
var utils = require('./utils'); // options.subject
// options.orientation 0 top, 1 left, 2 bottom, 3 right
// options.showMain(div, subject) function to show subject in div when tab selected
// options.renderTabSettings function(subject, domContainer)
// options.renderTabSettings like showMain but when user has held Alt down
// options.onClose if given, will present a cancelButton next to tabs that calls this optional method
//
UI.tabs.tabWidget = function (options) {
var kb = UI.store;
var subject = options.subject;
var dom = options.dom;
var box = dom.createElement('div');
var orientation = parseInt(options.orientation || '0');
var backgroundColor = options.backgroundColor || '#ddddcc';
var color;
var flipped = orientation & 2;
var vertical = orientation & 1;
var wholetable = box.appendChild(dom.createElement('table'));
var mainTR, mainTD, tabTR;
var tabContainer, tabElement;
var onClose = options.onClose;
var isLight = function isLight(x) {
var total = 0;
for (var i = 0; i < 3; i++) {
total += parseInt(x.slice(i * 2 + 1, i * 2 + 3), 16);
}
return total > 128 * 3;
};
var colorBlend = function colorBlend(a, b, mix) {
var ca, cb, res;
var str = '#';
var hex = '0123456789abcdef';
for (var i = 0; i < 3; i++) {
ca = parseInt(a.slice(i * 2 + 1, i * 2 + 3), 16);
cb = parseInt(b.slice(i * 2 + 1, i * 2 + 3), 16);
res = ca * (1.0 - mix) + cb * mix; // @@@ rounding
var res2 = parseInt(('' + res).split('.')[0]); // @@ ugh
var h = parseInt(('' + res2 / 16).split('.')[0]); // @@ ugh
var l = parseInt(('' + res2 % 16).split('.')[0]); // @@ ugh
str += hex[h] + hex[l];
} // console.log('Blending colors ' + a + ' with ' + mix + ' of ' + b + ' to give ' + str)
return str;
};
var selectedColor;
if (isLight(backgroundColor)) {
selectedColor = colorBlend(backgroundColor, '#ffffff', 0.3);
color = '#000000';
} else {
selectedColor = colorBlend(backgroundColor, '#000000', 0.3);
color = '#ffffff';
}
var bodyDivStyle = 'resize: both; overflow: scroll; margin:0; border: 0.5em; border-style: solid; border-color: ' + selectedColor + '; padding: 1em; min-width: 30em; min-height: 450px; width:100%;';
if (vertical) {
var onlyTR = wholetable.appendChild(dom.createElement('tr'));
mainTD = dom.createElement('td');
mainTD.setAttribute('style', 'margin: 0;'); // override tabbedtab.css
var tabTD = dom.createElement('td');
tabTD.setAttribute('style', 'margin: 0;');
if (flipped) {
onlyTR.appendChild(mainTD);
onlyTR.appendChild(tabTD);
} else {
onlyTR.appendChild(tabTD);
onlyTR.appendChild(mainTD);
}
tabContainer = tabTD.appendChild(dom.createElement('table'));
tabElement = 'tr'; // tabBar = tabTD // drop zone
// mainTD.appendChild(bodyDiv)
} else {
// horizontal
tabContainer = dom.createElement('tr');
mainTR = wholetable.appendChild(dom.createElement('tr'));
if (flipped) {
mainTR = wholetable.appendChild(dom.createElement('tr'));
tabTR = wholetable.appendChild(dom.createElement('tr'));
} else {
tabTR = wholetable.appendChild(dom.createElement('tr'));
mainTR = wholetable.appendChild(dom.createElement('tr'));
}
tabContainer = tabTR;
mainTD = mainTR.appendChild(dom.createElement('td'));
tabElement = 'td'; // mainTD.appendChild(bodyDiv)
}
var bodyContainer = mainTD.appendChild(dom.createElement('table'));
box.tabContainer = tabContainer;
box.bodyContainer = bodyContainer;
var getItems = function getItems() {
if (options.items) return options.items;
if (options.ordered !== false) {
// default to true
var list = kb.the(subject, options.predicate);
return list.elements;
} else {
return kb.each(subject, options.predicate);
}
};
var corners = ['2em', '2em', '0', '0']; // top left, TR, BR, BL
corners = corners.concat(corners).slice(orientation, orientation + 4);
corners = 'border-radius: ' + corners.join(' ') + ';';
var margins = ['0.3em', '0.3em', '0', '0.3em']; // top, right, bottom, left
margins = margins.concat(margins).slice(orientation, orientation + 4);
margins = 'margin: ' + margins.join(' ') + ';';
var tabStyle = corners + 'padding: 0.7em; max-width: 20em;'; // border: 0.05em 0 0.5em 0.05em; border-color: grey;
tabStyle += 'color: ' + color + ';';
var unselectedStyle = tabStyle + 'opacity: 50%; margin: 0.3em; background-color: ' + backgroundColor + ';'; // @@ rotate border
var selectedStyle = tabStyle + margins + ' background-color: ' + selectedColor + ';';
var shownStyle = '';
var hiddenStyle = shownStyle + 'display: none;';
var resetTabStyle = function resetTabStyle() {
for (var i = 0; i < tabContainer.children.length; i++) {
var _tab = tabContainer.children[i];
if (_tab.classList.contains('unstyled')) {
continue;
}
_tab.firstChild.setAttribute('style', unselectedStyle);
}
};
var resetBodyStyle = function resetBodyStyle() {
for (var i = 0; i < bodyContainer.children.length; i++) {
bodyContainer.children[i].setAttribute('style', hiddenStyle);
}
};
var makeNewSlot = function makeNewSlot(item) {
var ele = dom.createElement(tabElement);
ele.subject = item;
var div = ele.appendChild(dom.createElement('div'));
div.setAttribute('style', unselectedStyle);
div.addEventListener('click', function (e) {
if (!e.metaKey) {
resetTabStyle();
resetBodyStyle();
}
div.setAttribute('style', selectedStyle);
ele.bodyTR.setAttribute('style', shownStyle);
var bodyDiv = ele.bodyTR.firstChild;
if (!bodyDiv) {
bodyDiv = ele.bodyTR.appendChild(dom.createElement('div'));
bodyDiv.setAttribute('style', bodyDivStyle);
}
if (options.renderTabSettings && e.altKey) {
if (bodyDiv.asSetttings !== true) {
bodyDiv.innerHTML = 'loading settings ...' + item;
options.renderTabSettings(bodyDiv, ele.subject);
bodyDiv.asSetttings = true;
}
} else {
if (bodyDiv.asSetttings !== false) {
bodyDiv.innerHTML = 'loading item ...' + item;
options.renderMain(bodyDiv, ele.subject);
bodyDiv.asSetttings = false;
}
}
});
if (options.renderTab) {
options.renderTab(div, item);
} else {
div.textContent = utils.label(item);
}
return ele;
};
var orderedSync = function orderedSync() {
var items = getItems();
if (!vertical) {
mainTD.setAttribute('colspan', items.length + (onClose ? 1 : 0));
}
var slot, i, j, left, right;
var differ = false; // Find how many match at each end
for (left = 0; left < tabContainer.children.length; left++) {
slot = tabContainer.children[left];
if (left >= items.length || slot.subject && !slot.subject.sameTerm(items[left])) {
differ = true;
break;
}
}
if (!differ && items.length === tabContainer.children.length) {
return; // The two just match in order: a case to optimize for
}
for (right = tabContainer.children.length - 1; right >= 0; right--) {
slot = tabContainer.children[right];
j = right - tabContainer.children.length + items.length;
if (slot.subject && !slot.subject.sameTerm(items[j])) {
break;
}
} // The elements left ... right in tabContainer.children do not match
var insertables = items.slice(left, right - tabContainer.children.length + items.length + 1);
while (right >= left) {
// remove extra
tabContainer.removeChild(tabContainer.children[left]);
bodyContainer.removeChild(bodyContainer.children[left]);
right -= 1;
}
for (i = 0; i < insertables.length; i++) {
var newSlot = makeNewSlot(insertables[i]);
var newBodyTR = dom.createElement('tr'); // var newBodyDiv = newBodyTR.appendChild(dom.createElement('div'))
newSlot.bodyTR = newBodyTR;
dom.createElement('tr');
if (left === tabContainer.children.length) {
// None left of original on right
tabContainer.appendChild(newSlot);
bodyContainer.appendChild(newBodyTR); // console.log(' appending new ' + insertables[i])
} else {
// console.log(' inserting at ' + (left + i) + ' new ' + insertables[i])
tabContainer.insertBefore(newSlot, tabContainer.children[left + i]);
bodyContainer.insertBefore(newBodyTR, bodyContainer.children[left + i]);
}
}
if (onClose) {
addCancelButton(tabContainer);
}
}; // UNMAINTAINED
var unorderedSync = function unorderedSync() {
var items = getItems();
if (!vertical) {
mainTD.setAttribute('colspan', items.length + (onClose ? 1 : 0));
}
var slot, i, j, found, pair;
var missing = [];
for (i = 0; i < tabContainer.children.length; i++) {
slot = tabContainer.children[i];
slot.deleteMe = true;
}
for (j = 0; j < items.length; j++) {
found = false;
for (i = 0; i < tabContainer.children.length; i++) {
if (tabContainer.children[i].subject === items[j]) {
found = true;
}
}
if (!found) {
missing.push([j, items[j]]);
}
}
for (j = 0; j < missing.length; j++) {
pair = missing[j];
i = pair[0];
slot = makeNewSlot(pair[1]);
if (i >= tabContainer.length) {
tabContainer.appendChild(slot);
} else {
tabContainer.insertBefore(slot, tabContainer.children[i + 1]);
}
}
for (i = 0; i < tabContainer.children.length; i++) {
slot = tabContainer.children[i];
if (slot.deleteMe) {
tabContainer.removeChild(slot);
}
}
if (onClose) {
addCancelButton(tabContainer);
}
};
var sync = function sync() {
if (options.ordered) {
orderedSync();
} else {
unorderedSync();
}
};
box.refresh = sync;
sync(); // From select-tabs branch by hand
if (!options.startEmpty && tabContainer.children.length && options.selectedTab) {
var tab;
var found = false;
for (var i = 0; i < tabContainer.children.length; i++) {
tab = tabContainer.children[i];
if (tab.firstChild && tab.firstChild.dataset.name === options.selectedTab) {
tab.firstChild.click();
found = true;
}
}
if (!found) {
tabContainer.children[0].firstChild.click(); // Open first tab
}
} else if (!options.startEmpty && tabContainer.children.length) {
tabContainer.children[0].firstChild.click(); // Open first tab
}
return box;
function addCancelButton(tabContainer) {
if (tabContainer.dataset.onCloseSet) {
// @@ TODO: this is only here to make the tests work
// Discussion at https://github.com/solid/solid-ui/pull/110#issuecomment-527080663
var existingCancelButton = tabContainer.querySelector('.unstyled');
tabContainer.removeChild(existingCancelButton);
}
var extraTab = dom.createElement(tabElement);
extraTab.classList.add('unstyled');
if (tabElement === 'td') {
extraTab.style.textAlign = 'right';
}
var cancelButton = UI.widgets.cancelButton(dom, onClose);
extraTab.appendChild(cancelButton);
tabContainer.appendChild(extraTab);
tabContainer.dataset.onCloseSet = 'true';
}
};
//# sourceMappingURL=tabs.js.map