diff --git a/imgsrc/srv/caret-down.svg b/imgsrc/srv/caret-down.svg new file mode 100644 index 0000000000..38a1a25364 --- /dev/null +++ b/imgsrc/srv/caret-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/imgsrc/srv/caret-right.svg b/imgsrc/srv/caret-right.svg new file mode 100644 index 0000000000..02a55b7df5 --- /dev/null +++ b/imgsrc/srv/caret-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/pyj/dom.pyj b/src/pyj/dom.pyj index 7911d16f90..3337887425 100644 --- a/src/pyj/dom.pyj +++ b/src/pyj/dom.pyj @@ -71,12 +71,19 @@ def create_keyframes(animation_name, *frames): ans.push('}') return ans.join('\n') + '\n' +def change_icon_image(icon_element, new_name): + if new_name: + icon_element.firstChild.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon-' + new_name) + else: + icon_element.firstChild.removeAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href') + def svgicon(name, height, width): ans = document.createElementNS('http://www.w3.org/2000/svg', 'svg') ans.setAttribute('style', 'fill: currentColor; height: {}; width: {}; vertical-align: text-top'.format(height ? '2ex', width ? '2ex')) u = document.createElementNS('http://www.w3.org/2000/svg', 'use') - u.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon-' + name) ans.appendChild(u) + if name: + ans.firstChild.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon-' + name) return ans def element(elem_id, child_selector): diff --git a/src/pyj/widgets.pyj b/src/pyj/widgets.pyj index 635d5b5631..9a67414616 100644 --- a/src/pyj/widgets.pyj +++ b/src/pyj/widgets.pyj @@ -2,7 +2,7 @@ # License: GPL v3 Copyright: 2015, Kovid Goyal from __python__ import hash_literals -from dom import build_rule, clear, svgicon, create_keyframes, set_css +from dom import build_rule, clear, svgicon, create_keyframes, set_css, change_icon_image from elementmaker import E from book_list.theme import get_color @@ -94,6 +94,53 @@ class Breadcrumbs: self.container.appendChild(li) return li +def create_tree(root, populate_data, onclick): + container = E.div() + set_css(container, overflow='auto') + + def toggle_node(li): + if li.dataset.treeState is 'closed': + li.dataset.treeState = 'open' + li.lastChild.style.display = 'block' + change_icon_image(li.firstChild.firstChild, 'caret-down') + else: + li.dataset.treeState = 'closed' + li.lastChild.style.display = 'none' + change_icon_image(li.firstChild.firstChild, 'caret-right') + + def process_node(node, parent_container, level): + if node.children?.length: + ul = E.div() + parent_container.appendChild(ul) + for child in node.children: + icon = 'caret-right' if child.children?.length else None + li = E.div(style='display:flex; flex-direction:column; margin: 1ex 1em; margin-left: {}em'.format(level+1), + E.div(style='display:flex; align-items: center', + svgicon(icon), + E.span('\xa0'), + E.a( + href='javascript: void(0)', + onclick=def (event): + if onclick: + onclick(event.currentTarget.parentNode.parentNode) + ), + ), + E.div(style='display:none'), + data_tree_state='closed', + ) + ul.appendChild(li) + populate_data(child, li, li.firstChild.lastChild) + if icon: + set_css(li.firstChild.firstChild, cursor='pointer') + li.firstChild.firstChild.addEventListener('click', def(event): + toggle_node(event.currentTarget.parentNode.parentNode) + ) + process_node(child, li.lastChild, level + 1) + + if root: + process_node(root, container, 0) + return container + def get_widget_css(): ans = 'a, button:focus { outline: none }; a, button::-moz-focus-inner { border: 0 }\n' ans += create_button.style