dkz_page_tree.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. class MRMBEDKZSELECTOR{
  2. constructor(el, AjaxRequestClass){
  3. this._rootEl = el;
  4. this._AjaxRequestClass = AjaxRequestClass;
  5. this._fetchCodeNrs();
  6. this._createTabsView();
  7. this._createTreeView();
  8. this._createSearchView();
  9. this._createResultView();
  10. this._createLoaderView();
  11. this._vdom = {};
  12. this._idSuffix = '__mrm_be_dkz_pagetreeview__';
  13. this.sendBranchRequest({branch: "root"});
  14. this.updateResults();
  15. }
  16. _createLoaderView(){
  17. this._$loader = document.createElement("div");
  18. this._$loader.classList.add("mrm_be_dkz_loader");
  19. this._$loader.innerHTML = `<strong>loading</strong><div class="loadingio-spinner-ellipsis"><div class="ldio"><div></div><div></div><div></div><div></div><div></div></div></div>`;
  20. this._rootEl.parentNode.insertBefore(this._$loader, this._rootEl);
  21. }
  22. _createResultView(){
  23. this._$resultView = document.createElement('div');
  24. this._$resultView.classList.add('mrm_be_dkz_pagetree_result_holder');
  25. this._$resultView.innerHTML = `
  26. <div class="selections">
  27. <strong>Selektionen: (<span></span>)</strong>
  28. <ul></ul>
  29. </div>
  30. <div class="sws">
  31. <strong>Ausgewählte Suchwörter: (<span></span>)</strong>
  32. <ul></ul>
  33. </div>
  34. `;
  35. this._rootEl.parentNode.insertBefore(this._$resultView, this._rootEl);
  36. }
  37. _createSearchView(){
  38. this._$searchView = document.createElement('div');
  39. this._$searchView.classList.add('mrm_be_dkz_search_holder');
  40. this._$searchView.classList.add('out');
  41. this._$searchView.innerHTML = `<div class="search-results"></div>`;
  42. let $searchForm = document.createElement('form');
  43. $searchForm.innerHTML = `<input id="searchQuery" placeholder="Bezeichnung" required><button type="submit">Suchen</button>`;
  44. let $results = this._$searchView.querySelectorAll(".search-results")[0];
  45. $searchForm.addEventListener('submit', (e) => {
  46. e.preventDefault();
  47. e.stopPropagation();
  48. this._$loader.classList.add("in");
  49. $results.innerHTML = '';
  50. let query = e.target.querySelectorAll('#searchQuery')[0].value;
  51. this.sendRequest(TYPO3.settings.ajaxUrls.mrm_be_dkz_search, {query})
  52. .then(response => {
  53. if(!response) return $par.innerHTML = 'Keine Ergebnisse';
  54. response.list.forEach((item, i) => {
  55. let x = new MRMBEDKZPageTreeView($results, item, this.handleCallbacks.bind(this), "search");
  56. if(this._codenrs.includes(item.codenr)) x.check();
  57. });
  58. })
  59. .finally(() => {
  60. this._$loader.classList.remove("in");
  61. })
  62. });
  63. this._$searchView.prepend($searchForm);
  64. this._rootEl.parentNode.insertBefore(this._$searchView, this._rootEl);
  65. }
  66. _createTreeView(){
  67. this._view = document.createElement("div");
  68. this._view.classList.add('mrm_be_dkz_pagetree_holder');
  69. this._rootEl.parentNode.insertBefore(this._view, this._rootEl);
  70. }
  71. _createTabsView(){
  72. this._$tabs = document.createElement("div");
  73. this._$tabs.classList.add('mrm_be_dkz_pagetree_tabs');
  74. [["tree", "Baum"], ["search", "Suche"]].forEach((item, i) => {
  75. let $tab = document.createElement("div");
  76. $tab.classList.add(item[0]);
  77. $tab.classList.add('tab');
  78. if(i == 0)
  79. $tab.classList.add('active');
  80. $tab.innerHTML = item[1];
  81. this._$tabs.append($tab);
  82. $tab.addEventListener("click", this._changeView.bind(this));
  83. });
  84. this._rootEl.parentNode.insertBefore(this._$tabs, this._rootEl);
  85. }
  86. _changeView(e){
  87. e.preventDefault();
  88. e.stopPropagation();
  89. let $target = e.target;
  90. this._$tabs.querySelectorAll(".tab").forEach((itm) => {
  91. itm.classList.remove("active");
  92. if(itm == $target) itm.classList.add("active");
  93. })
  94. if($target.classList.contains('search')){
  95. this._$searchView.classList.remove('out');
  96. this._view.classList.add('out');
  97. this._$searchView.querySelectorAll("input")[0].value = '';
  98. this._$searchView.querySelectorAll(".search-results")[0].innerHTML = '';
  99. }
  100. if($target.classList.contains('tree')){
  101. this._$searchView.classList.add('out');
  102. this._view.classList.remove('out');
  103. }
  104. }
  105. sendRequest(url, args){
  106. this._$loader.classList.add("in");
  107. return new this._AjaxRequestClass(url)
  108. .withQueryArguments(args)
  109. .get()
  110. .then(function(response){ return response.resolve() })
  111. .then(function(resolved){
  112. //console.log("response", resolved);
  113. let data = JSON.parse(resolved.data);
  114. if(!data.success) return null;
  115. return data.data;
  116. })
  117. .finally(() => {
  118. this._$loader.classList.remove("in");
  119. })
  120. }
  121. sendBranchRequest(args){
  122. return this.sendRequest(TYPO3.settings.ajaxUrls.mrm_be_dkz_branch, args)
  123. .then(data => this.refreshVDOM(data))
  124. .then((x) => this.runCheckerStatus())
  125. .catch(function(e){
  126. console.log("e", e);
  127. })
  128. }
  129. updateResults(){
  130. this.sendRequest(TYPO3.settings.ajaxUrls.mrm_be_dkz_lists, {codenrs: this._codenrs.join(',')})
  131. .then(response => {
  132. if(!response) response = {};
  133. if(!response.berufe) response.berufe = [];
  134. if(!response.sws) response.sws = [];
  135. let $ulSelections = this._$resultView.querySelectorAll('.selections ul')[0];
  136. if($ulSelections){
  137. $ulSelections.innerHTML = '';
  138. if(response.berufe.length > 0){
  139. response.berufe.forEach(b => { $ulSelections.innerHTML += `<li><strong>${b.codenr}:</strong>&nbsp;${b.bezeichnung}</li>` });
  140. }
  141. let $spanSelections = this._$resultView.querySelectorAll('.selections strong span')[0];
  142. if($spanSelections){
  143. $spanSelections.innerHTML = response.berufe.length;
  144. }
  145. }
  146. let $ulSws = this._$resultView.querySelectorAll('.sws ul')[0];
  147. if($ulSws){
  148. $ulSws.innerHTML = '';
  149. if(response.sws.length > 0){
  150. response.sws.forEach(b => { $ulSws.innerHTML += `<li>${b}</li>` });
  151. }
  152. let $spanSws = this._$resultView.querySelectorAll('.sws strong span')[0];
  153. if($spanSws){
  154. $spanSws.innerHTML = response.sws.length;
  155. }
  156. }
  157. })
  158. ;
  159. }
  160. getView(){ return this._view }
  161. _fetchCodeNrs(){
  162. this._codenrs = this._rootEl.value.trim(" ,").split(",");
  163. this._codenrs.forEach(nr => { nr = nr.trim(); });
  164. }
  165. _generateId(nr){
  166. let _id = nr.split(" ").join("");
  167. let hash = 0, i, chr;
  168. for (i = 0; i < _id.length; i++) {
  169. chr = _id.charCodeAt(i);
  170. hash = ((hash << 5) - hash) + chr;
  171. hash |= 0; // Convert to 32bit integer
  172. }
  173. return `${this._idSuffix}${hash}`;
  174. }
  175. refreshVDOM(data) {
  176. data.list.forEach(itm => {
  177. let _id = this._generateId(itm.codenr);
  178. let parentId = itm.obercodenr ? this._generateId(itm.obercodenr) : null;
  179. let parent = parentId ? this._vdom[parentId] : null;
  180. if(!this._vdom[_id]){
  181. this._vdom[_id] = new MRMBEDKZPageTreeView((parent || this).getView(), itm, this.handleCallbacks.bind(this));
  182. }
  183. });
  184. //console.log(this._vdom);
  185. return true;
  186. }
  187. handleCallbacks(msg, data){
  188. //console.log("hcb", msg, data);
  189. switch(msg){
  190. case "codenr_add":
  191. this._codenrs.push(data.data);
  192. this.updateResults();
  193. break;
  194. case "codenr_remove":
  195. let index = this._codenrs.indexOf(data.data);
  196. this._codenrs.splice(index, 1);
  197. this.updateResults();
  198. break;
  199. case "codenr_toggle":
  200. let elementId = this._generateId(data.data.codenr);
  201. let element = this._vdom[elementId];
  202. if(data.data.fetch && (!element || (!element.hasItems() && element.isCollapsible())))
  203. this.sendBranchRequest({branch: data.data.codenr});
  204. break;
  205. }
  206. this._rootEl.value = this._codenrs.join(",");
  207. }
  208. runCheckerStatus(){
  209. this._codenrs.forEach(nr => {
  210. let _id = this._generateId(nr);
  211. if(this._vdom[_id]) this._vdom[_id].check();
  212. });
  213. }
  214. }
  215. class MRMBEDKZPageTreeView{
  216. constructor(parentView, rootData, callback, type = "tree"){
  217. this._callback = callback;
  218. this._$parentView = parentView;
  219. this._$el = null;
  220. this._isChecked = false;
  221. this._$checker = null;
  222. this._$detail = null;
  223. this._rootData = rootData;
  224. this._type = type;
  225. this._render();
  226. }
  227. check(status = true, emitCallback = false){
  228. if(!this._$checker) return;
  229. this._isChecked = status;
  230. let whattodo = status ? "add" : "remove";
  231. this._$checker.classList[whattodo]("checked");
  232. if(emitCallback)
  233. this._callback(`codenr_${whattodo}`, {data: this._rootData.codenr, type: this._type});
  234. }
  235. _render(){
  236. const collapsible = this._type == "tree" && this._rootData.codenr.indexOf('-') == -1;
  237. const _id = `mrm_be_dkz_pagetree_item-${this._rootData.codenr.split(" ").join("_")}`;
  238. let $itm = document.createElement("div");
  239. let $checker = document.createElement("a");
  240. $checker.innerHTML = `<span class="no">❌</span><span class="yes">✅</span>`;
  241. $checker.setAttribute("href", "#");
  242. $checker.classList.add("itm-checker");
  243. this.check(this._isChecked);
  244. $checker.addEventListener("click", (e) => {
  245. e.preventDefault();
  246. e.stopPropagation();
  247. this.check(!this._isChecked, true);
  248. });
  249. let $text = document.createElement("span");
  250. $text.innerHTML = `<strong>${this._rootData.codenr}:</strong> ${this._rootData.bezeichnung} (${this._rootData.swcount} SWs)`;
  251. $itm.setAttribute("id", _id);
  252. if(collapsible){
  253. let $detail = document.createElement("details");
  254. let $summary = document.createElement("summary");
  255. $summary.append($checker);
  256. $summary.append($text);
  257. $detail.append($summary);
  258. $itm.append($detail);
  259. $detail.addEventListener('click', e => {
  260. e.preventDefault();
  261. e.stopPropagation();
  262. $detail.open = !$detail.open;
  263. this._callback(`codenr_toggle`, {data: {codenr: this._rootData.codenr, fetch: $detail.open}, type: this._type});
  264. });
  265. this._$detail = $detail;
  266. }
  267. else{
  268. $itm.classList.add("not-collapsible");
  269. $itm.append($checker);
  270. $itm.append($text);
  271. }
  272. this._$parentView.append($itm);
  273. this._$checker = $checker;
  274. this._$el = $itm;
  275. }
  276. hasItems(){ return this._$detail != null && this._$detail.querySelectorAll('div').length > 0; }
  277. isCollapsible(){ return !this._$el.classList.contains("not-collapsible"); }
  278. getView(){ return this._$detail ? this._$detail : this._$el; }
  279. getRootData(){ return this._rootData; }
  280. }
  281. require(['TYPO3/CMS/Core/DocumentService'], function (DocumentService) {
  282. DocumentService.ready().then(() => {
  283. var el = document.getElementById(window.dkzFormElementId);
  284. if(!el) return;
  285. require(['TYPO3/CMS/Core/Ajax/AjaxRequest'], function (AjaxRequest) {
  286. new MRMBEDKZSELECTOR(el, AjaxRequest);
  287. });
  288. });
  289. });