@acorex/components 21.0.0-next.4 → 21.0.0-next.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accordion/index.d.ts +0 -1
- package/autocomplete/index.d.ts +23 -9
- package/button/index.d.ts +38 -17
- package/button-group/index.d.ts +6 -4
- package/calendar/index.d.ts +4 -0
- package/chips/index.d.ts +3 -8
- package/code-editor/README.md +291 -1
- package/code-editor/index.d.ts +260 -12
- package/command/index.d.ts +1 -0
- package/conversation2/README.md +426 -0
- package/conversation2/index.d.ts +6139 -0
- package/data-table/index.d.ts +79 -7
- package/dialog/index.d.ts +1 -1
- package/drawer/README.md +2 -2
- package/drawer/index.d.ts +33 -57
- package/drawer-legacy/README.md +3 -0
- package/drawer-legacy/index.d.ts +86 -0
- package/editor/README.md +3 -0
- package/editor/index.d.ts +79 -0
- package/fesm2022/acorex-components-accordion.mjs +19 -24
- package/fesm2022/acorex-components-accordion.mjs.map +1 -1
- package/fesm2022/acorex-components-action-sheet.mjs +12 -12
- package/fesm2022/acorex-components-action-sheet.mjs.map +1 -1
- package/fesm2022/acorex-components-alert.mjs +14 -14
- package/fesm2022/acorex-components-alert.mjs.map +1 -1
- package/fesm2022/acorex-components-aspect-ratio.mjs +4 -4
- package/fesm2022/acorex-components-aspect-ratio.mjs.map +1 -1
- package/fesm2022/acorex-components-audio-wave.mjs +12 -11
- package/fesm2022/acorex-components-audio-wave.mjs.map +1 -1
- package/fesm2022/acorex-components-autocomplete.mjs +30 -13
- package/fesm2022/acorex-components-autocomplete.mjs.map +1 -1
- package/fesm2022/acorex-components-avatar.mjs +13 -13
- package/fesm2022/acorex-components-avatar.mjs.map +1 -1
- package/fesm2022/acorex-components-badge.mjs +10 -10
- package/fesm2022/acorex-components-badge.mjs.map +1 -1
- package/fesm2022/acorex-components-bottom-navigation.mjs +12 -12
- package/fesm2022/acorex-components-bottom-navigation.mjs.map +1 -1
- package/fesm2022/acorex-components-breadcrumbs.mjs +12 -12
- package/fesm2022/acorex-components-breadcrumbs.mjs.map +1 -1
- package/fesm2022/acorex-components-button-group.mjs +25 -21
- package/fesm2022/acorex-components-button-group.mjs.map +1 -1
- package/fesm2022/acorex-components-button.mjs +68 -28
- package/fesm2022/acorex-components-button.mjs.map +1 -1
- package/fesm2022/acorex-components-calendar.mjs +33 -18
- package/fesm2022/acorex-components-calendar.mjs.map +1 -1
- package/fesm2022/acorex-components-check-box.mjs +11 -11
- package/fesm2022/acorex-components-check-box.mjs.map +1 -1
- package/fesm2022/acorex-components-chips.mjs +12 -14
- package/fesm2022/acorex-components-chips.mjs.map +1 -1
- package/fesm2022/acorex-components-circular-progress.mjs +13 -11
- package/fesm2022/acorex-components-circular-progress.mjs.map +1 -1
- package/fesm2022/acorex-components-code-editor.mjs +494 -162
- package/fesm2022/acorex-components-code-editor.mjs.map +1 -1
- package/fesm2022/acorex-components-collapse.mjs +13 -28
- package/fesm2022/acorex-components-collapse.mjs.map +1 -1
- package/fesm2022/acorex-components-color-box.mjs +11 -11
- package/fesm2022/acorex-components-color-box.mjs.map +1 -1
- package/fesm2022/acorex-components-color-palette.mjs +32 -32
- package/fesm2022/acorex-components-color-palette.mjs.map +1 -1
- package/fesm2022/acorex-components-command.mjs +18 -11
- package/fesm2022/acorex-components-command.mjs.map +1 -1
- package/fesm2022/acorex-components-comment.mjs +34 -34
- package/fesm2022/acorex-components-comment.mjs.map +1 -1
- package/fesm2022/acorex-components-conversation.mjs +56 -65
- package/fesm2022/acorex-components-conversation.mjs.map +1 -1
- package/fesm2022/acorex-components-conversation2.mjs +17641 -0
- package/fesm2022/acorex-components-conversation2.mjs.map +1 -0
- package/fesm2022/acorex-components-cron-job.mjs +53 -53
- package/fesm2022/acorex-components-cron-job.mjs.map +1 -1
- package/fesm2022/acorex-components-data-list.mjs +5 -5
- package/fesm2022/acorex-components-data-list.mjs.map +1 -1
- package/fesm2022/acorex-components-data-pager.mjs +56 -41
- package/fesm2022/acorex-components-data-pager.mjs.map +1 -1
- package/fesm2022/acorex-components-data-table.mjs +462 -105
- package/fesm2022/acorex-components-data-table.mjs.map +1 -1
- package/fesm2022/acorex-components-datetime-box.mjs +10 -10
- package/fesm2022/acorex-components-datetime-box.mjs.map +1 -1
- package/fesm2022/acorex-components-datetime-input.mjs +10 -10
- package/fesm2022/acorex-components-datetime-input.mjs.map +1 -1
- package/fesm2022/acorex-components-datetime-picker.mjs +11 -11
- package/fesm2022/acorex-components-datetime-picker.mjs.map +1 -1
- package/fesm2022/acorex-components-decorators.mjs +96 -54
- package/fesm2022/acorex-components-decorators.mjs.map +1 -1
- package/fesm2022/acorex-components-dialog.mjs +26 -16
- package/fesm2022/acorex-components-dialog.mjs.map +1 -1
- package/fesm2022/acorex-components-drawer-legacy.mjs +218 -0
- package/fesm2022/acorex-components-drawer-legacy.mjs.map +1 -0
- package/fesm2022/acorex-components-drawer.mjs +66 -150
- package/fesm2022/acorex-components-drawer.mjs.map +1 -1
- package/fesm2022/acorex-components-dropdown-button.mjs +9 -9
- package/fesm2022/acorex-components-dropdown-button.mjs.map +1 -1
- package/fesm2022/acorex-components-dropdown.mjs +20 -18
- package/fesm2022/acorex-components-dropdown.mjs.map +1 -1
- package/fesm2022/acorex-components-editor.mjs +195 -0
- package/fesm2022/acorex-components-editor.mjs.map +1 -0
- package/fesm2022/acorex-components-file-explorer.mjs +34 -34
- package/fesm2022/acorex-components-file-explorer.mjs.map +1 -1
- package/fesm2022/acorex-components-flow-chart.mjs +18 -18
- package/fesm2022/acorex-components-flow-chart.mjs.map +1 -1
- package/fesm2022/acorex-components-form.mjs +62 -37
- package/fesm2022/acorex-components-form.mjs.map +1 -1
- package/fesm2022/acorex-components-grid-layout-builder.mjs +14 -15
- package/fesm2022/acorex-components-grid-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-components-image-editor.mjs +206 -156
- package/fesm2022/acorex-components-image-editor.mjs.map +1 -1
- package/fesm2022/acorex-components-image.mjs +10 -10
- package/fesm2022/acorex-components-image.mjs.map +1 -1
- package/fesm2022/acorex-components-json-viewer.mjs +9 -9
- package/fesm2022/acorex-components-json-viewer.mjs.map +1 -1
- package/fesm2022/acorex-components-kanban.mjs +9 -7
- package/fesm2022/acorex-components-kanban.mjs.map +1 -1
- package/fesm2022/acorex-components-kbd.mjs +29 -11
- package/fesm2022/acorex-components-kbd.mjs.map +1 -1
- package/fesm2022/acorex-components-label.mjs +9 -9
- package/fesm2022/acorex-components-label.mjs.map +1 -1
- package/fesm2022/acorex-components-list.mjs +10 -10
- package/fesm2022/acorex-components-list.mjs.map +1 -1
- package/fesm2022/acorex-components-loading-dialog.mjs +23 -14
- package/fesm2022/acorex-components-loading-dialog.mjs.map +1 -1
- package/fesm2022/acorex-components-loading.mjs +23 -23
- package/fesm2022/acorex-components-loading.mjs.map +1 -1
- package/fesm2022/acorex-components-map.mjs +290 -31
- package/fesm2022/acorex-components-map.mjs.map +1 -1
- package/fesm2022/acorex-components-media-viewer.mjs +83 -82
- package/fesm2022/acorex-components-media-viewer.mjs.map +1 -1
- package/fesm2022/acorex-components-menu.mjs +24 -24
- package/fesm2022/acorex-components-menu.mjs.map +1 -1
- package/fesm2022/{acorex-components-modal-acorex-components-modal-h5Y-qcbh.mjs → acorex-components-modal-acorex-components-modal-CjHPRiZD.mjs} +24 -24
- package/fesm2022/acorex-components-modal-acorex-components-modal-CjHPRiZD.mjs.map +1 -0
- package/fesm2022/acorex-components-modal-modal-content.component-j-bS0WGj.mjs +214 -0
- package/fesm2022/acorex-components-modal-modal-content.component-j-bS0WGj.mjs.map +1 -0
- package/fesm2022/acorex-components-modal.mjs +1 -1
- package/fesm2022/acorex-components-navbar.mjs +9 -9
- package/fesm2022/acorex-components-navbar.mjs.map +1 -1
- package/fesm2022/acorex-components-notification.mjs +16 -23
- package/fesm2022/acorex-components-notification.mjs.map +1 -1
- package/fesm2022/acorex-components-number-box-legacy.mjs +412 -0
- package/fesm2022/acorex-components-number-box-legacy.mjs.map +1 -0
- package/fesm2022/acorex-components-number-box.mjs +112 -331
- package/fesm2022/acorex-components-number-box.mjs.map +1 -1
- package/fesm2022/acorex-components-otp.mjs +10 -10
- package/fesm2022/acorex-components-otp.mjs.map +1 -1
- package/fesm2022/acorex-components-page.mjs +10 -10
- package/fesm2022/acorex-components-page.mjs.map +1 -1
- package/fesm2022/acorex-components-paint.mjs +35 -40
- package/fesm2022/acorex-components-paint.mjs.map +1 -1
- package/fesm2022/acorex-components-password-box.mjs +13 -13
- package/fesm2022/acorex-components-password-box.mjs.map +1 -1
- package/fesm2022/acorex-components-pdf-reader.mjs +9 -9
- package/fesm2022/acorex-components-pdf-reader.mjs.map +1 -1
- package/fesm2022/acorex-components-phone-box.mjs +47 -10
- package/fesm2022/acorex-components-phone-box.mjs.map +1 -1
- package/fesm2022/acorex-components-picker.mjs +17 -17
- package/fesm2022/acorex-components-picker.mjs.map +1 -1
- package/fesm2022/acorex-components-popover.mjs +12 -12
- package/fesm2022/acorex-components-popover.mjs.map +1 -1
- package/fesm2022/acorex-components-popup.mjs +13 -13
- package/fesm2022/acorex-components-popup.mjs.map +1 -1
- package/fesm2022/acorex-components-progress-bar.mjs +11 -9
- package/fesm2022/acorex-components-progress-bar.mjs.map +1 -1
- package/fesm2022/acorex-components-qrcode.mjs +8 -8
- package/fesm2022/acorex-components-qrcode.mjs.map +1 -1
- package/fesm2022/acorex-components-query-builder.mjs +9 -9
- package/fesm2022/acorex-components-query-builder.mjs.map +1 -1
- package/fesm2022/acorex-components-radio.mjs +7 -7
- package/fesm2022/acorex-components-radio.mjs.map +1 -1
- package/fesm2022/acorex-components-rail-navigation.mjs +40 -38
- package/fesm2022/acorex-components-rail-navigation.mjs.map +1 -1
- package/fesm2022/acorex-components-range-slider.mjs +11 -11
- package/fesm2022/acorex-components-range-slider.mjs.map +1 -1
- package/fesm2022/acorex-components-rate-picker.mjs +20 -35
- package/fesm2022/acorex-components-rate-picker.mjs.map +1 -1
- package/fesm2022/acorex-components-rest-api-generator.mjs +23 -23
- package/fesm2022/acorex-components-rest-api-generator.mjs.map +1 -1
- package/fesm2022/acorex-components-result.mjs +8 -8
- package/fesm2022/acorex-components-result.mjs.map +1 -1
- package/fesm2022/acorex-components-routing-progress.mjs +8 -8
- package/fesm2022/acorex-components-routing-progress.mjs.map +1 -1
- package/fesm2022/acorex-components-rrule.mjs +111 -16
- package/fesm2022/acorex-components-rrule.mjs.map +1 -1
- package/fesm2022/acorex-components-scheduler-picker.mjs +2339 -0
- package/fesm2022/acorex-components-scheduler-picker.mjs.map +1 -0
- package/fesm2022/acorex-components-scheduler.mjs +52 -52
- package/fesm2022/acorex-components-scheduler.mjs.map +1 -1
- package/fesm2022/acorex-components-scss.mjs +4 -4
- package/fesm2022/acorex-components-scss.mjs.map +1 -1
- package/fesm2022/acorex-components-search-box.mjs +23 -12
- package/fesm2022/acorex-components-search-box.mjs.map +1 -1
- package/fesm2022/acorex-components-select-box.mjs +36 -17
- package/fesm2022/acorex-components-select-box.mjs.map +1 -1
- package/fesm2022/acorex-components-selection-list-2.mjs +12 -12
- package/fesm2022/acorex-components-selection-list-2.mjs.map +1 -1
- package/fesm2022/acorex-components-selection-list.mjs +10 -10
- package/fesm2022/acorex-components-selection-list.mjs.map +1 -1
- package/fesm2022/acorex-components-side-menu.mjs +31 -38
- package/fesm2022/acorex-components-side-menu.mjs.map +1 -1
- package/fesm2022/acorex-components-skeleton.mjs +8 -8
- package/fesm2022/acorex-components-skeleton.mjs.map +1 -1
- package/fesm2022/acorex-components-slider.mjs +11 -11
- package/fesm2022/acorex-components-slider.mjs.map +1 -1
- package/fesm2022/acorex-components-sliding-item.mjs +17 -17
- package/fesm2022/acorex-components-sliding-item.mjs.map +1 -1
- package/fesm2022/acorex-components-step-wizard.mjs +16 -16
- package/fesm2022/acorex-components-step-wizard.mjs.map +1 -1
- package/fesm2022/acorex-components-switch.mjs +14 -14
- package/fesm2022/acorex-components-switch.mjs.map +1 -1
- package/fesm2022/acorex-components-tabs.mjs +28 -20
- package/fesm2022/acorex-components-tabs.mjs.map +1 -1
- package/fesm2022/acorex-components-tag-box.mjs +51 -21
- package/fesm2022/acorex-components-tag-box.mjs.map +1 -1
- package/fesm2022/acorex-components-tag.mjs +47 -11
- package/fesm2022/acorex-components-tag.mjs.map +1 -1
- package/fesm2022/acorex-components-text-area.mjs +9 -9
- package/fesm2022/acorex-components-text-area.mjs.map +1 -1
- package/fesm2022/acorex-components-text-box.mjs +13 -13
- package/fesm2022/acorex-components-text-box.mjs.map +1 -1
- package/fesm2022/acorex-components-time-duration.mjs +54 -14
- package/fesm2022/acorex-components-time-duration.mjs.map +1 -1
- package/fesm2022/acorex-components-time-line.mjs +14 -29
- package/fesm2022/acorex-components-time-line.mjs.map +1 -1
- package/fesm2022/acorex-components-toast.mjs +14 -14
- package/fesm2022/acorex-components-toast.mjs.map +1 -1
- package/fesm2022/acorex-components-toolbar.mjs +9 -9
- package/fesm2022/acorex-components-toolbar.mjs.map +1 -1
- package/fesm2022/acorex-components-tooltip.mjs +12 -12
- package/fesm2022/acorex-components-tooltip.mjs.map +1 -1
- package/fesm2022/acorex-components-tree-view-legacy.mjs +511 -0
- package/fesm2022/acorex-components-tree-view-legacy.mjs.map +1 -0
- package/fesm2022/acorex-components-tree-view.mjs +1668 -424
- package/fesm2022/acorex-components-tree-view.mjs.map +1 -1
- package/fesm2022/acorex-components-uploader.mjs +28 -641
- package/fesm2022/acorex-components-uploader.mjs.map +1 -1
- package/fesm2022/acorex-components-video-player.mjs +8 -8
- package/fesm2022/acorex-components-video-player.mjs.map +1 -1
- package/fesm2022/acorex-components-wysiwyg.mjs +213 -462
- package/fesm2022/acorex-components-wysiwyg.mjs.map +1 -1
- package/fesm2022/acorex-components.mjs.map +1 -1
- package/file-explorer/index.d.ts +6 -6
- package/form/index.d.ts +3 -3
- package/grid-layout-builder/index.d.ts +1 -2
- package/image-editor/index.d.ts +11 -13
- package/kbd/index.d.ts +13 -7
- package/loading/index.d.ts +1 -1
- package/map/index.d.ts +28 -1
- package/media-viewer/index.d.ts +2 -1
- package/notification/index.d.ts +0 -2
- package/number-box/README.md +2 -2
- package/number-box/index.d.ts +32 -171
- package/number-box-legacy/README.md +3 -0
- package/number-box-legacy/index.d.ts +191 -0
- package/package.json +41 -14
- package/paint/index.d.ts +1 -6
- package/phone-box/index.d.ts +5 -4
- package/rate-picker/index.d.ts +5 -15
- package/rrule/index.d.ts +96 -1
- package/scheduler-picker/README.md +15 -0
- package/scheduler-picker/index.d.ts +1360 -0
- package/search-box/index.d.ts +6 -1
- package/select-box/index.d.ts +15 -10
- package/side-menu/index.d.ts +3 -2
- package/tag/index.d.ts +8 -2
- package/tag-box/index.d.ts +12 -3
- package/time-duration/index.d.ts +19 -3
- package/tree-view/index.d.ts +658 -168
- package/tree-view-legacy/README.md +3 -0
- package/tree-view-legacy/index.d.ts +184 -0
- package/uploader/index.d.ts +4 -331
- package/wysiwyg/index.d.ts +57 -159
- package/drawer-2/README.md +0 -3
- package/drawer-2/index.d.ts +0 -62
- package/fesm2022/acorex-components-drawer-2.mjs +0 -134
- package/fesm2022/acorex-components-drawer-2.mjs.map +0 -1
- package/fesm2022/acorex-components-modal-acorex-components-modal-h5Y-qcbh.mjs.map +0 -1
- package/fesm2022/acorex-components-modal-modal-content.component-B6tyMLdU.mjs +0 -235
- package/fesm2022/acorex-components-modal-modal-content.component-B6tyMLdU.mjs.map +0 -1
- package/fesm2022/acorex-components-number-box-2.mjs +0 -183
- package/fesm2022/acorex-components-number-box-2.mjs.map +0 -1
- package/number-box-2/README.md +0 -3
- package/number-box-2/index.d.ts +0 -41
|
@@ -1,534 +1,1778 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { moveItemInArray, transferArrayItem, AXDragDirective, AXDragHandleDirective, AXDropListDirective } from '@acorex/cdk/drag-drop';
|
|
2
|
+
import { AXFocusTrapDirective } from '@acorex/cdk/focus-trap';
|
|
3
|
+
import { AXBadgeComponent } from '@acorex/components/badge';
|
|
4
|
+
import { AXButtonComponent } from '@acorex/components/button';
|
|
5
|
+
import { AXCheckBoxComponent } from '@acorex/components/check-box';
|
|
6
|
+
import { AXDecoratorIconComponent } from '@acorex/components/decorators';
|
|
4
7
|
import { AXPlatform } from '@acorex/core/platform';
|
|
5
|
-
import { AXTranslatorPipe } from '@acorex/core/translation';
|
|
6
|
-
import { trigger, state, transition, style, animate } from '@angular/animations';
|
|
7
8
|
import * as i1 from '@angular/common';
|
|
8
|
-
import { CommonModule, NgTemplateOutlet
|
|
9
|
+
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
9
10
|
import * as i0 from '@angular/core';
|
|
10
|
-
import { inject,
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import * as i1$1 from '@angular/forms';
|
|
11
|
+
import { Injectable, inject, DestroyRef, model, input, output, signal, computed, effect, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
|
|
12
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
13
|
+
import * as i2 from '@angular/forms';
|
|
14
14
|
import { FormsModule } from '@angular/forms';
|
|
15
|
-
import {
|
|
15
|
+
import { map } from 'rxjs/operators';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Service for tree node operations
|
|
19
|
+
* Handles all business logic for tree node manipulation
|
|
20
|
+
*/
|
|
21
|
+
class AXTreeViewService {
|
|
22
|
+
// ==================== Constants ====================
|
|
23
|
+
static { this.ROOT_LIST_ID = 'ax-tree-view-root-list'; }
|
|
24
|
+
static { this.NODE_DROP_PREFIX = 'ax-tree-view-node-drop-'; }
|
|
25
|
+
static { this.LIST_PREFIX = 'ax-tree-view-list-'; }
|
|
26
|
+
// ==================== Node Finding & Traversal ====================
|
|
27
|
+
/**
|
|
28
|
+
* Find a node by ID in the tree
|
|
29
|
+
*/
|
|
30
|
+
findNodeById(nodes, id) {
|
|
31
|
+
for (const node of nodes) {
|
|
32
|
+
if (node.id === id)
|
|
33
|
+
return node;
|
|
34
|
+
if (node.children) {
|
|
35
|
+
const found = this.findNodeById(node.children, id);
|
|
36
|
+
if (found)
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Find parent node of a given node
|
|
44
|
+
*/
|
|
45
|
+
findParentNode(nodes, targetNode) {
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
if (node.children?.some((child) => child.id === targetNode.id)) {
|
|
48
|
+
return node;
|
|
49
|
+
}
|
|
50
|
+
if (node.children) {
|
|
51
|
+
const found = this.findParentNode(node.children, targetNode);
|
|
52
|
+
if (found)
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
49
55
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if targetNode is a descendant of ancestorNode (or the same node)
|
|
60
|
+
* Prevents circular references by checking if target exists in ancestor's children tree
|
|
61
|
+
*/
|
|
62
|
+
isValidDropTarget(movedNode, targetNode) {
|
|
63
|
+
if (movedNode.id === targetNode.id || this.isNodeDescendantOf(targetNode, movedNode)) {
|
|
64
|
+
return false;
|
|
54
65
|
}
|
|
55
|
-
|
|
66
|
+
return true;
|
|
56
67
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Check if targetNode is a descendant of ancestorNode
|
|
70
|
+
*/
|
|
71
|
+
isNodeDescendantOf(targetNode, ancestorNode) {
|
|
72
|
+
if (targetNode.id === ancestorNode.id) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (!ancestorNode.children || ancestorNode.children.length === 0) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
for (const child of ancestorNode.children) {
|
|
79
|
+
if (child.id === targetNode.id) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (this.isNodeDescendantOf(targetNode, child)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
60
85
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build a flat list of all visible focusable nodes
|
|
90
|
+
*/
|
|
91
|
+
buildFlatNodeList(nodes) {
|
|
92
|
+
const flatList = [];
|
|
93
|
+
const traverse = (nodeList, level, parent) => {
|
|
94
|
+
for (const node of nodeList) {
|
|
95
|
+
if (node.visible !== false && !node.disabled) {
|
|
96
|
+
flatList.push({ node, level, parent });
|
|
97
|
+
if (node.expanded && node.children) {
|
|
98
|
+
traverse(node.children, level + 1, node);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
traverse(nodes, 0);
|
|
104
|
+
return flatList;
|
|
105
|
+
}
|
|
106
|
+
// ==================== Node State Checks ====================
|
|
107
|
+
/**
|
|
108
|
+
* Check if node has children
|
|
109
|
+
*/
|
|
110
|
+
hasChildren(node) {
|
|
111
|
+
return Boolean(node.children?.length);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if node can be lazy loaded
|
|
115
|
+
*/
|
|
116
|
+
canLazyLoad(node, isLazyDataSource) {
|
|
117
|
+
return isLazyDataSource && Boolean(node.childrenCount && node.childrenCount > 0 && !this.hasChildren(node));
|
|
118
|
+
}
|
|
119
|
+
// ==================== Selection Management ====================
|
|
120
|
+
/**
|
|
121
|
+
* Recursively select/deselect all children
|
|
122
|
+
*/
|
|
123
|
+
selectAllChildren(children, selected) {
|
|
124
|
+
for (const child of children) {
|
|
125
|
+
child.selected = selected;
|
|
126
|
+
child.indeterminate = false;
|
|
127
|
+
if (child.children) {
|
|
128
|
+
this.selectAllChildren(child.children, selected);
|
|
129
|
+
}
|
|
64
130
|
}
|
|
65
|
-
this.treeView.onNodeClick.emit({ component: this, data: this.item(), nativeElement: this.nativeElement });
|
|
66
131
|
}
|
|
67
|
-
|
|
68
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Get selection state of children
|
|
134
|
+
*/
|
|
135
|
+
getChildrenSelectionState(children) {
|
|
136
|
+
let selectedCount = 0;
|
|
137
|
+
let indeterminateCount = 0;
|
|
138
|
+
for (const child of children) {
|
|
139
|
+
if (child.selected && !child.indeterminate) {
|
|
140
|
+
selectedCount++;
|
|
141
|
+
}
|
|
142
|
+
if (child.indeterminate) {
|
|
143
|
+
indeterminateCount++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
allSelected: selectedCount === children.length,
|
|
148
|
+
someSelected: selectedCount > 0 || indeterminateCount > 0,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Update parent node states based on children selection (with intermediate state support)
|
|
153
|
+
*/
|
|
154
|
+
updateParentStates(nodes, changedNode, intermediateState) {
|
|
155
|
+
const parent = this.findParentNode(nodes, changedNode);
|
|
156
|
+
if (!parent || !parent.children)
|
|
69
157
|
return;
|
|
158
|
+
const { allSelected, someSelected } = this.getChildrenSelectionState(parent.children);
|
|
159
|
+
if (allSelected) {
|
|
160
|
+
parent.selected = true;
|
|
161
|
+
parent.indeterminate = false;
|
|
70
162
|
}
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.treeView.setNodeLoading(this.item()[this.treeView.valueField()], true);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
this.treeView.onNodedbClick.emit({ component: this, data: this.item(), nativeElement: this.nativeElement });
|
|
79
|
-
this.treeView.onCollapsedChanged.emit({ component: this, data: this.item(), nativeElement: this.nativeElement });
|
|
80
|
-
}
|
|
81
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
82
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AXTreeViewItemComponent, isStandalone: true, selector: "ax-tree-view-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, isExpanded: { classPropertyName: "isExpanded", publicName: "isExpanded", isSignal: true, isRequired: false, transformFunction: null }, isActive: { classPropertyName: "isActive", publicName: "isActive", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, executorChanges: { classPropertyName: "executorChanges", publicName: "executorChanges", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isExpanded: "isExpandedChange", isActive: "isActiveChange" }, providers: [{ provide: AXComponent, useExisting: AXTreeViewItemComponent }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-tree-view-container\">\n @if (\n (treeView.showEmptyNodeMassage() && item().hasOwnProperty(treeView.childrenField())) ||\n (item()[this.treeView.childrenField()]?.length && !isLoading()) ||\n (item()[treeView.hasChildField()] && !isLoading())\n ) {\n <div class=\"ax-tree-view-icon-container\" (click)=\"handleArrowNodeClick()\">\n <i [class.ax-state-disabled]=\"item()[treeView.disableField()]\" class=\"{{ arrowIcon() }}\"></i>\n </div>\n } @else if (isLoading()) {\n <ax-loading></ax-loading>\n }\n\n <div class=\"ax-tree-view-items\">\n <ng-content select=\"ax-check-box\"></ng-content>\n <div\n [class.ax-state-disabled]=\"item()[treeView.disableField()]\"\n [axTooltip]=\"item()[treeView.tooltipField()]\"\n axTooltipPlacement=\"end-bottom\"\n (click)=\"handleTextClick()\"\n (dblclick)=\"handleTextDbClick()\"\n class=\"ax-tree-view-items-prefix ax-noselect-tree-view\"\n [class.ax-state-tree-view-active]=\"item()[treeView.activeField()]\"\n >\n @if (treeView.itemTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"treeView.itemTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item() }\"\n ></ng-container>\n } @else {\n <ng-content select=\"ax-prefix\"></ng-content>\n <ng-content select=\"ax-text\"></ng-content>\n <ng-content select=\"ax-suffix\"></ng-content>\n }\n </div>\n </div>\n</div>\n\n<div\n class=\"ax-tree-view-child\"\n [class.ax-tree-view-empty-child]=\"\n treeView.showEmptyNodeMassage() &&\n item().hasOwnProperty(treeView.childrenField()) &&\n !item()[treeView.childrenField()]?.length\n \"\n [@collapseExpand]=\"isExpanded() ? 'expanded' : 'collapsed'\"\n>\n @if (\n treeView.showEmptyNodeMassage() &&\n item().hasOwnProperty(treeView.childrenField()) &&\n !item()[treeView.childrenField()]?.length\n ) {\n <ng-container [ngTemplateOutlet]=\"empty\"></ng-container>\n } @else {\n <ng-content></ng-content>\n }\n</div>\n\n<ng-template #empty>\n {{ '@acorex:common.general.no-result-found' | translate | async }}\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], animations: [
|
|
83
|
-
trigger('collapseExpand', [
|
|
84
|
-
state('collapsed', style({
|
|
85
|
-
height: '0',
|
|
86
|
-
overflow: 'hidden',
|
|
87
|
-
opacity: 0,
|
|
88
|
-
})),
|
|
89
|
-
state('expanded', style({
|
|
90
|
-
height: '*',
|
|
91
|
-
overflow: 'hidden',
|
|
92
|
-
opacity: 1,
|
|
93
|
-
})),
|
|
94
|
-
transition('collapsed <=> expanded', [animate('300ms ease-in-out')]),
|
|
95
|
-
]),
|
|
96
|
-
], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
97
|
-
}
|
|
98
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewItemComponent, decorators: [{
|
|
99
|
-
type: Component,
|
|
100
|
-
args: [{ selector: 'ax-tree-view-item', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, animations: [
|
|
101
|
-
trigger('collapseExpand', [
|
|
102
|
-
state('collapsed', style({
|
|
103
|
-
height: '0',
|
|
104
|
-
overflow: 'hidden',
|
|
105
|
-
opacity: 0,
|
|
106
|
-
})),
|
|
107
|
-
state('expanded', style({
|
|
108
|
-
height: '*',
|
|
109
|
-
overflow: 'hidden',
|
|
110
|
-
opacity: 1,
|
|
111
|
-
})),
|
|
112
|
-
transition('collapsed <=> expanded', [animate('300ms ease-in-out')]),
|
|
113
|
-
]),
|
|
114
|
-
], imports: [CommonModule, AXLoadingComponent, AXTooltipDirective, NgTemplateOutlet, AXTranslatorPipe, AsyncPipe], providers: [{ provide: AXComponent, useExisting: AXTreeViewItemComponent }], template: "<div class=\"ax-tree-view-container\">\n @if (\n (treeView.showEmptyNodeMassage() && item().hasOwnProperty(treeView.childrenField())) ||\n (item()[this.treeView.childrenField()]?.length && !isLoading()) ||\n (item()[treeView.hasChildField()] && !isLoading())\n ) {\n <div class=\"ax-tree-view-icon-container\" (click)=\"handleArrowNodeClick()\">\n <i [class.ax-state-disabled]=\"item()[treeView.disableField()]\" class=\"{{ arrowIcon() }}\"></i>\n </div>\n } @else if (isLoading()) {\n <ax-loading></ax-loading>\n }\n\n <div class=\"ax-tree-view-items\">\n <ng-content select=\"ax-check-box\"></ng-content>\n <div\n [class.ax-state-disabled]=\"item()[treeView.disableField()]\"\n [axTooltip]=\"item()[treeView.tooltipField()]\"\n axTooltipPlacement=\"end-bottom\"\n (click)=\"handleTextClick()\"\n (dblclick)=\"handleTextDbClick()\"\n class=\"ax-tree-view-items-prefix ax-noselect-tree-view\"\n [class.ax-state-tree-view-active]=\"item()[treeView.activeField()]\"\n >\n @if (treeView.itemTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"treeView.itemTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: item() }\"\n ></ng-container>\n } @else {\n <ng-content select=\"ax-prefix\"></ng-content>\n <ng-content select=\"ax-text\"></ng-content>\n <ng-content select=\"ax-suffix\"></ng-content>\n }\n </div>\n </div>\n</div>\n\n<div\n class=\"ax-tree-view-child\"\n [class.ax-tree-view-empty-child]=\"\n treeView.showEmptyNodeMassage() &&\n item().hasOwnProperty(treeView.childrenField()) &&\n !item()[treeView.childrenField()]?.length\n \"\n [@collapseExpand]=\"isExpanded() ? 'expanded' : 'collapsed'\"\n>\n @if (\n treeView.showEmptyNodeMassage() &&\n item().hasOwnProperty(treeView.childrenField()) &&\n !item()[treeView.childrenField()]?.length\n ) {\n <ng-container [ngTemplateOutlet]=\"empty\"></ng-container>\n } @else {\n <ng-content></ng-content>\n }\n</div>\n\n<ng-template #empty>\n {{ '@acorex:common.general.no-result-found' | translate | async }}\n</ng-template>\n" }]
|
|
115
|
-
}], ctorParameters: () => [] });
|
|
116
|
-
|
|
117
|
-
class AXTreeViewComponent extends NXComponent {
|
|
118
|
-
constructor() {
|
|
119
|
-
super(...arguments);
|
|
120
|
-
this.itemsSignal = signal([], ...(ngDevMode ? [{ debugName: "itemsSignal" }] : []));
|
|
121
|
-
this.items = input(...(ngDevMode ? [undefined, { debugName: "items" }] : []));
|
|
122
|
-
this.showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
|
|
123
|
-
this.hasCheckboxField = input('hasCheckbox', ...(ngDevMode ? [{ debugName: "hasCheckboxField" }] : []));
|
|
124
|
-
this.selectionMode = input('single', ...(ngDevMode ? [{ debugName: "selectionMode" }] : []));
|
|
125
|
-
this.selectionBehavior = input(...(ngDevMode ? [undefined, { debugName: "selectionBehavior" }] : []));
|
|
126
|
-
this.selectionScope = input('all', ...(ngDevMode ? [{ debugName: "selectionScope" }] : []));
|
|
127
|
-
this.focusNodeEnabled = input(true, ...(ngDevMode ? [{ debugName: "focusNodeEnabled" }] : []));
|
|
128
|
-
this.valueField = input('id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
|
|
129
|
-
this.textField = input('text', ...(ngDevMode ? [{ debugName: "textField" }] : []));
|
|
130
|
-
this.visibleField = input('visible', ...(ngDevMode ? [{ debugName: "visibleField" }] : []));
|
|
131
|
-
this.disableField = input('disabled', ...(ngDevMode ? [{ debugName: "disableField" }] : []));
|
|
132
|
-
this.hasChildField = input('hasChild', ...(ngDevMode ? [{ debugName: "hasChildField" }] : []));
|
|
133
|
-
this.selectedField = input('selected', ...(ngDevMode ? [{ debugName: "selectedField" }] : []));
|
|
134
|
-
this.expandedField = input('expanded', ...(ngDevMode ? [{ debugName: "expandedField" }] : []));
|
|
135
|
-
this.tooltipField = input('tooltip', ...(ngDevMode ? [{ debugName: "tooltipField" }] : []));
|
|
136
|
-
this.childrenField = input('children', ...(ngDevMode ? [{ debugName: "childrenField" }] : []));
|
|
137
|
-
this.activeField = input('active', ...(ngDevMode ? [{ debugName: "activeField" }] : []));
|
|
138
|
-
this.indeterminateField = input('indeterminate', ...(ngDevMode ? [{ debugName: "indeterminateField" }] : []));
|
|
139
|
-
this.parentField = input('parentId', ...(ngDevMode ? [{ debugName: "parentField" }] : []));
|
|
140
|
-
this.iconField = input('icon', ...(ngDevMode ? [{ debugName: "iconField" }] : []));
|
|
141
|
-
this.toggleIcons = input(...(ngDevMode ? [undefined, { debugName: "toggleIcons" }] : []));
|
|
142
|
-
this.look = input('defult', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
143
|
-
this.showEmptyNodeMassage = input(false, ...(ngDevMode ? [{ debugName: "showEmptyNodeMassage" }] : []));
|
|
144
|
-
this.onSelectionChanged = output();
|
|
145
|
-
this.onItemSelectedChanged = output();
|
|
146
|
-
this.onNodeClick = output();
|
|
147
|
-
this.onCollapsedChanged = output();
|
|
148
|
-
this.onNodedbClick = output();
|
|
149
|
-
this.executorChanges = signal(null, ...(ngDevMode ? [{ debugName: "executorChanges" }] : []));
|
|
150
|
-
this.platformService = inject(AXPlatform);
|
|
151
|
-
this.#effect = effect(() => {
|
|
152
|
-
const itemsInput = this.items();
|
|
153
|
-
if (typeof itemsInput === 'function') {
|
|
154
|
-
const result = itemsInput();
|
|
155
|
-
if (result instanceof Promise) {
|
|
156
|
-
this.itemsPromise = (options) => itemsInput(options);
|
|
157
|
-
this.fetchData();
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
this.itemsSignal.set(result);
|
|
161
|
-
this.itemsPromise = null;
|
|
162
|
-
}
|
|
163
|
+
else if (someSelected) {
|
|
164
|
+
if (intermediateState) {
|
|
165
|
+
parent.selected = true;
|
|
166
|
+
parent.indeterminate = true;
|
|
163
167
|
}
|
|
164
168
|
else {
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
parent.selected = false;
|
|
170
|
+
parent.indeterminate = false;
|
|
167
171
|
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
parent.selected = false;
|
|
175
|
+
parent.indeterminate = false;
|
|
176
|
+
}
|
|
177
|
+
this.updateParentStates(nodes, parent, intermediateState);
|
|
171
178
|
}
|
|
172
|
-
/**
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Recursively deselect all nodes
|
|
181
|
+
*/
|
|
182
|
+
deselectAllNodes(nodes) {
|
|
183
|
+
for (const node of nodes) {
|
|
184
|
+
node.selected = false;
|
|
185
|
+
node.indeterminate = false;
|
|
186
|
+
if (node.children) {
|
|
187
|
+
this.deselectAllNodes(node.children);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
175
190
|
}
|
|
176
|
-
|
|
177
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Recursively set selection state for all nodes
|
|
193
|
+
*/
|
|
194
|
+
setAllSelection(nodes, selected) {
|
|
195
|
+
for (const node of nodes) {
|
|
196
|
+
node.selected = selected;
|
|
197
|
+
node.indeterminate = false;
|
|
198
|
+
if (node.children) {
|
|
199
|
+
this.setAllSelection(node.children, selected);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
178
202
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Recursively count selected nodes
|
|
205
|
+
*/
|
|
206
|
+
countSelected(nodes) {
|
|
207
|
+
let count = 0;
|
|
208
|
+
for (const node of nodes) {
|
|
209
|
+
if (node.selected)
|
|
210
|
+
count++;
|
|
211
|
+
if (node.children) {
|
|
212
|
+
count += this.countSelected(node.children);
|
|
213
|
+
}
|
|
183
214
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
215
|
+
return count;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Recursively collect selected nodes
|
|
219
|
+
*/
|
|
220
|
+
collectSelected(nodes, result) {
|
|
221
|
+
for (const node of nodes) {
|
|
222
|
+
if (node.selected)
|
|
223
|
+
result.push(node);
|
|
224
|
+
if (node.children) {
|
|
225
|
+
this.collectSelected(node.children, result);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Recursively remove selected nodes
|
|
231
|
+
*/
|
|
232
|
+
removeSelected(nodes) {
|
|
233
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
234
|
+
if (nodes[i].selected && !nodes[i].indeterminate) {
|
|
235
|
+
nodes.splice(i, 1);
|
|
236
|
+
}
|
|
237
|
+
else if (nodes[i].children) {
|
|
238
|
+
this.removeSelected(nodes[i].children ?? []);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Recursively update all parent states in the tree (used after deletion)
|
|
244
|
+
*/
|
|
245
|
+
updateAllParentStates(nodes, intermediateState) {
|
|
246
|
+
for (const node of nodes) {
|
|
247
|
+
if (node.children && node.children.length > 0) {
|
|
248
|
+
this.updateAllParentStates(node.children, intermediateState);
|
|
249
|
+
const { allSelected, someSelected } = this.getChildrenSelectionState(node.children);
|
|
250
|
+
if (allSelected) {
|
|
251
|
+
node.selected = true;
|
|
252
|
+
node.indeterminate = false;
|
|
253
|
+
}
|
|
254
|
+
else if (someSelected) {
|
|
255
|
+
if (intermediateState) {
|
|
256
|
+
node.selected = true;
|
|
257
|
+
node.indeterminate = true;
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
node.selected = false;
|
|
261
|
+
node.indeterminate = false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
node.selected = false;
|
|
266
|
+
node.indeterminate = false;
|
|
211
267
|
}
|
|
212
268
|
}
|
|
213
|
-
this.onItemSelectedChanged.emit({
|
|
214
|
-
component: this,
|
|
215
|
-
data: item,
|
|
216
|
-
nativeElement: this.nativeElement,
|
|
217
|
-
});
|
|
218
|
-
const result = this.findSelectedNodes(this.itemsSignal());
|
|
219
|
-
this.onSelectionChanged.emit({
|
|
220
|
-
component: this,
|
|
221
|
-
data: result,
|
|
222
|
-
nativeElement: this.nativeElement,
|
|
223
|
-
});
|
|
224
269
|
}
|
|
225
270
|
}
|
|
271
|
+
// ==================== Expansion Management ====================
|
|
226
272
|
/**
|
|
227
|
-
*
|
|
228
|
-
* auto expand
|
|
229
|
-
*
|
|
273
|
+
* Recursively set expanded state (with lazy loading)
|
|
230
274
|
*/
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
275
|
+
async setExpandedState(nodes, expanded, isLazyDataSource, loadNodeChildren) {
|
|
276
|
+
for (const node of nodes) {
|
|
277
|
+
const hasChildren = this.hasChildren(node);
|
|
278
|
+
const canLazyLoad = this.canLazyLoad(node, isLazyDataSource);
|
|
279
|
+
if (hasChildren || canLazyLoad) {
|
|
280
|
+
if (expanded && canLazyLoad) {
|
|
281
|
+
await loadNodeChildren(node);
|
|
282
|
+
}
|
|
283
|
+
node.expanded = expanded;
|
|
284
|
+
if (node.children) {
|
|
285
|
+
await this.setExpandedState(node.children, expanded, isLazyDataSource, loadNodeChildren);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
234
288
|
}
|
|
235
289
|
}
|
|
290
|
+
// ==================== Drag & Drop Helpers ====================
|
|
236
291
|
/**
|
|
237
|
-
*
|
|
238
|
-
* expand and change value parent change
|
|
239
|
-
*
|
|
292
|
+
* Get array reference by drop list ID
|
|
240
293
|
*/
|
|
241
|
-
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
await this.fetchData(item);
|
|
294
|
+
getArrayByListId(nodes, listId) {
|
|
295
|
+
if (listId === AXTreeViewService.ROOT_LIST_ID) {
|
|
296
|
+
return nodes;
|
|
245
297
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.
|
|
298
|
+
if (listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)) {
|
|
299
|
+
const nodeId = listId.replace(AXTreeViewService.NODE_DROP_PREFIX, '');
|
|
300
|
+
const node = this.findNodeById(nodes, nodeId);
|
|
301
|
+
return node ? [node] : null;
|
|
249
302
|
}
|
|
303
|
+
const nodeId = listId.replace(AXTreeViewService.LIST_PREFIX, '');
|
|
304
|
+
const node = this.findNodeById(nodes, nodeId);
|
|
305
|
+
return node?.children ?? null;
|
|
250
306
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
307
|
+
/**
|
|
308
|
+
* Find parent node by list ID
|
|
309
|
+
*/
|
|
310
|
+
findParentByListId(nodes, listId) {
|
|
311
|
+
if (listId === AXTreeViewService.ROOT_LIST_ID) {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
const prefix = listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)
|
|
315
|
+
? AXTreeViewService.NODE_DROP_PREFIX
|
|
316
|
+
: AXTreeViewService.LIST_PREFIX;
|
|
317
|
+
const nodeId = listId.replace(prefix, '');
|
|
318
|
+
return this.findNodeById(nodes, nodeId) ?? undefined;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Generate unique list ID for each node
|
|
322
|
+
*/
|
|
323
|
+
getListId(node) {
|
|
324
|
+
return node ? `${AXTreeViewService.LIST_PREFIX}${node.id}` : AXTreeViewService.ROOT_LIST_ID;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get root list ID constant
|
|
328
|
+
*/
|
|
329
|
+
getRootListId() {
|
|
330
|
+
return AXTreeViewService.ROOT_LIST_ID;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get node drop prefix constant
|
|
334
|
+
*/
|
|
335
|
+
getNodeDropPrefix() {
|
|
336
|
+
return AXTreeViewService.NODE_DROP_PREFIX;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get list prefix constant
|
|
340
|
+
*/
|
|
341
|
+
getListPrefix() {
|
|
342
|
+
return AXTreeViewService.LIST_PREFIX;
|
|
343
|
+
}
|
|
344
|
+
// ==================== Node Utilities ====================
|
|
345
|
+
/**
|
|
346
|
+
* Get all nodes in a flat array
|
|
347
|
+
*/
|
|
348
|
+
getAllNodes(nodes) {
|
|
349
|
+
const allNodes = [];
|
|
350
|
+
const traverse = (nodeList) => {
|
|
351
|
+
for (const node of nodeList) {
|
|
352
|
+
allNodes.push(node);
|
|
353
|
+
if (node.children) {
|
|
354
|
+
traverse(node.children);
|
|
355
|
+
}
|
|
265
356
|
}
|
|
266
|
-
}
|
|
357
|
+
};
|
|
358
|
+
traverse(nodes);
|
|
359
|
+
return allNodes;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get the path to a node (array of parent nodes from root to node)
|
|
363
|
+
*/
|
|
364
|
+
getNodePath(nodes, nodeId) {
|
|
365
|
+
const path = [];
|
|
366
|
+
const node = this.findNodeById(nodes, nodeId);
|
|
367
|
+
if (!node) {
|
|
368
|
+
return path;
|
|
369
|
+
}
|
|
370
|
+
let current = node;
|
|
371
|
+
while (current) {
|
|
372
|
+
path.unshift(current);
|
|
373
|
+
const parent = this.findParentNode(nodes, current);
|
|
374
|
+
current = parent ?? null;
|
|
375
|
+
}
|
|
376
|
+
return path;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get the level/depth of a node (0 = root level)
|
|
380
|
+
*/
|
|
381
|
+
getNodeLevel(nodes, nodeId) {
|
|
382
|
+
const path = this.getNodePath(nodes, nodeId);
|
|
383
|
+
return path.length > 0 ? path.length - 1 : -1;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Get sibling nodes of a given node
|
|
387
|
+
*/
|
|
388
|
+
getSiblings(nodes, nodeId) {
|
|
389
|
+
const node = this.findNodeById(nodes, nodeId);
|
|
390
|
+
if (!node) {
|
|
391
|
+
return [];
|
|
392
|
+
}
|
|
393
|
+
const parent = this.findParentNode(nodes, node);
|
|
394
|
+
const siblingsArray = parent?.children ?? nodes;
|
|
395
|
+
return siblingsArray.filter((n) => n.id !== nodeId);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Clone a node (creates a deep copy)
|
|
399
|
+
*/
|
|
400
|
+
cloneNode(node) {
|
|
401
|
+
const cloned = {
|
|
402
|
+
id: `${node.id}-clone-${Date.now()}`,
|
|
403
|
+
label: node.label,
|
|
404
|
+
icon: node.icon,
|
|
405
|
+
expanded: node.expanded,
|
|
406
|
+
selected: false, // Cloned nodes are not selected by default
|
|
407
|
+
indeterminate: false,
|
|
408
|
+
disabled: node.disabled,
|
|
409
|
+
visible: node.visible,
|
|
410
|
+
childrenCount: node.childrenCount,
|
|
411
|
+
loading: false, // Cloned nodes are not loading
|
|
412
|
+
data: node.data ? JSON.parse(JSON.stringify(node.data)) : undefined,
|
|
413
|
+
};
|
|
414
|
+
if (node.children && node.children.length > 0) {
|
|
415
|
+
cloned.children = node.children.map((child) => this.cloneNode(child));
|
|
416
|
+
cloned.childrenCount = cloned.children.length;
|
|
417
|
+
}
|
|
418
|
+
return cloned;
|
|
267
419
|
}
|
|
268
420
|
/**
|
|
269
|
-
*
|
|
270
|
-
* indeterminate logic
|
|
271
|
-
*
|
|
421
|
+
* Validate node structure (check for required fields and circular references)
|
|
272
422
|
*/
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
423
|
+
validateNode(node, visitedIds = new Set()) {
|
|
424
|
+
const errors = [];
|
|
425
|
+
if (!node.id) {
|
|
426
|
+
errors.push('Node must have an id');
|
|
427
|
+
}
|
|
428
|
+
if (!node.label) {
|
|
429
|
+
errors.push('Node must have a label');
|
|
430
|
+
}
|
|
431
|
+
if (visitedIds.has(node.id)) {
|
|
432
|
+
errors.push(`Circular reference detected: node ${node.id} appears multiple times in the tree`);
|
|
433
|
+
}
|
|
434
|
+
if (node.children) {
|
|
435
|
+
const newVisited = new Set(visitedIds);
|
|
436
|
+
newVisited.add(node.id);
|
|
437
|
+
for (const child of node.children) {
|
|
438
|
+
const childValidation = this.validateNode(child, newVisited);
|
|
439
|
+
if (!childValidation.valid) {
|
|
440
|
+
errors.push(...childValidation.errors.map((e) => `Child of ${node.id}: ${e}`));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (node.childrenCount !== undefined && node.childrenCount !== node.children.length) {
|
|
444
|
+
errors.push(`Node ${node.id}: childrenCount (${node.childrenCount}) does not match children array length (${node.children.length})`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
valid: errors.length === 0,
|
|
449
|
+
errors,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
453
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewService }); }
|
|
454
|
+
}
|
|
455
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewService, decorators: [{
|
|
456
|
+
type: Injectable
|
|
457
|
+
}] });
|
|
458
|
+
|
|
459
|
+
class AXTreeViewComponent {
|
|
460
|
+
constructor() {
|
|
461
|
+
// ==================== Dependencies ====================
|
|
462
|
+
this.treeService = inject(AXTreeViewService);
|
|
463
|
+
this.platformService = inject(AXPlatform);
|
|
464
|
+
this.destroyRef = inject(DestroyRef);
|
|
465
|
+
// ==================== Inputs ====================
|
|
466
|
+
/** Tree data source - can be static array or lazy loading function */
|
|
467
|
+
this.datasource = model.required(...(ngDevMode ? [{ debugName: "datasource" }] : []));
|
|
468
|
+
/** Selection mode: 'single' (click to select) or 'multiple' (checkbox selection) */
|
|
469
|
+
this.selectMode = input('multiple', ...(ngDevMode ? [{ debugName: "selectMode" }] : []));
|
|
470
|
+
/** Whether to show checkboxes for selection (only applies to multiple mode) */
|
|
471
|
+
this.showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
|
|
472
|
+
/** When true, selecting a parent also selects all loaded children (only for multiple mode) */
|
|
473
|
+
this.checkChildrenOnSelect = input(true, ...(ngDevMode ? [{ debugName: "checkChildrenOnSelect" }] : []));
|
|
474
|
+
/** When true, selecting a child makes parents indeterminate (only for multiple mode) */
|
|
475
|
+
this.intermediateState = input(true, ...(ngDevMode ? [{ debugName: "intermediateState" }] : []));
|
|
476
|
+
/** When true, clicking on a node toggles its selection (works for both single and multiple modes) */
|
|
477
|
+
this.checkOnClick = input(false, ...(ngDevMode ? [{ debugName: "checkOnClick" }] : []));
|
|
478
|
+
/** Drag and drop mode: 'none' (disabled), 'handler' (drag handle), 'item' (entire item) */
|
|
479
|
+
this.dragMode = input('handler', ...(ngDevMode ? [{ debugName: "dragMode" }] : []));
|
|
480
|
+
/** Drag operation type: 'order-only' (reorder only), 'move' (move between parents), 'both' (allow both) */
|
|
481
|
+
this.dragOperationType = input('both', ...(ngDevMode ? [{ debugName: "dragOperationType" }] : []));
|
|
482
|
+
/** Whether to show icons */
|
|
483
|
+
this.showIcons = input(true, ...(ngDevMode ? [{ debugName: "showIcons" }] : []));
|
|
484
|
+
/** Whether to show children count badge */
|
|
485
|
+
this.showChildrenBadge = input(true, ...(ngDevMode ? [{ debugName: "showChildrenBadge" }] : []));
|
|
486
|
+
/** Custom icon for expanded nodes */
|
|
487
|
+
this.expandedIcon = input('fa-solid fa-chevron-down', ...(ngDevMode ? [{ debugName: "expandedIcon" }] : []));
|
|
488
|
+
/** Custom icon for collapsed nodes */
|
|
489
|
+
this.collapsedIcon = input('fa-solid fa-chevron-right', ...(ngDevMode ? [{ debugName: "collapsedIcon" }] : []));
|
|
490
|
+
/** Indent size in pixels for each level */
|
|
491
|
+
this.indentSize = input(12, ...(ngDevMode ? [{ debugName: "indentSize" }] : []));
|
|
492
|
+
/** Node height in pixels */
|
|
493
|
+
this.nodeHeight = input('normal', ...(ngDevMode ? [{ debugName: "nodeHeight" }] : []));
|
|
494
|
+
/** Visual style variant */
|
|
495
|
+
this.look = input('default', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
496
|
+
/** Custom template for tree items */
|
|
497
|
+
this.itemTemplate = input(...(ngDevMode ? [undefined, { debugName: "itemTemplate" }] : []));
|
|
498
|
+
// ==================== Outputs ====================
|
|
499
|
+
/** Emitted before a drop operation - set canceled to true to prevent drop */
|
|
500
|
+
this.onBeforeDrop = output();
|
|
501
|
+
/** Emitted when a node is toggled (expanded/collapsed) */
|
|
502
|
+
this.onNodeToggle = output();
|
|
503
|
+
/** Emitted when a node is selected/deselected */
|
|
504
|
+
this.onNodeSelect = output();
|
|
505
|
+
/** Emitted when nodes are reordered within the same parent */
|
|
506
|
+
this.onOrderChange = output();
|
|
507
|
+
/** Emitted when a node is moved to a different parent */
|
|
508
|
+
this.onMoveChange = output();
|
|
509
|
+
/** Emitted for any item change (order or move) */
|
|
510
|
+
this.onItemsChange = output();
|
|
511
|
+
// ==================== Internal State ====================
|
|
512
|
+
/** Internal signal for tree nodes */
|
|
513
|
+
this.nodes = signal([], ...(ngDevMode ? [{ debugName: "nodes" }] : []));
|
|
514
|
+
/** Internal signal for tracking loading state */
|
|
515
|
+
this.loadingNodes = signal(new Set(), ...(ngDevMode ? [{ debugName: "loadingNodes" }] : []));
|
|
516
|
+
/** Currently focused node ID for keyboard navigation */
|
|
517
|
+
this.focusedNodeId = signal(null, ...(ngDevMode ? [{ debugName: "focusedNodeId" }] : []));
|
|
518
|
+
/** RTL detection signal */
|
|
519
|
+
this.isRtl = signal(this.platformService.isRtl(), ...(ngDevMode ? [{ debugName: "isRtl" }] : []));
|
|
520
|
+
/** Computed chevron icons that flip for RTL */
|
|
521
|
+
this.directionExpandedIcon = computed(() => this.expandedIcon(), ...(ngDevMode ? [{ debugName: "directionExpandedIcon" }] : []));
|
|
522
|
+
this.directionCollapsedIcon = computed(() => {
|
|
523
|
+
const isRtlDirection = this.isRtl();
|
|
524
|
+
const defaultIcon = this.collapsedIcon();
|
|
525
|
+
if (isRtlDirection && defaultIcon === 'fa-solid fa-chevron-right') {
|
|
526
|
+
return 'fa-solid fa-chevron-left';
|
|
282
527
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
parent[this.selectedField()] = null;
|
|
528
|
+
if (!isRtlDirection && defaultIcon === 'fa-solid fa-chevron-left') {
|
|
529
|
+
return 'fa-solid fa-chevron-right';
|
|
286
530
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
531
|
+
return defaultIcon;
|
|
532
|
+
}, ...(ngDevMode ? [{ debugName: "directionCollapsedIcon" }] : []));
|
|
533
|
+
/** Flag to prevent infinite loops when syncing datasource */
|
|
534
|
+
this.isUpdatingFromDatasource = false;
|
|
535
|
+
/** Computed to check if datasource is a function */
|
|
536
|
+
this.isLazyDataSource = computed(() => typeof this.datasource() === 'function', ...(ngDevMode ? [{ debugName: "isLazyDataSource" }] : []));
|
|
537
|
+
// ==================== Effects ====================
|
|
538
|
+
/** Effect to handle datasource changes */
|
|
539
|
+
this.#datasourceEffect = effect(() => {
|
|
540
|
+
if (this.isUpdatingFromDatasource)
|
|
541
|
+
return;
|
|
542
|
+
const ds = this.datasource();
|
|
543
|
+
if (Array.isArray(ds)) {
|
|
544
|
+
this.nodes.set([...ds]);
|
|
290
545
|
}
|
|
291
|
-
else {
|
|
292
|
-
|
|
293
|
-
|
|
546
|
+
else if (typeof ds === 'function') {
|
|
547
|
+
this.loadRootItems(ds).catch((error) => {
|
|
548
|
+
this.handleError('Failed to load root items', error);
|
|
549
|
+
});
|
|
294
550
|
}
|
|
295
|
-
|
|
551
|
+
}, ...(ngDevMode ? [{ debugName: "#datasourceEffect" }] : []));
|
|
552
|
+
/** Initialize direction change listener */
|
|
553
|
+
this.#initDirectionListener = afterNextRender(() => {
|
|
554
|
+
this.platformService.directionChange
|
|
555
|
+
.pipe(map((event) => event.data === 'rtl'), takeUntilDestroyed(this.destroyRef))
|
|
556
|
+
.subscribe((isRtl) => this.isRtl.set(isRtl));
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
// ==================== Effects ====================
|
|
560
|
+
/** Effect to handle datasource changes */
|
|
561
|
+
#datasourceEffect;
|
|
562
|
+
/** Initialize direction change listener */
|
|
563
|
+
#initDirectionListener;
|
|
564
|
+
// ==================== Public API ====================
|
|
565
|
+
/**
|
|
566
|
+
* Expand all nodes in the tree (with lazy loading support)
|
|
567
|
+
*/
|
|
568
|
+
async expandAll() {
|
|
569
|
+
await this.treeService.setExpandedState(this.nodes(), true, this.isLazyDataSource(), (node) => this.loadNodeChildren(node));
|
|
570
|
+
this.refreshNodes();
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Collapse all nodes in the tree
|
|
574
|
+
*/
|
|
575
|
+
collapseAll() {
|
|
576
|
+
this.treeService.setExpandedState(this.nodes(), false, this.isLazyDataSource(), (node) => this.loadNodeChildren(node));
|
|
577
|
+
this.refreshNodes();
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get count of selected nodes
|
|
581
|
+
*/
|
|
582
|
+
getSelectedCount() {
|
|
583
|
+
return this.treeService.countSelected(this.nodes());
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Check if any nodes are selected
|
|
587
|
+
*/
|
|
588
|
+
hasSelection() {
|
|
589
|
+
return this.getSelectedCount() > 0;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Get all selected nodes
|
|
593
|
+
*/
|
|
594
|
+
getSelectedNodes() {
|
|
595
|
+
const selected = [];
|
|
596
|
+
this.treeService.collectSelected(this.nodes(), selected);
|
|
597
|
+
return selected;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Delete selected nodes from the tree
|
|
601
|
+
*/
|
|
602
|
+
deleteSelected() {
|
|
603
|
+
this.treeService.removeSelected(this.nodes());
|
|
604
|
+
this.treeService.updateAllParentStates(this.nodes(), this.intermediateState());
|
|
605
|
+
this.refreshNodes();
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Select all nodes in the tree
|
|
609
|
+
*/
|
|
610
|
+
selectAll() {
|
|
611
|
+
this.treeService.setAllSelection(this.nodes(), true);
|
|
612
|
+
this.refreshNodes();
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Deselect all nodes in the tree
|
|
616
|
+
*/
|
|
617
|
+
deselectAll() {
|
|
618
|
+
this.treeService.setAllSelection(this.nodes(), false);
|
|
619
|
+
this.refreshNodes();
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Find a node by ID in the tree
|
|
623
|
+
*/
|
|
624
|
+
findNode(id) {
|
|
625
|
+
return this.treeService.findNodeById(this.nodes(), id);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Refresh the tree to trigger change detection
|
|
629
|
+
*/
|
|
630
|
+
refresh() {
|
|
631
|
+
this.refreshNodes();
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Check if a node is currently loading
|
|
635
|
+
*/
|
|
636
|
+
isNodeLoading(nodeId) {
|
|
637
|
+
return this.loadingNodes().has(nodeId);
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Edit/update a node's properties
|
|
641
|
+
* @param nodeId - The ID of the node to edit
|
|
642
|
+
* @param updates - Partial node object with properties to update
|
|
643
|
+
* @returns true if node was found and updated, false otherwise
|
|
644
|
+
*/
|
|
645
|
+
editNode(nodeId, updates) {
|
|
646
|
+
const node = this.findNode(nodeId);
|
|
647
|
+
if (!node) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
// Update node properties
|
|
651
|
+
Object.assign(node, updates);
|
|
652
|
+
// If children array is provided, ensure it exists
|
|
653
|
+
if (updates.children !== undefined) {
|
|
654
|
+
node.children = updates.children;
|
|
296
655
|
}
|
|
656
|
+
// Update childrenCount if children array is provided
|
|
657
|
+
if (updates.children !== undefined) {
|
|
658
|
+
node.childrenCount = updates.children.length;
|
|
659
|
+
}
|
|
660
|
+
this.refreshNodes();
|
|
661
|
+
return true;
|
|
297
662
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
663
|
+
/**
|
|
664
|
+
* Add a child node to a parent node
|
|
665
|
+
* @param parentId - The ID of the parent node
|
|
666
|
+
* @param childNode - The child node to add
|
|
667
|
+
* @param index - Optional index to insert at (default: append to end)
|
|
668
|
+
* @returns true if parent was found and child was added, false otherwise
|
|
669
|
+
*/
|
|
670
|
+
addChild(parentId, childNode, index) {
|
|
671
|
+
const parent = this.findNode(parentId);
|
|
672
|
+
if (!parent) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
// Ensure children array exists
|
|
676
|
+
if (!parent.children) {
|
|
677
|
+
parent.children = [];
|
|
678
|
+
}
|
|
679
|
+
// Insert or append child
|
|
680
|
+
if (index !== undefined && index >= 0 && index <= parent.children.length) {
|
|
681
|
+
parent.children.splice(index, 0, childNode);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
parent.children.push(childNode);
|
|
685
|
+
}
|
|
686
|
+
// Update childrenCount
|
|
687
|
+
parent.childrenCount = parent.children.length;
|
|
688
|
+
// Auto-expand parent if it was collapsed
|
|
689
|
+
if (!parent.expanded) {
|
|
690
|
+
parent.expanded = true;
|
|
691
|
+
}
|
|
692
|
+
this.refreshNodes();
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Remove a node from the tree
|
|
697
|
+
* @param nodeId - The ID of the node to remove
|
|
698
|
+
* @returns The removed node if found, null otherwise
|
|
699
|
+
*/
|
|
700
|
+
removeNode(nodeId) {
|
|
701
|
+
const node = this.findNode(nodeId);
|
|
702
|
+
if (!node) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
// Find parent to remove from its children array
|
|
706
|
+
const parent = this.treeService.findParentNode(this.nodes(), node);
|
|
707
|
+
const targetArray = parent?.children ?? this.nodes();
|
|
708
|
+
// Find and remove the node
|
|
709
|
+
const index = targetArray.findIndex((n) => n.id === nodeId);
|
|
710
|
+
if (index !== -1) {
|
|
711
|
+
const removed = targetArray.splice(index, 1)[0];
|
|
712
|
+
// Update parent's childrenCount if it exists
|
|
713
|
+
if (parent) {
|
|
714
|
+
parent.childrenCount = parent.children?.length ?? 0;
|
|
302
715
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return parent;
|
|
716
|
+
// Update parent states if needed
|
|
717
|
+
if (this.intermediateState()) {
|
|
718
|
+
this.treeService.updateAllParentStates(this.nodes(), this.intermediateState());
|
|
307
719
|
}
|
|
720
|
+
this.refreshNodes();
|
|
721
|
+
return removed;
|
|
308
722
|
}
|
|
309
723
|
return null;
|
|
310
724
|
}
|
|
311
725
|
/**
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
726
|
+
* Expand a specific node
|
|
727
|
+
* @param nodeId - The ID of the node to expand
|
|
728
|
+
* @returns Promise that resolves when expansion is complete (if lazy loading)
|
|
315
729
|
*/
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
730
|
+
async expandNode(nodeId) {
|
|
731
|
+
const node = this.findNode(nodeId);
|
|
732
|
+
if (!node) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const hasChildren = this.treeService.hasChildren(node);
|
|
736
|
+
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource());
|
|
737
|
+
if (hasChildren || canLazyLoad) {
|
|
738
|
+
if (canLazyLoad) {
|
|
739
|
+
await this.loadNodeChildren(node);
|
|
321
740
|
}
|
|
322
|
-
|
|
323
|
-
|
|
741
|
+
node.expanded = true;
|
|
742
|
+
this.refreshNodes();
|
|
743
|
+
this.onNodeToggle.emit({ component: this, node, nativeEvent: new Event('expand') });
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Collapse a specific node
|
|
748
|
+
* @param nodeId - The ID of the node to collapse
|
|
749
|
+
*/
|
|
750
|
+
collapseNode(nodeId) {
|
|
751
|
+
const node = this.findNode(nodeId);
|
|
752
|
+
if (!node) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (node.expanded) {
|
|
756
|
+
node.expanded = false;
|
|
757
|
+
this.refreshNodes();
|
|
758
|
+
this.onNodeToggle.emit({ component: this, node, nativeEvent: new Event('collapse') });
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Toggle expansion state of a specific node
|
|
763
|
+
* @param nodeId - The ID of the node to toggle
|
|
764
|
+
* @returns Promise that resolves when toggle is complete (if lazy loading)
|
|
765
|
+
*/
|
|
766
|
+
async toggleNodeExpansion(nodeId) {
|
|
767
|
+
const node = this.findNode(nodeId);
|
|
768
|
+
if (!node) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (node.expanded) {
|
|
772
|
+
this.collapseNode(nodeId);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
await this.expandNode(nodeId);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Programmatically select a node
|
|
780
|
+
* @param nodeId - The ID of the node to select
|
|
781
|
+
* @returns true if node was found and selected, false otherwise
|
|
782
|
+
*/
|
|
783
|
+
selectNode(nodeId) {
|
|
784
|
+
const node = this.findNode(nodeId);
|
|
785
|
+
if (!node || node.disabled) {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
const mode = this.selectMode();
|
|
789
|
+
if (mode === 'single') {
|
|
790
|
+
this.treeService.deselectAllNodes(this.nodes());
|
|
791
|
+
node.selected = true;
|
|
792
|
+
node.indeterminate = false;
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
node.selected = true;
|
|
796
|
+
node.indeterminate = false;
|
|
797
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
798
|
+
this.treeService.selectAllChildren(node.children, true);
|
|
799
|
+
}
|
|
800
|
+
if (this.intermediateState()) {
|
|
801
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
324
802
|
}
|
|
803
|
+
}
|
|
804
|
+
this.refreshNodes();
|
|
805
|
+
this.onNodeSelect.emit({
|
|
806
|
+
component: this,
|
|
807
|
+
node,
|
|
808
|
+
isUserInteraction: false,
|
|
325
809
|
});
|
|
326
|
-
return
|
|
810
|
+
return true;
|
|
327
811
|
}
|
|
328
812
|
/**
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
813
|
+
* Programmatically deselect a node
|
|
814
|
+
* @param nodeId - The ID of the node to deselect
|
|
815
|
+
* @returns true if node was found and deselected, false otherwise
|
|
332
816
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
817
|
+
deselectNode(nodeId) {
|
|
818
|
+
const node = this.findNode(nodeId);
|
|
819
|
+
if (!node) {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
node.selected = false;
|
|
823
|
+
node.indeterminate = false;
|
|
824
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
825
|
+
this.treeService.selectAllChildren(node.children, false);
|
|
826
|
+
}
|
|
827
|
+
if (this.intermediateState()) {
|
|
828
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
829
|
+
}
|
|
830
|
+
this.refreshNodes();
|
|
831
|
+
this.onNodeSelect.emit({
|
|
832
|
+
component: this,
|
|
833
|
+
node,
|
|
834
|
+
isUserInteraction: false,
|
|
339
835
|
});
|
|
836
|
+
return true;
|
|
340
837
|
}
|
|
341
838
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
839
|
+
* Get parent node of a given node
|
|
840
|
+
* @param nodeId - The ID of the node
|
|
841
|
+
* @returns The parent node if found, null otherwise
|
|
345
842
|
*/
|
|
346
|
-
|
|
347
|
-
this.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
843
|
+
getParent(nodeId) {
|
|
844
|
+
const node = this.findNode(nodeId);
|
|
845
|
+
if (!node) {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
return this.treeService.findParentNode(this.nodes(), node) ?? null;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Get children of a node
|
|
852
|
+
* @param nodeId - The ID of the parent node
|
|
853
|
+
* @returns Array of child nodes, or null if node not found
|
|
854
|
+
*/
|
|
855
|
+
getChildren(nodeId) {
|
|
856
|
+
const node = this.findNode(nodeId);
|
|
857
|
+
if (!node) {
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
return node.children ?? [];
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Get all root nodes
|
|
864
|
+
* @returns Array of root nodes
|
|
865
|
+
*/
|
|
866
|
+
getRootNodes() {
|
|
867
|
+
return [...this.nodes()];
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Get all nodes in a flat array
|
|
871
|
+
* @returns Array of all nodes in the tree
|
|
872
|
+
*/
|
|
873
|
+
getAllNodes() {
|
|
874
|
+
const allNodes = [];
|
|
875
|
+
const traverse = (nodes) => {
|
|
876
|
+
for (const node of nodes) {
|
|
877
|
+
allNodes.push(node);
|
|
878
|
+
if (node.children) {
|
|
879
|
+
traverse(node.children);
|
|
352
880
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
traverse(this.nodes());
|
|
884
|
+
return allNodes;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Get the path to a node (array of parent IDs from root to node)
|
|
888
|
+
* @param nodeId - The ID of the node
|
|
889
|
+
* @returns Array of node IDs representing the path, or empty array if node not found
|
|
890
|
+
*/
|
|
891
|
+
getNodePath(nodeId) {
|
|
892
|
+
const path = [];
|
|
893
|
+
const node = this.findNode(nodeId);
|
|
894
|
+
if (!node) {
|
|
895
|
+
return path;
|
|
896
|
+
}
|
|
897
|
+
let current = node;
|
|
898
|
+
while (current) {
|
|
899
|
+
path.unshift(current.id);
|
|
900
|
+
const parent = this.treeService.findParentNode(this.nodes(), current);
|
|
901
|
+
current = parent ?? null;
|
|
902
|
+
}
|
|
903
|
+
return path;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Get the level/depth of a node (0 = root level)
|
|
907
|
+
* @param nodeId - The ID of the node
|
|
908
|
+
* @returns The level of the node, or -1 if node not found
|
|
909
|
+
*/
|
|
910
|
+
getNodeLevel(nodeId) {
|
|
911
|
+
const path = this.getNodePath(nodeId);
|
|
912
|
+
return path.length > 0 ? path.length - 1 : -1;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Programmatically move a node to a new parent
|
|
916
|
+
* @param nodeId - The ID of the node to move
|
|
917
|
+
* @param newParentId - The ID of the new parent (undefined for root level)
|
|
918
|
+
* @param index - Optional index to insert at (default: append to end)
|
|
919
|
+
* @returns true if move was successful, false otherwise
|
|
920
|
+
*/
|
|
921
|
+
moveNode(nodeId, newParentId, index) {
|
|
922
|
+
const node = this.findNode(nodeId);
|
|
923
|
+
if (!node) {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
// Find current parent
|
|
927
|
+
const currentParent = this.treeService.findParentNode(this.nodes(), node);
|
|
928
|
+
const currentArray = currentParent?.children ?? this.nodes();
|
|
929
|
+
// Find and remove from current location
|
|
930
|
+
const currentIndex = currentArray.findIndex((n) => n.id === nodeId);
|
|
931
|
+
if (currentIndex === -1) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
const movedNode = currentArray.splice(currentIndex, 1)[0];
|
|
935
|
+
// Find new parent
|
|
936
|
+
let targetArray;
|
|
937
|
+
let newParent;
|
|
938
|
+
if (newParentId) {
|
|
939
|
+
newParent = this.findNode(newParentId);
|
|
940
|
+
if (!newParent) {
|
|
941
|
+
// Restore node if new parent not found
|
|
942
|
+
currentArray.splice(currentIndex, 0, movedNode);
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
// Validate drop target
|
|
946
|
+
if (!this.treeService.isValidDropTarget(movedNode, newParent)) {
|
|
947
|
+
// Restore node if invalid drop target
|
|
948
|
+
currentArray.splice(currentIndex, 0, movedNode);
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
if (!newParent.children) {
|
|
952
|
+
newParent.children = [];
|
|
953
|
+
}
|
|
954
|
+
targetArray = newParent.children;
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
targetArray = this.nodes();
|
|
958
|
+
}
|
|
959
|
+
// Calculate new index before inserting
|
|
960
|
+
const newIndex = index !== undefined && index >= 0 && index <= targetArray.length ? index : targetArray.length;
|
|
961
|
+
// Insert at new location
|
|
962
|
+
if (index !== undefined && index >= 0 && index <= targetArray.length) {
|
|
963
|
+
targetArray.splice(index, 0, movedNode);
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
targetArray.push(movedNode);
|
|
967
|
+
}
|
|
968
|
+
// Update childrenCount
|
|
969
|
+
if (currentParent) {
|
|
970
|
+
currentParent.childrenCount = currentParent.children?.length ?? 0;
|
|
971
|
+
}
|
|
972
|
+
if (newParent) {
|
|
973
|
+
newParent.childrenCount = newParent.children?.length ?? 0;
|
|
974
|
+
newParent.expanded = true; // Auto-expand new parent
|
|
975
|
+
}
|
|
976
|
+
// Emit drop events
|
|
977
|
+
this.emitDropEvents(movedNode, currentParent, newParent, currentIndex, newIndex, false);
|
|
978
|
+
this.refreshNodes();
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Clone a node (creates a deep copy)
|
|
983
|
+
* @param nodeId - The ID of the node to clone
|
|
984
|
+
* @returns The cloned node, or null if node not found
|
|
985
|
+
*/
|
|
986
|
+
cloneNode(nodeId) {
|
|
987
|
+
const node = this.findNode(nodeId);
|
|
988
|
+
if (!node) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
return this.treeService.cloneNode(node);
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Focus a specific node by ID
|
|
995
|
+
* @param nodeId - The ID of the node to focus
|
|
996
|
+
* @returns true if node was found and focused, false otherwise
|
|
997
|
+
*/
|
|
998
|
+
focusNode(nodeId) {
|
|
999
|
+
const node = this.findNode(nodeId);
|
|
1000
|
+
if (!node || node.visible === false || node.disabled) {
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
this.focusNodeById(nodeId);
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Get all expanded nodes
|
|
1008
|
+
* @returns Array of expanded nodes
|
|
1009
|
+
*/
|
|
1010
|
+
getExpandedNodes() {
|
|
1011
|
+
const expanded = [];
|
|
1012
|
+
const traverse = (nodes) => {
|
|
1013
|
+
for (const node of nodes) {
|
|
1014
|
+
if (node.expanded) {
|
|
1015
|
+
expanded.push(node);
|
|
1016
|
+
}
|
|
1017
|
+
if (node.children) {
|
|
1018
|
+
traverse(node.children);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
traverse(this.nodes());
|
|
1023
|
+
return expanded;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Get all collapsed nodes that have children
|
|
1027
|
+
* @returns Array of collapsed nodes with children
|
|
1028
|
+
*/
|
|
1029
|
+
getCollapsedNodes() {
|
|
1030
|
+
const collapsed = [];
|
|
1031
|
+
const traverse = (nodes) => {
|
|
1032
|
+
for (const node of nodes) {
|
|
1033
|
+
if (!node.expanded && (node.children?.length || node.childrenCount)) {
|
|
1034
|
+
collapsed.push(node);
|
|
1035
|
+
}
|
|
1036
|
+
if (node.children) {
|
|
1037
|
+
traverse(node.children);
|
|
359
1038
|
}
|
|
360
1039
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
1040
|
+
};
|
|
1041
|
+
traverse(this.nodes());
|
|
1042
|
+
return collapsed;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Check if a node is expanded
|
|
1046
|
+
* @param nodeId - The ID of the node
|
|
1047
|
+
* @returns true if node is expanded, false otherwise
|
|
1048
|
+
*/
|
|
1049
|
+
isNodeExpanded(nodeId) {
|
|
1050
|
+
const node = this.findNode(nodeId);
|
|
1051
|
+
return node?.expanded ?? false;
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Check if a node is selected
|
|
1055
|
+
* @param nodeId - The ID of the node
|
|
1056
|
+
* @returns true if node is selected, false otherwise
|
|
1057
|
+
*/
|
|
1058
|
+
isNodeSelected(nodeId) {
|
|
1059
|
+
const node = this.findNode(nodeId);
|
|
1060
|
+
return node?.selected ?? false;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Check if a node has children
|
|
1064
|
+
* @param nodeId - The ID of the node
|
|
1065
|
+
* @returns true if node has children, false otherwise
|
|
1066
|
+
*/
|
|
1067
|
+
hasChildren(nodeId) {
|
|
1068
|
+
const node = this.findNode(nodeId);
|
|
1069
|
+
return this.treeService.hasChildren(node ?? { id: '', label: '' });
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Get template context for a node
|
|
1073
|
+
*/
|
|
1074
|
+
getTemplateContext(node, level = 0) {
|
|
1075
|
+
return {
|
|
1076
|
+
$implicit: node,
|
|
1077
|
+
node,
|
|
1078
|
+
level,
|
|
1079
|
+
expanded: node.expanded ?? false,
|
|
1080
|
+
childrenCount: node.childrenCount ?? node.children?.length ?? 0,
|
|
1081
|
+
loading: node.loading ?? false,
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Calculate padding-inline for a node based on its level
|
|
1086
|
+
*/
|
|
1087
|
+
getNodePaddingInline(level) {
|
|
1088
|
+
const indent = this.indentSize();
|
|
1089
|
+
const currentLook = this.look();
|
|
1090
|
+
const multiplier = currentLook === 'with-line' ? 1 / 3 : 1;
|
|
1091
|
+
return level * indent * multiplier;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Check if node should show expand toggle
|
|
1095
|
+
*/
|
|
1096
|
+
shouldShowExpandToggle(node) {
|
|
1097
|
+
return this.treeService.hasChildren(node) || this.treeService.canLazyLoad(node, this.isLazyDataSource());
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Check if checkboxes should be shown (only for multiple mode)
|
|
1101
|
+
*/
|
|
1102
|
+
shouldShowCheckbox() {
|
|
1103
|
+
return this.selectMode() === 'multiple' && this.showCheckbox();
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Generate unique list ID for each node
|
|
1107
|
+
*/
|
|
1108
|
+
getListId(node) {
|
|
1109
|
+
return this.treeService.getListId(node);
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Check if a node is currently focused
|
|
1113
|
+
*/
|
|
1114
|
+
isNodeFocused(nodeId) {
|
|
1115
|
+
return this.focusedNodeId() === nodeId;
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Get ARIA level for a node
|
|
1119
|
+
*/
|
|
1120
|
+
getNodeAriaLevel(level) {
|
|
1121
|
+
return level + 1;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Get ARIA expanded state for a node
|
|
1125
|
+
*/
|
|
1126
|
+
getNodeAriaExpanded(node) {
|
|
1127
|
+
if (!this.shouldShowExpandToggle(node)) {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
return node.expanded ? 'true' : 'false';
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Get ARIA selected state for a node
|
|
1134
|
+
*/
|
|
1135
|
+
getNodeAriaSelected(node) {
|
|
1136
|
+
if (this.selectMode() === 'single') {
|
|
1137
|
+
return node.selected ? 'true' : 'false';
|
|
1138
|
+
}
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
// ==================== Event Handlers ====================
|
|
1142
|
+
/**
|
|
1143
|
+
* Handle node click - for single selection mode or multiple mode with checkOnClick enabled
|
|
1144
|
+
*/
|
|
1145
|
+
onNodeClick(node, event) {
|
|
1146
|
+
if (node.disabled)
|
|
1147
|
+
return;
|
|
1148
|
+
const mode = this.selectMode();
|
|
1149
|
+
const shouldCheckOnClick = this.checkOnClick();
|
|
1150
|
+
if (mode === 'single') {
|
|
1151
|
+
this.handleSingleSelection(node, event);
|
|
1152
|
+
}
|
|
1153
|
+
else if (mode === 'multiple' && shouldCheckOnClick) {
|
|
1154
|
+
this.handleMultipleSelection(node, event);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Toggle node expansion state with lazy loading support
|
|
1159
|
+
*/
|
|
1160
|
+
async toggleNode(node, event) {
|
|
1161
|
+
if (node.disabled)
|
|
1162
|
+
return;
|
|
1163
|
+
if (this.isEvent(event) && typeof event.stopPropagation === 'function') {
|
|
1164
|
+
event.stopPropagation();
|
|
1165
|
+
}
|
|
1166
|
+
const hasChildren = this.treeService.hasChildren(node);
|
|
1167
|
+
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource());
|
|
1168
|
+
if (hasChildren || canLazyLoad) {
|
|
1169
|
+
const willExpand = !node.expanded;
|
|
1170
|
+
if (willExpand && canLazyLoad) {
|
|
1171
|
+
await this.loadNodeChildren(node);
|
|
1172
|
+
}
|
|
1173
|
+
node.expanded = willExpand;
|
|
1174
|
+
this.refreshNodes();
|
|
1175
|
+
this.onNodeToggle.emit({ component: this, node, nativeEvent: event });
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Toggle node selection state with indeterminate support (for multiple mode)
|
|
1180
|
+
*/
|
|
1181
|
+
toggleSelection(node, event) {
|
|
1182
|
+
if (!event.isUserInteraction)
|
|
1183
|
+
return;
|
|
1184
|
+
const mode = this.selectMode();
|
|
1185
|
+
if (mode !== 'multiple')
|
|
1186
|
+
return;
|
|
1187
|
+
const newValue = event.value === null ? true : event.value;
|
|
1188
|
+
node.selected = newValue;
|
|
1189
|
+
node.indeterminate = false;
|
|
1190
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1191
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
1192
|
+
}
|
|
1193
|
+
if (this.intermediateState()) {
|
|
1194
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
1195
|
+
}
|
|
1196
|
+
this.refreshNodes();
|
|
1197
|
+
this.onNodeSelect.emit({
|
|
1198
|
+
component: this,
|
|
1199
|
+
node,
|
|
1200
|
+
isUserInteraction: event.isUserInteraction,
|
|
364
1201
|
});
|
|
365
1202
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
1203
|
+
/**
|
|
1204
|
+
* Handle drop events for tree nodes
|
|
1205
|
+
*/
|
|
1206
|
+
onDrop(event, parentNode) {
|
|
1207
|
+
const targetArray = parentNode?.children ?? this.nodes();
|
|
1208
|
+
const isReordering = event.previousContainer === event.container;
|
|
1209
|
+
if (isReordering) {
|
|
1210
|
+
this.handleReorder(event, targetArray, parentNode);
|
|
1211
|
+
}
|
|
1212
|
+
else {
|
|
1213
|
+
this.handleMove(event, targetArray, parentNode);
|
|
1214
|
+
}
|
|
1215
|
+
this.refreshNodes();
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Handle drop events when dropping directly onto a node (to make it a child)
|
|
1219
|
+
*/
|
|
1220
|
+
onDropOntoNode(event, targetNode) {
|
|
1221
|
+
if (!this.canMoveToParent())
|
|
1222
|
+
return;
|
|
1223
|
+
const sourceListId = event.previousContainer.element.id;
|
|
1224
|
+
const sourceArray = this.getArrayByListId(sourceListId);
|
|
1225
|
+
if (!sourceArray)
|
|
1226
|
+
return;
|
|
1227
|
+
const movedNode = sourceArray[event.previousIndex];
|
|
1228
|
+
if (!this.treeService.isValidDropTarget(movedNode, targetNode))
|
|
1229
|
+
return;
|
|
1230
|
+
if (!this.emitBeforeDropEvent(movedNode, sourceListId, targetNode, event.previousIndex, 0))
|
|
1231
|
+
return;
|
|
1232
|
+
targetNode.children ??= [];
|
|
1233
|
+
sourceArray.splice(event.previousIndex, 1);
|
|
1234
|
+
targetNode.children.unshift(movedNode);
|
|
1235
|
+
targetNode.expanded = true;
|
|
1236
|
+
this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), targetNode, event.previousIndex, 0, false);
|
|
1237
|
+
this.refreshNodes();
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Handle node focus event
|
|
1241
|
+
*/
|
|
1242
|
+
onNodeFocus(nodeId) {
|
|
1243
|
+
this.focusedNodeId.set(nodeId);
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Handle tree container focus - focus first node if none is focused
|
|
1247
|
+
*/
|
|
1248
|
+
onTreeFocus(event) {
|
|
1249
|
+
if (event.target === event.currentTarget) {
|
|
1250
|
+
const flatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
1251
|
+
if (flatList.length > 0) {
|
|
1252
|
+
const focusedId = this.focusedNodeId();
|
|
1253
|
+
if (focusedId) {
|
|
1254
|
+
// Check if the focused node still exists and is visible
|
|
1255
|
+
const focusedNode = this.treeService.findNodeById(this.nodes(), focusedId);
|
|
1256
|
+
if (focusedNode && focusedNode.visible !== false) {
|
|
1257
|
+
this.focusNodeById(focusedId);
|
|
1258
|
+
}
|
|
1259
|
+
else {
|
|
1260
|
+
// Focused node no longer exists, focus first node
|
|
1261
|
+
this.focusNodeById(flatList[0].node.id);
|
|
372
1262
|
}
|
|
373
|
-
element[this.childrenField()] = _children;
|
|
374
1263
|
}
|
|
375
1264
|
else {
|
|
376
|
-
|
|
377
|
-
|
|
1265
|
+
// No node is focused, focus first node
|
|
1266
|
+
this.focusNodeById(flatList[0].node.id);
|
|
378
1267
|
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Handle tree container blur
|
|
1273
|
+
*/
|
|
1274
|
+
onTreeBlur(event) {
|
|
1275
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) {
|
|
1276
|
+
this.focusedNodeId.set(null);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Handle keyboard navigation
|
|
1281
|
+
*/
|
|
1282
|
+
handleKeyDown(event) {
|
|
1283
|
+
const flatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
1284
|
+
if (flatList.length === 0)
|
|
1285
|
+
return;
|
|
1286
|
+
const currentFocused = this.getFocusedNode();
|
|
1287
|
+
let currentIndex = currentFocused ? flatList.findIndex((item) => item.node.id === currentFocused.id) : -1;
|
|
1288
|
+
if (currentIndex === -1 && event.target === event.currentTarget) {
|
|
1289
|
+
currentIndex = 0;
|
|
1290
|
+
}
|
|
1291
|
+
const navigationResult = this.handleNavigationKey(event, flatList, currentIndex, currentFocused);
|
|
1292
|
+
if (navigationResult.handled) {
|
|
1293
|
+
if (navigationResult.shouldPreventDefault) {
|
|
1294
|
+
event.preventDefault();
|
|
1295
|
+
event.stopPropagation();
|
|
1296
|
+
}
|
|
1297
|
+
if (navigationResult.targetIndex !== null &&
|
|
1298
|
+
navigationResult.targetIndex >= 0 &&
|
|
1299
|
+
navigationResult.targetIndex < flatList.length) {
|
|
1300
|
+
this.focusNodeById(flatList[navigationResult.targetIndex].node.id);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
// ==================== Private Methods ====================
|
|
1305
|
+
/**
|
|
1306
|
+
* Load root items when datasource is a function
|
|
1307
|
+
*/
|
|
1308
|
+
async loadRootItems(loadFn) {
|
|
1309
|
+
try {
|
|
1310
|
+
const result = loadFn();
|
|
1311
|
+
const rootNodes = result instanceof Promise ? await result : result;
|
|
1312
|
+
this.nodes.set(rootNodes);
|
|
1313
|
+
}
|
|
1314
|
+
catch (error) {
|
|
1315
|
+
this.handleError('Failed to load root items', error);
|
|
1316
|
+
this.nodes.set([]);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Load children for a node using lazy loading
|
|
1321
|
+
*/
|
|
1322
|
+
async loadNodeChildren(node) {
|
|
1323
|
+
if (!this.isLazyDataSource() || node.loading)
|
|
1324
|
+
return;
|
|
1325
|
+
if (this.treeService.hasChildren(node) || !node.childrenCount || node.childrenCount === 0) {
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
const ds = this.datasource();
|
|
1329
|
+
if (typeof ds !== 'function')
|
|
1330
|
+
return;
|
|
1331
|
+
try {
|
|
1332
|
+
node.loading = true;
|
|
1333
|
+
this.loadingNodes.update((set) => new Set(set).add(node.id));
|
|
1334
|
+
this.refreshNodes();
|
|
1335
|
+
const result = ds(node.id);
|
|
1336
|
+
const children = result instanceof Promise ? await result : result;
|
|
1337
|
+
node.children = children;
|
|
1338
|
+
node.childrenCount = children.length;
|
|
1339
|
+
}
|
|
1340
|
+
catch (error) {
|
|
1341
|
+
this.handleError('Failed to load children', error);
|
|
1342
|
+
node.childrenCount = 0;
|
|
1343
|
+
}
|
|
1344
|
+
finally {
|
|
1345
|
+
node.loading = false;
|
|
1346
|
+
this.loadingNodes.update((set) => {
|
|
1347
|
+
const newSet = new Set(set);
|
|
1348
|
+
newSet.delete(node.id);
|
|
1349
|
+
return newSet;
|
|
379
1350
|
});
|
|
1351
|
+
this.refreshNodes();
|
|
380
1352
|
}
|
|
381
1353
|
}
|
|
382
1354
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
1355
|
+
* Internal method to refresh nodes signal and sync back to datasource if it's an array
|
|
1356
|
+
* Creates new array references for all nested children to ensure reactivity
|
|
386
1357
|
*/
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
1358
|
+
refreshNodes() {
|
|
1359
|
+
const currentNodes = this.nodes();
|
|
1360
|
+
// Create new array references for all nested children to ensure reactivity
|
|
1361
|
+
// This ensures Angular's change detection picks up changes even with callback datasource
|
|
1362
|
+
this.ensureNewArrayReferences(currentNodes);
|
|
1363
|
+
this.nodes.set([...currentNodes]);
|
|
1364
|
+
if (!this.isLazyDataSource() && !this.isUpdatingFromDatasource) {
|
|
1365
|
+
this.isUpdatingFromDatasource = true;
|
|
1366
|
+
this.datasource.set([...currentNodes]);
|
|
1367
|
+
setTimeout(() => {
|
|
1368
|
+
this.isUpdatingFromDatasource = false;
|
|
1369
|
+
}, 0);
|
|
392
1370
|
}
|
|
393
|
-
this.onCollapsedChanged.emit({ component: this, data: node.data, nativeElement: this.nativeElement });
|
|
394
1371
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
1372
|
+
/**
|
|
1373
|
+
* Recursively ensure all children arrays have new references to trigger change detection
|
|
1374
|
+
* Mutates the tree structure by replacing children arrays with new array references
|
|
1375
|
+
*/
|
|
1376
|
+
ensureNewArrayReferences(nodes) {
|
|
1377
|
+
for (const node of nodes) {
|
|
1378
|
+
if (node.children && node.children.length > 0) {
|
|
1379
|
+
// Create new array reference for children
|
|
1380
|
+
node.children = [...node.children];
|
|
1381
|
+
// Recursively process nested children
|
|
1382
|
+
this.ensureNewArrayReferences(node.children);
|
|
400
1383
|
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Handle single selection mode
|
|
1388
|
+
*/
|
|
1389
|
+
handleSingleSelection(node, event) {
|
|
1390
|
+
this.treeService.deselectAllNodes(this.nodes());
|
|
1391
|
+
node.selected = true;
|
|
1392
|
+
node.indeterminate = false;
|
|
1393
|
+
this.refreshNodes();
|
|
1394
|
+
this.onNodeSelect.emit({
|
|
1395
|
+
component: this,
|
|
1396
|
+
node,
|
|
1397
|
+
nativeEvent: event,
|
|
1398
|
+
isUserInteraction: true,
|
|
401
1399
|
});
|
|
402
1400
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
1401
|
+
/**
|
|
1402
|
+
* Handle multiple selection mode with checkOnClick
|
|
1403
|
+
*/
|
|
1404
|
+
handleMultipleSelection(node, event) {
|
|
1405
|
+
const newValue = !node.selected;
|
|
1406
|
+
node.selected = newValue;
|
|
1407
|
+
node.indeterminate = false;
|
|
1408
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1409
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
1410
|
+
}
|
|
1411
|
+
if (this.intermediateState()) {
|
|
1412
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
1413
|
+
}
|
|
1414
|
+
this.refreshNodes();
|
|
1415
|
+
this.onNodeSelect.emit({
|
|
1416
|
+
component: this,
|
|
1417
|
+
node,
|
|
1418
|
+
nativeEvent: event,
|
|
1419
|
+
isUserInteraction: true,
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Get array reference by drop list ID
|
|
1424
|
+
*/
|
|
1425
|
+
getArrayByListId(listId) {
|
|
1426
|
+
return this.treeService.getArrayByListId(this.nodes(), listId);
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Find parent node by list ID
|
|
1430
|
+
*/
|
|
1431
|
+
findParentByListId(listId) {
|
|
1432
|
+
return this.treeService.findParentByListId(this.nodes(), listId);
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Check if move operation is allowed based on dragOperationType
|
|
1436
|
+
*/
|
|
1437
|
+
canMoveToParent() {
|
|
1438
|
+
return this.dragOperationType() !== 'order-only';
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Check if reorder operation is allowed based on dragOperationType
|
|
1442
|
+
*/
|
|
1443
|
+
canReorder() {
|
|
1444
|
+
return this.dragOperationType() !== 'move';
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Handle reordering within the same list */
|
|
1448
|
+
handleReorder(event, targetArray, parentNode) {
|
|
1449
|
+
if (!this.canReorder())
|
|
1450
|
+
return;
|
|
1451
|
+
const movedNode = targetArray[event.previousIndex];
|
|
1452
|
+
moveItemInArray(targetArray, event.previousIndex, event.currentIndex);
|
|
1453
|
+
this.emitDropEvents(movedNode, parentNode, parentNode, event.previousIndex, event.currentIndex, true);
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Handle moving between different lists
|
|
1457
|
+
*/
|
|
1458
|
+
handleMove(event, targetArray, parentNode) {
|
|
1459
|
+
if (!this.canMoveToParent())
|
|
1460
|
+
return;
|
|
1461
|
+
const sourceListId = event.previousContainer.element.id;
|
|
1462
|
+
const sourceArray = this.getArrayByListId(sourceListId);
|
|
1463
|
+
if (!sourceArray)
|
|
1464
|
+
return;
|
|
1465
|
+
const movedNode = sourceArray[event.previousIndex];
|
|
1466
|
+
if (parentNode && !this.treeService.isValidDropTarget(movedNode, parentNode))
|
|
1467
|
+
return;
|
|
1468
|
+
if (!this.emitBeforeDropEvent(movedNode, sourceListId, parentNode, event.previousIndex, event.currentIndex)) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
transferArrayItem(sourceArray, targetArray, event.previousIndex, event.currentIndex);
|
|
1472
|
+
this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), parentNode, event.previousIndex, event.currentIndex, false);
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Emit beforeDrop event and return whether to continue
|
|
1476
|
+
*/
|
|
1477
|
+
emitBeforeDropEvent(movedNode, sourceListId, currentParent, previousIndex, currentIndex) {
|
|
1478
|
+
const beforeDropEvent = {
|
|
1479
|
+
component: this,
|
|
1480
|
+
movedNode,
|
|
1481
|
+
previousParent: this.findParentByListId(sourceListId),
|
|
1482
|
+
currentParent,
|
|
1483
|
+
previousIndex,
|
|
1484
|
+
currentIndex,
|
|
1485
|
+
canceled: false,
|
|
1486
|
+
};
|
|
1487
|
+
this.onBeforeDrop.emit(beforeDropEvent);
|
|
1488
|
+
return !beforeDropEvent.canceled;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Emit drop events based on operation type
|
|
1492
|
+
*/
|
|
1493
|
+
emitDropEvents(node, previousParent, currentParent, previousIndex, currentIndex, isReorder) {
|
|
1494
|
+
const dropEvent = {
|
|
1495
|
+
component: this,
|
|
1496
|
+
node,
|
|
1497
|
+
previousParent,
|
|
1498
|
+
currentParent,
|
|
1499
|
+
previousIndex,
|
|
1500
|
+
currentIndex,
|
|
1501
|
+
};
|
|
1502
|
+
if (isReorder) {
|
|
1503
|
+
this.onOrderChange.emit(dropEvent);
|
|
1504
|
+
}
|
|
1505
|
+
else {
|
|
1506
|
+
this.onMoveChange.emit(dropEvent);
|
|
1507
|
+
}
|
|
1508
|
+
this.onItemsChange.emit(dropEvent);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Get the currently focused node
|
|
1512
|
+
*/
|
|
1513
|
+
getFocusedNode() {
|
|
1514
|
+
const focusedId = this.focusedNodeId();
|
|
1515
|
+
if (!focusedId)
|
|
1516
|
+
return null;
|
|
1517
|
+
return this.treeService.findNodeById(this.nodes(), focusedId);
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Set focus to a node by ID
|
|
1521
|
+
*/
|
|
1522
|
+
focusNodeById(nodeId) {
|
|
1523
|
+
this.focusedNodeId.set(nodeId);
|
|
1524
|
+
setTimeout(() => {
|
|
1525
|
+
const element = document.querySelector(`[data-tree-node-id="${nodeId}"]`);
|
|
1526
|
+
if (element) {
|
|
1527
|
+
element.focus();
|
|
1528
|
+
element.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
1529
|
+
}
|
|
1530
|
+
}, 0);
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Handle keyboard navigation keys
|
|
1534
|
+
*/
|
|
1535
|
+
handleNavigationKey(event, flatList, currentIndex, currentFocused) {
|
|
1536
|
+
let targetIndex = currentIndex;
|
|
1537
|
+
let shouldPreventDefault = true;
|
|
1538
|
+
let handled = true;
|
|
1539
|
+
switch (event.key) {
|
|
1540
|
+
case 'ArrowUp':
|
|
1541
|
+
if (currentIndex > 0) {
|
|
1542
|
+
targetIndex = currentIndex - 1;
|
|
1543
|
+
}
|
|
1544
|
+
else {
|
|
1545
|
+
shouldPreventDefault = false;
|
|
1546
|
+
}
|
|
416
1547
|
break;
|
|
417
|
-
case '
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
1548
|
+
case 'ArrowDown':
|
|
1549
|
+
if (currentIndex < flatList.length - 1) {
|
|
1550
|
+
targetIndex = currentIndex + 1;
|
|
1551
|
+
}
|
|
1552
|
+
else if (currentIndex === -1) {
|
|
1553
|
+
targetIndex = 0;
|
|
1554
|
+
}
|
|
1555
|
+
else {
|
|
1556
|
+
shouldPreventDefault = false;
|
|
421
1557
|
}
|
|
422
1558
|
break;
|
|
423
|
-
case '
|
|
424
|
-
|
|
1559
|
+
case 'ArrowLeft':
|
|
1560
|
+
if (currentFocused) {
|
|
1561
|
+
if (currentFocused.expanded && this.shouldShowExpandToggle(currentFocused)) {
|
|
1562
|
+
this.toggleNode(currentFocused, event);
|
|
1563
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
const currentItem = flatList[currentIndex];
|
|
1567
|
+
if (currentItem?.parent) {
|
|
1568
|
+
targetIndex = flatList.findIndex((item) => item.node.id === currentItem.parent.id);
|
|
1569
|
+
}
|
|
1570
|
+
else {
|
|
1571
|
+
shouldPreventDefault = false;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
else {
|
|
1576
|
+
shouldPreventDefault = false;
|
|
1577
|
+
}
|
|
425
1578
|
break;
|
|
426
|
-
case '
|
|
427
|
-
|
|
1579
|
+
case 'ArrowRight':
|
|
1580
|
+
if (currentFocused) {
|
|
1581
|
+
if (!currentFocused.expanded && this.shouldShowExpandToggle(currentFocused)) {
|
|
1582
|
+
this.toggleNode(currentFocused, event);
|
|
1583
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1584
|
+
}
|
|
1585
|
+
else if (currentFocused.expanded &&
|
|
1586
|
+
this.treeService.hasChildren(currentFocused) &&
|
|
1587
|
+
currentFocused.children) {
|
|
1588
|
+
const children = currentFocused.children;
|
|
1589
|
+
if (children.length > 0) {
|
|
1590
|
+
const firstChild = children[0];
|
|
1591
|
+
targetIndex = flatList.findIndex((item) => item.node.id === firstChild.id);
|
|
1592
|
+
if (targetIndex === -1) {
|
|
1593
|
+
const updatedFlatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
1594
|
+
targetIndex = updatedFlatList.findIndex((item) => item.node.id === firstChild.id);
|
|
1595
|
+
if (targetIndex >= 0 && targetIndex < updatedFlatList.length) {
|
|
1596
|
+
this.focusNodeById(updatedFlatList[targetIndex].node.id);
|
|
1597
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
else {
|
|
1602
|
+
shouldPreventDefault = false;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
else {
|
|
1606
|
+
shouldPreventDefault = false;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
else {
|
|
1610
|
+
shouldPreventDefault = false;
|
|
1611
|
+
}
|
|
1612
|
+
break;
|
|
1613
|
+
case 'Home':
|
|
1614
|
+
targetIndex = 0;
|
|
1615
|
+
break;
|
|
1616
|
+
case 'End':
|
|
1617
|
+
targetIndex = flatList.length - 1;
|
|
1618
|
+
break;
|
|
1619
|
+
case ' ':
|
|
1620
|
+
case 'Space':
|
|
1621
|
+
if (currentFocused && this.selectMode() === 'multiple') {
|
|
1622
|
+
event.preventDefault();
|
|
1623
|
+
this.handleSpaceKeySelection(currentFocused, event);
|
|
1624
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1625
|
+
}
|
|
1626
|
+
shouldPreventDefault = false;
|
|
1627
|
+
break;
|
|
1628
|
+
case 'Enter':
|
|
1629
|
+
if (currentFocused) {
|
|
1630
|
+
event.preventDefault();
|
|
1631
|
+
this.handleEnterKeySelection(currentFocused, event);
|
|
1632
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1633
|
+
}
|
|
1634
|
+
shouldPreventDefault = false;
|
|
428
1635
|
break;
|
|
429
1636
|
default:
|
|
1637
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
1638
|
+
if (currentFocused && this.selectMode() === 'multiple') {
|
|
1639
|
+
event.preventDefault();
|
|
1640
|
+
this.handleCtrlEnterSelection(currentFocused, event);
|
|
1641
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
handled = false;
|
|
1645
|
+
shouldPreventDefault = false;
|
|
430
1646
|
break;
|
|
431
1647
|
}
|
|
432
|
-
|
|
433
|
-
*
|
|
434
|
-
* for detect changes treeviewitem
|
|
435
|
-
*
|
|
436
|
-
*/
|
|
437
|
-
this.executorChanges.set(operation);
|
|
1648
|
+
return { handled, shouldPreventDefault, targetIndex };
|
|
438
1649
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1650
|
+
/**
|
|
1651
|
+
* Handle Space key selection
|
|
1652
|
+
*/
|
|
1653
|
+
handleSpaceKeySelection(node, event) {
|
|
1654
|
+
const newValue = !node.selected;
|
|
1655
|
+
node.selected = newValue;
|
|
1656
|
+
node.indeterminate = false;
|
|
1657
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1658
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
442
1659
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const nodesToExpand = [];
|
|
446
|
-
for (const valueField of valueFields) {
|
|
447
|
-
const foundNodes = this.findNodesByValueField(this.itemsSignal(), valueField);
|
|
448
|
-
nodesToExpand.push(...foundNodes);
|
|
1660
|
+
if (this.intermediateState()) {
|
|
1661
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
449
1662
|
}
|
|
450
|
-
|
|
1663
|
+
this.refreshNodes();
|
|
1664
|
+
this.onNodeSelect.emit({
|
|
1665
|
+
component: this,
|
|
1666
|
+
node,
|
|
1667
|
+
nativeEvent: event,
|
|
1668
|
+
isUserInteraction: true,
|
|
1669
|
+
});
|
|
451
1670
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
1671
|
+
/**
|
|
1672
|
+
* Handle Enter key selection
|
|
1673
|
+
*/
|
|
1674
|
+
handleEnterKeySelection(node, event) {
|
|
1675
|
+
const mode = this.selectMode();
|
|
1676
|
+
this.treeService.deselectAllNodes(this.nodes());
|
|
1677
|
+
node.selected = true;
|
|
1678
|
+
node.indeterminate = false;
|
|
1679
|
+
if (mode === 'multiple') {
|
|
1680
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1681
|
+
this.treeService.selectAllChildren(node.children, true);
|
|
457
1682
|
}
|
|
458
|
-
if (
|
|
459
|
-
|
|
1683
|
+
if (this.intermediateState()) {
|
|
1684
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
460
1685
|
}
|
|
461
1686
|
}
|
|
462
|
-
|
|
1687
|
+
this.refreshNodes();
|
|
1688
|
+
this.onNodeSelect.emit({
|
|
1689
|
+
component: this,
|
|
1690
|
+
node,
|
|
1691
|
+
nativeEvent: event,
|
|
1692
|
+
isUserInteraction: true,
|
|
1693
|
+
});
|
|
463
1694
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
1695
|
+
/**
|
|
1696
|
+
* Handle Ctrl/Cmd + Enter key selection
|
|
1697
|
+
*/
|
|
1698
|
+
handleCtrlEnterSelection(node, event) {
|
|
1699
|
+
const newValue = !node.selected;
|
|
1700
|
+
node.selected = newValue;
|
|
1701
|
+
node.indeterminate = false;
|
|
1702
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1703
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
469
1704
|
}
|
|
470
|
-
if (
|
|
471
|
-
|
|
1705
|
+
if (this.intermediateState()) {
|
|
1706
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
472
1707
|
}
|
|
1708
|
+
this.refreshNodes();
|
|
1709
|
+
this.onNodeSelect.emit({
|
|
1710
|
+
component: this,
|
|
1711
|
+
node,
|
|
1712
|
+
nativeEvent: event,
|
|
1713
|
+
isUserInteraction: true,
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Type guard to check if value is an Event
|
|
1718
|
+
*/
|
|
1719
|
+
isEvent(value) {
|
|
1720
|
+
return value instanceof Event;
|
|
473
1721
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
1722
|
+
/**
|
|
1723
|
+
* Handle errors consistently
|
|
1724
|
+
*/
|
|
1725
|
+
handleError(message, error) {
|
|
1726
|
+
if (error instanceof Error) {
|
|
1727
|
+
console.error(`${message}:`, error.message);
|
|
1728
|
+
}
|
|
1729
|
+
else {
|
|
1730
|
+
console.error(`${message}:`, error);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1734
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: AXTreeViewComponent, isStandalone: true, selector: "ax-tree-view", inputs: { datasource: { classPropertyName: "datasource", publicName: "datasource", isSignal: true, isRequired: true, transformFunction: null }, selectMode: { classPropertyName: "selectMode", publicName: "selectMode", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, checkChildrenOnSelect: { classPropertyName: "checkChildrenOnSelect", publicName: "checkChildrenOnSelect", isSignal: true, isRequired: false, transformFunction: null }, intermediateState: { classPropertyName: "intermediateState", publicName: "intermediateState", isSignal: true, isRequired: false, transformFunction: null }, checkOnClick: { classPropertyName: "checkOnClick", publicName: "checkOnClick", isSignal: true, isRequired: false, transformFunction: null }, dragMode: { classPropertyName: "dragMode", publicName: "dragMode", isSignal: true, isRequired: false, transformFunction: null }, dragOperationType: { classPropertyName: "dragOperationType", publicName: "dragOperationType", isSignal: true, isRequired: false, transformFunction: null }, showIcons: { classPropertyName: "showIcons", publicName: "showIcons", isSignal: true, isRequired: false, transformFunction: null }, showChildrenBadge: { classPropertyName: "showChildrenBadge", publicName: "showChildrenBadge", isSignal: true, isRequired: false, transformFunction: null }, expandedIcon: { classPropertyName: "expandedIcon", publicName: "expandedIcon", isSignal: true, isRequired: false, transformFunction: null }, collapsedIcon: { classPropertyName: "collapsedIcon", publicName: "collapsedIcon", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, nodeHeight: { classPropertyName: "nodeHeight", publicName: "nodeHeight", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { datasource: "datasourceChange", onBeforeDrop: "onBeforeDrop", onNodeToggle: "onNodeToggle", onNodeSelect: "onNodeSelect", onOrderChange: "onOrderChange", onMoveChange: "onMoveChange", onItemsChange: "onItemsChange" }, host: { attributes: { "role": "tree", "tabindex": "0" }, listeners: { "keydown": "handleKeyDown($event)", "focus": "onTreeFocus($event)", "blur": "onTreeBlur($event)" }, properties: { "class.ax-tree-view-default": "look() === 'default'", "class.ax-tree-view-card": "look() === 'card'", "class.ax-tree-view-with-line": "look() === 'with-line'", "class.ax-tree-view-rtl": "isRtl", "style.--ax-tree-view-indent-size": "indentSize() + 'px'", "style.--ax-tree-view-line-offset": "(indentSize() / 2) + 'px'", "attr.aria-label": "\"Tree navigation\"" }, classAttribute: "ax-tree-view" }, providers: [AXTreeViewService], ngImport: i0, template: "<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragMode() !== 'none'\"\n [sortingDisabled]=\"false\"\n [id]=\"getListId()\"\n [attr.data-node-id]=\"null\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event)\"\n class=\"ax-tree-view-drop-list\"\n [class.ax-tree-view-card]=\"look() === 'card'\"\n [class.ax-tree-view-with-lines]=\"look() === 'with-line'\"\n [class.ax-tree-view-compact]=\"nodeHeight() === 'compact'\"\n [class.ax-tree-view-comfortable]=\"nodeHeight() === 'comfortable'\"\n role=\"group\"\n>\n @for (node of nodes(); track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(0)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) && onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n</div>\n\n<!-- Recursive children template -->\n<ng-template #childrenList let-children=\"children\" let-parent=\"parent\" let-level=\"level\">\n <div\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"parent.id\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event, parent)\"\n class=\"ax-tree-view-drop-list\"\n role=\"group\"\n >\n @for (node of children; track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(node.id)\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(level)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [style.padding-inline-start.px]=\"getNodePaddingInline(level)\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) &&\n onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: level + 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n </div>\n</ng-template>\n", styles: [".ax-tree-view{display:block;width:100%;--ax-comp-tree-view-indent-size: 12px;--ax-comp-tree-view-node-hover-bg: rgba(var(--ax-sys-color-on-lightest-surface), .04);--ax-comp-tree-view-node-selected-bg: rgba(var(--ax-sys-color-primary-500), .12);--ax-comp-tree-view-node-border-radius: 6px;--ax-comp-tree-view-node-margin: .25rem;--ax-comp-tree-view-line-color: rgba(var(--ax-sys-color-on-lightest-surface), .15);--ax-comp-tree-view-drag-preview-opacity: .9;--ax-comp-tree-view-drag-placeholder-bg: rgba(var(--ax-sys-color-on-lightest-surface), .02);--ax-comp-tree-view-drop-active-bg: rgba(var(--ax-sys-color-primary-500), .08);--ax-comp-tree-view-drop-active-outline: rgba(var(--ax-sys-color-primary-500), .3);--ax-comp-tree-view-content-padding: 0;--ax-comp-tree-view-content-gap: .5rem;--ax-comp-tree-view-drop-list-min-height: 2rem;--ax-comp-tree-view-drag-handle-padding: .25rem;--ax-comp-tree-view-expand-toggle-padding: .25rem;--ax-comp-tree-view-card-node-margin: .5rem;--ax-comp-tree-view-card-content-padding: 1rem;--ax-comp-tree-view-outline-offset: 2px;--ax-comp-tree-view-outline-offset-negative: -2px}.ax-tree-view-drop-list{min-height:var(--ax-comp-tree-view-drop-list-min-height)}.ax-tree-view-compact .ax-tree-view-node-content{padding:var(--ax-comp-tree-view-content-padding, .25rem .5rem);gap:var(--ax-comp-tree-view-content-gap, .375rem);font-size:.8125rem}.ax-tree-view-comfortable .ax-tree-view-node-content{padding:var(--ax-comp-tree-view-content-padding, .75rem .625rem);gap:var(--ax-comp-tree-view-content-gap, .625rem);font-size:.9375rem}.ax-tree-view-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);border:1px solid transparent;cursor:move}.ax-tree-view-node:hover:not(.ax-dragging){background:var(--ax-comp-tree-view-node-hover-bg)}.ax-tree-view-node.ax-tree-view-node-selected{background:var(--ax-comp-tree-view-node-selected-bg);border-color:currentColor}.ax-tree-view-node.ax-dragging{opacity:var(--ax-comp-tree-view-drag-placeholder-opacity);cursor:grabbing!important}.ax-tree-view-node.ax-drag-placeholder{background:var(--ax-comp-tree-view-drag-placeholder-bg)}.ax-drag-preview{opacity:var(--ax-comp-tree-view-drag-preview-opacity)!important;box-shadow:0 4px 12px rgba(var(--ax-sys-color-on-lightest-surface),.2)!important;cursor:grabbing!important;border:2px dashed currentColor!important}.ax-tree-view-node-content.ax-drop-list-sorting-active{background:var(--ax-comp-tree-view-drop-active-bg);border-radius:var(--ax-comp-tree-view-node-border-radius);outline:2px dashed var(--ax-comp-tree-view-drop-active-outline);outline-offset:var(--ax-comp-tree-view-outline-offset-negative)}.ax-tree-view-node-content{display:flex;align-items:center;gap:var(--ax-comp-tree-view-content-gap);padding:var(--ax-comp-tree-view-content-padding);cursor:pointer;-webkit-user-select:none;user-select:none;outline:none;border:1px solid transparent;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-node-content:focus{outline:none}.ax-tree-view-node-content:focus-visible{outline:2px solid rgba(var(--ax-sys-color-primary-500),.8);outline-offset:var(--ax-comp-tree-view-outline-offset);border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-drag-handle{cursor:grab;opacity:.6;padding:var(--ax-comp-tree-view-drag-handle-padding)}.ax-tree-view-drag-handle:hover{opacity:1}.ax-tree-view-drag-handle:active{cursor:grabbing}.ax-tree-view-expand-toggle{background:none;border:none;cursor:pointer;padding:var(--ax-comp-tree-view-expand-toggle-padding);min-width:1.5rem;height:1.5rem}.ax-tree-view-expand-toggle:not(.ax-tree-view-has-children){opacity:0;pointer-events:none}.ax-tree-view-toggle-icon{font-size:.75rem}.ax-tree-view-node-icon{font-size:1.125rem;flex-shrink:0}.ax-tree-view-node-label{flex:1;font-size:.875rem;line-height:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-tree-view-children{padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-node-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ax-tree-view-node-loading{opacity:.7}.ax-tree-view-card .ax-tree-view-node{border:1px solid rgba(var(--ax-sys-color-border-lightest-surface),1);margin:var(--ax-comp-tree-view-card-node-margin) 0}.ax-tree-view-card .ax-tree-view-node-content{padding:var(--ax-comp-tree-view-card-content-padding)}.ax-tree-view-with-lines .ax-tree-view-children{position:relative;padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-with-lines .ax-tree-view-children:before{content:\"\";position:absolute;inset-inline-start:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));top:0;height:calc(100% - .875rem);width:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines .ax-tree-view-node{position:relative}.ax-tree-view-with-lines .ax-tree-view-node:before{content:\"\";position:absolute;inset-inline-start:calc(-1 * var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2)));top:60%;width:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));height:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines>.ax-tree-view-drop-list>.ax-tree-view-node:before,.ax-tree-view-with-lines>.ax-tree-view-node:before{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDragDirective, selector: "[axDrag]", inputs: ["axDrag", "dragData", "dragDisabled", "dragTransition", "dragElementClone", "dropZoneGroup", "dragStartDelay", "dragResetOnDblClick", "dragLockAxis", "dragClonedTemplate", "dragCursor", "dragBoundary", "dragTransitionDuration"], outputs: ["dragPositionChanged"] }, { kind: "directive", type: AXDragHandleDirective, selector: "[axDragHandle]" }, { kind: "directive", type: AXDropListDirective, selector: "[axDropList]", inputs: ["axDropList", "sortingDisabled", "dropListGroup", "dropListOrientation"], outputs: ["dropListDropped"], exportAs: ["axDropList"] }, { kind: "directive", type: AXFocusTrapDirective, selector: "[axFocusTrap]" }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
479
1735
|
}
|
|
480
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewComponent, decorators: [{
|
|
481
1737
|
type: Component,
|
|
482
|
-
args: [{ selector: 'ax-tree-view', encapsulation: ViewEncapsulation.None,
|
|
1738
|
+
args: [{ selector: 'ax-tree-view', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [AXTreeViewService], imports: [
|
|
1739
|
+
CommonModule,
|
|
1740
|
+
FormsModule,
|
|
1741
|
+
AXDragDirective,
|
|
1742
|
+
AXDragHandleDirective,
|
|
1743
|
+
AXDropListDirective,
|
|
1744
|
+
AXFocusTrapDirective,
|
|
483
1745
|
NgTemplateOutlet,
|
|
484
|
-
|
|
1746
|
+
AXButtonComponent,
|
|
485
1747
|
AXCheckBoxComponent,
|
|
486
|
-
|
|
487
|
-
AXDecoratorGenericComponent,
|
|
1748
|
+
AXBadgeComponent,
|
|
488
1749
|
AXDecoratorIconComponent,
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
1750
|
+
], host: {
|
|
1751
|
+
class: 'ax-tree-view',
|
|
1752
|
+
'[class.ax-tree-view-default]': "look() === 'default'",
|
|
1753
|
+
'[class.ax-tree-view-card]': "look() === 'card'",
|
|
1754
|
+
'[class.ax-tree-view-with-line]': "look() === 'with-line'",
|
|
1755
|
+
'[class.ax-tree-view-rtl]': 'isRtl',
|
|
1756
|
+
'[style.--ax-tree-view-indent-size]': "indentSize() + 'px'",
|
|
1757
|
+
'[style.--ax-tree-view-line-offset]': "(indentSize() / 2) + 'px'",
|
|
1758
|
+
role: 'tree',
|
|
1759
|
+
'[attr.aria-label]': '"Tree navigation"',
|
|
1760
|
+
'(keydown)': 'handleKeyDown($event)',
|
|
1761
|
+
'(focus)': 'onTreeFocus($event)',
|
|
1762
|
+
'(blur)': 'onTreeBlur($event)',
|
|
1763
|
+
tabindex: '0',
|
|
1764
|
+
}, template: "<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragMode() !== 'none'\"\n [sortingDisabled]=\"false\"\n [id]=\"getListId()\"\n [attr.data-node-id]=\"null\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event)\"\n class=\"ax-tree-view-drop-list\"\n [class.ax-tree-view-card]=\"look() === 'card'\"\n [class.ax-tree-view-with-lines]=\"look() === 'with-line'\"\n [class.ax-tree-view-compact]=\"nodeHeight() === 'compact'\"\n [class.ax-tree-view-comfortable]=\"nodeHeight() === 'comfortable'\"\n role=\"group\"\n>\n @for (node of nodes(); track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(0)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) && onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n</div>\n\n<!-- Recursive children template -->\n<ng-template #childrenList let-children=\"children\" let-parent=\"parent\" let-level=\"level\">\n <div\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"parent.id\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event, parent)\"\n class=\"ax-tree-view-drop-list\"\n role=\"group\"\n >\n @for (node of children; track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(node.id)\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(level)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [style.padding-inline-start.px]=\"getNodePaddingInline(level)\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) &&\n onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: level + 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n </div>\n</ng-template>\n", styles: [".ax-tree-view{display:block;width:100%;--ax-comp-tree-view-indent-size: 12px;--ax-comp-tree-view-node-hover-bg: rgba(var(--ax-sys-color-on-lightest-surface), .04);--ax-comp-tree-view-node-selected-bg: rgba(var(--ax-sys-color-primary-500), .12);--ax-comp-tree-view-node-border-radius: 6px;--ax-comp-tree-view-node-margin: .25rem;--ax-comp-tree-view-line-color: rgba(var(--ax-sys-color-on-lightest-surface), .15);--ax-comp-tree-view-drag-preview-opacity: .9;--ax-comp-tree-view-drag-placeholder-bg: rgba(var(--ax-sys-color-on-lightest-surface), .02);--ax-comp-tree-view-drop-active-bg: rgba(var(--ax-sys-color-primary-500), .08);--ax-comp-tree-view-drop-active-outline: rgba(var(--ax-sys-color-primary-500), .3);--ax-comp-tree-view-content-padding: 0;--ax-comp-tree-view-content-gap: .5rem;--ax-comp-tree-view-drop-list-min-height: 2rem;--ax-comp-tree-view-drag-handle-padding: .25rem;--ax-comp-tree-view-expand-toggle-padding: .25rem;--ax-comp-tree-view-card-node-margin: .5rem;--ax-comp-tree-view-card-content-padding: 1rem;--ax-comp-tree-view-outline-offset: 2px;--ax-comp-tree-view-outline-offset-negative: -2px}.ax-tree-view-drop-list{min-height:var(--ax-comp-tree-view-drop-list-min-height)}.ax-tree-view-compact .ax-tree-view-node-content{padding:var(--ax-comp-tree-view-content-padding, .25rem .5rem);gap:var(--ax-comp-tree-view-content-gap, .375rem);font-size:.8125rem}.ax-tree-view-comfortable .ax-tree-view-node-content{padding:var(--ax-comp-tree-view-content-padding, .75rem .625rem);gap:var(--ax-comp-tree-view-content-gap, .625rem);font-size:.9375rem}.ax-tree-view-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);border:1px solid transparent;cursor:move}.ax-tree-view-node:hover:not(.ax-dragging){background:var(--ax-comp-tree-view-node-hover-bg)}.ax-tree-view-node.ax-tree-view-node-selected{background:var(--ax-comp-tree-view-node-selected-bg);border-color:currentColor}.ax-tree-view-node.ax-dragging{opacity:var(--ax-comp-tree-view-drag-placeholder-opacity);cursor:grabbing!important}.ax-tree-view-node.ax-drag-placeholder{background:var(--ax-comp-tree-view-drag-placeholder-bg)}.ax-drag-preview{opacity:var(--ax-comp-tree-view-drag-preview-opacity)!important;box-shadow:0 4px 12px rgba(var(--ax-sys-color-on-lightest-surface),.2)!important;cursor:grabbing!important;border:2px dashed currentColor!important}.ax-tree-view-node-content.ax-drop-list-sorting-active{background:var(--ax-comp-tree-view-drop-active-bg);border-radius:var(--ax-comp-tree-view-node-border-radius);outline:2px dashed var(--ax-comp-tree-view-drop-active-outline);outline-offset:var(--ax-comp-tree-view-outline-offset-negative)}.ax-tree-view-node-content{display:flex;align-items:center;gap:var(--ax-comp-tree-view-content-gap);padding:var(--ax-comp-tree-view-content-padding);cursor:pointer;-webkit-user-select:none;user-select:none;outline:none;border:1px solid transparent;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-node-content:focus{outline:none}.ax-tree-view-node-content:focus-visible{outline:2px solid rgba(var(--ax-sys-color-primary-500),.8);outline-offset:var(--ax-comp-tree-view-outline-offset);border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-drag-handle{cursor:grab;opacity:.6;padding:var(--ax-comp-tree-view-drag-handle-padding)}.ax-tree-view-drag-handle:hover{opacity:1}.ax-tree-view-drag-handle:active{cursor:grabbing}.ax-tree-view-expand-toggle{background:none;border:none;cursor:pointer;padding:var(--ax-comp-tree-view-expand-toggle-padding);min-width:1.5rem;height:1.5rem}.ax-tree-view-expand-toggle:not(.ax-tree-view-has-children){opacity:0;pointer-events:none}.ax-tree-view-toggle-icon{font-size:.75rem}.ax-tree-view-node-icon{font-size:1.125rem;flex-shrink:0}.ax-tree-view-node-label{flex:1;font-size:.875rem;line-height:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-tree-view-children{padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-node-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ax-tree-view-node-loading{opacity:.7}.ax-tree-view-card .ax-tree-view-node{border:1px solid rgba(var(--ax-sys-color-border-lightest-surface),1);margin:var(--ax-comp-tree-view-card-node-margin) 0}.ax-tree-view-card .ax-tree-view-node-content{padding:var(--ax-comp-tree-view-card-content-padding)}.ax-tree-view-with-lines .ax-tree-view-children{position:relative;padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-with-lines .ax-tree-view-children:before{content:\"\";position:absolute;inset-inline-start:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));top:0;height:calc(100% - .875rem);width:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines .ax-tree-view-node{position:relative}.ax-tree-view-with-lines .ax-tree-view-node:before{content:\"\";position:absolute;inset-inline-start:calc(-1 * var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2)));top:60%;width:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));height:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines>.ax-tree-view-drop-list>.ax-tree-view-node:before,.ax-tree-view-with-lines>.ax-tree-view-node:before{display:none}\n"] }]
|
|
1765
|
+
}], propDecorators: { datasource: [{ type: i0.Input, args: [{ isSignal: true, alias: "datasource", required: true }] }, { type: i0.Output, args: ["datasourceChange"] }], selectMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectMode", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], checkChildrenOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkChildrenOnSelect", required: false }] }], intermediateState: [{ type: i0.Input, args: [{ isSignal: true, alias: "intermediateState", required: false }] }], checkOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkOnClick", required: false }] }], dragMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragMode", required: false }] }], dragOperationType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragOperationType", required: false }] }], showIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcons", required: false }] }], showChildrenBadge: [{ type: i0.Input, args: [{ isSignal: true, alias: "showChildrenBadge", required: false }] }], expandedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIcon", required: false }] }], collapsedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedIcon", required: false }] }], indentSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "indentSize", required: false }] }], nodeHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeHeight", required: false }] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }], itemTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], onBeforeDrop: [{ type: i0.Output, args: ["onBeforeDrop"] }], onNodeToggle: [{ type: i0.Output, args: ["onNodeToggle"] }], onNodeSelect: [{ type: i0.Output, args: ["onNodeSelect"] }], onOrderChange: [{ type: i0.Output, args: ["onOrderChange"] }], onMoveChange: [{ type: i0.Output, args: ["onMoveChange"] }], onItemsChange: [{ type: i0.Output, args: ["onItemsChange"] }] } });
|
|
503
1766
|
|
|
504
|
-
const COMPONENT = [AXTreeViewComponent, AXTreeViewItemComponent];
|
|
505
|
-
const MODULES = [
|
|
506
|
-
CommonModule,
|
|
507
|
-
AXCommonModule,
|
|
508
|
-
AXDecoratorModule,
|
|
509
|
-
AXCheckBoxModule,
|
|
510
|
-
AXFormModule,
|
|
511
|
-
FormsModule,
|
|
512
|
-
AXTooltipModule,
|
|
513
|
-
AXLoadingModule,
|
|
514
|
-
];
|
|
515
1767
|
class AXTreeViewModule {
|
|
516
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
517
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.
|
|
518
|
-
|
|
519
|
-
AXDecoratorModule,
|
|
520
|
-
AXCheckBoxModule,
|
|
521
|
-
AXFormModule,
|
|
522
|
-
FormsModule,
|
|
523
|
-
AXTooltipModule,
|
|
524
|
-
AXLoadingModule], exports: [AXTreeViewComponent, AXTreeViewItemComponent] }); }
|
|
525
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewModule, imports: [COMPONENT, MODULES] }); }
|
|
1768
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1769
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent] }); }
|
|
1770
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent] }); }
|
|
526
1771
|
}
|
|
527
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1772
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AXTreeViewModule, decorators: [{
|
|
528
1773
|
type: NgModule,
|
|
529
1774
|
args: [{
|
|
530
|
-
imports: [
|
|
531
|
-
exports: [...COMPONENT],
|
|
1775
|
+
imports: [AXTreeViewComponent],
|
|
532
1776
|
}]
|
|
533
1777
|
}] });
|
|
534
1778
|
|
|
@@ -536,5 +1780,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
|
|
|
536
1780
|
* Generated bundle index. Do not edit.
|
|
537
1781
|
*/
|
|
538
1782
|
|
|
539
|
-
export {
|
|
1783
|
+
export { AXTreeViewComponent, AXTreeViewModule, AXTreeViewService };
|
|
540
1784
|
//# sourceMappingURL=acorex-components-tree-view.mjs.map
|