SelectTreeElement.ts 6.76 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

14
import {SelectTree} from './SelectTree';
15
16
17
18
19
20
import {Tooltip} from 'bootstrap';
import {html, customElement, LitElement, TemplateResult} from 'lit-element';
import {lll} from 'TYPO3/CMS/Core/lit-helper';
import 'TYPO3/CMS/Backend/Element/IconElement';

const toolbarComponentName: string = 'typo3-backend-form-selecttree-toolbar';
21

22
export class SelectTreeElement {
23
24
  private readonly treeWrapper: HTMLElement = null;
  private readonly recordField: HTMLInputElement = null;
25
  private readonly tree: SelectTree = null;
26
27

  constructor(treeWrapperId: string, treeRecordFieldId: string, callback: Function) {
28
29
    this.treeWrapper = <HTMLElement>document.getElementById(treeWrapperId);
    this.recordField = <HTMLInputElement>document.getElementById(treeRecordFieldId);
30
    this.tree = new SelectTree();
31
    this.tree.dispatch.on('nodeSelectedAfter.requestUpdate', () => { callback(); } );
32
33

    const settings = {
34
      dataUrl: this.generateRequestUrl(),
35
36
37
38
39
      readOnlyMode: parseInt(this.recordField.dataset.readOnly, 10) === 1,
      input: this.recordField,
      exclusiveNodesIdentifiers: this.recordField.dataset.treeExclusiveKeys,
      validation: JSON.parse(this.recordField.dataset.formengineValidationRules)[0],
      expandUpToLevel: this.recordField.dataset.treeExpandUpToLevel,
40
      unselectableElements: [] as Array<any>
41
    };
42
43
44
45
46
47
    this.treeWrapper.addEventListener('svg-tree:initialized', () => {
      const toolbarElement = document.createElement(toolbarComponentName) as TreeToolbar;
      toolbarElement.tree = this.tree;
      this.treeWrapper.prepend(toolbarElement);
    });
    this.tree.initialize(this.treeWrapper, settings);
48
49
    this.listenForVisibleTree();
  }
50

51
52
53
54
55
56
57
58
59
60
61
62
  /**
   * If the Select item is in an invisible tab, it needs to be rendered once the tab
   * becomes visible.
   */
  private listenForVisibleTree(): void {
    if (!this.treeWrapper.offsetParent) {
      // Search for the parents that are tab containers
      let idOfTabContainer = this.treeWrapper.closest('.tab-pane').getAttribute('id');
      if (idOfTabContainer) {
        let btn = document.querySelector('[aria-controls="' + idOfTabContainer + '"]');
        btn.addEventListener('shown.bs.tab', () => { this.treeWrapper.dispatchEvent(new Event('svg-tree:visible')); });
      }
63
64
65
66
67
68
69
70
71
    }
  }

  private generateRequestUrl(): string {
    const params = {
      tableName: this.recordField.dataset.tablename,
      fieldName: this.recordField.dataset.fieldname,
      uid: this.recordField.dataset.uid,
      recordTypeValue: this.recordField.dataset.recordtypevalue,
72
      dataStructureIdentifier: this.recordField.dataset.datastructureidentifier,
73
74
75
76
77
78
79
80
      flexFormSheetName: this.recordField.dataset.flexformsheetname,
      flexFormFieldName: this.recordField.dataset.flexformfieldname,
      flexFormContainerName: this.recordField.dataset.flexformcontainername,
      flexFormContainerIdentifier: this.recordField.dataset.flexformcontaineridentifier,
      flexFormContainerFieldName: this.recordField.dataset.flexformcontainerfieldname,
      flexFormSectionContainerIsNew: this.recordField.dataset.flexformsectioncontainerisnew,
      command: this.recordField.dataset.command,
    };
81
    return TYPO3.settings.ajaxUrls.record_tree_data + '&' + new URLSearchParams(params);
82
83
  }
}
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

@customElement(toolbarComponentName)
class TreeToolbar extends LitElement {
  public tree: SelectTree;
  private settings = {
    collapseAllBtn: 'collapse-all-btn',
    expandAllBtn: 'expand-all-btn',
    searchInput: 'search-input',
    toggleHideUnchecked: 'hide-unchecked-btn'
  };

  /**
   * State of the hide unchecked toggle button
   *
   * @type {boolean}
   */
  private hideUncheckedState: boolean = false;

  // disable shadow dom for now
  protected createRenderRoot(): HTMLElement | ShadowRoot {
    return this;
  }

  protected firstUpdated(): void {
    this.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((tooltipTriggerEl: HTMLElement) => new Tooltip(tooltipTriggerEl));
  }

  protected render(): TemplateResult {
    return html`
      <div class="tree-toolbar btn-toolbar">
        <div class="input-group">
          <span class="input-group-addon input-group-icon filter">
            <typo3-backend-icon identifier="actions-filter" size="small"></typo3-backend-icon>
          </span>
118
          <input type="text" class="form-control ${this.settings.searchInput}" placeholder="${lll('tcatree.findItem')}" @input="${(evt: InputEvent) => this.filter(evt)}">
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
        </div>
        <div class="btn-group">
          <button type="button" data-bs-toggle="tooltip" class="btn btn-default ${this.settings.expandAllBtn}" title="${lll('tcatree.expandAll')}" @click="${() => this.expandAll()}">
            <typo3-backend-icon identifier="apps-pagetree-category-expand-all" size="small"></typo3-backend-icon>
          </button>
          <button type="button" data-bs-toggle="tooltip" class="btn btn-default ${this.settings.collapseAllBtn}" title="${lll('tcatree.collapseAll')}" @click="${() => this.collapseAll()}">
            <typo3-backend-icon identifier="apps-pagetree-category-collapse-all" size="small"></typo3-backend-icon>
          </button>
          <button type="button" data-bs-toggle="tooltip" class="btn btn-default ${this.settings.toggleHideUnchecked}" title="${lll('tcatree.toggleHideUnchecked')}" @click="${() => this.toggleHideUnchecked()}">
            <typo3-backend-icon identifier="apps-pagetree-category-toggle-hide-checked" size="small"></typo3-backend-icon>
          </button>
        </div>
      </div>
    `;
  }
134

135
136
137
138
139
  /**
   * Collapse children of root node
   */
  private collapseAll() {
    this.tree.collapseAll();
140
  }
141
142
143
144
145
146

  /**
   * Expand all nodes
   */
  private expandAll() {
    this.tree.expandAll();
147
  }
148

149
  private filter(event: InputEvent): void {
150
    const inputEl = <HTMLInputElement>event.target;
151
    this.tree.filter(inputEl.value.trim());
152
153
154
155
156
157
158
159
160
161
  }

  /**
   * Show only checked items
   */
  private toggleHideUnchecked(): void {
    this.hideUncheckedState = !this.hideUncheckedState;
    if (this.hideUncheckedState) {
      this.tree.nodes.forEach((node: any) => {
        if (node.checked) {
162
          this.tree.showParents(node);
163
164
165
166
167
168
169
170
171
172
173
          node.expanded = true;
          node.hidden = false;
        } else {
          node.hidden = true;
          node.expanded = false;
        }
      });
    } else {
      this.tree.nodes.forEach((node: any) => node.hidden = false);
    }
    this.tree.prepareDataForVisibleNodes();
174
    this.tree.updateVisibleNodes();
175
176
  }
}