diff --git a/assets/controllers/elements/sidebar_tree_controller.js b/assets/controllers/elements/sidebar_tree_controller.js new file mode 100644 index 00000000..6f43bbe3 --- /dev/null +++ b/assets/controllers/elements/sidebar_tree_controller.js @@ -0,0 +1,63 @@ +import {Controller} from "@hotwired/stimulus"; +import {default as TreeController} from "./tree_controller"; + +import "patternfly-bootstrap-treeview/src/css/bootstrap-treeview.css" +import "patternfly-bootstrap-treeview"; + +export default class extends TreeController { + static targets = [ "tree", 'sourceText' ]; + + _storage_key; + + connect() { + const default_mode = this.element.dataset.defaultMode; + + this._storage_key = 'tree_' + this.element.id; + + //Check if we have a saved mode + const stored_mode = localStorage.getItem(this._storage_key); + + //Use stored mode if possible, otherwise use default + if(stored_mode) { + try { + this.setMode(stored_mode); + } catch (e) { + console.error(e); + //If an error happenes, use the default mode + this.setMode(default_mode); + } + } else { + this.setMode(default_mode); + } + } + + setMode(mode) { + //Find the button for this mode + const modeButton = this.element.querySelector(`[data-mode="${mode}"]`); + if(!modeButton) { + throw new Error(`Could not find button for mode ${mode}`); + } + + //Get the url and text from the button + const url = modeButton.dataset.url; + const text = modeButton.dataset.text; + + this.sourceTextTarget.innerText = text; + + this.setURL(url); + } + + changeDataSource(event) + { + const mode = event.params.mode ?? event.target.dataset.mode; + const url = event.params.url ?? event.target.dataset.url; + const text = event.params.text ?? event.target.dataset.text; + + this.sourceTextTarget.innerText = text; + + this.setURL(url); + + //Save the mode in local storage + localStorage.setItem(this._storage_key, mode); + } +} diff --git a/assets/controllers/elements/tree-controller.js b/assets/controllers/elements/tree_controller.js similarity index 71% rename from assets/controllers/elements/tree-controller.js rename to assets/controllers/elements/tree_controller.js index 17137259..6631ecac 100644 --- a/assets/controllers/elements/tree-controller.js +++ b/assets/controllers/elements/tree_controller.js @@ -6,6 +6,9 @@ import "patternfly-bootstrap-treeview"; export default class extends Controller { static targets = [ "tree" ]; + _url = null; + _data = null; + connect() { const treeElement = this.treeTarget; if (!treeElement) { @@ -13,9 +16,30 @@ export default class extends Controller { return; } - //Fetch data and initialize tree - this._getData().then(this._fillTree.bind(this)); + this._url = this.element.dataset.treeUrl; + this._data = this.element.dataset.treeData; + this.reinitTree(); + } + + reinitTree() + { + //Fetch data and initialize tree + this._getData() + .then(this._fillTree.bind(this)) + .catch((err) => { + console.error("Could not load the tree data: " + err); + }); + } + + setData(data) { + this._data = data; + this.reinitTree(); + } + + setURL(url) { + this._url = url; + this.reinitTree(); } _fillTree(data) { @@ -65,7 +89,7 @@ export default class extends Controller { } searchInput(event) { - const data = event.data; + const data = event.target.value; //Do nothing if no data was passed const tree = this.treeTarget; @@ -73,10 +97,19 @@ export default class extends Controller { $(tree).treeview('search', [data]); } + _getData() { //Use lambda function to preserve this context return new Promise((myResolve, myReject) => { - return myResolve(this.element.dataset.treeData); + //If a url is defined, fetch the data from the url + if (this._url) { + return fetch(this._url) + .then((response) => myResolve(response.json())) + .catch((err) => myReject(err)); + } + + //Otherwise load the data provided via the data attribute + return myResolve(this._data); }); } } \ No newline at end of file diff --git a/assets/js/sidebar.js b/assets/js/sidebar.js index e392e225..606bdc6b 100644 --- a/assets/js/sidebar.js +++ b/assets/js/sidebar.js @@ -13,7 +13,7 @@ class SidebarHelper { console.info("Base path is " + this.BASE); this.registerSidebarHideButton(); - this.fillTrees(); + //this.fillTrees(); } registerSidebarHideButton() @@ -55,146 +55,6 @@ class SidebarHelper { } }); } - - /** - * Fill the trees with the given data. - */ - fillTrees() - { - let categories = localStorage.getItem("tree_datasource_tree-categories"); - let devices = localStorage.getItem("tree_datasource_tree-devices"); - let tools = localStorage.getItem("tree_datasource_tree-tools"); - - if(categories == null) { - categories = "categories"; - } - - if(devices == null) { - devices = "devices"; - } - - if(tools == null) { - tools = "tools"; - } - - this.treeLoadDataSource("tree-categories", categories); - this.treeLoadDataSource("tree-devices", devices); - this.treeLoadDataSource("tree-tools", tools); - - this.trees_filled = true; - - let _this = this; - - //Register tree btns to expand all, or to switch datasource. - $(".tree-btns").click(function (event) { - event.preventDefault(); - $(this).parents("div.dropdown").removeClass('show'); - //$(this).closest(".dropdown-menu").removeClass('show'); - $(".dropdown-menu.show").removeClass("show"); - let mode = $(this).data("mode"); - let target = $(this).data("target"); - let text = $(this).text() + " \n"; //Add caret or it will be removed, when written into title - - if (mode==="collapse") { - // @ts-ignore - $('#' + target).treeview('collapseAll', { silent: true }); - } - else if(mode==="expand") { - // @ts-ignore - $('#' + target).treeview('expandAll', { silent: true }); - } else { - localStorage.setItem("tree_datasource_" + target, mode); - _this.treeLoadDataSource(target, mode); - } - - return false; - }); - } - - /** - * Load the given url into the tree with the given id. - * @param target_id - * @param datasource - */ - treeLoadDataSource(target_id, datasource) { - let text = $(".tree-btns[data-mode='" + datasource + "']").html(); - text = text + " \n"; //Add caret or it will be removed, when written into title - switch(datasource) { - case "categories": - this.initTree("#" + target_id, 'tree/categories'); - break; - case "locations": - this.initTree("#" + target_id, 'tree/locations'); - break; - case "footprints": - this.initTree("#" + target_id, 'tree/footprints'); - break; - case "manufacturers": - this.initTree("#" + target_id, 'tree/manufacturers'); - break; - case "suppliers": - this.initTree("#" + target_id, 'tree/suppliers'); - break; - case "tools": - this.initTree("#" + target_id, 'tree/tools'); - break; - case "devices": - this.initTree("#" + target_id, 'tree/devices'); - break; - } - - $( "#" + target_id + "-title").html(text); - } - - /** - * Fill a treeview with data from the given url. - * @param tree The Jquery selector for the tree (e.g. "#tree-tools") - * @param url The url from where the data should be loaded - */ - initTree(tree, url) { - //Get primary color from css variable - const primary_color = getComputedStyle(document.documentElement).getPropertyValue('--bs-info'); - - //let contextmenu_handler = this.onNodeContextmenu; - $.getJSON(this.BASE + url, function (data) { - // @ts-ignore - $(tree).treeview({ - data: data, - enableLinks: true, - showIcon: false, - showBorder: true, - searchResultBackColor: primary_color, - searchResultColor: '#000', - onNodeSelected: function(event, data) { - if(data.href) { - - //Simulate a click so we just change the inner frame - let a = document.createElement('a'); - a.setAttribute('href', data.href); - a.innerHTML = ""; - $(tree).append(a); - a.click(); - a.remove(); - //Turbo.visit(data.href) - } - }, - //onNodeContextmenu: contextmenu_handler, - expandIcon: "fas fa-plus fa-fw fa-treeview", collapseIcon: "fas fa-minus fa-fw fa-treeview"}) - .on('initialized', function() { - $(this).treeview('collapseAll', { silent: true }); - - //Implement searching if needed. - if($(this).data('treeSearch')) { - let _this = this; - let $search = $($(this).data('treeSearch')); - $search.on( 'input', function() { - $(_this).treeview('collapseAll', { silent: true }); - $(_this).treeview('search', [$search.val()]); - }); - } - }); - }); - } } export default new SidebarHelper(); \ No newline at end of file diff --git a/src/Controller/TreeController.php b/src/Controller/TreeController.php index ca5617a1..af4546f4 100644 --- a/src/Controller/TreeController.php +++ b/src/Controller/TreeController.php @@ -80,7 +80,7 @@ class TreeController extends AbstractController /** * @Route("/category/{id}", name="tree_category") - * @Route("/categories") + * @Route("/categories", name="tree_category_root") */ public function categoryTree(?Category $category = null): JsonResponse { @@ -91,7 +91,7 @@ class TreeController extends AbstractController /** * @Route("/footprint/{id}", name="tree_footprint") - * @Route("/footprints") + * @Route("/footprints", name="tree_footprint_root") */ public function footprintTree(?Footprint $footprint = null): JsonResponse { @@ -102,7 +102,7 @@ class TreeController extends AbstractController /** * @Route("/location/{id}", name="tree_location") - * @Route("/locations") + * @Route("/locations", name="tree_location_root") */ public function locationTree(?Storelocation $location = null): JsonResponse { @@ -113,7 +113,7 @@ class TreeController extends AbstractController /** * @Route("/manufacturer/{id}", name="tree_manufacturer") - * @Route("/manufacturers") + * @Route("/manufacturers", name="tree_manufacturer_root") */ public function manufacturerTree(?Manufacturer $manufacturer = null): JsonResponse { @@ -124,7 +124,7 @@ class TreeController extends AbstractController /** * @Route("/supplier/{id}", name="tree_supplier") - * @Route("/suppliers") + * @Route("/suppliers", name="tree_supplier_root") */ public function supplierTree(?Supplier $supplier = null): JsonResponse { @@ -135,7 +135,7 @@ class TreeController extends AbstractController /** * @Route("/device/{id}", name="tree_device") - * @Route("/devices") + * @Route("/devices", name="tree_device_root") */ public function deviceTree(?Device $device = null): JsonResponse { diff --git a/templates/_sidebar.html.twig b/templates/_sidebar.html.twig index f3e05d0f..86a9cb00 100644 --- a/templates/_sidebar.html.twig +++ b/templates/_sidebar.html.twig @@ -1,21 +1,13 @@ +{% import "elements/tree_macros.html.twig" as tree %} -{% macro sidebar_dropdown(target) %} - -
  • {% trans %}expandAll{% endtrans %}
  • -
  • {% trans %}reduceAll{% endtrans %}
  • - - -
  • {% trans %}category.labelp{% endtrans %}
  • -
  • {% trans %}storelocation.labelp{% endtrans %}
  • -
  • {% trans %}footprint.labelp{% endtrans %}
  • -
  • {% trans %}manufacturer.labelp{% endtrans %}
  • -
  • {% trans %}supplier.labelp{% endtrans %}
  • -
  • {% trans %}device.labelp{% endtrans %}
  • -
  • {% trans %}tools.label{% endtrans %}
  • -{% endmacro %} - \ No newline at end of file + #} + \ No newline at end of file diff --git a/templates/elements/tree_macros.html.twig b/templates/elements/tree_macros.html.twig new file mode 100644 index 00000000..ddba88de --- /dev/null +++ b/templates/elements/tree_macros.html.twig @@ -0,0 +1,40 @@ +{% macro sidebar_dropdown() %} + {# Format is [mode, route, label] #} + {% set data_sources = [ + ['categories', path('tree_category_root'), 'category.labelp'], + ['locations', path('tree_location_root'), 'storelocation.labelp'], + ['footprints', path('tree_footprint_root'), 'footprint.labelp'], + ['manufacturer', path('tree_manufacturer_root'), 'manufacturer.labelp'], + ['suppliers', path('tree_supplier_root'), 'supplier.labelp'], + ['devices', path('tree_device_root'), 'device.labelp'], + ['tools', path('tree_tools'), 'tools.label'], + ] %} + + +
  • +
  • + + + + {% for source in data_sources %} +
  • + {% endfor %} +{% endmacro %} + +{% macro treeview_sidebar(id, default_mode) %} +
    +
    + + + +
    + +
    +
    +{% endmacro %} \ No newline at end of file