solid-ui
Version:
UI library for writing Solid read-write-web applications
448 lines (442 loc) • 18.4 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TabWidgetElement = void 0;
exports.tabWidget = tabWidget;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _widgets = require("./widgets");
var _utils = require("./utils");
var style = _interopRequireWildcard(require("./style"));
var _solidLogic = require("solid-logic");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2["default"])(o), (0, _possibleConstructorReturn2["default"])(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2["default"])(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/**
* @ignore
*/
var ContainerElement = /*#__PURE__*/function (_HTMLElement) {
function ContainerElement() {
var _this;
(0, _classCallCheck2["default"])(this, ContainerElement);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, ContainerElement, [].concat(args));
(0, _defineProperty2["default"])(_this, "asSettings", void 0);
return _this;
}
(0, _inherits2["default"])(ContainerElement, _HTMLElement);
return (0, _createClass2["default"])(ContainerElement);
}(/*#__PURE__*/(0, _wrapNativeSuper2["default"])(HTMLElement));
var TabWidgetElement = exports.TabWidgetElement = /*#__PURE__*/function (_HTMLElement2) {
function TabWidgetElement() {
var _this2;
(0, _classCallCheck2["default"])(this, TabWidgetElement);
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
_this2 = _callSuper(this, TabWidgetElement, [].concat(args));
(0, _defineProperty2["default"])(_this2, "bodyContainer", void 0);
(0, _defineProperty2["default"])(_this2, "refresh", void 0);
(0, _defineProperty2["default"])(_this2, "tabContainer", void 0);
return _this2;
}
(0, _inherits2["default"])(TabWidgetElement, _HTMLElement2);
return (0, _createClass2["default"])(TabWidgetElement);
}(/*#__PURE__*/(0, _wrapNativeSuper2["default"])(HTMLElement));
/**
* @ignore
*/
var TabElement = /*#__PURE__*/function (_HTMLElement3) {
function TabElement() {
var _this3;
(0, _classCallCheck2["default"])(this, TabElement);
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
_this3 = _callSuper(this, TabElement, [].concat(args));
(0, _defineProperty2["default"])(_this3, "bodyTR", void 0);
(0, _defineProperty2["default"])(_this3, "subject", void 0);
return _this3;
}
(0, _inherits2["default"])(TabElement, _HTMLElement3);
return (0, _createClass2["default"])(TabElement);
}(/*#__PURE__*/(0, _wrapNativeSuper2["default"])(HTMLElement));
/**
* Use this widget to generate tabs from triples set in the global store.
*
* [Here you can see examples of the tabs](https://solidos.github.io/solid-ui/examples/tabs/).
*
* It assumes that items to use for tabs will be in a collection by default,
* e.g.:
*
* ```turtle
* :subject :predicate ( :item1 :item2 ) .
* ```
*
* You can override this by setting `ordered: false`, in which case it expects
* unordered triples:
*
* ```turtle
* :subject :predicate :item1, :item 2 .
* ```
*
* Triples that are not ordered in collection are in principle not sorted,
* which means that tabs could change order every time you render the widget.
* But in this case the widget will try to sort it in order to keep it
* consistent.
*
* In both of these cases you need to define options `subject` and `predicate`
* to tell the widget which triples it should be looking for.
*
* Finally you can set items manually, using the `items` option, e.g.:
*
* ```javascript
* {
* items: [
* namedNode('https://domain.tld/#item1'),
* namedNode('https://domain.tld/#item2')
* ]
* }
* ```
*
* When you set items manually you do not need to set `subject` and
* `predicate`.
*
* In any case you probably want to set the renderMain option to specify
* what should be rendered for the various items, e.g.:
*
* ```javascript
* {
* renderMain: (bodyMain, subject) => {
* bodyMain.innerHTML = renderItem(subject)
* }
* }
* ```
*
* **Note:** `renderItem` is a custom function that you need to define yourself.
*
* The option `renderTabSettings` allows you to render a custom view in the
* body container that is shown when you hold the ALT key and click on a
* tab. It works very much like the `renderMain` option:
*
* ```javascript
* {
* renderTabSettings: (bodyMain, subject) => {
* bodyMain.innerHTML = renderTabSettings(subject)
* }
* }
* ```
*
* **Note:** `renderTabSettings` is a custom function that you need to define
* yourself.
*
* By default the widget will try to guess the label by using the
* [[utils.label]] function. If you want to customize this yourself, you can
* use the `renderTab` option:
*
* ```javascript
* {
* renderTab: (tabDiv, subject) => {
* tabDiv.innerText = renderTabText(subject)
* }
* }
* ```
*
* **Note:** `renderTabText` is a custom function you need to define yourself.
*
* The option renderTab is also important if you want to set which tab should
* be selected once the widget is rendered. By default it will simply select
* the first tab, but you can override by setting `dataset.name` on the tab
* and referring to the same string in `selectedTab`:
*
* ```javascript
* {
* renderTab: (tabDiv, subject) => {
* tabDiv.dataset.name = subject.uri
* },
* selectedTab: item2.uri
* }
* ```
*
* You can apply a color to use for tabs and border of the container by using
* option `background-color`. This is #ddddcc by default.
*
* You can override the document object that the widget uses to generate DOM
* elements by setting the option `dom`. This is encouraged to set if you
* intend your functionality to be used in environments that don't provide
* a global `document` object.
*
* If you want to render a close button next to the tabs you can set option
* `onClose` which takes a callback function that is triggered when the
* button is clicked:
*
* ```javascript
* {
* onClose: (event) => {
* // do something that hides the widget altogether
* }
* }
* ```
*
* The option `orientation` allows you to set which side the tabs should be
* located: `'0'` = Top, `'1'` = Left, `'2'` = Bottom, `'3'` = Right
*
* If you don't want to render anything in the body container by default,
* you can set the option `startEmpty` to `true`.
*
* @param options
*/
var tabsDefaultBackgroundColor = '#ddddcc';
function tabWidget(options) {
var subject = options.subject;
var dom = options.dom || document;
var orientation = parseInt(options.orientation || '0');
var backgroundColor = options.backgroundColor || tabsDefaultBackgroundColor;
var flipped = orientation & 2;
var vertical = orientation & 1;
var onClose = options.onClose;
var _getColors = getColors(backgroundColor),
_getColors2 = (0, _slicedToArray2["default"])(_getColors, 2),
selectedColor = _getColors2[0],
color = _getColors2[1];
var bodyMainStyle = "flex: 2; width: auto; height: 100%; border: 0.1em; border-style: solid; border-color: ".concat(selectedColor, "; padding: 1em;");
var rootElement = dom.createElement('div'); // 20200117a
rootElement.setAttribute('style', style.tabsRootElement);
rootElement.style.flexDirection = (vertical ? 'row' : 'column') + (flipped ? '-reverse' : '');
var navElement = rootElement.appendChild(dom.createElement('nav'));
navElement.setAttribute('style', style.tabsNavElement);
var mainElement = rootElement.appendChild(dom.createElement('main'));
mainElement.setAttribute('style', style.tabsMainElement); // override tabbedtab.css
var tabContainer = navElement.appendChild(dom.createElement('ul'));
tabContainer.setAttribute('style', style.tabContainer);
tabContainer.style.flexDirection = "".concat(vertical ? 'column' : 'row');
var tabElement = 'li';
var bodyContainer = mainElement;
rootElement.tabContainer = tabContainer;
rootElement.bodyContainer = bodyContainer;
var corners = ['0.2em', '0.2em', '0', '0']; // top left, TR, BR, BL
var cornersPrepped = corners.concat(corners).slice(orientation, orientation + 4);
var cornersStyle = "border-radius: ".concat(cornersPrepped.join(' '), ";");
var margins = ['0.3em', '0.3em', '0', '0.3em']; // top, right, bottom, left
var marginsPrepped = margins.concat(margins).slice(orientation, orientation + 4);
var marginsStyle = "margin: ".concat(marginsPrepped.join(' '), ";");
var paddingStyle = "padding: ".concat(marginsPrepped.join(' '), ";");
var tabStyle = cornersStyle + "position: relative; padding: 0.7em; max-width: 20em; color: ".concat(color, ";");
var unselectedStyle = "".concat(tabStyle + marginsStyle, " opacity: 50%; background-color: ").concat(backgroundColor, ";");
var selectedStyle = "".concat(tabStyle + marginsStyle, " background-color: ").concat(selectedColor, ";");
var shownStyle = 'height: 100%; width: 100%;';
var hiddenStyle = shownStyle + 'display: none;';
rootElement.refresh = orderedSync;
orderedSync();
if (!options.startEmpty && tabContainer.children.length && options.selectedTab) {
var selectedTab0 = Array.from(tabContainer.children) // Version left for compatibility with ??
.map(function (tab) {
return tab.firstChild;
}).find(function (tab) {
return tab.dataset.name === options.selectedTab;
});
var selectedTabURI = options.selectedTab.uri;
var selectedTab1 = Array.from(tabContainer.children)
// @ts-ignore
.find(function (tab) {
return tab.subject &&
// @ts-ignore
tab.subject.uri &&
// @ts-ignore
tab.subject.uri === selectedTabURI;
});
var tab = selectedTab1 || selectedTab0 || tabContainer.children[0];
var clickMe = tab.firstChild;
// @ts-ignore
if (clickMe) clickMe.click();
} else if (!options.startEmpty) {
tabContainer.children[0].firstChild.click(); // Open first tab
}
return rootElement;
function addCancelButton(tabContainer) {
if (tabContainer.dataset.onCloseSet) {
// @@ TODO: this is only here to make the browser tests work
// Discussion at https://github.com/solidos/solid-ui/pull/110#issuecomment-527080663
var existingCancelButton = tabContainer.querySelector('.unstyled');
tabContainer.removeChild(existingCancelButton);
}
var extraTab = dom.createElement(tabElement);
extraTab.classList.add('unstyled');
var tabCancelButton = (0, _widgets.cancelButton)(dom, onClose);
tabCancelButton.setAttribute('style', tabCancelButton.getAttribute('style') + paddingStyle);
extraTab.appendChild(tabCancelButton);
tabContainer.appendChild(extraTab);
tabContainer.dataset.onCloseSet = 'true';
}
function getItems() {
if (options.items) return options.items;
if (options.ordered !== false) {
// options.ordered defaults to true
return _solidLogic.store.the(subject, options.predicate).elements;
} else {
return _solidLogic.store.each(subject, options.predicate);
}
}
function makeNewSlot(item) {
var ele = dom.createElement(tabElement);
ele.setAttribute('style', unselectedStyle);
ele.subject = item;
var div = ele.appendChild(dom.createElement('button'));
div.setAttribute('style', style.makeNewSlot);
div.onclick = function () {
resetTabStyle();
resetBodyStyle();
ele.setAttribute('style', selectedStyle);
if (!ele.bodyTR) return;
ele.bodyTR.setAttribute('style', shownStyle);
var bodyMain = getOrCreateContainerElement(ele);
if (options.renderMain && ele.subject && bodyMain.asSettings !== false) {
bodyMain.innerHTML = 'loading item ...' + item;
options.renderMain(bodyMain, ele.subject);
bodyMain.asSettings = false;
}
};
if (options.renderTabSettings && ele.subject) {
var ellipsis = dom.createElement('button');
ellipsis.textContent = '...';
ellipsis.setAttribute('style', style.ellipsis);
ellipsis.onclick = function () {
resetTabStyle();
resetBodyStyle();
ele.setAttribute('style', selectedStyle);
if (!ele.bodyTR) return;
ele.bodyTR.setAttribute('style', shownStyle);
var bodyMain = getOrCreateContainerElement(ele);
if (options.renderTabSettings && ele.subject && bodyMain.asSettings !== true) {
bodyMain.innerHTML = 'loading settings ...' + item;
options.renderTabSettings(bodyMain, ele.subject);
bodyMain.asSettings = true;
}
};
ele.appendChild(ellipsis);
}
if (options.renderTab) {
options.renderTab(div, item);
} else {
div.innerHTML = (0, _utils.label)(item);
}
return ele;
function getOrCreateContainerElement(ele) {
var _ele$bodyTR;
var bodyMain = (_ele$bodyTR = ele.bodyTR) === null || _ele$bodyTR === void 0 ? void 0 : _ele$bodyTR.children[0];
if (bodyMain) return bodyMain;
var newBodyMain = ele.bodyTR.appendChild(dom.createElement('main'));
newBodyMain.setAttribute('style', bodyMainStyle);
return newBodyMain;
}
}
// @@ Use common one from utils?
function orderedSync() {
var items = getItems();
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 newBodyDiv = dom.createElement('div');
newSlot.bodyTR = newBodyDiv;
if (left === tabContainer.children.length) {
// None left of original on right
tabContainer.appendChild(newSlot);
bodyContainer.appendChild(newBodyDiv);
} else {
tabContainer.insertBefore(newSlot, tabContainer.children[left + i]);
bodyContainer.insertBefore(newBodyDiv, bodyContainer.children[left + i]);
}
}
if (onClose) {
addCancelButton(tabContainer);
}
}
function resetTabStyle() {
for (var i = 0; i < tabContainer.children.length; i++) {
var _tab = tabContainer.children[i];
if (_tab.classList.contains('unstyled')) {
continue;
} else {
_tab.setAttribute('style', unselectedStyle);
}
}
}
function resetBodyStyle() {
for (var i = 0; i < bodyContainer.children.length; i++) {
bodyContainer.children[i].setAttribute('style', hiddenStyle);
}
}
}
/**
* @internal
*/
function getColors(backgroundColor) {
return isLight(backgroundColor) ? [colorBlend(backgroundColor, '#ffffff', 0.3), '#000000'] : [colorBlend(backgroundColor, '#000000', 0.3), '#ffffff'];
}
/**
* @internal
*/
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];
}
return str;
}
/**
* @internal
*/
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;
}
//# sourceMappingURL=tabs.js.map
;