From 3d43a93b57bec421856260de7bf5d85578155de7 Mon Sep 17 00:00:00 2001 From: Claas Kazzer <kazzer@ub.uni-leipzig.de> Date: Mon, 1 Oct 2018 14:58:19 +0200 Subject: [PATCH] refs #14068 * restyles hierarchy tree --- local/languages/de.ini | 3 + local/languages/en.ini | 3 + themes/finc/js/hierarchyTree.js | 257 ++++++++++++++++++ themes/finc/scss/_customVariables.scss | 3 + themes/finc/scss/compiled.scss | 113 +++++--- .../templates/RecordTab/hierarchytree.phtml | 11 +- 6 files changed, 354 insertions(+), 36 deletions(-) create mode 100644 themes/finc/js/hierarchyTree.js diff --git a/local/languages/de.ini b/local/languages/de.ini index c4fcde43d73..26bdf479aff 100644 --- a/local/languages/de.ini +++ b/local/languages/de.ini @@ -1957,3 +1957,6 @@ support_by_dfg = "Die Nationallizenzen wurden gefördert durch die" (fa-home object) = "Online-Ressource" (fa-globe) = "Online-Ressource" (fa-home passive) = "beschaffbar" + +; HierarchyTree select +hierarchyTreeSelect = "Übergeordnete Werke:" \ No newline at end of file diff --git a/local/languages/en.ini b/local/languages/en.ini index 6db676eafe6..e6563aebb07 100644 --- a/local/languages/en.ini +++ b/local/languages/en.ini @@ -1893,3 +1893,6 @@ support_by_dfg = "National licenses were sponsored by " (fa-home object) = "online resource" (fa-globe) = "online resource" (fa-home passive) = "available on request" + +; HierarchyTree select +hierarchyTreeSelect = "Parent items:" \ No newline at end of file diff --git a/themes/finc/js/hierarchyTree.js b/themes/finc/js/hierarchyTree.js new file mode 100644 index 00000000000..1d25c1a9958 --- /dev/null +++ b/themes/finc/js/hierarchyTree.js @@ -0,0 +1,257 @@ +/*global VuFind */ + +var hierarchyID, recordID, htmlID, hierarchyContext, hierarchySettings; + +/* Utility functions */ +function htmlEncodeId(id) { + return id.replace(/\W/g, "-"); // Also change Hierarchy/TreeRenderer/JSTree.php +} +function html_entity_decode(string) { + var hash_map = { + '&': '&', + '>': '>', + '<': '<' + }; + var tmp_str = string.toString(); + + for (var symbol in hash_map) { + if (hash_map.hasOwnProperty(symbol)) { + var entity = hash_map[symbol]; + tmp_str = tmp_str.split(entity).join(symbol); + } + } + tmp_str = tmp_str.split(''').join("'"); + + return tmp_str; +} + +function getRecord(id) { + $.ajax({ + url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id}), + dataType: 'html' + }) + .done(function getRecordDone(response) { + $('#tree-preview').html(html_entity_decode(response)); + // Remove the old path highlighting + $('#hierarchyTree a').removeClass("jstree-highlight"); + // Add Current path highlighting + var jsTreeNode = $(":input[value='" + id + "']").parent(); + jsTreeNode.children("a").addClass("jstree-highlight"); + jsTreeNode.parents("li").children("a").addClass("jstree-highlight"); + }); +} + +function changeNoResultLabel(display) { + if (display) { + $("#treeSearchNoResults").removeClass('hidden'); + } else { + $("#treeSearchNoResults").addClass('hidden'); + } +} + +function changeLimitReachedLabel(display) { + if (display) { + $("#treeSearchLimitReached").removeClass('hidden'); + } else { + $("#treeSearchLimitReached").addClass('hidden'); + } +} + +var searchAjax = false; +function doTreeSearch() { + $('#treeSearchLoadingImg').removeClass('hidden'); + var keyword = $("#treeSearchText").val(); + if (keyword.length === 0) { + $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search'); + var tree = $('#hierarchyTree').jstree(true); + tree.close_all(); + tree._open_to(htmlID); + $('#treeSearchLoadingImg').addClass('hidden'); + } else { + if (searchAjax) { + searchAjax.abort(); + } + searchAjax = $.ajax({ + url: VuFind.path + '/Hierarchy/SearchTree?' + $.param({ + lookfor: keyword, + hierarchyID: hierarchyID, + type: $("#treeSearchType").val() + }) + "&format=true" + }) + .done(function searchTreeAjaxDone(data) { + if (data.results.length > 0) { + $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search'); + var jstree = $('#hierarchyTree').jstree(true); + jstree.close_all(); + for (var i = data.results.length; i--;) { + var id = htmlEncodeId(data.results[i]); + jstree._open_to(id); + } + for (var j = data.results.length; j--;) { + var tid = htmlEncodeId(data.results[j]); + $('#hierarchyTree').find('#' + tid).addClass('jstree-search'); + } + changeNoResultLabel(false); + changeLimitReachedLabel(data.limitReached); + } else { + changeNoResultLabel(true); + } + $('#treeSearchLoadingImg').addClass('hidden'); + }); + } +} + +function scrollToClicked() { + // Scroll to the current record + var hTree = $('#hierarchyTree'); + hTree.animate({ + scrollTop: $('.jstree-clicked').offset().top - hTree.offset().top + hTree.scrollTop() - 50 + }, 1000); +} + +function hideFullHierarchy() { + var $selected = $('.jstree-clicked'); + // Hide all nodes + $('#hierarchyTree li').hide(); + // Show the nodes on the current path + $selected.show().parents().show(); + // Show the nodes below the current path + $selected.find("li").show(); +} + +function buildJSONNodes(xml) { + var jsonNode = []; + $(xml).children('item').each(function xmlTreeChildren() { + var content = $(this).children('content'); + var id = content.children("name[class='JSTreeID']"); + var name = content.children('name[href]'); + jsonNode.push({ + id: htmlEncodeId(id.text()), + text: name.text(), + li_attr: { recordid: id.text() }, + a_attr: { + href: name.attr('href'), + title: name.text() + }, + type: name.attr('href').match(/\/Collection\//) ? 'collection' : 'record', + children: buildJSONNodes(this) + }); + }); + return jsonNode; +} + +function buildTreeWithXml(cb) { + $.ajax({ + url: VuFind.path + '/Hierarchy/GetTree', + data: { + hierarchyID: hierarchyID, + id: recordID, + context: hierarchyContext, + mode: 'Tree' + } + }) + .done(function getTreeDone(xml) { + var nodes = buildJSONNodes($(xml).find('root')); + cb.call(this, nodes); + }); +} + +$(document).ready(function hierarchyTreeReady() { + // Code for the search button + hierarchyID = $("#hierarchyTree").find(".hiddenHierarchyId")[0].value; + recordID = $("#hierarchyTree").find(".hiddenRecordId")[0].value; + htmlID = htmlEncodeId(recordID); + hierarchyContext = $("#hierarchyTree").find(".hiddenContext")[0].value; + var inLightbox = $("#hierarchyTree").parents("#modal").length > 0; + + if (!hierarchySettings.fullHierarchy) { + // Set Up Partial Hierarchy View Toggle + $('#hierarchyTree').parent().prepend('<a href="#" id="toggleTree" class="closed">' + VuFind.translate("showTree") + '</a>'); + $('#toggleTree').click(function toggleFullTree(e) { + e.preventDefault(); + $(this).toggleClass("open"); + if ($(this).hasClass("open")) { + $(this).html(VuFind.translate("hideTree")); + $('#hierarchyTree li').show(); + } else { + $(this).html(VuFind.translate("showTree")); + hideFullHierarchy(); + } + scrollToClicked(); + $("#hierarchyTree").jstree("toggle_dots"); + }); + } + + $("#hierarchyLoading").removeClass('hide'); + + $("#hierarchyTree") + .bind("ready.jstree", function jsTreeReady(/*event, data*/) { + $("#hierarchyLoading").addClass('hide'); + var tree = $("#hierarchyTree").jstree(true); + tree.select_node(htmlID); + tree._open_to(htmlID); + + if (!inLightbox && hierarchyContext === "Collection") { + getRecord(recordID); + } + + $("#hierarchyTree").bind('select_node.jstree', function jsTreeSelect(e, resp) { + if (inLightbox || hierarchyContext === "Record") { + window.location.href = resp.node.a_attr.href; + } else { + getRecord(resp.node.li_attr.recordid); + } + }); + + if (!hierarchySettings.fullHierarchy) { + // Initial hide of nodes outside current path + hideFullHierarchy(); + $("#hierarchyTree").jstree("toggle_dots"); + } + + scrollToClicked(); + }) + .jstree({ + plugins: ['search', 'types'], + core: { + data: function jsTreeCoreData(obj, cb) { + $.ajax({ + url: VuFind.path + '/Hierarchy/GetTreeJSON', + data: { + hierarchyID: hierarchyID, + id: recordID + }, + statusCode: { + 200: function jsTree200Status(json /*, status, request*/) { + cb.call(this, json); + }, + 204: function jsTree204Status(/*json, status, request*/) { // No Content + buildTreeWithXml(cb); + }, + 503: function jsTree503Status(/*json, status, request*/) { // Service Unavailable + buildTreeWithXml(cb); + } + } + }); + } + }, + types: { + record: { + // remove file icon, insert via SCSS, CK +// icon: 'fa fa-file-o' + }, + collection: { + icon: 'fa fa-folder' + } + } + }); + + $('#treeSearch').removeClass('hidden'); + $('#treeSearch [type=submit]').click(doTreeSearch); + $('#treeSearchText').keyup(function treeSearchKeyup(e) { + var code = (e.keyCode ? e.keyCode : e.which); + if (code === 13 || $(this).val().length === 0) { + doTreeSearch(); + } + }); +}); diff --git a/themes/finc/scss/_customVariables.scss b/themes/finc/scss/_customVariables.scss index f9c5dbe6415..c3c5503d225 100644 --- a/themes/finc/scss/_customVariables.scss +++ b/themes/finc/scss/_customVariables.scss @@ -185,6 +185,9 @@ $sidebar-item-padding: .75em 1em !default; //// Table cell padding - adjust in themes to avoid content jumps when switching tabs // $table-cell-padding: 5px !default; +// JS Tree +// left padding on JSTree child icon elements +$jstree-li-left-padding: 1.5em !default; //// MODAL dimensions $modal-lg: 900px !default; diff --git a/themes/finc/scss/compiled.scss b/themes/finc/scss/compiled.scss index 7eb2069bdf7..87454c4e71d 100644 --- a/themes/finc/scss/compiled.scss +++ b/themes/finc/scss/compiled.scss @@ -1512,47 +1512,93 @@ footer { } ////// Hierarchy tree -#hierarchyTree { - margin-top: 1em; +#treeSelector { + border-bottom: $border-default-styles; + margin-bottom: 1em; + padding-bottom: 1em; + + .item { + margin-right: 2em; + + &:last-of-type { + margin-right: 0; + } + } } -.jstree-anchor + .jstree-children { - margin-left: 18px; - margin-top: 7.5px; - padding-left: 0; - // 2nd level link, align in one vertical line - .jstree-anchor { + +#hierarchyTreeHolder { + border-right: 0; +} + +#hierarchyTree { + ul.jstree-container-ul { + padding-left: 0; + } + + /////// Make dropdown-icon wider + .jstree-closed > .jstree-ocl::before, + .jstree-open > .jstree-ocl::before { + font-size: 150%; margin-left: 0; + margin-top: -5px; + min-width: 18px;// do not translate into em + } + + li.jstree-facet, + li.jstree-node { + line-height: 1.75; + list-style: none; + padding-left: $jstree-li-left-padding; + } + + li li:before { + // FontAwesome Unicode fa-file-o + content: "\f016"; + font-family: FontAwesome; + display: inline-block; + // same as padding-left set on li, above + margin-left: -$jstree-li-left-padding; + //same as padding-left set on li, above + width: $jstree-li-left-padding; + } + + // Align child elements under open/close toggle + .jstree-children { + margin-top: .75em; padding-left: 0; } -} -/////// prevent tree LIs from overspilling on small! -.hierarchy-tree .jstree-anchor { - hyphens: auto; - white-space: normal; -} + // prevent tree LIs from overspilling on small! + .jstree-anchor { + hyphens: auto; + padding-left: 0; + white-space: normal; + } -// Current item -.hierarchy-tree .jstree-clicked { - //background-color: $color-action; - padding-bottom: .25em; - padding-right: .5em; - padding-top: .5em; - &:hover, - &:focus { - color: $brand-secondary; + // Current item + .jstree-clicked { + padding-bottom: .25em; + padding-right: .5em; + padding-top: .3em; + + &:hover, + &:focus { + color: $brand-secondary; + } } } -/////// Make dropdown-icon wider -.hierarchy-tree .jstree-closed > .jstree-ocl::before, -.hierarchy-tree .jstree-open > .jstree-ocl::before { - font-size: 150%; - margin-top: -5px; - min-width: 30px; // do not translate into em +.hierarchy-tree .jstree-ocl::before, +.jstree-facet .jstree-ocl::before { + float: left; + margin-left: 0; + padding: 0; + width: 0; } + + ////// Hierarchy tree - END ////// Tabs - END @@ -1654,8 +1700,8 @@ footer { } } } -//// Sources List - END +//// Sources List - END // MAIN CONTENT - END @@ -1686,7 +1732,6 @@ footer { padding: $sidebar-item-padding; } - .facet-group .facetOR.active { // Style active facets to get proper left padding .text { @@ -1699,9 +1744,9 @@ footer { .sidebar.right { .facet-group .facet, .facet-group .title, -.facet-group .collapse, -.facet-group .collapsing, -.facet-group > .facet { + .facet-group .collapse, + .facet-group .collapsing, + .facet-group > .facet { @include right-border-on-sidebar($border-right-width); } } diff --git a/themes/finc/templates/RecordTab/hierarchytree.phtml b/themes/finc/templates/RecordTab/hierarchytree.phtml index 29e71d067ec..5550db12e45 100644 --- a/themes/finc/templates/RecordTab/hierarchytree.phtml +++ b/themes/finc/templates/RecordTab/hierarchytree.phtml @@ -22,11 +22,18 @@ ?> <? if (count($hierarchyTreeList) > 1): ?> <div id="treeSelector"> + <strong><?=$this->transEsc('hierarchyTreeSelect')?></strong><br> + <? foreach ($hierarchyTreeList as $hierarchy => $hierarchyTitle): ?> <? if($activeTree == $hierarchy): ?> - <i class="fa fa-sitemap" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?> + <span class="item"> + <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?> + </span> <? else: ?> - <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a> + <span class="item"> + <i class="fa fa-sitemap" aria-hidden="true"></i> + <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a> + </span> <? endif; ?> <? endforeach; ?> </div> -- GitLab