|
10 | 10 | <?php $_divId = 'tree' . $block->getId() ?>
|
11 | 11 | <div id="<?= $block->escapeHtmlAttr($_divId) ?>" class="tree"></div>
|
12 | 12 | <?php
|
13 |
| -$isUseMassaction = $block->getUseMassaction() ? 1 : 0; |
| 13 | +$isUseMassAction = $block->getUseMassaction() ? 1 : 0; |
14 | 14 | $isAnchorOnly = $block->getIsAnchorOnly() ? 1 : 0;
|
15 |
| -$intCategoryId = (int)$block->getCategoryId(); |
16 |
| -$intRootId = (int) $block->getRoot()->getId(); |
17 | 15 | $scriptString = <<<script
|
18 | 16 |
|
19 |
| -require(['jquery', 'prototype', 'extjs/ext-tree-checkbox'], function(jQuery){ |
| 17 | +require(['jquery', 'jquery/jstree/jquery.jstree'], function($) { |
20 | 18 |
|
21 |
| -var tree{$block->escapeJs($block->getId())}; |
| 19 | + let tree = $('#tree{$block->escapeJs($block->getId())}'); |
| 20 | + let useMassAction = {$isUseMassAction}; |
| 21 | + let isAnchorOnly = {$isAnchorOnly}; |
| 22 | + let checkedNodes = []; |
22 | 23 |
|
23 |
| -var useMassaction = {$isUseMassaction}; |
24 |
| -
|
25 |
| -var isAnchorOnly = {$isAnchorOnly}; |
26 |
| -
|
27 |
| -Ext.tree.TreePanel.Enhanced = function(el, config) |
28 |
| -{ |
29 |
| - Ext.tree.TreePanel.Enhanced.superclass.constructor.call(this, el, config); |
30 |
| -}; |
31 |
| -
|
32 |
| -Ext.extend(Ext.tree.TreePanel.Enhanced, Ext.tree.TreePanel, { |
33 |
| -
|
34 |
| - loadTree : function(config, firstLoad) |
35 |
| - { |
36 |
| - var parameters = config['parameters']; |
37 |
| - var data = config['data']; |
| 24 | + function addLastNodeProperty(nodes) { |
| 25 | + return nodes.map(node => { |
| 26 | + return node.children |
| 27 | + ? { ...node, children: addLastNodeProperty(node.children) } |
| 28 | + : { ...node, lastNode: true }; |
| 29 | + }); |
| 30 | + } |
38 | 31 |
|
39 |
| - if ((typeof parameters['root_visible']) != 'undefined') { |
40 |
| - this.rootVisible = parameters['root_visible']*1; |
41 |
| - } |
| 32 | + function actionBasedOnIsAnchorOnly() { |
| 33 | + tree.jstree().get_json('#', { flat: true }).each((node, value) => { |
| 34 | + const attrId = node.a_attr.id; |
| 35 | + const rootNode = tree.jstree().get_node("#"); |
| 36 | + const rootId = rootNode.children[0]; |
42 | 37 |
|
43 |
| - var root = new Ext.tree.TreeNode(parameters); |
| 38 | + if (isAnchorOnly === 1 && node.id === rootId) { |
| 39 | + tree.jstree(true).disable_node(node); |
| 40 | + } else if (isAnchorOnly === 0 && node.id !== rootId) { |
| 41 | + tree.jstree(true).disable_node(node); |
| 42 | + } |
| 43 | + }); |
| 44 | + } |
44 | 45 |
|
45 |
| - this.nodeHash = {}; |
46 |
| - this.setRootNode(root); |
| 46 | + function handleLoadedTree(e, data) { |
| 47 | + const container = $(e.target).closest('div.chooser_container'); |
| 48 | + checkedNodes = container.find('input[type="text"].entities').val().split(',').map(item => item.trim()); |
47 | 49 |
|
48 |
| - if (firstLoad) { |
| 50 | + data.instance.get_json('#', { flat: true }).forEach(nodeId => { |
| 51 | + const node = data.instance.get_node(nodeId); |
49 | 52 |
|
50 |
| -script; |
51 |
| -if ($block->getNodeClickListener()): |
52 |
| - $scriptString .= 'this.addListener(\'click\', ' . /* @noEscape */ $block->getNodeClickListener() . |
53 |
| - '.createDelegate(this));' . PHP_EOL; |
54 |
| -endif; |
55 |
| -$scriptString .= <<<script |
56 |
| - } |
| 53 | + if (checkedNodes.includes(node.id)) { |
| 54 | + tree.jstree(true).select_node(node.id); |
| 55 | + } |
57 | 56 |
|
58 |
| - this.loader.buildCategoryTree(root, data); |
59 |
| - this.el.dom.innerHTML = ''; |
60 |
| - // render the tree |
61 |
| - this.render(); |
| 57 | + actionBasedOnIsAnchorOnly(); |
| 58 | + }); |
62 | 59 | }
|
63 |
| -}); |
64 |
| -
|
65 |
| -jQuery(function() |
66 |
| -{ |
67 |
| -
|
68 |
| -script; |
69 |
| - $scriptString .= 'var emptyNodeAdded = ' . ($block->getWithEmptyNode() ? 'false' : 'true') . ';' . PHP_EOL; |
70 | 60 |
|
71 |
| -$scriptString .= <<<script |
| 61 | + function handleChange(e, data) { |
| 62 | + if (data.action === 'ready') { |
| 63 | + return; |
| 64 | + } |
72 | 65 |
|
73 |
| - var categoryLoader = new Ext.tree.TreeLoader({ |
74 |
| - dataUrl: '{$block->escapeJs($block->escapeUrl($block->getLoadTreeUrl()))}' |
75 |
| - }); |
| 66 | + if (useMassAction) { |
| 67 | + const clickedNodeID = data.node.id; |
| 68 | + const currentCheckedNodes = data.instance.get_checked(); |
76 | 69 |
|
77 |
| - categoryLoader.processResponse = function (response, parent, callback) { |
78 |
| - var config = JSON.parse(response.responseText); |
| 70 | + if (data.action === 'select_node' && !checkedNodes.includes(clickedNodeID)) { |
| 71 | + checkedNodes = currentCheckedNodes; |
| 72 | + } else if (data.action === 'deselect_node') { |
| 73 | + checkedNodes = currentCheckedNodes.filter(nodeID => nodeID !== clickedNodeID); |
| 74 | + } |
79 | 75 |
|
80 |
| - this.buildCategoryTree(parent, config); |
| 76 | + checkedNodes.sort((a, b) => a - b); |
81 | 77 |
|
82 |
| - if (typeof callback == "function") { |
83 |
| - callback(this, parent); |
| 78 | + const container = $(e.target).closest('div.chooser_container'); |
| 79 | + container.find('input[type="text"].entities').val(checkedNodes.join(', ')); |
| 80 | + } else { |
| 81 | + node = data.node; |
| 82 | + node.attributes = node.original; |
| 83 | + const nodeClickListener = {$block->getNodeClickListener()}; |
| 84 | + nodeClickListener(node); |
84 | 85 | }
|
85 |
| - }; |
| 86 | + } |
86 | 87 |
|
87 |
| - categoryLoader.buildCategoryTree = function(parent, config) |
88 |
| - { |
89 |
| - if (!config) return null; |
90 |
| -
|
91 |
| -
|
92 |
| - if (parent && config && config.length){ |
93 |
| - for (var i = 0; i < config.length; i++) { |
94 |
| - var node; |
95 |
| - if (useMassaction && config[i].is_anchor == isAnchorOnly) { |
96 |
| - config[i].uiProvider = Ext.tree.CheckboxNodeUI; |
97 |
| - } |
98 |
| - var _node = Object.clone(config[i]); |
99 |
| -
|
100 |
| - // Add empty node to reset category filter |
101 |
| - if(!emptyNodeAdded) { |
102 |
| - var empty = Object.clone(_node); |
103 |
| - empty.text = '{$block->escapeJs(__('None'))}'; |
104 |
| - empty.children = []; |
105 |
| - empty.id = 'none'; |
106 |
| - empty.path = '1/none'; |
107 |
| - empty.cls = 'leaf'; |
108 |
| - parent.appendChild(new Ext.tree.TreeNode(empty)); |
109 |
| - emptyNodeAdded = true; |
110 |
| - } |
111 |
| -
|
112 |
| - if (_node.children && !_node.children.length) { |
113 |
| - delete(_node.children); |
114 |
| - node = new Ext.tree.AsyncTreeNode(_node); |
115 |
| - } else { |
116 |
| - node = new Ext.tree.TreeNode(config[i]); |
117 |
| - } |
118 |
| - parent.appendChild(node); |
119 |
| - node.loader = node.getOwnerTree().loader; |
120 |
| - node.loader = node.getOwnerTree().loader; |
121 |
| - if (_node.children) { |
122 |
| - this.buildCategoryTree(node, _node.children); |
123 |
| - } |
| 88 | + function getCheckedNodeIds(tree, node) { |
| 89 | + if (node.children_d && node.children_d.length > 0) { |
| 90 | + const selectChildrenNodes = node.children_d.filter(item => checkedNodes.includes(item)); |
| 91 | +
|
| 92 | + if (selectChildrenNodes.length > 0) { |
| 93 | + tree.jstree(true).select_node(selectChildrenNodes); |
124 | 94 | }
|
125 | 95 | }
|
126 |
| - }; |
| 96 | + } |
127 | 97 |
|
128 |
| - categoryLoader.createNode = function(config) { |
129 |
| - var node; |
130 |
| - if (useMassaction && config.is_anchor == isAnchorOnly) { |
131 |
| - config.uiProvider = Ext.tree.CheckboxNodeUI; |
132 |
| - } |
133 |
| - var _node = Object.clone(config); |
134 |
| - if (config.children && !config.children.length) { |
135 |
| - delete(config.children); |
136 |
| - node = new Ext.tree.AsyncTreeNode(config); |
| 98 | + function addLastNodeFlag(treeData) { |
| 99 | + if (treeData.children) { |
| 100 | + treeData.children.forEach(child => addLastNodeFlag(child)); |
137 | 101 | } else {
|
138 |
| - node = new Ext.tree.TreeNode(config); |
| 102 | + treeData.lastNode = true; |
139 | 103 | }
|
140 |
| - return node; |
141 |
| - }; |
142 |
| -
|
143 |
| - categoryLoader.buildHash = function(node) |
144 |
| - { |
145 |
| - var hash = {}; |
146 |
| -
|
147 |
| - hash = this.toArray(node.attributes); |
148 |
| -
|
149 |
| - if (node.childNodes.length>0 || (node.loaded==false && node.loading==false)) { |
150 |
| - hash['children'] = new Array; |
| 104 | + } |
151 | 105 |
|
152 |
| - for (var i = 0, len = node.childNodes.length; i < len; i++) { |
153 |
| - if (!hash['children']) { |
154 |
| - hash['children'] = new Array; |
155 |
| - } |
156 |
| - hash['children'].push(this.buildHash(node.childNodes[i])); |
157 |
| - } |
| 106 | + function handleSuccessResponse(response, childNode, data) { |
| 107 | + if (response.length > 0) { |
| 108 | + response.forEach(newNode => { |
| 109 | + addLastNodeFlag(newNode); |
| 110 | +
|
| 111 | + // Create the new node and execute node callback |
| 112 | + data.instance.create_node(childNode, newNode, 'last', node => { |
| 113 | + if (useMassAction) { |
| 114 | + if (checkedNodes.includes(node.id)) { |
| 115 | + tree.jstree(true).select_node(node.id); |
| 116 | + } |
| 117 | + getCheckedNodeIds(tree, node); |
| 118 | + actionBasedOnIsAnchorOnly(); |
| 119 | + } |
| 120 | + }); |
| 121 | + }); |
158 | 122 | }
|
| 123 | + } |
159 | 124 |
|
160 |
| - return hash; |
161 |
| - }; |
162 |
| -
|
163 |
| - categoryLoader.toArray = function(attributes) { |
164 |
| - var data = {}; |
165 |
| - for (var key in attributes) { |
166 |
| - var value = attributes[key]; |
167 |
| - data[key] = value; |
| 125 | + function handleOpenNode(e, data) { |
| 126 | + let parentNode = data.node; |
| 127 | +
|
| 128 | + if (parentNode.children.length > 0) { |
| 129 | + let childNode = data.instance.get_node(parentNode.children, false); |
| 130 | +
|
| 131 | + // Check if the child node has no children (is not yet loaded) |
| 132 | + if (childNode.children && childNode.children.length === 0 |
| 133 | + && childNode.original && !childNode.original.lastNode) { |
| 134 | + $.ajax({ |
| 135 | + url: '{$block->escapeJs($block->escapeUrl($block->getLoadTreeUrl()))}', |
| 136 | + data: { |
| 137 | + id: childNode.original.id, |
| 138 | + store: childNode.original.store, |
| 139 | + form_key: FORM_KEY |
| 140 | + }, |
| 141 | + dataType: 'json', |
| 142 | + success: function (response) { |
| 143 | + handleSuccessResponse(response, childNode, data); |
| 144 | + }, |
| 145 | + error: function (jqXHR, status, error) { |
| 146 | + console.log(status + ': ' + error + 'Response text:' + jqXHR.responseText); |
| 147 | + } |
| 148 | + }); |
| 149 | + } |
168 | 150 | }
|
| 151 | + } |
169 | 152 |
|
170 |
| - return data; |
| 153 | + var jstreeConfig = { |
| 154 | + core: { |
| 155 | + data: addLastNodeProperty({$block->getTreeJson()}), |
| 156 | + check_callback: true |
| 157 | + }, |
| 158 | + plugins: [] |
171 | 159 | };
|
172 | 160 |
|
173 |
| - categoryLoader.on("beforeload", function(treeLoader, node) { |
174 |
| - treeLoader.baseParams.id = node.attributes.id; |
175 |
| - treeLoader.baseParams.store = node.attributes.store; |
176 |
| - treeLoader.baseParams.form_key = FORM_KEY; |
177 |
| - $('{$block->escapeJs($_divId)}').fire('category:beforeLoad', {treeLoader:treeLoader}); |
178 |
| - }); |
179 |
| -
|
180 |
| - tree{$block->escapeJs($block->getId())} = new Ext.tree.TreePanel.Enhanced('{$block->escapeJs($_divId)}', { |
181 |
| - animate: false, |
182 |
| - loader: categoryLoader, |
183 |
| - enableDD: false, |
184 |
| - containerScroll: true, |
185 |
| - rootVisible: false, |
186 |
| - useAjax: true, |
187 |
| - currentNodeId: {$intCategoryId}, |
188 |
| - addNodeTo: false |
189 |
| - }); |
190 |
| -
|
191 |
| - if (useMassaction) { |
192 |
| - tree{$block->escapeJs($block->getId())}.on('check', function(node) { |
193 |
| - $('{$block->escapeJs($_divId)}').fire('node:changed', {node:node}); |
194 |
| - }, tree{$block->escapeJs($block->getId())}); |
| 161 | + if (useMassAction) { |
| 162 | + jstreeConfig.plugins.push('checkbox'); |
| 163 | + jstreeConfig.checkbox = { |
| 164 | + three_state: false |
| 165 | + }; |
195 | 166 | }
|
196 | 167 |
|
197 |
| - // set the root node |
198 |
| - var parameters = { |
199 |
| - text: 'Psw', |
200 |
| - draggable: false, |
201 |
| - id: {$intRootId}, |
202 |
| - expanded: true, |
203 |
| - category_id: {$intCategoryId} |
204 |
| - }; |
| 168 | + tree.jstree(jstreeConfig); |
205 | 169 |
|
206 |
| - tree{$block->escapeJs($block->getId())}.loadTree({parameters:parameters, data:{$block->getTreeJson()}},true); |
| 170 | + if (useMassAction) { |
| 171 | + tree.on('loaded.jstree', (e, data) => handleLoadedTree(e, data)); |
| 172 | + } |
207 | 173 |
|
| 174 | + tree.on('changed.jstree', (e, data) => handleChange(e, data)); |
| 175 | + tree.on('open_node.jstree', (e, data) => handleOpenNode(e, data)); |
208 | 176 | });
|
209 | 177 |
|
210 |
| -}); |
211 | 178 | script;
|
212 | 179 | ?>
|
| 180 | +<?= /* @noEscape */ $secureRenderer->renderStyleAsTag( |
| 181 | + 'overflow-x: auto;', |
| 182 | + '#tree' . $block->escapeJs($block->getId()) |
| 183 | +); |
| 184 | +?> |
213 | 185 | <?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false); ?>
|
0 commit comments