SelectTreeElement.ts 6.82 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 type {SelectTree} from './SelectTree';
15
16
17
18
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';
19
import './SelectTree';
20
21

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

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

  constructor(treeWrapperId: string, treeRecordFieldId: string, callback: Function) {
28
    this.recordField = <HTMLInputElement>document.getElementById(treeRecordFieldId);
29
30
31
    const treeWrapper = <HTMLElement>document.getElementById(treeWrapperId);
    this.tree = document.createElement('typo3-backend-form-selecttree') as SelectTree;
    this.tree.classList.add('svg-tree-wrapper');
32
    this.tree.dispatch.on('nodeSelectedAfter.requestUpdate', () => { callback(); } );
33
34

    const settings = {
35
      dataUrl: this.generateRequestUrl(),
36
37
38
39
40
      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,
41
      unselectableElements: [] as Array<any>
42
    };
43
    this.tree.addEventListener('svg-tree:initialized', () => {
44
45
      const toolbarElement = document.createElement(toolbarComponentName) as TreeToolbar;
      toolbarElement.tree = this.tree;
46
      this.tree.prepend(toolbarElement);
47
    });
48
49
    this.tree.setup = settings;
    treeWrapper.append(this.tree);
50
51
    this.listenForVisibleTree();
  }
52

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

  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,
74
      dataStructureIdentifier: this.recordField.dataset.datastructureidentifier,
75
76
77
78
79
80
81
82
      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,
    };
83
    return TYPO3.settings.ajaxUrls.record_tree_data + '&' + new URLSearchParams(params);
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
118
119

@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>
120
          <input type="text" class="form-control ${this.settings.searchInput}" placeholder="${lll('tcatree.findItem')}" @input="${(evt: InputEvent) => this.filter(evt)}">
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
        </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>
    `;
  }
136

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

  /**
   * Expand all nodes
   */
  private expandAll() {
    this.tree.expandAll();
149
  }
150

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

  /**
   * Show only checked items
   */
  private toggleHideUnchecked(): void {
    this.hideUncheckedState = !this.hideUncheckedState;
    if (this.hideUncheckedState) {
      this.tree.nodes.forEach((node: any) => {
        if (node.checked) {
164
          this.tree.showParents(node);
165
166
167
168
169
170
171
172
173
174
175
          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();
176
    this.tree.updateVisibleNodes();
177
178
  }
}