diff options
author | Jo-Philipp Wich <jo@mein.io> | 2018-12-05 08:48:35 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2018-12-10 13:41:34 +0100 |
commit | 76e9c0305ed65480e0d1e3e86d831ac24de98bcd (patch) | |
tree | b5059bb0b138d4acd48f54814205f43ea888d878 /modules/luci-base/htdocs | |
parent | 747e10bae6e9ec45e7d10f962c8c25ae804e97e7 (diff) |
luci-base: rework ui tabbing code
- Instantiate tab menus on the client side
- Simplify server side markup generation
- Show error indicators in cbi tabs
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-base/htdocs')
-rw-r--r-- | modules/luci-base/htdocs/luci-static/resources/cbi.js | 78 | ||||
-rw-r--r-- | modules/luci-base/htdocs/luci-static/resources/luci.js | 176 |
2 files changed, 180 insertions, 74 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/cbi.js b/modules/luci-base/htdocs/luci-static/resources/cbi.js index 635740a70..61b83e829 100644 --- a/modules/luci-base/htdocs/luci-static/resources/cbi.js +++ b/modules/luci-base/htdocs/luci-static/resources/cbi.js @@ -12,7 +12,6 @@ */ var cbi_d = []; -var cbi_t = []; var cbi_strings = { path: {}, label: {} }; function s8(bytes, off) { @@ -727,13 +726,13 @@ function cbi_d_update() { parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : ''; } - if (entry && entry.parent) { - if (!cbi_t_update()) - cbi_tag_last(parent); - } + if (entry && entry.parent) + cbi_tag_last(parent); if (state) cbi_d_update(); + else if (parent) + parent.dispatchEvent(new CustomEvent('dependency-update', { bubbles: true })); } function cbi_init() { @@ -1045,75 +1044,6 @@ function cbi_dynlist_init(dl, datatype, optional, choices) cbi_dynlist_init.prototype = CBIDynamicList; -function cbi_t_add(section, tab) { - var t = document.getElementById('tab.' + section + '.' + tab); - var c = document.getElementById('container.' + section + '.' + tab); - - if (t && c) { - cbi_t[section] = (cbi_t[section] || [ ]); - cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id }; - } -} - -function cbi_t_switch(section, tab) { - if (cbi_t[section] && cbi_t[section][tab]) { - var o = cbi_t[section][tab]; - var h = document.getElementById('tab.' + section); - - for (var tid in cbi_t[section]) { - var o2 = cbi_t[section][tid]; - - if (o.tab.id != o2.tab.id) { - o2.tab.classList.remove('cbi-tab'); - o2.tab.classList.add('cbi-tab-disabled'); - o2.container.style.display = 'none'; - } - else { - if(h) - h.value = tab; - - o2.tab.classList.remove('cbi-tab-disabled'); - o2.tab.classList.add('cbi-tab'); - o2.container.style.display = 'block'; - } - } - } - - return false; -} - -function cbi_t_update() { - var hl_tabs = [ ]; - var updated = false; - - for (var sid in cbi_t) - for (var tid in cbi_t[sid]) { - var t = cbi_t[sid][tid].tab; - var c = cbi_t[sid][tid].container; - - if (!c.firstElementChild) { - t.style.display = 'none'; - } - else if (t.style.display == 'none') { - t.style.display = ''; - t.classList.add('cbi-tab-highlighted'); - hl_tabs.push(t); - } - - cbi_tag_last(c); - updated = true; - } - - if (hl_tabs.length > 0) - window.setTimeout(function() { - for (var i = 0; i < hl_tabs.length; i++) - hl_tabs[i].classList.remove('cbi-tab-highlighted'); - }, 750); - - return updated; -} - - function cbi_validate_form(form, errmsg) { /* if triggered by a section removal or addition, don't validate */ diff --git a/modules/luci-base/htdocs/luci-static/resources/luci.js b/modules/luci-base/htdocs/luci-static/resources/luci.js index c1c1b0dd3..4cb8bf4e5 100644 --- a/modules/luci-base/htdocs/luci-static/resources/luci.js +++ b/modules/luci-base/htdocs/luci-static/resources/luci.js @@ -181,6 +181,175 @@ } }; + /* Tabs */ + LuCI.prototype.tabs = { + init: function() { + var groups = [], prevGroup = null, currGroup = null; + + document.querySelectorAll('[data-tab]').forEach(function(tab) { + var parent = tab.parentNode; + + if (!parent.hasAttribute('data-tab-group')) + parent.setAttribute('data-tab-group', groups.length); + + currGroup = +parent.getAttribute('data-tab-group'); + + if (currGroup !== prevGroup) { + prevGroup = currGroup; + + if (!groups[currGroup]) + groups[currGroup] = []; + } + + groups[currGroup].push(tab); + }); + + for (var i = 0; i < groups.length; i++) + this.initTabGroup(groups[i]); + + document.addEventListener('dependency-update', this.updateTabs.bind(this)); + + this.updateTabs(); + + if (!groups.length) + this.setActiveTabId(-1, -1); + }, + + initTabGroup: function(panes) { + if (!Array.isArray(panes) || panes.length === 0) + return; + + var menu = E('ul', { 'class': 'cbi-tabmenu' }), + group = panes[0].parentNode, + groupId = +group.getAttribute('data-tab-group'), + selected = null; + + for (var i = 0, pane; pane = panes[i]; i++) { + var name = pane.getAttribute('data-tab'), + title = pane.getAttribute('data-tab-title'), + active = pane.getAttribute('data-tab-active') === 'true'; + + menu.appendChild(E('li', { + 'class': active ? 'cbi-tab' : 'cbi-tab-disabled', + 'data-tab': name + }, E('a', { + 'href': '#', + 'click': this.switchTab.bind(this) + }, title))); + + if (active) + selected = i; + } + + group.parentNode.insertBefore(menu, group); + + if (selected === null) { + selected = this.getActiveTabId(groupId); + + if (selected < 0 || selected >= panes.length) + selected = 0; + + menu.childNodes[selected].classList.add('cbi-tab'); + menu.childNodes[selected].classList.remove('cbi-tab-disabled'); + panes[selected].setAttribute('data-tab-active', 'true'); + + this.setActiveTabId(groupId, selected); + } + }, + + getActiveTabState: function() { + var page = document.body.getAttribute('data-page'); + + try { + var val = JSON.parse(window.sessionStorage.getItem('tab')); + if (val.page === page && Array.isArray(val.groups)) + return val; + } + catch(e) {} + + window.sessionStorage.removeItem('tab'); + return { page: page, groups: [] }; + }, + + getActiveTabId: function(groupId) { + return +this.getActiveTabState().groups[groupId] || 0; + }, + + setActiveTabId: function(groupId, tabIndex) { + try { + var state = this.getActiveTabState(); + state.groups[groupId] = tabIndex; + + window.sessionStorage.setItem('tab', JSON.stringify(state)); + } + catch (e) { return false; } + + return true; + }, + + updateTabs: function(ev) { + document.querySelectorAll('[data-tab-title]').forEach(function(pane) { + var menu = pane.parentNode.previousElementSibling, + tab = menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))), + n_errors = pane.querySelectorAll('.cbi-input-invalid').length; + + if (!pane.firstElementChild) { + tab.style.display = 'none'; + tab.classList.remove('flash'); + } + else if (tab.style.display === 'none') { + tab.style.display = ''; + requestAnimationFrame(function() { tab.classList.add('flash') }); + } + + if (n_errors) { + tab.setAttribute('data-errors', n_errors); + tab.setAttribute('data-tooltip', _('%d invalid field(s)').format(n_errors)); + tab.setAttribute('data-tooltip-style', 'error'); + } + else { + tab.removeAttribute('data-errors'); + tab.removeAttribute('data-tooltip'); + } + }); + }, + + switchTab: function(ev) { + var tab = ev.target.parentNode, + name = tab.getAttribute('data-tab'), + menu = tab.parentNode, + group = menu.nextElementSibling, + groupId = +group.getAttribute('data-tab-group'), + index = 0; + + ev.preventDefault(); + + if (!tab.classList.contains('cbi-tab-disabled')) + return; + + menu.querySelectorAll('[data-tab]').forEach(function(tab) { + tab.classList.remove('cbi-tab'); + tab.classList.remove('cbi-tab-disabled'); + tab.classList.add( + tab.getAttribute('data-tab') === name ? 'cbi-tab' : 'cbi-tab-disabled'); + }); + + group.childNodes.forEach(function(pane) { + if (L.dom.matches(pane, '[data-tab]')) { + if (pane.getAttribute('data-tab') === name) { + pane.setAttribute('data-tab-active', 'true'); + L.tabs.setActiveTabId(groupId, index); + } + else { + pane.setAttribute('data-tab-active', 'false'); + } + + index++; + } + }); + } + }; + /* DOM manipulation */ LuCI.prototype.dom = { elem: function(e) { @@ -316,6 +485,11 @@ } }; + /* Setup */ + LuCI.prototype.setupDOM = function(ev) { + this.tabs.init(); + }; + function LuCI(env) { this.env = env; @@ -329,6 +503,8 @@ document.addEventListener('mouseout', this.hideTooltip.bind(this), true); document.addEventListener('focus', this.showTooltip.bind(this), true); document.addEventListener('blur', this.hideTooltip.bind(this), true); + + document.addEventListener('DOMContentLoaded', this.setupDOM.bind(this)); } window.LuCI = LuCI; |