@acorex/components 21.0.0-next.5 → 21.0.0-next.50

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.
Files changed (279) hide show
  1. package/accordion/index.d.ts +0 -1
  2. package/autocomplete/index.d.ts +23 -9
  3. package/button/index.d.ts +38 -17
  4. package/button-group/index.d.ts +6 -4
  5. package/calendar/index.d.ts +4 -0
  6. package/chips/index.d.ts +3 -8
  7. package/code-editor/README.md +291 -1
  8. package/code-editor/index.d.ts +260 -12
  9. package/command/index.d.ts +1 -0
  10. package/conversation2/README.md +426 -0
  11. package/conversation2/index.d.ts +6139 -0
  12. package/data-table/index.d.ts +97 -261
  13. package/dialog/index.d.ts +1 -1
  14. package/drawer/README.md +2 -2
  15. package/drawer/index.d.ts +33 -57
  16. package/drawer-legacy/README.md +3 -0
  17. package/drawer-legacy/index.d.ts +86 -0
  18. package/editor/README.md +3 -0
  19. package/editor/index.d.ts +79 -0
  20. package/fesm2022/acorex-components-accordion.mjs +19 -24
  21. package/fesm2022/acorex-components-accordion.mjs.map +1 -1
  22. package/fesm2022/acorex-components-action-sheet.mjs +12 -12
  23. package/fesm2022/acorex-components-action-sheet.mjs.map +1 -1
  24. package/fesm2022/acorex-components-alert.mjs +14 -14
  25. package/fesm2022/acorex-components-alert.mjs.map +1 -1
  26. package/fesm2022/acorex-components-aspect-ratio.mjs +4 -4
  27. package/fesm2022/acorex-components-aspect-ratio.mjs.map +1 -1
  28. package/fesm2022/acorex-components-audio-wave.mjs +12 -11
  29. package/fesm2022/acorex-components-audio-wave.mjs.map +1 -1
  30. package/fesm2022/acorex-components-autocomplete.mjs +30 -13
  31. package/fesm2022/acorex-components-autocomplete.mjs.map +1 -1
  32. package/fesm2022/acorex-components-avatar.mjs +13 -13
  33. package/fesm2022/acorex-components-avatar.mjs.map +1 -1
  34. package/fesm2022/acorex-components-badge.mjs +10 -10
  35. package/fesm2022/acorex-components-badge.mjs.map +1 -1
  36. package/fesm2022/acorex-components-bottom-navigation.mjs +12 -12
  37. package/fesm2022/acorex-components-bottom-navigation.mjs.map +1 -1
  38. package/fesm2022/acorex-components-breadcrumbs.mjs +12 -12
  39. package/fesm2022/acorex-components-breadcrumbs.mjs.map +1 -1
  40. package/fesm2022/acorex-components-button-group.mjs +25 -21
  41. package/fesm2022/acorex-components-button-group.mjs.map +1 -1
  42. package/fesm2022/acorex-components-button.mjs +68 -28
  43. package/fesm2022/acorex-components-button.mjs.map +1 -1
  44. package/fesm2022/acorex-components-calendar.mjs +39 -18
  45. package/fesm2022/acorex-components-calendar.mjs.map +1 -1
  46. package/fesm2022/acorex-components-check-box.mjs +11 -11
  47. package/fesm2022/acorex-components-check-box.mjs.map +1 -1
  48. package/fesm2022/acorex-components-chips.mjs +12 -14
  49. package/fesm2022/acorex-components-chips.mjs.map +1 -1
  50. package/fesm2022/acorex-components-circular-progress.mjs +13 -11
  51. package/fesm2022/acorex-components-circular-progress.mjs.map +1 -1
  52. package/fesm2022/acorex-components-code-editor.mjs +494 -162
  53. package/fesm2022/acorex-components-code-editor.mjs.map +1 -1
  54. package/fesm2022/acorex-components-collapse.mjs +13 -28
  55. package/fesm2022/acorex-components-collapse.mjs.map +1 -1
  56. package/fesm2022/acorex-components-color-box.mjs +11 -11
  57. package/fesm2022/acorex-components-color-box.mjs.map +1 -1
  58. package/fesm2022/acorex-components-color-palette.mjs +32 -32
  59. package/fesm2022/acorex-components-color-palette.mjs.map +1 -1
  60. package/fesm2022/acorex-components-command.mjs +18 -11
  61. package/fesm2022/acorex-components-command.mjs.map +1 -1
  62. package/fesm2022/acorex-components-comment.mjs +34 -34
  63. package/fesm2022/acorex-components-comment.mjs.map +1 -1
  64. package/fesm2022/acorex-components-conversation.mjs +56 -65
  65. package/fesm2022/acorex-components-conversation.mjs.map +1 -1
  66. package/fesm2022/acorex-components-conversation2.mjs +17641 -0
  67. package/fesm2022/acorex-components-conversation2.mjs.map +1 -0
  68. package/fesm2022/acorex-components-cron-job.mjs +53 -53
  69. package/fesm2022/acorex-components-cron-job.mjs.map +1 -1
  70. package/fesm2022/acorex-components-data-list.mjs +5 -5
  71. package/fesm2022/acorex-components-data-list.mjs.map +1 -1
  72. package/fesm2022/acorex-components-data-pager.mjs +63 -47
  73. package/fesm2022/acorex-components-data-pager.mjs.map +1 -1
  74. package/fesm2022/acorex-components-data-table.mjs +509 -551
  75. package/fesm2022/acorex-components-data-table.mjs.map +1 -1
  76. package/fesm2022/acorex-components-datetime-box.mjs +10 -10
  77. package/fesm2022/acorex-components-datetime-box.mjs.map +1 -1
  78. package/fesm2022/acorex-components-datetime-input.mjs +10 -10
  79. package/fesm2022/acorex-components-datetime-input.mjs.map +1 -1
  80. package/fesm2022/acorex-components-datetime-picker.mjs +11 -11
  81. package/fesm2022/acorex-components-datetime-picker.mjs.map +1 -1
  82. package/fesm2022/acorex-components-decorators.mjs +96 -54
  83. package/fesm2022/acorex-components-decorators.mjs.map +1 -1
  84. package/fesm2022/acorex-components-dialog.mjs +26 -16
  85. package/fesm2022/acorex-components-dialog.mjs.map +1 -1
  86. package/fesm2022/acorex-components-drawer-legacy.mjs +218 -0
  87. package/fesm2022/acorex-components-drawer-legacy.mjs.map +1 -0
  88. package/fesm2022/acorex-components-drawer.mjs +66 -150
  89. package/fesm2022/acorex-components-drawer.mjs.map +1 -1
  90. package/fesm2022/acorex-components-dropdown-button.mjs +9 -9
  91. package/fesm2022/acorex-components-dropdown-button.mjs.map +1 -1
  92. package/fesm2022/acorex-components-dropdown.mjs +20 -18
  93. package/fesm2022/acorex-components-dropdown.mjs.map +1 -1
  94. package/fesm2022/acorex-components-editor.mjs +195 -0
  95. package/fesm2022/acorex-components-editor.mjs.map +1 -0
  96. package/fesm2022/acorex-components-file-explorer.mjs +34 -34
  97. package/fesm2022/acorex-components-file-explorer.mjs.map +1 -1
  98. package/fesm2022/acorex-components-flow-chart.mjs +18 -18
  99. package/fesm2022/acorex-components-flow-chart.mjs.map +1 -1
  100. package/fesm2022/acorex-components-form.mjs +62 -37
  101. package/fesm2022/acorex-components-form.mjs.map +1 -1
  102. package/fesm2022/acorex-components-grid-layout-builder.mjs +14 -15
  103. package/fesm2022/acorex-components-grid-layout-builder.mjs.map +1 -1
  104. package/fesm2022/acorex-components-image-editor.mjs +206 -156
  105. package/fesm2022/acorex-components-image-editor.mjs.map +1 -1
  106. package/fesm2022/acorex-components-image.mjs +10 -10
  107. package/fesm2022/acorex-components-image.mjs.map +1 -1
  108. package/fesm2022/acorex-components-json-viewer.mjs +9 -9
  109. package/fesm2022/acorex-components-json-viewer.mjs.map +1 -1
  110. package/fesm2022/acorex-components-kanban.mjs +9 -7
  111. package/fesm2022/acorex-components-kanban.mjs.map +1 -1
  112. package/fesm2022/acorex-components-kbd.mjs +29 -11
  113. package/fesm2022/acorex-components-kbd.mjs.map +1 -1
  114. package/fesm2022/acorex-components-label.mjs +9 -9
  115. package/fesm2022/acorex-components-label.mjs.map +1 -1
  116. package/fesm2022/acorex-components-list.mjs +10 -10
  117. package/fesm2022/acorex-components-list.mjs.map +1 -1
  118. package/fesm2022/acorex-components-loading-dialog.mjs +23 -14
  119. package/fesm2022/acorex-components-loading-dialog.mjs.map +1 -1
  120. package/fesm2022/acorex-components-loading.mjs +23 -23
  121. package/fesm2022/acorex-components-loading.mjs.map +1 -1
  122. package/fesm2022/acorex-components-map.mjs +290 -31
  123. package/fesm2022/acorex-components-map.mjs.map +1 -1
  124. package/fesm2022/acorex-components-media-viewer.mjs +95 -99
  125. package/fesm2022/acorex-components-media-viewer.mjs.map +1 -1
  126. package/fesm2022/acorex-components-menu.mjs +24 -24
  127. package/fesm2022/acorex-components-menu.mjs.map +1 -1
  128. package/fesm2022/{acorex-components-modal-acorex-components-modal-h5Y-qcbh.mjs → acorex-components-modal-acorex-components-modal-WaTo81yi.mjs} +24 -24
  129. package/fesm2022/acorex-components-modal-acorex-components-modal-WaTo81yi.mjs.map +1 -0
  130. package/fesm2022/acorex-components-modal-modal-content.component-D61_wSet.mjs +214 -0
  131. package/fesm2022/acorex-components-modal-modal-content.component-D61_wSet.mjs.map +1 -0
  132. package/fesm2022/acorex-components-modal.mjs +1 -1
  133. package/fesm2022/acorex-components-navbar.mjs +9 -9
  134. package/fesm2022/acorex-components-navbar.mjs.map +1 -1
  135. package/fesm2022/acorex-components-notification.mjs +16 -23
  136. package/fesm2022/acorex-components-notification.mjs.map +1 -1
  137. package/fesm2022/acorex-components-number-box-legacy.mjs +412 -0
  138. package/fesm2022/acorex-components-number-box-legacy.mjs.map +1 -0
  139. package/fesm2022/acorex-components-number-box.mjs +113 -331
  140. package/fesm2022/acorex-components-number-box.mjs.map +1 -1
  141. package/fesm2022/acorex-components-otp.mjs +10 -10
  142. package/fesm2022/acorex-components-otp.mjs.map +1 -1
  143. package/fesm2022/acorex-components-page.mjs +10 -10
  144. package/fesm2022/acorex-components-page.mjs.map +1 -1
  145. package/fesm2022/acorex-components-paint.mjs +35 -40
  146. package/fesm2022/acorex-components-paint.mjs.map +1 -1
  147. package/fesm2022/acorex-components-password-box.mjs +14 -14
  148. package/fesm2022/acorex-components-password-box.mjs.map +1 -1
  149. package/fesm2022/acorex-components-pdf-reader.mjs +9 -9
  150. package/fesm2022/acorex-components-pdf-reader.mjs.map +1 -1
  151. package/fesm2022/acorex-components-phone-box.mjs +47 -10
  152. package/fesm2022/acorex-components-phone-box.mjs.map +1 -1
  153. package/fesm2022/acorex-components-picker.mjs +17 -17
  154. package/fesm2022/acorex-components-picker.mjs.map +1 -1
  155. package/fesm2022/acorex-components-popover.mjs +12 -12
  156. package/fesm2022/acorex-components-popover.mjs.map +1 -1
  157. package/fesm2022/acorex-components-popup.mjs +13 -13
  158. package/fesm2022/acorex-components-popup.mjs.map +1 -1
  159. package/fesm2022/acorex-components-progress-bar.mjs +11 -9
  160. package/fesm2022/acorex-components-progress-bar.mjs.map +1 -1
  161. package/fesm2022/acorex-components-qrcode.mjs +8 -8
  162. package/fesm2022/acorex-components-qrcode.mjs.map +1 -1
  163. package/fesm2022/acorex-components-query-builder.mjs +9 -9
  164. package/fesm2022/acorex-components-query-builder.mjs.map +1 -1
  165. package/fesm2022/acorex-components-radio.mjs +7 -7
  166. package/fesm2022/acorex-components-radio.mjs.map +1 -1
  167. package/fesm2022/acorex-components-rail-navigation.mjs +40 -38
  168. package/fesm2022/acorex-components-rail-navigation.mjs.map +1 -1
  169. package/fesm2022/acorex-components-range-slider.mjs +11 -11
  170. package/fesm2022/acorex-components-range-slider.mjs.map +1 -1
  171. package/fesm2022/acorex-components-rate-picker.mjs +20 -35
  172. package/fesm2022/acorex-components-rate-picker.mjs.map +1 -1
  173. package/fesm2022/acorex-components-rest-api-generator.mjs +23 -23
  174. package/fesm2022/acorex-components-rest-api-generator.mjs.map +1 -1
  175. package/fesm2022/acorex-components-result.mjs +8 -8
  176. package/fesm2022/acorex-components-result.mjs.map +1 -1
  177. package/fesm2022/acorex-components-routing-progress.mjs +8 -8
  178. package/fesm2022/acorex-components-routing-progress.mjs.map +1 -1
  179. package/fesm2022/acorex-components-rrule.mjs +111 -16
  180. package/fesm2022/acorex-components-rrule.mjs.map +1 -1
  181. package/fesm2022/acorex-components-scheduler-picker.mjs +2339 -0
  182. package/fesm2022/acorex-components-scheduler-picker.mjs.map +1 -0
  183. package/fesm2022/acorex-components-scheduler.mjs +52 -52
  184. package/fesm2022/acorex-components-scheduler.mjs.map +1 -1
  185. package/fesm2022/acorex-components-scss.mjs +4 -4
  186. package/fesm2022/acorex-components-scss.mjs.map +1 -1
  187. package/fesm2022/acorex-components-search-box.mjs +23 -12
  188. package/fesm2022/acorex-components-search-box.mjs.map +1 -1
  189. package/fesm2022/acorex-components-select-box.mjs +36 -17
  190. package/fesm2022/acorex-components-select-box.mjs.map +1 -1
  191. package/fesm2022/acorex-components-selection-list-2.mjs +12 -12
  192. package/fesm2022/acorex-components-selection-list-2.mjs.map +1 -1
  193. package/fesm2022/acorex-components-selection-list.mjs +10 -10
  194. package/fesm2022/acorex-components-selection-list.mjs.map +1 -1
  195. package/fesm2022/acorex-components-side-menu.mjs +31 -38
  196. package/fesm2022/acorex-components-side-menu.mjs.map +1 -1
  197. package/fesm2022/acorex-components-skeleton.mjs +8 -8
  198. package/fesm2022/acorex-components-skeleton.mjs.map +1 -1
  199. package/fesm2022/acorex-components-slider.mjs +11 -11
  200. package/fesm2022/acorex-components-slider.mjs.map +1 -1
  201. package/fesm2022/acorex-components-sliding-item.mjs +17 -17
  202. package/fesm2022/acorex-components-sliding-item.mjs.map +1 -1
  203. package/fesm2022/acorex-components-step-wizard.mjs +16 -16
  204. package/fesm2022/acorex-components-step-wizard.mjs.map +1 -1
  205. package/fesm2022/acorex-components-switch.mjs +14 -14
  206. package/fesm2022/acorex-components-switch.mjs.map +1 -1
  207. package/fesm2022/acorex-components-tabs.mjs +28 -20
  208. package/fesm2022/acorex-components-tabs.mjs.map +1 -1
  209. package/fesm2022/acorex-components-tag-box.mjs +51 -21
  210. package/fesm2022/acorex-components-tag-box.mjs.map +1 -1
  211. package/fesm2022/acorex-components-tag.mjs +47 -11
  212. package/fesm2022/acorex-components-tag.mjs.map +1 -1
  213. package/fesm2022/acorex-components-text-area.mjs +9 -9
  214. package/fesm2022/acorex-components-text-area.mjs.map +1 -1
  215. package/fesm2022/acorex-components-text-box.mjs +13 -13
  216. package/fesm2022/acorex-components-text-box.mjs.map +1 -1
  217. package/fesm2022/acorex-components-time-duration.mjs +54 -14
  218. package/fesm2022/acorex-components-time-duration.mjs.map +1 -1
  219. package/fesm2022/acorex-components-time-line.mjs +14 -29
  220. package/fesm2022/acorex-components-time-line.mjs.map +1 -1
  221. package/fesm2022/acorex-components-toast.mjs +14 -14
  222. package/fesm2022/acorex-components-toast.mjs.map +1 -1
  223. package/fesm2022/acorex-components-toolbar.mjs +9 -9
  224. package/fesm2022/acorex-components-toolbar.mjs.map +1 -1
  225. package/fesm2022/acorex-components-tooltip.mjs +12 -12
  226. package/fesm2022/acorex-components-tooltip.mjs.map +1 -1
  227. package/fesm2022/acorex-components-tree-view-legacy.mjs +511 -0
  228. package/fesm2022/acorex-components-tree-view-legacy.mjs.map +1 -0
  229. package/fesm2022/acorex-components-tree-view.mjs +2221 -435
  230. package/fesm2022/acorex-components-tree-view.mjs.map +1 -1
  231. package/fesm2022/acorex-components-uploader.mjs +28 -641
  232. package/fesm2022/acorex-components-uploader.mjs.map +1 -1
  233. package/fesm2022/acorex-components-video-player.mjs +8 -8
  234. package/fesm2022/acorex-components-video-player.mjs.map +1 -1
  235. package/fesm2022/acorex-components-wysiwyg.mjs +229 -473
  236. package/fesm2022/acorex-components-wysiwyg.mjs.map +1 -1
  237. package/fesm2022/acorex-components.mjs.map +1 -1
  238. package/file-explorer/index.d.ts +6 -6
  239. package/form/index.d.ts +3 -3
  240. package/grid-layout-builder/index.d.ts +1 -2
  241. package/image-editor/index.d.ts +11 -13
  242. package/kbd/index.d.ts +13 -7
  243. package/loading/index.d.ts +1 -1
  244. package/map/index.d.ts +28 -1
  245. package/media-viewer/index.d.ts +7 -3
  246. package/notification/index.d.ts +0 -2
  247. package/number-box/README.md +2 -2
  248. package/number-box/index.d.ts +32 -171
  249. package/number-box-legacy/README.md +3 -0
  250. package/number-box-legacy/index.d.ts +191 -0
  251. package/package.json +37 -10
  252. package/paint/index.d.ts +1 -6
  253. package/phone-box/index.d.ts +5 -4
  254. package/rate-picker/index.d.ts +5 -15
  255. package/rrule/index.d.ts +96 -1
  256. package/scheduler-picker/README.md +15 -0
  257. package/scheduler-picker/index.d.ts +1360 -0
  258. package/search-box/index.d.ts +6 -1
  259. package/select-box/index.d.ts +15 -10
  260. package/side-menu/index.d.ts +3 -2
  261. package/tag/index.d.ts +8 -2
  262. package/tag-box/index.d.ts +12 -3
  263. package/time-duration/index.d.ts +19 -3
  264. package/tree-view/index.d.ts +941 -168
  265. package/tree-view-legacy/README.md +3 -0
  266. package/tree-view-legacy/index.d.ts +184 -0
  267. package/uploader/index.d.ts +4 -331
  268. package/wysiwyg/index.d.ts +57 -159
  269. package/drawer-2/README.md +0 -3
  270. package/drawer-2/index.d.ts +0 -62
  271. package/fesm2022/acorex-components-drawer-2.mjs +0 -134
  272. package/fesm2022/acorex-components-drawer-2.mjs.map +0 -1
  273. package/fesm2022/acorex-components-modal-acorex-components-modal-h5Y-qcbh.mjs.map +0 -1
  274. package/fesm2022/acorex-components-modal-modal-content.component-B6tyMLdU.mjs +0 -235
  275. package/fesm2022/acorex-components-modal-modal-content.component-B6tyMLdU.mjs.map +0 -1
  276. package/fesm2022/acorex-components-number-box-2.mjs +0 -183
  277. package/fesm2022/acorex-components-number-box-2.mjs.map +0 -1
  278. package/number-box-2/README.md +0 -3
  279. package/number-box-2/index.d.ts +0 -41
@@ -1,534 +1,2320 @@
1
- import { AXClickEvent, NXComponent, AXComponent, AXCommonModule } from '@acorex/cdk/common';
2
- import { AXLoadingComponent, AXLoadingModule } from '@acorex/components/loading';
3
- import { AXTooltipDirective, AXTooltipModule } from '@acorex/components/tooltip';
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';
7
+ import { AXTooltipDirective } from '@acorex/components/tooltip';
4
8
  import { AXPlatform } from '@acorex/core/platform';
5
9
  import { AXTranslatorPipe } from '@acorex/core/translation';
6
- import { trigger, state, transition, style, animate } from '@angular/animations';
7
10
  import * as i1 from '@angular/common';
8
- import { CommonModule, NgTemplateOutlet, AsyncPipe } from '@angular/common';
11
+ import { CommonModule, NgTemplateOutlet } from '@angular/common';
9
12
  import * as i0 from '@angular/core';
10
- import { inject, input, model, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, signal, output, effect, Input, HostBinding, NgModule } from '@angular/core';
11
- import { AXCheckBoxComponent, AXCheckBoxModule } from '@acorex/components/check-box';
12
- import { AXDecoratorGenericComponent, AXDecoratorIconComponent, AXDecoratorModule } from '@acorex/components/decorators';
13
- import * as i1$1 from '@angular/forms';
13
+ import { Injectable, inject, DestroyRef, model, input, output, signal, computed, effect, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
14
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
15
+ import * as i2 from '@angular/forms';
14
16
  import { FormsModule } from '@angular/forms';
15
- import { AXFormModule } from '@acorex/components/form';
17
+ import { map } from 'rxjs/operators';
16
18
 
17
- class AXTreeItemClickBaseEvent extends AXClickEvent {
18
- }
19
- class AXTreeViewBase {
19
+ /**
20
+ * Service for tree node operations
21
+ * Handles all business logic for tree node manipulation
22
+ */
23
+ class AXTreeViewService {
24
+ // ==================== Constants ====================
25
+ static { this.ROOT_LIST_ID = 'ax-tree-view-root-list'; }
26
+ static { this.NODE_DROP_PREFIX = 'ax-tree-view-node-drop-'; }
27
+ static { this.LIST_PREFIX = 'ax-tree-view-list-'; }
28
+ // ==================== Node Finding & Traversal ====================
29
+ /**
30
+ * Find a node by ID in the tree
31
+ */
32
+ findNodeById(nodes, id, idField = 'id', childrenField = 'children') {
33
+ for (const node of nodes) {
34
+ if (node[idField] === id)
35
+ return node;
36
+ const children = node[childrenField];
37
+ if (children) {
38
+ const found = this.findNodeById(children, id, idField, childrenField);
39
+ if (found)
40
+ return found;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ /**
46
+ * Find parent node of a given node
47
+ */
48
+ findParentNode(nodes, targetNode, idField = 'id', childrenField = 'children') {
49
+ const targetId = targetNode[idField];
50
+ for (const node of nodes) {
51
+ const children = node[childrenField];
52
+ if (children?.some((child) => child[idField] === targetId)) {
53
+ return node;
54
+ }
55
+ if (children) {
56
+ const found = this.findParentNode(children, targetNode, idField, childrenField);
57
+ if (found)
58
+ return found;
59
+ }
60
+ }
61
+ return undefined;
62
+ }
63
+ /**
64
+ * Check if targetNode is a descendant of ancestorNode (or the same node)
65
+ * Prevents circular references by checking if target exists in ancestor's children tree
66
+ */
67
+ isValidDropTarget(movedNode, targetNode, idField = 'id', childrenField = 'children') {
68
+ const movedId = movedNode[idField];
69
+ const targetId = targetNode[idField];
70
+ if (movedId === targetId || this.isNodeDescendantOf(targetNode, movedNode, idField, childrenField)) {
71
+ return false;
72
+ }
73
+ return true;
74
+ }
75
+ /**
76
+ * Check if targetNode is a descendant of ancestorNode
77
+ */
78
+ isNodeDescendantOf(targetNode, ancestorNode, idField = 'id', childrenField = 'children') {
79
+ const targetId = targetNode[idField];
80
+ const ancestorId = ancestorNode[idField];
81
+ if (targetId === ancestorId) {
82
+ return true;
83
+ }
84
+ const children = ancestorNode[childrenField];
85
+ if (!children || children.length === 0) {
86
+ return false;
87
+ }
88
+ for (const child of children) {
89
+ const childId = child[idField];
90
+ if (childId === targetId) {
91
+ return true;
92
+ }
93
+ if (this.isNodeDescendantOf(targetNode, child, idField, childrenField)) {
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ }
99
+ /**
100
+ * Build a flat list of all visible focusable nodes
101
+ */
102
+ buildFlatNodeList(nodes, hiddenField = 'hidden', disabledField = 'disabled', expandedField = 'expanded', childrenField = 'children') {
103
+ const flatList = [];
104
+ const traverse = (nodeList, level, parent) => {
105
+ for (const node of nodeList) {
106
+ const hidden = node[hiddenField];
107
+ const disabled = node[disabledField];
108
+ if (hidden !== true && !disabled) {
109
+ flatList.push({ node, level, parent });
110
+ const expanded = node[expandedField];
111
+ const children = node[childrenField];
112
+ if (expanded && children) {
113
+ traverse(children, level + 1, node);
114
+ }
115
+ }
116
+ }
117
+ };
118
+ traverse(nodes, 0);
119
+ return flatList;
120
+ }
121
+ // ==================== Node State Checks ====================
122
+ /**
123
+ * Check if node has children
124
+ */
125
+ hasChildren(node, childrenField = 'children') {
126
+ const children = node[childrenField];
127
+ return Boolean(children?.length);
128
+ }
129
+ /**
130
+ * Check if node can be lazy loaded
131
+ */
132
+ canLazyLoad(node, isLazyDataSource, childrenCountField = 'childrenCount', childrenField = 'children') {
133
+ const childrenCount = node[childrenCountField];
134
+ return isLazyDataSource && Boolean(childrenCount && childrenCount > 0 && !this.hasChildren(node, childrenField));
135
+ }
136
+ // ==================== Selection Management ====================
137
+ /**
138
+ * Recursively select/deselect all children
139
+ */
140
+ selectAllChildren(children, selected, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
141
+ for (const child of children) {
142
+ child[selectedField] = selected;
143
+ child[indeterminateField] = false;
144
+ const childChildren = child[childrenField];
145
+ if (childChildren) {
146
+ this.selectAllChildren(childChildren, selected, selectedField, indeterminateField, childrenField);
147
+ }
148
+ }
149
+ }
150
+ /**
151
+ * Get selection state of children
152
+ */
153
+ getChildrenSelectionState(children, selectedField = 'selected', indeterminateField = 'indeterminate') {
154
+ let selectedCount = 0;
155
+ let indeterminateCount = 0;
156
+ for (const child of children) {
157
+ const selected = child[selectedField];
158
+ const indeterminate = child[indeterminateField];
159
+ if (selected && !indeterminate) {
160
+ selectedCount++;
161
+ }
162
+ if (indeterminate) {
163
+ indeterminateCount++;
164
+ }
165
+ }
166
+ return {
167
+ allSelected: selectedCount === children.length,
168
+ someSelected: selectedCount > 0 || indeterminateCount > 0,
169
+ };
170
+ }
171
+ /**
172
+ * Update parent node states based on children selection (with intermediate state support)
173
+ */
174
+ updateParentStates(nodes, changedNode, intermediateState, idField = 'id', childrenField = 'children', selectedField = 'selected', indeterminateField = 'indeterminate') {
175
+ const parent = this.findParentNode(nodes, changedNode, idField, childrenField);
176
+ const parentChildren = parent?.[childrenField];
177
+ if (!parent || !parentChildren)
178
+ return;
179
+ const { allSelected, someSelected } = this.getChildrenSelectionState(parentChildren, selectedField, indeterminateField);
180
+ if (allSelected) {
181
+ parent[selectedField] = true;
182
+ parent[indeterminateField] = false;
183
+ }
184
+ else if (someSelected) {
185
+ if (intermediateState) {
186
+ parent[selectedField] = true;
187
+ parent[indeterminateField] = true;
188
+ }
189
+ else {
190
+ parent[selectedField] = false;
191
+ parent[indeterminateField] = false;
192
+ }
193
+ }
194
+ else {
195
+ parent[selectedField] = false;
196
+ parent[indeterminateField] = false;
197
+ }
198
+ this.updateParentStates(nodes, parent, intermediateState, idField, childrenField, selectedField, indeterminateField);
199
+ }
200
+ /**
201
+ * Recursively deselect all nodes
202
+ */
203
+ deselectAllNodes(nodes, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
204
+ for (const node of nodes) {
205
+ node[selectedField] = false;
206
+ node[indeterminateField] = false;
207
+ const children = node[childrenField];
208
+ if (children) {
209
+ this.deselectAllNodes(children, selectedField, indeterminateField, childrenField);
210
+ }
211
+ }
212
+ }
213
+ /**
214
+ * Recursively set selection state for all nodes
215
+ */
216
+ setAllSelection(nodes, selected, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
217
+ for (const node of nodes) {
218
+ node[selectedField] = selected;
219
+ node[indeterminateField] = false;
220
+ const children = node[childrenField];
221
+ if (children) {
222
+ this.setAllSelection(children, selected, selectedField, indeterminateField, childrenField);
223
+ }
224
+ }
225
+ }
226
+ /**
227
+ * Recursively count selected nodes
228
+ */
229
+ countSelected(nodes, selectedField = 'selected', childrenField = 'children') {
230
+ let count = 0;
231
+ for (const node of nodes) {
232
+ if (node[selectedField])
233
+ count++;
234
+ const children = node[childrenField];
235
+ if (children) {
236
+ count += this.countSelected(children, selectedField, childrenField);
237
+ }
238
+ }
239
+ return count;
240
+ }
241
+ /**
242
+ * Recursively collect selected nodes
243
+ */
244
+ collectSelected(nodes, result, selectedField = 'selected', childrenField = 'children') {
245
+ for (const node of nodes) {
246
+ if (node[selectedField])
247
+ result.push(node);
248
+ const children = node[childrenField];
249
+ if (children) {
250
+ this.collectSelected(children, result, selectedField, childrenField);
251
+ }
252
+ }
253
+ }
254
+ /**
255
+ * Recursively remove selected nodes
256
+ */
257
+ removeSelected(nodes, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
258
+ for (let i = nodes.length - 1; i >= 0; i--) {
259
+ const node = nodes[i];
260
+ const selected = node[selectedField];
261
+ const indeterminate = node[indeterminateField];
262
+ if (selected && !indeterminate) {
263
+ nodes.splice(i, 1);
264
+ }
265
+ else {
266
+ const children = node[childrenField];
267
+ if (children) {
268
+ this.removeSelected(children, selectedField, indeterminateField, childrenField);
269
+ }
270
+ }
271
+ }
272
+ }
273
+ /**
274
+ * Recursively update all parent states in the tree (used after deletion)
275
+ */
276
+ updateAllParentStates(nodes, intermediateState, childrenField = 'children', selectedField = 'selected', indeterminateField = 'indeterminate') {
277
+ for (const node of nodes) {
278
+ const children = node[childrenField];
279
+ if (children && children.length > 0) {
280
+ this.updateAllParentStates(children, intermediateState, childrenField, selectedField, indeterminateField);
281
+ const { allSelected, someSelected } = this.getChildrenSelectionState(children, selectedField, indeterminateField);
282
+ if (allSelected) {
283
+ node[selectedField] = true;
284
+ node[indeterminateField] = false;
285
+ }
286
+ else if (someSelected) {
287
+ if (intermediateState) {
288
+ node[selectedField] = true;
289
+ node[indeterminateField] = true;
290
+ }
291
+ else {
292
+ node[selectedField] = false;
293
+ node[indeterminateField] = false;
294
+ }
295
+ }
296
+ else {
297
+ node[selectedField] = false;
298
+ node[indeterminateField] = false;
299
+ }
300
+ }
301
+ }
302
+ }
303
+ // ==================== Expansion Management ====================
304
+ /**
305
+ * Recursively set expanded state (with lazy loading)
306
+ */
307
+ async setExpandedState(nodes, expanded, isLazyDataSource, loadNodeChildren, expandedField = 'expanded', childrenField = 'children', childrenCountField = 'childrenCount') {
308
+ for (const node of nodes) {
309
+ const hasChildren = this.hasChildren(node, childrenField);
310
+ const canLazyLoad = this.canLazyLoad(node, isLazyDataSource, childrenCountField, childrenField);
311
+ if (hasChildren || canLazyLoad) {
312
+ if (expanded && canLazyLoad) {
313
+ await loadNodeChildren(node);
314
+ }
315
+ node[expandedField] = expanded;
316
+ const children = node[childrenField];
317
+ if (children) {
318
+ await this.setExpandedState(children, expanded, isLazyDataSource, loadNodeChildren, expandedField, childrenField, childrenCountField);
319
+ }
320
+ }
321
+ }
322
+ }
323
+ // ==================== Drag & Drop Helpers ====================
324
+ /**
325
+ * Get array reference by drop list ID
326
+ */
327
+ getArrayByListId(nodes, listId, idField = 'id', childrenField = 'children') {
328
+ if (listId === AXTreeViewService.ROOT_LIST_ID) {
329
+ return nodes;
330
+ }
331
+ if (listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)) {
332
+ const nodeId = listId.replace(AXTreeViewService.NODE_DROP_PREFIX, '');
333
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
334
+ return node ? [node] : null;
335
+ }
336
+ const nodeId = listId.replace(AXTreeViewService.LIST_PREFIX, '');
337
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
338
+ const children = node?.[childrenField];
339
+ return children ?? null;
340
+ }
341
+ /**
342
+ * Find parent node by list ID
343
+ */
344
+ findParentByListId(nodes, listId, idField = 'id', childrenField = 'children') {
345
+ if (listId === AXTreeViewService.ROOT_LIST_ID) {
346
+ return undefined;
347
+ }
348
+ const prefix = listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)
349
+ ? AXTreeViewService.NODE_DROP_PREFIX
350
+ : AXTreeViewService.LIST_PREFIX;
351
+ const nodeId = listId.replace(prefix, '');
352
+ return this.findNodeById(nodes, nodeId, idField, childrenField) ?? undefined;
353
+ }
354
+ /**
355
+ * Generate unique list ID for each node
356
+ */
357
+ getListId(node, idField = 'id') {
358
+ if (!node)
359
+ return AXTreeViewService.ROOT_LIST_ID;
360
+ const nodeId = node[idField];
361
+ return `${AXTreeViewService.LIST_PREFIX}${nodeId}`;
362
+ }
363
+ /**
364
+ * Get root list ID constant
365
+ */
366
+ getRootListId() {
367
+ return AXTreeViewService.ROOT_LIST_ID;
368
+ }
369
+ /**
370
+ * Get node drop prefix constant
371
+ */
372
+ getNodeDropPrefix() {
373
+ return AXTreeViewService.NODE_DROP_PREFIX;
374
+ }
375
+ /**
376
+ * Get list prefix constant
377
+ */
378
+ getListPrefix() {
379
+ return AXTreeViewService.LIST_PREFIX;
380
+ }
381
+ // ==================== Node Utilities ====================
382
+ /**
383
+ * Get all nodes in a flat array
384
+ */
385
+ getAllNodes(nodes, childrenField = 'children') {
386
+ const allNodes = [];
387
+ const traverse = (nodeList) => {
388
+ for (const node of nodeList) {
389
+ allNodes.push(node);
390
+ const children = node[childrenField];
391
+ if (children) {
392
+ traverse(children);
393
+ }
394
+ }
395
+ };
396
+ traverse(nodes);
397
+ return allNodes;
398
+ }
399
+ /**
400
+ * Get the path to a node (array of parent nodes from root to node)
401
+ */
402
+ getNodePath(nodes, nodeId, idField = 'id', childrenField = 'children') {
403
+ const path = [];
404
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
405
+ if (!node) {
406
+ return path;
407
+ }
408
+ let current = node;
409
+ while (current) {
410
+ path.unshift(current);
411
+ const parent = this.findParentNode(nodes, current, idField, childrenField);
412
+ current = parent ?? null;
413
+ }
414
+ return path;
415
+ }
416
+ /**
417
+ * Get the level/depth of a node (0 = root level)
418
+ */
419
+ getNodeLevel(nodes, nodeId, idField = 'id', childrenField = 'children') {
420
+ const path = this.getNodePath(nodes, nodeId, idField, childrenField);
421
+ return path.length > 0 ? path.length - 1 : -1;
422
+ }
423
+ /**
424
+ * Get sibling nodes of a given node
425
+ */
426
+ getSiblings(nodes, nodeId, idField = 'id', childrenField = 'children') {
427
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
428
+ if (!node) {
429
+ return [];
430
+ }
431
+ const parent = this.findParentNode(nodes, node, idField, childrenField);
432
+ const siblingsArray = parent?.[childrenField] ?? nodes;
433
+ return siblingsArray.filter((n) => n[idField] !== nodeId);
434
+ }
435
+ /**
436
+ * Clone a node (creates a deep copy)
437
+ */
438
+ cloneNode(node, idField = 'id', titleField = 'title', tooltipField = 'tooltip', iconField = 'icon', expandedField = 'expanded', selectedField = 'selected', indeterminateField = 'indeterminate', disabledField = 'disabled', hiddenField = 'hidden', childrenCountField = 'childrenCount', dataField = 'data', childrenField = 'children') {
439
+ const cloned = {
440
+ [idField]: `${node[idField]}-clone-${Date.now()}`,
441
+ [titleField]: node[titleField],
442
+ [tooltipField]: node[tooltipField],
443
+ [iconField]: node[iconField],
444
+ [expandedField]: node[expandedField],
445
+ [selectedField]: false, // Cloned nodes are not selected by default
446
+ [indeterminateField]: false,
447
+ [disabledField]: node[disabledField],
448
+ [hiddenField]: node[hiddenField],
449
+ [childrenCountField]: node[childrenCountField],
450
+ [dataField]: node[dataField] ? JSON.parse(JSON.stringify(node[dataField])) : undefined,
451
+ };
452
+ const children = node[childrenField];
453
+ if (children && children.length > 0) {
454
+ cloned[childrenField] = children.map((child) => this.cloneNode(child, idField, titleField, tooltipField, iconField, expandedField, selectedField, indeterminateField, disabledField, hiddenField, childrenCountField, dataField, childrenField));
455
+ cloned[childrenCountField] = cloned[childrenField].length;
456
+ }
457
+ return cloned;
458
+ }
459
+ // ==================== Node Manipulation ====================
460
+ /**
461
+ * Move a node to a new parent in the tree
462
+ * @param nodes - Root nodes array
463
+ * @param nodeId - The ID of the node to move
464
+ * @param newParentId - The ID of the new parent (undefined for root level)
465
+ * @param index - Optional index to insert at (default: append to end)
466
+ * @param idField - Field name for node ID
467
+ * @param childrenField - Field name for children array
468
+ * @param childrenCountField - Field name for children count
469
+ * @param expandedField - Field name for expanded state
470
+ * @returns Object with success status, moved node, previous parent, new parent, and indices
471
+ */
472
+ moveNode(nodes, nodeId, newParentId, index, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount', expandedField = 'expanded') {
473
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
474
+ if (!node) {
475
+ return { success: false, previousIndex: -1, currentIndex: -1 };
476
+ }
477
+ // Find current parent
478
+ const currentParent = this.findParentNode(nodes, node, idField, childrenField);
479
+ const currentParentChildren = currentParent
480
+ ? currentParent[childrenField]
481
+ : undefined;
482
+ const currentArray = currentParentChildren ?? nodes;
483
+ // Find and remove from current location
484
+ const currentIndex = currentArray.findIndex((n) => n[idField] === nodeId);
485
+ if (currentIndex === -1) {
486
+ return { success: false, previousIndex: -1, currentIndex: -1 };
487
+ }
488
+ const movedNode = currentArray.splice(currentIndex, 1)[0];
489
+ // Find new parent
490
+ let targetArray;
491
+ let newParent;
492
+ if (newParentId) {
493
+ newParent = this.findNodeById(nodes, newParentId, idField, childrenField);
494
+ if (!newParent) {
495
+ // Restore node if new parent not found
496
+ currentArray.splice(currentIndex, 0, movedNode);
497
+ return { success: false, previousIndex: currentIndex, currentIndex: -1 };
498
+ }
499
+ // Validate drop target
500
+ if (!this.isValidDropTarget(movedNode, newParent, idField, childrenField)) {
501
+ // Restore node if invalid drop target
502
+ currentArray.splice(currentIndex, 0, movedNode);
503
+ return { success: false, previousIndex: currentIndex, currentIndex: -1 };
504
+ }
505
+ let newParentChildren = newParent[childrenField];
506
+ if (!newParentChildren) {
507
+ newParentChildren = [];
508
+ newParent[childrenField] = newParentChildren;
509
+ }
510
+ targetArray = newParentChildren;
511
+ }
512
+ else {
513
+ targetArray = nodes;
514
+ }
515
+ // Calculate new index before inserting
516
+ const newIndex = index !== undefined && index >= 0 && index <= targetArray.length ? index : targetArray.length;
517
+ // Insert at new location
518
+ if (index !== undefined && index >= 0 && index <= targetArray.length) {
519
+ targetArray.splice(index, 0, movedNode);
520
+ }
521
+ else {
522
+ targetArray.push(movedNode);
523
+ }
524
+ // Update childrenCount
525
+ if (currentParent) {
526
+ const updatedChildren = currentParent[childrenField];
527
+ currentParent[childrenCountField] = updatedChildren?.length ?? 0;
528
+ }
529
+ if (newParent) {
530
+ const updatedChildren = newParent[childrenField];
531
+ newParent[childrenCountField] = updatedChildren?.length ?? 0;
532
+ newParent[expandedField] = true; // Auto-expand new parent
533
+ }
534
+ return {
535
+ success: true,
536
+ movedNode,
537
+ previousParent: currentParent,
538
+ newParent,
539
+ previousIndex: currentIndex,
540
+ currentIndex: newIndex,
541
+ };
542
+ }
543
+ /**
544
+ * Edit/update a node's properties
545
+ * @param nodes - Root nodes array
546
+ * @param nodeId - The ID of the node to edit
547
+ * @param updates - Partial node object with properties to update
548
+ * @param idField - Field name for node ID
549
+ * @param childrenField - Field name for children array
550
+ * @param childrenCountField - Field name for children count
551
+ * @returns The updated node if found, null otherwise
552
+ */
553
+ editNode(nodes, nodeId, updates, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount') {
554
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
555
+ if (!node) {
556
+ return null;
557
+ }
558
+ // Update node properties
559
+ Object.assign(node, updates);
560
+ // If children array is provided, ensure it exists and update childrenCount
561
+ if (updates[childrenField] !== undefined) {
562
+ const children = updates[childrenField];
563
+ node[childrenField] = children;
564
+ node[childrenCountField] = children?.length;
565
+ }
566
+ return node;
567
+ }
568
+ /**
569
+ * Add a child node to a parent node
570
+ * @param nodes - Root nodes array
571
+ * @param parentId - The ID of the parent node
572
+ * @param childNode - The child node to add
573
+ * @param index - Optional index to insert at (default: append to end)
574
+ * @param idField - Field name for node ID
575
+ * @param childrenField - Field name for children array
576
+ * @param childrenCountField - Field name for children count
577
+ * @param expandedField - Field name for expanded state
578
+ * @returns The parent node if found and child was added, null otherwise
579
+ */
580
+ addChild(nodes, parentId, childNode, index, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount', expandedField = 'expanded') {
581
+ const parent = this.findNodeById(nodes, parentId, idField, childrenField);
582
+ if (!parent) {
583
+ return null;
584
+ }
585
+ // Ensure children array exists
586
+ let children = parent[childrenField];
587
+ if (!children) {
588
+ children = [];
589
+ parent[childrenField] = children;
590
+ }
591
+ // Insert or append child
592
+ if (index !== undefined && index >= 0 && index <= children.length) {
593
+ children.splice(index, 0, childNode);
594
+ }
595
+ else {
596
+ children.push(childNode);
597
+ }
598
+ // Update childrenCount
599
+ parent[childrenCountField] = children.length;
600
+ // Auto-expand parent if it was collapsed
601
+ if (!parent[expandedField]) {
602
+ parent[expandedField] = true;
603
+ }
604
+ return parent;
605
+ }
606
+ /**
607
+ * Remove a node from the tree
608
+ * @param nodes - Root nodes array
609
+ * @param nodeId - The ID of the node to remove
610
+ * @param idField - Field name for node ID
611
+ * @param childrenField - Field name for children array
612
+ * @param childrenCountField - Field name for children count
613
+ * @returns The removed node if found, null otherwise
614
+ */
615
+ removeNode(nodes, nodeId, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount') {
616
+ const node = this.findNodeById(nodes, nodeId, idField, childrenField);
617
+ if (!node) {
618
+ return null;
619
+ }
620
+ // Find parent to remove from its children array
621
+ const parent = this.findParentNode(nodes, node, idField, childrenField);
622
+ const parentChildren = parent ? parent[childrenField] : undefined;
623
+ const targetArray = parentChildren ?? nodes;
624
+ // Find and remove the node
625
+ const index = targetArray.findIndex((n) => n[idField] === nodeId);
626
+ if (index !== -1) {
627
+ const removed = targetArray.splice(index, 1)[0];
628
+ // Update parent's childrenCount if it exists
629
+ if (parent) {
630
+ const updatedChildren = parent[childrenField];
631
+ parent[childrenCountField] = updatedChildren?.length ?? 0;
632
+ }
633
+ return removed;
634
+ }
635
+ return null;
636
+ }
637
+ /**
638
+ * Validate node structure (check for required fields and circular references)
639
+ */
640
+ validateNode(node, visitedIds = new Set(), idField = 'id', titleField = 'title', childrenField = 'children', childrenCountField = 'childrenCount') {
641
+ const errors = [];
642
+ const nodeId = node[idField];
643
+ const nodeTitle = node[titleField];
644
+ if (!nodeId) {
645
+ errors.push(`Node must have an ${idField}`);
646
+ }
647
+ if (!nodeTitle) {
648
+ errors.push(`Node must have a ${titleField}`);
649
+ }
650
+ if (nodeId && visitedIds.has(nodeId)) {
651
+ errors.push(`Circular reference detected: node ${nodeId} appears multiple times in the tree`);
652
+ }
653
+ const children = node[childrenField];
654
+ if (children) {
655
+ const newVisited = new Set(visitedIds);
656
+ if (nodeId) {
657
+ newVisited.add(nodeId);
658
+ }
659
+ for (const child of children) {
660
+ const childValidation = this.validateNode(child, newVisited, idField, titleField, childrenField, childrenCountField);
661
+ if (!childValidation.valid) {
662
+ errors.push(...childValidation.errors.map((e) => `Child of ${nodeId}: ${e}`));
663
+ }
664
+ }
665
+ const childrenCount = node[childrenCountField];
666
+ if (childrenCount !== undefined && childrenCount !== children.length) {
667
+ errors.push(`Node ${nodeId}: ${childrenCountField} (${childrenCount}) does not match ${childrenField} array length (${children.length})`);
668
+ }
669
+ }
670
+ return {
671
+ valid: errors.length === 0,
672
+ errors,
673
+ };
674
+ }
675
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
676
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewService }); }
20
677
  }
678
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewService, decorators: [{
679
+ type: Injectable
680
+ }] });
21
681
 
22
- class AXTreeViewItemComponent extends NXComponent {
682
+ class AXTreeViewComponent {
23
683
  constructor() {
24
- super();
25
- this.treeView = inject(AXTreeViewBase);
26
- this.item = input(...(ngDevMode ? [undefined, { debugName: "item" }] : []));
27
- this.isExpanded = model(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
28
- this.isActive = model(false, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
29
- this.isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
30
- this.executorChanges = input(...(ngDevMode ? [undefined, { debugName: "executorChanges" }] : []));
684
+ // ==================== Dependencies ====================
685
+ this.treeService = inject(AXTreeViewService);
31
686
  this.platformService = inject(AXPlatform);
32
- this.arrowIcon = computed(() => {
33
- const baseClasses = 'ax-tree-view-arrow ax-icon ax-icon-solid';
34
- const toggleIcons = this.treeView.toggleIcons();
35
- if (toggleIcons) {
36
- return this.isExpanded() ? `${baseClasses} ${toggleIcons.expanded}` : `${baseClasses} ${toggleIcons.collapsed}`;
37
- }
38
- if (this.isExpanded()) {
39
- return `${baseClasses} ax-icon-chevron-down`;
40
- }
41
- return this.platformService.isRtl()
42
- ? `${baseClasses} ax-icon-chevron-left`
43
- : `${baseClasses} ax-icon-chevron-right`;
44
- }, ...(ngDevMode ? [{ debugName: "arrowIcon" }] : []));
45
- }
46
- handleArrowNodeClick() {
47
- if (this.item()[this.treeView.disableField()] || this.isLoading() || this.treeView.expandOn() === 'dbClick') {
687
+ this.destroyRef = inject(DestroyRef);
688
+ // ==================== Inputs ====================
689
+ /** Tree data source - can be static array or lazy loading function */
690
+ this.datasource = model.required(...(ngDevMode ? [{ debugName: "datasource" }] : []));
691
+ /** Selection mode: 'single' (click to select) or 'multiple' (checkbox selection). Default: `'single'`. In multiple mode, checkboxes are automatically shown. */
692
+ this.selectMode = input('single', ...(ngDevMode ? [{ debugName: "selectMode" }] : []));
693
+ /** Selection behavior: 'all' (select anything, no special behavior), 'intermediate' (parent indeterminate state when children selected), 'leaf' (only leaf nodes selectable), 'nested' (selecting parent selects all children). Default: `'intermediate'` */
694
+ this.selectionBehavior = input('intermediate', ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : []));
695
+ /** Drag area: 'handler' (drag handle), 'item' (entire item). Default: `'handler'` */
696
+ this.dragArea = input('handler', ...(ngDevMode ? [{ debugName: "dragArea" }] : []));
697
+ /** Drag behavior: 'none' (disabled), 'order-only' (reorder only), 'move' (move between parents), 'both' (allow both). Default: `'none'` */
698
+ this.dragBehavior = input('none', ...(ngDevMode ? [{ debugName: "dragBehavior" }] : []));
699
+ /** Whether to show icons. Default: `true` */
700
+ this.showIcons = input(true, ...(ngDevMode ? [{ debugName: "showIcons" }] : []));
701
+ /** Whether to show children count badge. Default: `true` */
702
+ this.showChildrenBadge = input(true, ...(ngDevMode ? [{ debugName: "showChildrenBadge" }] : []));
703
+ /** Custom icon for expanded nodes. Default: `'fa-solid fa-chevron-down'` */
704
+ this.expandedIcon = input('fa-solid fa-chevron-down', ...(ngDevMode ? [{ debugName: "expandedIcon" }] : []));
705
+ /** Custom icon for collapsed nodes. Default: `'fa-solid fa-chevron-right'` */
706
+ this.collapsedIcon = input('fa-solid fa-chevron-right', ...(ngDevMode ? [{ debugName: "collapsedIcon" }] : []));
707
+ /** Indent size in pixels for each level. Default: `16` */
708
+ this.indentSize = input(16, ...(ngDevMode ? [{ debugName: "indentSize" }] : []));
709
+ /** Visual style variant. Default: `'default'` */
710
+ this.look = input('default', ...(ngDevMode ? [{ debugName: "look" }] : []));
711
+ /** Custom template for tree items. Default: `undefined` */
712
+ this.nodeTemplate = input(...(ngDevMode ? [undefined, { debugName: "nodeTemplate" }] : []));
713
+ /** Field name for node ID. Default: `'id'` */
714
+ this.idField = input('id', ...(ngDevMode ? [{ debugName: "idField" }] : []));
715
+ /** Field name for node title. Default: `'title'` */
716
+ this.titleField = input('title', ...(ngDevMode ? [{ debugName: "titleField" }] : []));
717
+ /** Field name for node tooltip. Default: `'tooltip'` */
718
+ this.tooltipField = input('tooltip', ...(ngDevMode ? [{ debugName: "tooltipField" }] : []));
719
+ /** Field name for node icon. Default: `'icon'` */
720
+ this.iconField = input('icon', ...(ngDevMode ? [{ debugName: "iconField" }] : []));
721
+ /** Field name for expanded state. Default: `'expanded'` */
722
+ this.expandedField = input('expanded', ...(ngDevMode ? [{ debugName: "expandedField" }] : []));
723
+ /** Field name for selected state. Default: `'selected'` */
724
+ this.selectedField = input('selected', ...(ngDevMode ? [{ debugName: "selectedField" }] : []));
725
+ /** Field name for indeterminate state. Default: `'indeterminate'` */
726
+ this.indeterminateField = input('indeterminate', ...(ngDevMode ? [{ debugName: "indeterminateField" }] : []));
727
+ /** Field name for disabled state. Default: `'disabled'` */
728
+ this.disabledField = input('disabled', ...(ngDevMode ? [{ debugName: "disabledField" }] : []));
729
+ /** Field name for hidden state. Default: `'hidden'` */
730
+ this.hiddenField = input('hidden', ...(ngDevMode ? [{ debugName: "hiddenField" }] : []));
731
+ /** Field name for children array. Default: `'children'` */
732
+ this.childrenField = input('children', ...(ngDevMode ? [{ debugName: "childrenField" }] : []));
733
+ /** Field name for children count. Default: `'childrenCount'` */
734
+ this.childrenCountField = input('childrenCount', ...(ngDevMode ? [{ debugName: "childrenCountField" }] : []));
735
+ /** Field name for custom data. Default: `'data'` */
736
+ this.dataField = input('data', ...(ngDevMode ? [{ debugName: "dataField" }] : []));
737
+ /** Whether disabled state is inherited from parent nodes. Default: `true`. */
738
+ this.inheritDisabled = input(true, ...(ngDevMode ? [{ debugName: "inheritDisabled" }] : []));
739
+ // ==================== Outputs ====================
740
+ /** Emitted before a drop operation - set canceled to true to prevent drop */
741
+ this.onBeforeDrop = output();
742
+ /** Emitted when a node is toggled (expanded/collapsed) */
743
+ this.onNodeToggle = output();
744
+ /** Emitted when a node is selected/deselected */
745
+ this.onNodeSelect = output();
746
+ /** Emitted when a node is double-clicked */
747
+ this.onNodeDoubleClick = output();
748
+ /** Emitted when a node is double-clicked */
749
+ this.onNodeClick = output();
750
+ /** Emitted when selection changes - returns all currently selected nodes */
751
+ this.onSelectionChange = output();
752
+ /** Emitted when nodes are reordered within the same parent */
753
+ this.onOrderChange = output();
754
+ /** Emitted when a node is moved to a different parent */
755
+ this.onMoveChange = output();
756
+ /** Emitted for any item change (order or move) */
757
+ this.onItemsChange = output();
758
+ // ==================== Internal State ====================
759
+ /** Internal signal for tree nodes */
760
+ this.nodes = signal([], ...(ngDevMode ? [{ debugName: "nodes" }] : []));
761
+ /** Internal signal for tracking loading state of individual nodes */
762
+ this.loadingNodes = signal(new Set(), ...(ngDevMode ? [{ debugName: "loadingNodes" }] : []));
763
+ /** Signal for tracking root/tree-level loading state (used by reloadData) */
764
+ this._isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
765
+ /** Public readonly signal for tree loading state - can be used in templates */
766
+ this.isLoading = this._isLoading.asReadonly();
767
+ /** Currently focused node ID for keyboard navigation */
768
+ this.focusedNodeId = signal(null, ...(ngDevMode ? [{ debugName: "focusedNodeId" }] : []));
769
+ /** RTL detection signal */
770
+ this.isRtl = signal(this.platformService.isRtl(), ...(ngDevMode ? [{ debugName: "isRtl" }] : []));
771
+ /** active node id */
772
+ this.activeNodeId = signal(null, ...(ngDevMode ? [{ debugName: "activeNodeId" }] : []));
773
+ /** Computed chevron icons that flip for RTL */
774
+ this.directionExpandedIcon = computed(() => this.expandedIcon(), ...(ngDevMode ? [{ debugName: "directionExpandedIcon" }] : []));
775
+ this.directionCollapsedIcon = computed(() => {
776
+ const isRtlDirection = this.isRtl();
777
+ const defaultIcon = this.collapsedIcon();
778
+ if (isRtlDirection && defaultIcon === 'fa-solid fa-chevron-right') {
779
+ return 'fa-solid fa-chevron-left';
780
+ }
781
+ if (!isRtlDirection && defaultIcon === 'fa-solid fa-chevron-left') {
782
+ return 'fa-solid fa-chevron-right';
783
+ }
784
+ return defaultIcon;
785
+ }, ...(ngDevMode ? [{ debugName: "directionCollapsedIcon" }] : []));
786
+ /** Flag to prevent infinite loops when syncing datasource */
787
+ this.isUpdatingFromDatasource = false;
788
+ /** Computed to check if datasource is a function */
789
+ this.isLazyDataSource = computed(() => typeof this.datasource() === 'function', ...(ngDevMode ? [{ debugName: "isLazyDataSource" }] : []));
790
+ /** Computed: Returns true when selection is restricted to leaf nodes only */
791
+ this.isLeafOnlyMode = computed(() => this.selectionBehavior() === 'leaf', ...(ngDevMode ? [{ debugName: "isLeafOnlyMode" }] : []));
792
+ /** Computed: Returns true when selecting a parent automatically selects all its children */
793
+ this.cascadesToChildren = computed(() => {
794
+ const behavior = this.selectionBehavior();
795
+ return behavior === 'nested' || behavior === 'intermediate-nested';
796
+ }, ...(ngDevMode ? [{ debugName: "cascadesToChildren" }] : []));
797
+ /** Computed: Returns true when parent nodes show indeterminate state based on children selection */
798
+ this.hasIntermediateState = computed(() => {
799
+ const behavior = this.selectionBehavior();
800
+ return behavior === 'intermediate' || behavior === 'intermediate-nested';
801
+ }, ...(ngDevMode ? [{ debugName: "hasIntermediateState" }] : []));
802
+ /** Computed: Returns true when drag handle should be shown */
803
+ this.shouldShowDragHandle = computed(() => {
804
+ return this.dragArea() === 'handler' && this.dragBehavior() !== 'none';
805
+ }, ...(ngDevMode ? [{ debugName: "shouldShowDragHandle" }] : []));
806
+ // ==================== Effects ====================
807
+ /** Effect to handle datasource changes */
808
+ this.#datasourceEffect = effect(async () => {
809
+ if (this.isUpdatingFromDatasource) {
810
+ return;
811
+ }
812
+ const ds = this.datasource();
813
+ if (Array.isArray(ds)) {
814
+ this.nodes.set([...ds]);
815
+ }
816
+ else if (typeof ds === 'function') {
817
+ try {
818
+ await this.loadRootItems(ds);
819
+ }
820
+ catch (error) {
821
+ this.handleError('Failed to load root items', error);
822
+ }
823
+ }
824
+ }, ...(ngDevMode ? [{ debugName: "#datasourceEffect" }] : []));
825
+ /** Initialize direction change listener */
826
+ this.#initDirectionListener = afterNextRender(() => {
827
+ this.platformService.directionChange
828
+ .pipe(map((event) => event.data === 'rtl'), takeUntilDestroyed(this.destroyRef))
829
+ .subscribe((isRtl) => this.isRtl.set(isRtl));
830
+ });
831
+ }
832
+ // ==================== Node Property Helpers ====================
833
+ /**
834
+ * Get a property value from a node using the configured field name
835
+ */
836
+ getNodeProp(node, fieldName, defaultValue) {
837
+ return node[fieldName] ?? defaultValue;
838
+ }
839
+ /**
840
+ * Set a property value on a node using the configured field name
841
+ */
842
+ setNodeProp(node, fieldName, value) {
843
+ node[fieldName] = value;
844
+ }
845
+ /**
846
+ * Get node ID
847
+ */
848
+ getNodeId(node) {
849
+ return this.getNodeProp(node, this.idField(), '');
850
+ }
851
+ /**
852
+ * Get node title
853
+ */
854
+ getNodeTitle(node) {
855
+ return this.getNodeProp(node, this.titleField(), '');
856
+ }
857
+ /**
858
+ * Get node tooltip
859
+ */
860
+ getNodeTooltip(node) {
861
+ return this.getNodeProp(node, this.tooltipField(), undefined);
862
+ }
863
+ /**
864
+ * Get node icon
865
+ */
866
+ getNodeIcon(node) {
867
+ return this.getNodeProp(node, this.iconField(), undefined);
868
+ }
869
+ /**
870
+ * Get node expanded state
871
+ */
872
+ getNodeExpanded(node) {
873
+ return this.getNodeProp(node, this.expandedField(), false);
874
+ }
875
+ /**
876
+ * Set node expanded state
877
+ */
878
+ setNodeExpanded(node, value) {
879
+ this.setNodeProp(node, this.expandedField(), value);
880
+ }
881
+ /**
882
+ * Get node selected state
883
+ */
884
+ getNodeSelected(node) {
885
+ return this.getNodeProp(node, this.selectedField(), false);
886
+ }
887
+ /**
888
+ * Set node selected state
889
+ */
890
+ setNodeSelected(node, value) {
891
+ this.setNodeProp(node, this.selectedField(), value);
892
+ }
893
+ /**
894
+ * Get node indeterminate state
895
+ */
896
+ getNodeIndeterminate(node) {
897
+ return this.getNodeProp(node, this.indeterminateField(), false);
898
+ }
899
+ /**
900
+ * Set node indeterminate state
901
+ */
902
+ setNodeIndeterminate(node, value) {
903
+ this.setNodeProp(node, this.indeterminateField(), value);
904
+ }
905
+ /**
906
+ * Get node's own disabled state (without inheritance)
907
+ */
908
+ getNodeDisabled(node) {
909
+ return this.getNodeProp(node, this.disabledField(), false);
910
+ }
911
+ /**
912
+ * Check if node is effectively disabled (considering inheritance from parents)
913
+ * When inheritDisabled is true:
914
+ * - Returns true if node itself is disabled
915
+ * - Returns true if any ancestor is disabled AND node doesn't have explicit `disabled: false`
916
+ * When inheritDisabled is false:
917
+ * - Only returns node's own disabled state
918
+ */
919
+ isNodeEffectivelyDisabled(node) {
920
+ // Check if node itself is disabled
921
+ if (this.getNodeDisabled(node)) {
922
+ return true;
923
+ }
924
+ // If not inheriting disabled state, just return false (node is not disabled)
925
+ if (!this.inheritDisabled()) {
926
+ return false;
927
+ }
928
+ // Check if node explicitly has disabled: false (override inheritance)
929
+ const nodeDisabledValue = node[this.disabledField()];
930
+ if (nodeDisabledValue === false) {
931
+ return false;
932
+ }
933
+ // Check ancestors for disabled state
934
+ return this.isAnyAncestorDisabled(node);
935
+ }
936
+ /**
937
+ * Check if any ancestor of the node is disabled
938
+ */
939
+ isAnyAncestorDisabled(node) {
940
+ const parent = this.treeService.findParentNode(this.nodes(), node, this.idField(), this.childrenField());
941
+ if (!parent) {
942
+ return false;
943
+ }
944
+ if (this.getNodeDisabled(parent)) {
945
+ return true;
946
+ }
947
+ return this.isAnyAncestorDisabled(parent);
948
+ }
949
+ /**
950
+ * Get node hidden state
951
+ */
952
+ getNodeHidden(node) {
953
+ return this.getNodeProp(node, this.hiddenField(), false);
954
+ }
955
+ /**
956
+ * Get node children array
957
+ */
958
+ getNodeChildren(node) {
959
+ return this.getNodeProp(node, this.childrenField(), undefined);
960
+ }
961
+ /**
962
+ * Set node children array
963
+ */
964
+ setNodeChildren(node, value) {
965
+ this.setNodeProp(node, this.childrenField(), value);
966
+ }
967
+ /**
968
+ * Get node children count
969
+ */
970
+ getNodeChildrenCount(node) {
971
+ return this.getNodeProp(node, this.childrenCountField(), undefined);
972
+ }
973
+ /**
974
+ * Set node children count
975
+ */
976
+ setNodeChildrenCount(node, value) {
977
+ this.setNodeProp(node, this.childrenCountField(), value);
978
+ }
979
+ // ==================== Effects ====================
980
+ /** Effect to handle datasource changes */
981
+ #datasourceEffect;
982
+ /** Initialize direction change listener */
983
+ #initDirectionListener;
984
+ // ==================== Public API ====================
985
+ /**
986
+ * Expand all nodes in the tree (with lazy loading support)
987
+ */
988
+ async expandAll() {
989
+ await this.treeService.setExpandedState(this.nodes(), true, this.isLazyDataSource(), (node) => this.loadNodeChildren(node), this.expandedField(), this.childrenField(), this.childrenCountField());
990
+ this.refreshNodes();
991
+ }
992
+ /**
993
+ * Collapse all nodes in the tree
994
+ */
995
+ collapseAll() {
996
+ this.treeService.setExpandedState(this.nodes(), false, this.isLazyDataSource(), (node) => this.loadNodeChildren(node), this.expandedField(), this.childrenField(), this.childrenCountField());
997
+ this.refreshNodes();
998
+ }
999
+ /**
1000
+ * Get count of selected nodes
1001
+ */
1002
+ getSelectedCount() {
1003
+ return this.treeService.countSelected(this.nodes(), this.selectedField(), this.childrenField());
1004
+ }
1005
+ /**
1006
+ * Check if any nodes are selected
1007
+ */
1008
+ hasSelection() {
1009
+ return this.getSelectedCount() > 0;
1010
+ }
1011
+ /**
1012
+ * Get all selected nodes
1013
+ */
1014
+ getSelectedNodes() {
1015
+ const selected = [];
1016
+ this.treeService.collectSelected(this.nodes(), selected, this.selectedField(), this.childrenField());
1017
+ return selected;
1018
+ }
1019
+ /**
1020
+ * Delete selected nodes from the tree
1021
+ */
1022
+ deleteSelected() {
1023
+ this.treeService.removeSelected(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
1024
+ if (!this.isLeafOnlyMode()) {
1025
+ this.treeService.updateAllParentStates(this.nodes(), this.hasIntermediateState(), this.childrenField(), this.selectedField(), this.indeterminateField());
1026
+ }
1027
+ this.refreshNodes();
1028
+ this.emitSelectionChange();
1029
+ }
1030
+ /**
1031
+ * Select all nodes in the tree (respects disabled state)
1032
+ */
1033
+ selectAll() {
1034
+ if (this.selectMode() === 'none') {
1035
+ return;
1036
+ }
1037
+ if (this.isLeafOnlyMode()) {
1038
+ // Only select leaf nodes that are not disabled
1039
+ const selectLeafs = (nodes) => {
1040
+ for (const node of nodes) {
1041
+ if (this.isLeafNode(node) && !this.isNodeEffectivelyDisabled(node)) {
1042
+ this.setNodeSelected(node, true);
1043
+ this.setNodeIndeterminate(node, false);
1044
+ }
1045
+ const children = this.getNodeChildren(node);
1046
+ if (children) {
1047
+ selectLeafs(children);
1048
+ }
1049
+ }
1050
+ };
1051
+ selectLeafs(this.nodes());
1052
+ }
1053
+ else {
1054
+ // Select all nodes that are not disabled
1055
+ const selectAllNonDisabled = (nodes) => {
1056
+ for (const node of nodes) {
1057
+ if (!this.isNodeEffectivelyDisabled(node)) {
1058
+ this.setNodeSelected(node, true);
1059
+ this.setNodeIndeterminate(node, false);
1060
+ }
1061
+ const children = this.getNodeChildren(node);
1062
+ if (children) {
1063
+ selectAllNonDisabled(children);
1064
+ }
1065
+ }
1066
+ };
1067
+ selectAllNonDisabled(this.nodes());
1068
+ }
1069
+ this.refreshNodes();
1070
+ this.emitSelectionChange();
1071
+ }
1072
+ /**
1073
+ * Deselect all nodes in the tree
1074
+ */
1075
+ deselectAll() {
1076
+ this.treeService.setAllSelection(this.nodes(), false, this.selectedField(), this.indeterminateField(), this.childrenField());
1077
+ this.refreshNodes();
1078
+ this.emitSelectionChange();
1079
+ }
1080
+ /**
1081
+ * Find a node by ID in the tree
1082
+ */
1083
+ findNode(id) {
1084
+ return this.treeService.findNodeById(this.nodes(), id, this.idField(), this.childrenField());
1085
+ }
1086
+ /**
1087
+ * Refresh the tree to trigger change detection
1088
+ */
1089
+ refresh() {
1090
+ this.refreshNodes();
1091
+ }
1092
+ /**
1093
+ * Reload the entire tree data from the datasource
1094
+ * For callback datasource: clears all nodes and reloads from root
1095
+ * For array datasource: refreshes the current data
1096
+ * @returns Promise that resolves when reload is complete
1097
+ */
1098
+ async reloadData() {
1099
+ // Prevent concurrent reloads
1100
+ if (this._isLoading()) {
1101
+ return;
1102
+ }
1103
+ const ds = this.datasource();
1104
+ if (typeof ds === 'function') {
1105
+ this._isLoading.set(true);
1106
+ // Clear states in batch for better performance
1107
+ this.loadingNodes.set(new Set());
1108
+ this.nodes.set([]);
1109
+ this.focusedNodeId.set(null);
1110
+ this.activeNodeId.set(null);
1111
+ try {
1112
+ const result = ds();
1113
+ const rootNodes = result instanceof Promise ? await result : result;
1114
+ this.nodes.set(rootNodes);
1115
+ }
1116
+ catch (error) {
1117
+ this.handleError('Failed to reload data', error);
1118
+ this.nodes.set([]);
1119
+ }
1120
+ finally {
1121
+ this._isLoading.set(false);
1122
+ }
1123
+ }
1124
+ else if (Array.isArray(ds)) {
1125
+ // For array datasource, just refresh from the current data
1126
+ this.nodes.set([...ds]);
1127
+ }
1128
+ }
1129
+ /**
1130
+ * Check if the tree is currently loading data (root level)
1131
+ * @returns true if the tree is loading root data
1132
+ */
1133
+ getIsLoading() {
1134
+ return this._isLoading();
1135
+ }
1136
+ /**
1137
+ * Check if a node is currently loading
1138
+ */
1139
+ isNodeLoading(nodeId) {
1140
+ return this.loadingNodes().has(nodeId);
1141
+ }
1142
+ /**
1143
+ * Get loading state for a node (internal state)
1144
+ */
1145
+ getNodeLoading(node) {
1146
+ return this.loadingNodes().has(this.getNodeId(node));
1147
+ }
1148
+ /**
1149
+ * Edit/update a node's properties
1150
+ * @param nodeId - The ID of the node to edit
1151
+ * @param updates - Partial node object with properties to update
1152
+ * @returns true if node was found and updated, false otherwise
1153
+ */
1154
+ editNode(nodeId, updates) {
1155
+ const updated = this.treeService.editNode(this.nodes(), nodeId, updates, this.idField(), this.childrenField(), this.childrenCountField());
1156
+ if (updated) {
1157
+ this.refreshNodes();
1158
+ return true;
1159
+ }
1160
+ return false;
1161
+ }
1162
+ /**
1163
+ * Add a child node to a parent node
1164
+ * @param parentId - The ID of the parent node
1165
+ * @param childNode - The child node to add
1166
+ * @param index - Optional index to insert at (default: append to end)
1167
+ * @returns true if parent was found and child was added, false otherwise
1168
+ */
1169
+ addChild(parentId, childNode, index) {
1170
+ const parent = this.treeService.addChild(this.nodes(), parentId, childNode, index, this.idField(), this.childrenField(), this.childrenCountField(), this.expandedField());
1171
+ if (parent) {
1172
+ this.refreshNodes();
1173
+ return true;
1174
+ }
1175
+ return false;
1176
+ }
1177
+ /**
1178
+ * Remove a node from the tree
1179
+ * @param nodeId - The ID of the node to remove
1180
+ * @returns The removed node if found, null otherwise
1181
+ */
1182
+ removeNode(nodeId) {
1183
+ const removed = this.treeService.removeNode(this.nodes(), nodeId, this.idField(), this.childrenField(), this.childrenCountField());
1184
+ if (removed) {
1185
+ // Update parent states if needed
1186
+ if (this.hasIntermediateState()) {
1187
+ this.treeService.updateAllParentStates(this.nodes(), this.hasIntermediateState(), this.childrenField(), this.selectedField(), this.indeterminateField());
1188
+ }
1189
+ this.refreshNodes();
1190
+ }
1191
+ return removed;
1192
+ }
1193
+ /**
1194
+ * Expand a specific node
1195
+ * @param nodeId - The ID of the node to expand
1196
+ * @returns Promise that resolves when expansion is complete (if lazy loading)
1197
+ */
1198
+ async expandNode(nodeId) {
1199
+ const node = this.findNode(nodeId);
1200
+ if (!node) {
1201
+ return;
1202
+ }
1203
+ const hasChildren = this.treeService.hasChildren(node, this.childrenField());
1204
+ const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField());
1205
+ if (hasChildren || canLazyLoad) {
1206
+ if (canLazyLoad) {
1207
+ await this.loadNodeChildren(node);
1208
+ }
1209
+ this.setNodeExpanded(node, true);
1210
+ this.refreshNodes();
1211
+ this.onNodeToggle.emit({ component: this, node, nativeEvent: new Event('expand') });
1212
+ }
1213
+ }
1214
+ /**
1215
+ * Collapse a specific node
1216
+ * @param nodeId - The ID of the node to collapse
1217
+ */
1218
+ collapseNode(nodeId) {
1219
+ const node = this.findNode(nodeId);
1220
+ if (!node) {
1221
+ return;
1222
+ }
1223
+ if (this.getNodeExpanded(node)) {
1224
+ this.setNodeExpanded(node, false);
1225
+ this.refreshNodes();
1226
+ this.onNodeToggle.emit({ component: this, node, nativeEvent: new Event('collapse') });
1227
+ }
1228
+ }
1229
+ /**
1230
+ * Toggle expansion state of a specific node
1231
+ * @param nodeId - The ID of the node to toggle
1232
+ * @returns Promise that resolves when toggle is complete (if lazy loading)
1233
+ */
1234
+ async toggleNodeExpansion(nodeId) {
1235
+ const node = this.findNode(nodeId);
1236
+ if (!node) {
1237
+ return;
1238
+ }
1239
+ if (this.getNodeExpanded(node)) {
1240
+ this.collapseNode(nodeId);
1241
+ }
1242
+ else {
1243
+ await this.expandNode(nodeId);
1244
+ }
1245
+ }
1246
+ /**
1247
+ * Programmatically select a node
1248
+ * @param nodeId - The ID of the node to select
1249
+ * @returns true if node was found and selected, false otherwise
1250
+ */
1251
+ selectNode(nodeId) {
1252
+ if (this.selectMode() === 'none') {
1253
+ return false;
1254
+ }
1255
+ const node = this.findNode(nodeId);
1256
+ if (!node || this.isNodeEffectivelyDisabled(node) || !this.canSelectNode(node)) {
1257
+ return false;
1258
+ }
1259
+ const mode = this.selectMode();
1260
+ if (mode === 'single') {
1261
+ this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
1262
+ this.setNodeSelected(node, true);
1263
+ this.setNodeIndeterminate(node, false);
1264
+ }
1265
+ else {
1266
+ this.setNodeSelected(node, true);
1267
+ this.setNodeIndeterminate(node, false);
1268
+ const children = this.getNodeChildren(node);
1269
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
1270
+ this.treeService.selectAllChildren(children, true, this.selectedField(), this.indeterminateField(), this.childrenField());
1271
+ }
1272
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
1273
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
1274
+ }
1275
+ }
1276
+ this.refreshNodes();
1277
+ this.onNodeSelect.emit({
1278
+ component: this,
1279
+ node,
1280
+ isUserInteraction: false,
1281
+ });
1282
+ this.emitSelectionChange();
1283
+ return true;
1284
+ }
1285
+ /**
1286
+ * Programmatically deselect a node
1287
+ * @param nodeId - The ID of the node to deselect
1288
+ * @returns true if node was found and deselected, false otherwise
1289
+ */
1290
+ deselectNode(nodeId) {
1291
+ const node = this.findNode(nodeId);
1292
+ if (!node) {
1293
+ return false;
1294
+ }
1295
+ this.setNodeSelected(node, false);
1296
+ this.setNodeIndeterminate(node, false);
1297
+ const children = this.getNodeChildren(node);
1298
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
1299
+ this.treeService.selectAllChildren(children, false, this.selectedField(), this.indeterminateField(), this.childrenField());
1300
+ }
1301
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
1302
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
1303
+ }
1304
+ this.refreshNodes();
1305
+ this.onNodeSelect.emit({
1306
+ component: this,
1307
+ node,
1308
+ isUserInteraction: false,
1309
+ });
1310
+ this.emitSelectionChange();
1311
+ return true;
1312
+ }
1313
+ /**
1314
+ * Get parent node of a given node
1315
+ * @param nodeId - The ID of the node
1316
+ * @returns The parent node if found, null otherwise
1317
+ */
1318
+ getParent(nodeId) {
1319
+ const node = this.findNode(nodeId);
1320
+ if (!node) {
1321
+ return null;
1322
+ }
1323
+ return this.treeService.findParentNode(this.nodes(), node, this.idField(), this.childrenField()) ?? null;
1324
+ }
1325
+ /**
1326
+ * Get children of a node
1327
+ * @param nodeId - The ID of the parent node
1328
+ * @returns Array of child nodes, or null if node not found
1329
+ */
1330
+ getChildren(nodeId) {
1331
+ const node = this.findNode(nodeId);
1332
+ if (!node) {
1333
+ return null;
1334
+ }
1335
+ return this.getNodeChildren(node) ?? [];
1336
+ }
1337
+ /**
1338
+ * Get all root nodes
1339
+ * @returns Array of root nodes
1340
+ */
1341
+ getRootNodes() {
1342
+ return [...this.nodes()];
1343
+ }
1344
+ /**
1345
+ * Get all nodes in a flat array
1346
+ * @returns Array of all nodes in the tree
1347
+ */
1348
+ getAllNodes() {
1349
+ return this.treeService.getAllNodes(this.nodes(), this.childrenField());
1350
+ }
1351
+ /**
1352
+ * Get the path to a node (array of parent IDs from root to node)
1353
+ * @param nodeId - The ID of the node
1354
+ * @returns Array of node IDs representing the path, or empty array if node not found
1355
+ */
1356
+ getNodePath(nodeId) {
1357
+ const nodePath = this.treeService.getNodePath(this.nodes(), nodeId, this.idField(), this.childrenField());
1358
+ return nodePath.map((node) => this.getNodeId(node));
1359
+ }
1360
+ /**
1361
+ * Get the level/depth of a node (0 = root level)
1362
+ * @param nodeId - The ID of the node
1363
+ * @returns The level of the node, or -1 if node not found
1364
+ */
1365
+ getNodeLevel(nodeId) {
1366
+ return this.treeService.getNodeLevel(this.nodes(), nodeId, this.idField(), this.childrenField());
1367
+ }
1368
+ /**
1369
+ * Programmatically move a node to a new parent
1370
+ * @param nodeId - The ID of the node to move
1371
+ * @param newParentId - The ID of the new parent (undefined for root level)
1372
+ * @param index - Optional index to insert at (default: append to end)
1373
+ * @returns true if move was successful, false otherwise
1374
+ */
1375
+ moveNode(nodeId, newParentId, index) {
1376
+ const result = this.treeService.moveNode(this.nodes(), nodeId, newParentId, index, this.idField(), this.childrenField(), this.childrenCountField(), this.expandedField());
1377
+ if (result.success && result.movedNode) {
1378
+ // Emit drop events
1379
+ this.emitDropEvents(result.movedNode, result.previousParent, result.newParent, result.previousIndex, result.currentIndex, false);
1380
+ this.refreshNodes();
1381
+ return true;
1382
+ }
1383
+ return false;
1384
+ }
1385
+ /**
1386
+ * Clone a node (creates a deep copy)
1387
+ * @param nodeId - The ID of the node to clone
1388
+ * @returns The cloned node, or null if node not found
1389
+ */
1390
+ cloneNode(nodeId) {
1391
+ const node = this.findNode(nodeId);
1392
+ if (!node) {
1393
+ return null;
1394
+ }
1395
+ return this.treeService.cloneNode(node, this.idField(), this.titleField(), this.tooltipField(), this.iconField(), this.expandedField(), this.selectedField(), this.indeterminateField(), this.disabledField(), this.hiddenField(), this.childrenCountField(), this.dataField(), this.childrenField());
1396
+ }
1397
+ /**
1398
+ * Focus a specific node by ID
1399
+ * @param nodeId - The ID of the node to focus
1400
+ * @returns true if node was found and focused, false otherwise
1401
+ */
1402
+ focusNode(nodeId) {
1403
+ const node = this.findNode(nodeId);
1404
+ if (!node || this.getNodeHidden(node) === true || this.isNodeEffectivelyDisabled(node)) {
1405
+ return false;
1406
+ }
1407
+ this.focusNodeById(nodeId);
1408
+ return true;
1409
+ }
1410
+ /**
1411
+ * Get all expanded nodes
1412
+ * @returns Array of expanded nodes
1413
+ */
1414
+ getExpandedNodes() {
1415
+ const expanded = [];
1416
+ const traverse = (nodes) => {
1417
+ for (const node of nodes) {
1418
+ if (this.getNodeExpanded(node)) {
1419
+ expanded.push(node);
1420
+ }
1421
+ const children = this.getNodeChildren(node);
1422
+ if (children) {
1423
+ traverse(children);
1424
+ }
1425
+ }
1426
+ };
1427
+ traverse(this.nodes());
1428
+ return expanded;
1429
+ }
1430
+ /**
1431
+ * Get all collapsed nodes that have children
1432
+ * @returns Array of collapsed nodes with children
1433
+ */
1434
+ getCollapsedNodes() {
1435
+ const collapsed = [];
1436
+ const traverse = (nodes) => {
1437
+ for (const node of nodes) {
1438
+ const children = this.getNodeChildren(node);
1439
+ const childrenCount = this.getNodeChildrenCount(node);
1440
+ if (!this.getNodeExpanded(node) && (children?.length || childrenCount)) {
1441
+ collapsed.push(node);
1442
+ }
1443
+ if (children) {
1444
+ traverse(children);
1445
+ }
1446
+ }
1447
+ };
1448
+ traverse(this.nodes());
1449
+ return collapsed;
1450
+ }
1451
+ /**
1452
+ * Check if a node is expanded
1453
+ * @param nodeId - The ID of the node
1454
+ * @returns true if node is expanded, false otherwise
1455
+ */
1456
+ isNodeExpanded(nodeId) {
1457
+ const node = this.findNode(nodeId);
1458
+ return node ? this.getNodeExpanded(node) : false;
1459
+ }
1460
+ /**
1461
+ * Check if a node is selected
1462
+ * @param nodeId - The ID of the node
1463
+ * @returns true if node is selected, false otherwise
1464
+ */
1465
+ isNodeSelected(nodeId) {
1466
+ const node = this.findNode(nodeId);
1467
+ return node ? this.getNodeSelected(node) : false;
1468
+ }
1469
+ /**
1470
+ * Check if a node has children
1471
+ * @param nodeId - The ID of the node
1472
+ * @returns true if node has children, false otherwise
1473
+ */
1474
+ hasChildren(nodeId) {
1475
+ const node = this.findNode(nodeId);
1476
+ return this.treeService.hasChildren(node ?? {}, this.childrenField());
1477
+ }
1478
+ /**
1479
+ * Check if a node is disabled (considering inheritance from parent nodes)
1480
+ * @param nodeId - The ID of the node
1481
+ * @returns true if node is disabled (directly or inherited), false otherwise
1482
+ */
1483
+ isDisabled(nodeId) {
1484
+ const node = this.findNode(nodeId);
1485
+ return node ? this.isNodeEffectivelyDisabled(node) : false;
1486
+ }
1487
+ /**
1488
+ * Get template context for a node
1489
+ */
1490
+ getTemplateContext(node, level = 0) {
1491
+ const children = this.getNodeChildren(node);
1492
+ const childrenCount = this.getNodeChildrenCount(node);
1493
+ return {
1494
+ $implicit: node,
1495
+ node,
1496
+ level,
1497
+ expanded: this.getNodeExpanded(node),
1498
+ childrenCount: childrenCount ?? children?.length ?? 0,
1499
+ loading: this.getNodeLoading(node),
1500
+ disabled: this.isNodeEffectivelyDisabled(node),
1501
+ };
1502
+ }
1503
+ /**
1504
+ * Calculate padding-inline for a node based on its level
1505
+ */
1506
+ getNodePaddingInline(level) {
1507
+ const indent = this.indentSize();
1508
+ const currentLook = this.look();
1509
+ const multiplier = currentLook === 'with-line' ? 1 / 3 : 1;
1510
+ return level * indent * multiplier;
1511
+ }
1512
+ /**
1513
+ * Check if node should show expand toggle
1514
+ */
1515
+ shouldShowExpandToggle(node) {
1516
+ return (this.treeService.hasChildren(node, this.childrenField()) ||
1517
+ this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField()));
1518
+ }
1519
+ /**
1520
+ * Check if checkboxes should be shown (only for multiple mode)
1521
+ */
1522
+ shouldShowCheckbox() {
1523
+ return this.selectMode() === 'multiple';
1524
+ }
1525
+ /**
1526
+ * Check if a node is a leaf (has no children)
1527
+ * A node is a leaf if it has no loaded children AND no childrenCount (or childrenCount is 0)
1528
+ */
1529
+ isLeafNode(node) {
1530
+ const hasChildren = this.treeService.hasChildren(node, this.childrenField());
1531
+ const childrenCount = this.getNodeChildrenCount(node);
1532
+ const hasChildrenCount = childrenCount && childrenCount > 0;
1533
+ const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField());
1534
+ // A node is a leaf if:
1535
+ // 1. It has no loaded children
1536
+ // 2. AND it has no childrenCount (or childrenCount is 0)
1537
+ // 3. AND it cannot be lazy loaded
1538
+ return !hasChildren && !hasChildrenCount && !canLazyLoad;
1539
+ }
1540
+ /**
1541
+ * Check if a node can be selected (considering selectMode and isLeafOnlyMode)
1542
+ */
1543
+ canSelectNode(node) {
1544
+ if (this.selectMode() === 'none') {
1545
+ return false;
1546
+ }
1547
+ if (this.isLeafOnlyMode()) {
1548
+ return this.isLeafNode(node);
1549
+ }
1550
+ return true;
1551
+ }
1552
+ /**
1553
+ * Check if checkbox should be shown for a specific node
1554
+ */
1555
+ shouldShowCheckboxForNode(node) {
1556
+ if (!this.shouldShowCheckbox()) {
1557
+ return false;
1558
+ }
1559
+ if (this.isLeafOnlyMode()) {
1560
+ return this.isLeafNode(node);
1561
+ }
1562
+ return true;
1563
+ }
1564
+ /**
1565
+ * Generate unique list ID for each node
1566
+ */
1567
+ getListId(node) {
1568
+ return this.treeService.getListId(node, this.idField());
1569
+ }
1570
+ /**
1571
+ * Check if a node is currently focused
1572
+ */
1573
+ isNodeFocused(nodeId) {
1574
+ return this.focusedNodeId() === nodeId;
1575
+ }
1576
+ /**
1577
+ * Get ARIA level for a node
1578
+ */
1579
+ getNodeAriaLevel(level) {
1580
+ return level + 1;
1581
+ }
1582
+ /**
1583
+ * Get ARIA expanded state for a node
1584
+ */
1585
+ getNodeAriaExpanded(node) {
1586
+ if (!this.shouldShowExpandToggle(node)) {
1587
+ return null;
1588
+ }
1589
+ return this.getNodeExpanded(node) ? 'true' : 'false';
1590
+ }
1591
+ /**
1592
+ * Get ARIA selected state for a node
1593
+ */
1594
+ getNodeAriaSelected(node) {
1595
+ if (this.selectMode() === 'none') {
1596
+ return null;
1597
+ }
1598
+ const selected = this.getNodeSelected(node);
1599
+ if (this.selectMode() === 'single') {
1600
+ return selected ? 'true' : 'false';
1601
+ }
1602
+ if (this.selectMode() === 'multiple') {
1603
+ return selected ? 'true' : 'false';
1604
+ }
1605
+ return null;
1606
+ }
1607
+ /**
1608
+ * Emit selection change event with all selected nodes
1609
+ */
1610
+ emitSelectionChange() {
1611
+ const selectedNodes = this.getSelectedNodes();
1612
+ this.onSelectionChange.emit({
1613
+ component: this,
1614
+ selectedNodes,
1615
+ });
1616
+ }
1617
+ // ==================== Event Handlers ====================
1618
+ /**
1619
+ * Handle node double click
1620
+ */
1621
+ onNodeDoubleClickHandle(node, event) {
1622
+ if (this.isNodeEffectivelyDisabled(node))
1623
+ return;
1624
+ this.onNodeDoubleClick.emit({
1625
+ component: this,
1626
+ node,
1627
+ nativeEvent: event,
1628
+ });
1629
+ }
1630
+ /**
1631
+ * Handle node click handle
1632
+ */
1633
+ onNodeClickHandle(node, event) {
1634
+ if (this.isNodeEffectivelyDisabled(node))
48
1635
  return;
1636
+ const mode = this.selectMode();
1637
+ this.activeNodeId.set(this.getNodeId(node));
1638
+ if (mode === 'single') {
1639
+ this.handleSingleSelection(node, event);
1640
+ }
1641
+ this.onNodeClick.emit({ component: this, node, nativeEvent: event });
1642
+ }
1643
+ /**
1644
+ * Toggle node expansion state with lazy loading support
1645
+ * Note: Disabled nodes can still be toggled (expanded/collapsed) for better UX
1646
+ */
1647
+ async toggleNode(node, event) {
1648
+ if (this.isEvent(event) && typeof event.stopPropagation === 'function') {
1649
+ event.stopPropagation();
49
1650
  }
50
- this.isExpanded.set(!this.isExpanded());
51
- if (this.treeView.itemsPromise && this.isExpanded() && !this.item()[this.treeView.childrenField()]?.length) {
52
- this.treeView.fetchData(this.item());
53
- this.treeView.setNodeLoading(this.item()[this.treeView.valueField()], true);
1651
+ const hasChildren = this.treeService.hasChildren(node, this.childrenField());
1652
+ const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField());
1653
+ if (hasChildren || canLazyLoad) {
1654
+ const willExpand = !this.getNodeExpanded(node);
1655
+ if (willExpand && canLazyLoad) {
1656
+ await this.loadNodeChildren(node);
1657
+ }
1658
+ this.setNodeExpanded(node, willExpand);
1659
+ this.refreshNodes();
1660
+ this.onNodeToggle.emit({ component: this, node, nativeEvent: event });
54
1661
  }
55
- this.treeView.onCollapsedChanged.emit({ component: this, data: this.item(), nativeElement: this.nativeElement });
56
1662
  }
57
- handleTextClick() {
58
- if (this.item()[this.treeView.disableField()]) {
1663
+ /**
1664
+ * Toggle node selection state with indeterminate support (for multiple mode)
1665
+ */
1666
+ toggleSelection(node, event) {
1667
+ if (!event.isUserInteraction)
59
1668
  return;
1669
+ if (this.selectMode() === 'none')
1670
+ return;
1671
+ if (!this.canSelectNode(node))
1672
+ return;
1673
+ const mode = this.selectMode();
1674
+ if (mode !== 'multiple')
1675
+ return;
1676
+ const newValue = event.value === null ? true : event.value;
1677
+ this.setNodeSelected(node, newValue);
1678
+ this.setNodeIndeterminate(node, false);
1679
+ const children = this.getNodeChildren(node);
1680
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
1681
+ this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
1682
+ }
1683
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
1684
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
1685
+ }
1686
+ this.refreshNodes();
1687
+ this.onNodeSelect.emit({
1688
+ component: this,
1689
+ node,
1690
+ isUserInteraction: event.isUserInteraction,
1691
+ });
1692
+ this.emitSelectionChange();
1693
+ }
1694
+ /**
1695
+ * Handle drop events for tree nodes
1696
+ */
1697
+ onDrop(event, parentNode) {
1698
+ const targetArray = parentNode ? (this.getNodeChildren(parentNode) ?? []) : this.nodes();
1699
+ const isReordering = event.previousContainer === event.container;
1700
+ if (isReordering) {
1701
+ this.handleReorder(event, targetArray, parentNode);
60
1702
  }
61
- if (this.treeView.focusNodeEnabled()) {
62
- this.treeView.handleUnActiveNode(this.treeView.itemsSignal());
63
- this.item()[this.treeView.activeField()] = true;
1703
+ else {
1704
+ this.handleMove(event, targetArray, parentNode);
64
1705
  }
65
- this.treeView.onNodeClick.emit({ component: this, data: this.item(), nativeElement: this.nativeElement });
1706
+ this.refreshNodes();
66
1707
  }
67
- handleTextDbClick() {
68
- if (this.item()[this.treeView.disableField()] || this.treeView.expandOn() === 'click') {
1708
+ /**
1709
+ * Handle drop events when dropping directly onto a node (to make it a child)
1710
+ */
1711
+ onDropOntoNode(event, targetNode) {
1712
+ if (!this.canMoveToParent())
1713
+ return;
1714
+ const sourceListId = event.previousContainer.element.id;
1715
+ const sourceArray = this.getArrayByListId(sourceListId);
1716
+ if (!sourceArray)
69
1717
  return;
1718
+ const movedNode = sourceArray[event.previousIndex];
1719
+ if (!this.treeService.isValidDropTarget(movedNode, targetNode, this.idField(), this.childrenField()))
1720
+ return;
1721
+ if (!this.emitBeforeDropEvent(movedNode, sourceListId, targetNode, event.previousIndex, 0))
1722
+ return;
1723
+ let targetChildren = this.getNodeChildren(targetNode);
1724
+ if (!targetChildren) {
1725
+ targetChildren = [];
1726
+ this.setNodeChildren(targetNode, targetChildren);
70
1727
  }
71
- if (this.item()?.[this.treeView.childrenField()] || this.item()[this.treeView.hasChildField()]) {
72
- this.isExpanded.set(!this.isExpanded());
73
- if (this.treeView.itemsPromise && this.isExpanded()) {
74
- this.treeView.fetchData(this.item());
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();
1728
+ sourceArray.splice(event.previousIndex, 1);
1729
+ targetChildren.unshift(movedNode);
1730
+ this.setNodeExpanded(targetNode, true);
1731
+ this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), targetNode, event.previousIndex, 0, false);
1732
+ this.refreshNodes();
1733
+ }
1734
+ /**
1735
+ * Handle node focus event
1736
+ */
1737
+ onNodeFocus(nodeId) {
1738
+ this.focusedNodeId.set(nodeId);
1739
+ }
1740
+ /**
1741
+ * Handle tree container focus - focus first node if none is focused
1742
+ */
1743
+ onTreeFocus(event) {
1744
+ if (event.target === event.currentTarget) {
1745
+ const flatList = this.treeService.buildFlatNodeList(this.nodes(), this.hiddenField(), this.disabledField(), this.expandedField(), this.childrenField());
1746
+ if (flatList.length > 0) {
1747
+ const focusedId = this.focusedNodeId();
1748
+ if (focusedId) {
1749
+ // Check if the focused node still exists and is not hidden
1750
+ const focusedNode = this.treeService.findNodeById(this.nodes(), focusedId, this.idField(), this.childrenField());
1751
+ if (focusedNode && this.getNodeHidden(focusedNode) !== true) {
1752
+ this.focusNodeById(focusedId);
1753
+ }
1754
+ else {
1755
+ // Focused node no longer exists, focus first node
1756
+ this.focusNodeById(this.getNodeId(flatList[0].node));
1757
+ }
158
1758
  }
159
1759
  else {
160
- this.itemsSignal.set(result);
161
- this.itemsPromise = null;
1760
+ // No node is focused, focus first node
1761
+ this.focusNodeById(this.getNodeId(flatList[0].node));
162
1762
  }
163
1763
  }
164
- else {
165
- this.itemsSignal.set(itemsInput);
166
- this.itemsPromise = null;
167
- }
168
- }, ...(ngDevMode ? [{ debugName: "#effect" }] : []));
169
- this.expandOn = input('defult', ...(ngDevMode ? [{ debugName: "expandOn" }] : []));
170
- this.loadingState = signal({}, ...(ngDevMode ? [{ debugName: "loadingState" }] : []));
171
- }
172
- /** @ignore */
173
- get __hostClass() {
174
- return [`ax-look-${this.look()}`];
1764
+ }
175
1765
  }
176
- get resolvedItems() {
177
- return this.itemsSignal();
1766
+ /**
1767
+ * Handle tree container blur
1768
+ */
1769
+ onTreeBlur(event) {
1770
+ if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) {
1771
+ this.focusedNodeId.set(null);
1772
+ }
178
1773
  }
179
- #effect;
180
- handleNodeSelectionClick(event, item) {
181
- if (item[this.disableField()] || this.isNodeLoading(item[this.valueField()])) {
1774
+ /**
1775
+ * Handle keyboard navigation
1776
+ */
1777
+ handleKeyDown(event) {
1778
+ const flatList = this.treeService.buildFlatNodeList(this.nodes(), this.hiddenField(), this.disabledField(), this.expandedField(), this.childrenField());
1779
+ if (flatList.length === 0)
182
1780
  return;
1781
+ const currentFocused = this.getFocusedNode();
1782
+ let currentIndex = currentFocused
1783
+ ? flatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(currentFocused))
1784
+ : -1;
1785
+ if (currentIndex === -1 && event.target === event.currentTarget) {
1786
+ currentIndex = 0;
183
1787
  }
184
- if (this.selectionMode() === 'single' && event.isUserInteraction) {
185
- this.handleUnSelectNode(this.itemsSignal());
186
- }
187
- if (event.isUserInteraction) {
188
- item[this.selectedField()] = event.value;
189
- if (event.value !== null) {
190
- switch (this.selectionBehavior()) {
191
- case 'autoExpand':
192
- if (event.value) {
193
- if (this.itemsPromise && item[this.hasChildField()] && !item?.[this.childrenField()]?.length) {
194
- this.setNodeLoading(item[this.valueField()], true);
195
- this.fetchData(item);
196
- }
197
- this.toggleExpand(item);
198
- }
199
- break;
200
- case 'cascade':
201
- this.expandAndToggleSelection(item, event.value);
202
- break;
203
- case 'indeterminate':
204
- if (item?.[this.childrenField()]?.length) {
205
- this.applySelectionToChildren(item, event.value, item[this.valueField()]);
206
- }
207
- this.updateParentSelection(item, event.value);
208
- break;
209
- default:
210
- break;
211
- }
1788
+ const navigationResult = this.handleNavigationKey(event, flatList, currentIndex, currentFocused);
1789
+ if (navigationResult.handled) {
1790
+ if (navigationResult.shouldPreventDefault) {
1791
+ event.preventDefault();
1792
+ event.stopPropagation();
1793
+ }
1794
+ if (navigationResult.targetIndex !== null &&
1795
+ navigationResult.targetIndex >= 0 &&
1796
+ navigationResult.targetIndex < flatList.length) {
1797
+ this.focusNodeById(this.getNodeId(flatList[navigationResult.targetIndex].node));
212
1798
  }
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
1799
  }
225
1800
  }
1801
+ // ==================== Private Methods ====================
226
1802
  /**
227
- *
228
- * auto expand
229
- *
1803
+ * Load root items when datasource is a function
230
1804
  */
231
- toggleExpand(item) {
232
- if (!item[this.expandedField()]) {
233
- item[this.expandedField()] = true;
1805
+ async loadRootItems(loadFn) {
1806
+ try {
1807
+ const result = loadFn();
1808
+ const rootNodes = result instanceof Promise ? await result : result;
1809
+ this.nodes.set(rootNodes);
1810
+ }
1811
+ catch (error) {
1812
+ this.handleError('Failed to load root items', error);
1813
+ this.nodes.set([]);
234
1814
  }
235
1815
  }
236
1816
  /**
237
- *
238
- * expand and change value parent change
239
- *
1817
+ * Load children for a node using lazy loading
1818
+ * Only calls callback if:
1819
+ * - children field is undefined or empty
1820
+ * - AND childrenCount > 0
240
1821
  */
241
- async expandAndToggleSelection(item, selected) {
242
- if (this.itemsPromise && item[this.hasChildField()] && !item?.[this.childrenField()]?.length) {
243
- await this.setNodeLoading(item[this.valueField()], true);
244
- await this.fetchData(item);
1822
+ async loadNodeChildren(node) {
1823
+ const nodeId = this.getNodeId(node);
1824
+ if (!this.isLazyDataSource() || this.loadingNodes().has(nodeId))
1825
+ return;
1826
+ // If children are already loaded, don't call callback
1827
+ const existingChildren = this.getNodeChildren(node);
1828
+ if (existingChildren && existingChildren.length > 0) {
1829
+ return;
245
1830
  }
246
- this.toggleExpand(item);
247
- if (item[this.childrenField()]?.length) {
248
- this.applySelectionToChildren(item, selected, item[this.valueField()]);
1831
+ // Only call callback if childrenCount > 0
1832
+ const childrenCount = this.getNodeChildrenCount(node);
1833
+ if (!childrenCount || childrenCount <= 0) {
1834
+ return;
1835
+ }
1836
+ const ds = this.datasource();
1837
+ if (typeof ds !== 'function')
1838
+ return;
1839
+ try {
1840
+ this.loadingNodes.update((set) => new Set(set).add(nodeId));
1841
+ this.refreshNodes();
1842
+ const result = ds(nodeId);
1843
+ const children = result instanceof Promise ? await result : result;
1844
+ this.setNodeChildren(node, children);
1845
+ this.setNodeChildrenCount(node, children.length);
1846
+ }
1847
+ catch (error) {
1848
+ this.handleError('Failed to load children', error);
1849
+ this.setNodeChildrenCount(node, 0);
1850
+ }
1851
+ finally {
1852
+ this.loadingNodes.update((set) => {
1853
+ const newSet = new Set(set);
1854
+ newSet.delete(nodeId);
1855
+ return newSet;
1856
+ });
1857
+ this.refreshNodes();
249
1858
  }
250
- }
251
- applySelectionToChildren(item, isSelected, parentId) {
252
- item[this.childrenField()].forEach(async (child) => {
253
- if (this.itemsPromise &&
254
- child[this.hasChildField()] &&
255
- !child?.[this.childrenField()]?.length &&
256
- this.selectionBehavior() === 'cascade') {
257
- await this.setNodeLoading(child[this.valueField()], true);
258
- await this.fetchData(child);
259
- this.toggleExpand(child);
260
- }
261
- child[this.parentField()] = parentId;
262
- child[this.selectedField()] = isSelected;
263
- if (child[this.childrenField()]?.length) {
264
- this.applySelectionToChildren(child, isSelected, child[this.valueField()]);
265
- }
266
- });
267
1859
  }
268
1860
  /**
269
- *
270
- * indeterminate logic
271
- *
1861
+ * Internal method to refresh nodes signal and sync back to datasource if it's an array
1862
+ * Creates new array references for all nested children to ensure reactivity
272
1863
  */
273
- updateParentSelection(item, selected) {
274
- item[this.selectedField()] = selected;
275
- let parent = this.findParent(item, this.itemsSignal());
276
- while ((parent && parent[this.selectedField()] != false) || (parent && item[this.selectedField()])) {
277
- const allSelected = parent?.[this.childrenField()]?.every((child) => child[this.selectedField()]);
278
- const someSelected = parent?.[this.childrenField()]?.some((child) => child[this.selectedField()] || child[this.indeterminateField()]);
279
- if (!allSelected && !someSelected) {
280
- parent[this.selectedField()] = false;
281
- parent[this.indeterminateField()] = null;
282
- }
283
- else if (!allSelected) {
284
- parent[this.indeterminateField()] = true;
285
- parent[this.selectedField()] = null;
286
- }
287
- else if (allSelected) {
288
- parent[this.selectedField()] = true;
289
- parent[this.indeterminateField()] = false;
290
- }
291
- else {
292
- parent[this.indeterminateField()] = false;
293
- parent[this.selectedField()] = true;
294
- }
295
- parent = this.findParent(parent, this.itemsSignal());
1864
+ refreshNodes() {
1865
+ const currentNodes = this.nodes();
1866
+ // Create new array references for all nested children to ensure reactivity
1867
+ // This ensures Angular's change detection picks up changes even with callback datasource
1868
+ this.ensureNewArrayReferences(currentNodes);
1869
+ this.nodes.set([...currentNodes]);
1870
+ if (!this.isLazyDataSource() && !this.isUpdatingFromDatasource) {
1871
+ this.isUpdatingFromDatasource = true;
1872
+ this.datasource.set([...currentNodes]);
1873
+ setTimeout(() => {
1874
+ this.isUpdatingFromDatasource = false;
1875
+ }, 0);
296
1876
  }
297
1877
  }
298
- findParent(item, nodes) {
1878
+ /**
1879
+ * Recursively ensure all children arrays have new references to trigger change detection
1880
+ * Mutates the tree structure by replacing children arrays with new array references
1881
+ */
1882
+ ensureNewArrayReferences(nodes) {
299
1883
  for (const node of nodes) {
300
- if (node[this.childrenField()]?.includes(item)) {
301
- return node;
302
- }
303
- else if (node[this.childrenField()]) {
304
- const parent = this.findParent(item, node[this.childrenField()]);
305
- if (parent)
306
- return parent;
1884
+ const children = this.getNodeChildren(node);
1885
+ if (children && children.length > 0) {
1886
+ // Create new array reference for children
1887
+ this.setNodeChildren(node, [...children]);
1888
+ // Recursively process nested children
1889
+ this.ensureNewArrayReferences(children);
307
1890
  }
308
1891
  }
309
- return null;
310
1892
  }
311
1893
  /**
312
- *
313
- * find node selected true for emit Selections
314
- *
1894
+ * Handle single selection mode
315
1895
  */
316
- findSelectedNodes(nodes) {
317
- let selectedNodes = [];
318
- nodes.forEach((node) => {
319
- if (node[this.selectedField()]) {
320
- selectedNodes.push(node);
321
- }
322
- if (node[this.childrenField()]) {
323
- selectedNodes = selectedNodes.concat(this.findSelectedNodes(node[this.childrenField()]));
324
- }
1896
+ handleSingleSelection(node, event) {
1897
+ this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
1898
+ this.setNodeSelected(node, true);
1899
+ this.setNodeIndeterminate(node, false);
1900
+ this.refreshNodes();
1901
+ this.onNodeSelect.emit({
1902
+ component: this,
1903
+ node,
1904
+ nativeEvent: event,
1905
+ isUserInteraction: true,
325
1906
  });
326
- return selectedNodes;
1907
+ this.emitSelectionChange();
327
1908
  }
328
1909
  /**
329
- *
330
- * find for emit Selections single mode
331
- *
1910
+ * Handle multiple selection mode (used internally, but in multiple mode selection is primarily handled by checkboxes)
332
1911
  */
333
- handleUnSelectNode(items) {
334
- items.forEach((child) => {
335
- child[this.selectedField()] = false;
336
- if (child?.[this.childrenField()]?.length) {
337
- this.handleUnSelectNode(child[this.childrenField()]);
338
- }
1912
+ handleMultipleSelection(node, event) {
1913
+ const newValue = !this.getNodeSelected(node);
1914
+ this.setNodeSelected(node, newValue);
1915
+ this.setNodeIndeterminate(node, false);
1916
+ const children = this.getNodeChildren(node);
1917
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
1918
+ this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
1919
+ }
1920
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
1921
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
1922
+ }
1923
+ this.refreshNodes();
1924
+ this.onNodeSelect.emit({
1925
+ component: this,
1926
+ node,
1927
+ nativeEvent: event,
1928
+ isUserInteraction: true,
339
1929
  });
1930
+ this.emitSelectionChange();
340
1931
  }
341
1932
  /**
342
- *
343
- * lazy load logic
344
- *
1933
+ * Get array reference by drop list ID
345
1934
  */
346
- fetchData(selectedNode) {
347
- this.itemsPromise(selectedNode?.[this.valueField()])
348
- .then((data) => {
349
- if (Array.isArray(data)) {
350
- if (selectedNode?.[this.valueField()]) {
351
- this.findNode(selectedNode[this.valueField()], data, this.itemsSignal());
352
- }
353
- else {
354
- const isNodeExpanded = data.filter((child) => child[this.expandedField()]);
355
- isNodeExpanded.forEach((child) => {
356
- this.fetchData(child);
357
- });
358
- this.itemsSignal.set(data);
359
- }
360
- }
361
- })
362
- .finally(() => {
363
- this.setNodeLoading(selectedNode?.[this.valueField()], false);
364
- });
1935
+ getArrayByListId(listId) {
1936
+ return this.treeService.getArrayByListId(this.nodes(), listId, this.idField(), this.childrenField());
365
1937
  }
366
- findNode(parentId, _children, source) {
367
- if (source.length) {
368
- source.forEach((element) => {
369
- if (element[this.valueField()] == parentId) {
370
- if (this.selectionBehavior() === 'indeterminate' && element[this.selectedField()]) {
371
- _children.forEach((child) => (child[this.selectedField()] = true));
372
- }
373
- element[this.childrenField()] = _children;
374
- }
375
- else {
376
- if (element?.[this.childrenField()])
377
- this.findNode(parentId, _children, element[this.childrenField()]);
378
- }
379
- });
1938
+ /**
1939
+ * Find parent node by list ID
1940
+ */
1941
+ findParentByListId(listId) {
1942
+ return this.treeService.findParentByListId(this.nodes(), listId, this.idField(), this.childrenField());
1943
+ }
1944
+ /**
1945
+ * Check if move operation is allowed based on dragBehavior
1946
+ */
1947
+ canMoveToParent() {
1948
+ const behavior = this.dragBehavior();
1949
+ return behavior !== 'none' && behavior !== 'order-only';
1950
+ }
1951
+ /**
1952
+ * Check if reorder operation is allowed based on dragBehavior
1953
+ */
1954
+ canReorder() {
1955
+ const behavior = this.dragBehavior();
1956
+ return behavior !== 'none' && behavior !== 'move';
1957
+ }
1958
+ /**
1959
+ * Handle reordering within the same list */
1960
+ handleReorder(event, targetArray, parentNode) {
1961
+ if (!this.canReorder())
1962
+ return;
1963
+ const movedNode = targetArray[event.previousIndex];
1964
+ moveItemInArray(targetArray, event.previousIndex, event.currentIndex);
1965
+ this.emitDropEvents(movedNode, parentNode, parentNode, event.previousIndex, event.currentIndex, true);
1966
+ }
1967
+ /**
1968
+ * Handle moving between different lists
1969
+ */
1970
+ handleMove(event, targetArray, parentNode) {
1971
+ if (!this.canMoveToParent())
1972
+ return;
1973
+ const sourceListId = event.previousContainer.element.id;
1974
+ const sourceArray = this.getArrayByListId(sourceListId);
1975
+ if (!sourceArray)
1976
+ return;
1977
+ const movedNode = sourceArray[event.previousIndex];
1978
+ if (parentNode && !this.treeService.isValidDropTarget(movedNode, parentNode))
1979
+ return;
1980
+ if (!this.emitBeforeDropEvent(movedNode, sourceListId, parentNode, event.previousIndex, event.currentIndex)) {
1981
+ return;
380
1982
  }
1983
+ transferArrayItem(sourceArray, targetArray, event.previousIndex, event.currentIndex);
1984
+ this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), parentNode, event.previousIndex, event.currentIndex, false);
1985
+ }
1986
+ /**
1987
+ * Emit beforeDrop event and return whether to continue
1988
+ */
1989
+ emitBeforeDropEvent(movedNode, sourceListId, currentParent, previousIndex, currentIndex) {
1990
+ const beforeDropEvent = {
1991
+ component: this,
1992
+ movedNode,
1993
+ previousParent: this.findParentByListId(sourceListId),
1994
+ currentParent,
1995
+ previousIndex,
1996
+ currentIndex,
1997
+ canceled: false,
1998
+ };
1999
+ this.onBeforeDrop.emit(beforeDropEvent);
2000
+ return !beforeDropEvent.canceled;
381
2001
  }
382
2002
  /**
383
- *
384
- * emit when arrow click
385
- *
2003
+ * Emit drop events based on operation type
386
2004
  */
387
- handleNodeExpandClick(node) {
388
- const selectedNode = node.data;
389
- if (this.itemsPromise && node.data[this.expandedField()] && !node?.data[this.childrenField()]?.length) {
390
- this.setNodeLoading(selectedNode[this.valueField()], true);
391
- this.fetchData(selectedNode);
2005
+ emitDropEvents(node, previousParent, currentParent, previousIndex, currentIndex, isReorder) {
2006
+ const dropEvent = {
2007
+ component: this,
2008
+ node,
2009
+ previousParent,
2010
+ currentParent,
2011
+ previousIndex,
2012
+ currentIndex,
2013
+ };
2014
+ if (isReorder) {
2015
+ this.onOrderChange.emit(dropEvent);
2016
+ }
2017
+ else {
2018
+ this.onMoveChange.emit(dropEvent);
392
2019
  }
393
- this.onCollapsedChanged.emit({ component: this, data: node.data, nativeElement: this.nativeElement });
2020
+ this.onItemsChange.emit(dropEvent);
2021
+ }
2022
+ /**
2023
+ * Get the currently focused node
2024
+ */
2025
+ getFocusedNode() {
2026
+ const focusedId = this.focusedNodeId();
2027
+ if (!focusedId)
2028
+ return null;
2029
+ return this.treeService.findNodeById(this.nodes(), focusedId, this.idField(), this.childrenField());
394
2030
  }
395
- handleUnActiveNode(unActiveSource) {
396
- unActiveSource.forEach((child) => {
397
- child[this.activeField()] = false;
398
- if (child?.[this.childrenField()]?.length) {
399
- this.handleUnActiveNode(child[this.childrenField()]);
2031
+ /**
2032
+ * Set focus to a node by ID
2033
+ */
2034
+ focusNodeById(nodeId) {
2035
+ this.focusedNodeId.set(nodeId);
2036
+ setTimeout(() => {
2037
+ const element = document.querySelector(`[data-tree-node-id="${nodeId}"]`);
2038
+ if (element) {
2039
+ element.focus();
2040
+ element.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
400
2041
  }
401
- });
2042
+ }, 0);
402
2043
  }
403
- isNodeLoading(nodeId) {
404
- return this.loadingState()[nodeId] || false;
405
- }
406
- setNodeLoading(nodeId, isLoading) {
407
- this.loadingState.update((state) => ({
408
- ...state,
409
- [nodeId]: isLoading,
410
- }));
411
- }
412
- executeOnTreeNode(node, operation, value) {
413
- switch (operation) {
414
- case 'active':
415
- node[this.activeField()] = value;
2044
+ /**
2045
+ * Handle keyboard navigation keys
2046
+ */
2047
+ handleNavigationKey(event, flatList, currentIndex, currentFocused) {
2048
+ let targetIndex = currentIndex;
2049
+ let shouldPreventDefault = true;
2050
+ let handled = true;
2051
+ switch (event.key) {
2052
+ case 'ArrowUp':
2053
+ if (currentIndex > 0) {
2054
+ targetIndex = currentIndex - 1;
2055
+ }
2056
+ else {
2057
+ shouldPreventDefault = false;
2058
+ }
2059
+ break;
2060
+ case 'ArrowDown':
2061
+ if (currentIndex < flatList.length - 1) {
2062
+ targetIndex = currentIndex + 1;
2063
+ }
2064
+ else if (currentIndex === -1) {
2065
+ targetIndex = 0;
2066
+ }
2067
+ else {
2068
+ shouldPreventDefault = false;
2069
+ }
2070
+ break;
2071
+ case 'ArrowLeft':
2072
+ if (currentFocused) {
2073
+ if (this.getNodeExpanded(currentFocused) && this.shouldShowExpandToggle(currentFocused)) {
2074
+ this.toggleNode(currentFocused, event);
2075
+ return { handled: true, shouldPreventDefault: true, targetIndex: null };
2076
+ }
2077
+ else {
2078
+ const currentItem = flatList[currentIndex];
2079
+ if (currentItem?.parent) {
2080
+ targetIndex = flatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(currentItem.parent));
2081
+ }
2082
+ else {
2083
+ shouldPreventDefault = false;
2084
+ }
2085
+ }
2086
+ }
2087
+ else {
2088
+ shouldPreventDefault = false;
2089
+ }
416
2090
  break;
417
- case 'expand':
418
- node[this.expandedField()] = true;
419
- if (this.itemsPromise && node[this.hasChildField()] && !node[this.childrenField()]?.length) {
420
- this.handleNodeExpandClick({ data: node, nativeElement: this.nativeElement, component: this });
2091
+ case 'ArrowRight':
2092
+ if (currentFocused) {
2093
+ if (!this.getNodeExpanded(currentFocused) && this.shouldShowExpandToggle(currentFocused)) {
2094
+ this.toggleNode(currentFocused, event);
2095
+ return { handled: true, shouldPreventDefault: true, targetIndex: null };
2096
+ }
2097
+ else if (this.getNodeExpanded(currentFocused) &&
2098
+ this.treeService.hasChildren(currentFocused, this.childrenField())) {
2099
+ const children = this.getNodeChildren(currentFocused);
2100
+ if (children && children.length > 0) {
2101
+ const firstChild = children[0];
2102
+ targetIndex = flatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(firstChild));
2103
+ if (targetIndex === -1) {
2104
+ const updatedFlatList = this.treeService.buildFlatNodeList(this.nodes(), this.hiddenField(), this.disabledField(), this.expandedField(), this.childrenField());
2105
+ targetIndex = updatedFlatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(firstChild));
2106
+ if (targetIndex >= 0 && targetIndex < updatedFlatList.length) {
2107
+ this.focusNodeById(this.getNodeId(updatedFlatList[targetIndex].node));
2108
+ return { handled: true, shouldPreventDefault: true, targetIndex: null };
2109
+ }
2110
+ }
2111
+ }
2112
+ else {
2113
+ shouldPreventDefault = false;
2114
+ }
2115
+ }
2116
+ else {
2117
+ shouldPreventDefault = false;
2118
+ }
2119
+ }
2120
+ else {
2121
+ shouldPreventDefault = false;
421
2122
  }
422
2123
  break;
423
- case 'visible':
424
- node[this.visibleField()] = value;
2124
+ case 'Home':
2125
+ targetIndex = 0;
2126
+ break;
2127
+ case 'End':
2128
+ targetIndex = flatList.length - 1;
425
2129
  break;
426
- case 'disabled':
427
- node[this.disableField()] = value;
2130
+ case ' ':
2131
+ case 'Space':
2132
+ if (currentFocused && this.selectMode() !== 'none' && this.canSelectNode(currentFocused)) {
2133
+ event.preventDefault();
2134
+ this.handleSpaceKeySelection(currentFocused, event);
2135
+ return { handled: true, shouldPreventDefault: true, targetIndex: null };
2136
+ }
2137
+ shouldPreventDefault = false;
2138
+ break;
2139
+ case 'Enter':
2140
+ if (currentFocused && this.canSelectNode(currentFocused)) {
2141
+ event.preventDefault();
2142
+ this.handleEnterKeySelection(currentFocused, event);
2143
+ return { handled: true, shouldPreventDefault: true, targetIndex: null };
2144
+ }
2145
+ shouldPreventDefault = false;
428
2146
  break;
429
2147
  default:
2148
+ if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
2149
+ if (currentFocused && this.selectMode() === 'multiple' && this.canSelectNode(currentFocused)) {
2150
+ event.preventDefault();
2151
+ this.handleCtrlEnterSelection(currentFocused, event);
2152
+ return { handled: true, shouldPreventDefault: true, targetIndex: null };
2153
+ }
2154
+ }
2155
+ handled = false;
2156
+ shouldPreventDefault = false;
430
2157
  break;
431
2158
  }
432
- /**
433
- *
434
- * for detect changes treeviewitem
435
- *
436
- */
437
- this.executorChanges.set(operation);
2159
+ return { handled, shouldPreventDefault, targetIndex };
438
2160
  }
439
- refresh() {
440
- if (this.itemsPromise) {
441
- this.fetchData();
2161
+ /**
2162
+ * Handle Space key selection
2163
+ * In single mode: replaces selection (deselects all, selects this node)
2164
+ * In multiple mode: toggles selection
2165
+ */
2166
+ handleSpaceKeySelection(node, event) {
2167
+ if (!this.canSelectNode(node))
2168
+ return;
2169
+ const mode = this.selectMode();
2170
+ if (mode === 'single') {
2171
+ // Single mode: replace selection
2172
+ this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
2173
+ this.setNodeSelected(node, true);
2174
+ this.setNodeIndeterminate(node, false);
442
2175
  }
443
- }
444
- async setNodeExpandAndChildren(valueFields, value) {
445
- const nodesToExpand = [];
446
- for (const valueField of valueFields) {
447
- const foundNodes = this.findNodesByValueField(this.itemsSignal(), valueField);
448
- nodesToExpand.push(...foundNodes);
2176
+ else {
2177
+ // Multiple mode: toggle selection
2178
+ const newValue = !this.getNodeSelected(node);
2179
+ this.setNodeSelected(node, newValue);
2180
+ this.setNodeIndeterminate(node, false);
2181
+ const children = this.getNodeChildren(node);
2182
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
2183
+ this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
2184
+ }
2185
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
2186
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
2187
+ }
449
2188
  }
450
- await Promise.all(nodesToExpand.map((node) => this.expandNodeAndAllChildren(node, value)));
2189
+ this.refreshNodes();
2190
+ this.onNodeSelect.emit({
2191
+ component: this,
2192
+ node,
2193
+ nativeEvent: event,
2194
+ isUserInteraction: true,
2195
+ });
2196
+ this.emitSelectionChange();
451
2197
  }
452
- findNodesByValueField(nodes, valueField) {
453
- const results = [];
454
- for (const node of nodes) {
455
- if (node[this.valueField()] === valueField) {
456
- results.push(node);
2198
+ /**
2199
+ * Handle Enter key selection
2200
+ * In single mode: replaces selection (deselects all, selects this node)
2201
+ * In multiple mode: replaces selection (deselects all, selects this node and respects selectionBehavior)
2202
+ */
2203
+ handleEnterKeySelection(node, event) {
2204
+ if (!this.canSelectNode(node))
2205
+ return;
2206
+ const mode = this.selectMode();
2207
+ // Enter key always replaces selection (deselects all first)
2208
+ this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
2209
+ this.setNodeSelected(node, true);
2210
+ this.setNodeIndeterminate(node, false);
2211
+ if (mode === 'multiple') {
2212
+ const children = this.getNodeChildren(node);
2213
+ // Respect selectionBehavior: cascade to children if enabled
2214
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
2215
+ this.treeService.selectAllChildren(children, true, this.selectedField(), this.indeterminateField(), this.childrenField());
457
2216
  }
458
- if (node[this.childrenField()]?.length) {
459
- results.push(...this.findNodesByValueField(node[this.childrenField()], valueField));
2217
+ // Respect selectionBehavior: update parent states if intermediate mode
2218
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
2219
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
460
2220
  }
461
2221
  }
462
- return results;
2222
+ this.refreshNodes();
2223
+ this.onNodeSelect.emit({
2224
+ component: this,
2225
+ node,
2226
+ nativeEvent: event,
2227
+ isUserInteraction: true,
2228
+ });
2229
+ this.emitSelectionChange();
2230
+ }
2231
+ /**
2232
+ * Handle Ctrl/Cmd + Enter key selection
2233
+ */
2234
+ handleCtrlEnterSelection(node, event) {
2235
+ if (!this.canSelectNode(node))
2236
+ return;
2237
+ const newValue = !this.getNodeSelected(node);
2238
+ this.setNodeSelected(node, newValue);
2239
+ this.setNodeIndeterminate(node, false);
2240
+ const children = this.getNodeChildren(node);
2241
+ if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
2242
+ this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
2243
+ }
2244
+ if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
2245
+ this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
2246
+ }
2247
+ this.refreshNodes();
2248
+ this.onNodeSelect.emit({
2249
+ component: this,
2250
+ node,
2251
+ nativeEvent: event,
2252
+ isUserInteraction: true,
2253
+ });
2254
+ this.emitSelectionChange();
2255
+ }
2256
+ /**
2257
+ * Type guard to check if value is an Event
2258
+ */
2259
+ isEvent(value) {
2260
+ return value instanceof Event;
463
2261
  }
464
- async expandNodeAndAllChildren(node, value) {
465
- node[this.expandedField()] = value;
466
- if (value && this.itemsPromise && node[this.hasChildField()] && !node[this.childrenField()]?.length) {
467
- await this.setNodeLoading(node[this.valueField()], true);
468
- await this.fetchData(node);
2262
+ /**
2263
+ * Handle errors consistently
2264
+ */
2265
+ handleError(message, error) {
2266
+ if (error instanceof Error) {
2267
+ console.error(`${message}:`, error.message);
469
2268
  }
470
- if (node[this.childrenField()]?.length) {
471
- await Promise.all(node[this.childrenField()].map((child) => this.expandNodeAndAllChildren(child, value)));
2269
+ else {
2270
+ console.error(`${message}:`, error);
472
2271
  }
473
2272
  }
474
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
475
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AXTreeViewComponent, isStandalone: true, selector: "ax-tree-view", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, hasCheckboxField: { classPropertyName: "hasCheckboxField", publicName: "hasCheckboxField", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectionBehavior: { classPropertyName: "selectionBehavior", publicName: "selectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, selectionScope: { classPropertyName: "selectionScope", publicName: "selectionScope", isSignal: true, isRequired: false, transformFunction: null }, focusNodeEnabled: { classPropertyName: "focusNodeEnabled", publicName: "focusNodeEnabled", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, visibleField: { classPropertyName: "visibleField", publicName: "visibleField", isSignal: true, isRequired: false, transformFunction: null }, disableField: { classPropertyName: "disableField", publicName: "disableField", isSignal: true, isRequired: false, transformFunction: null }, hasChildField: { classPropertyName: "hasChildField", publicName: "hasChildField", isSignal: true, isRequired: false, transformFunction: null }, selectedField: { classPropertyName: "selectedField", publicName: "selectedField", isSignal: true, isRequired: false, transformFunction: null }, expandedField: { classPropertyName: "expandedField", publicName: "expandedField", isSignal: true, isRequired: false, transformFunction: null }, tooltipField: { classPropertyName: "tooltipField", publicName: "tooltipField", isSignal: true, isRequired: false, transformFunction: null }, childrenField: { classPropertyName: "childrenField", publicName: "childrenField", isSignal: true, isRequired: false, transformFunction: null }, activeField: { classPropertyName: "activeField", publicName: "activeField", isSignal: true, isRequired: false, transformFunction: null }, indeterminateField: { classPropertyName: "indeterminateField", publicName: "indeterminateField", isSignal: true, isRequired: false, transformFunction: null }, parentField: { classPropertyName: "parentField", publicName: "parentField", isSignal: true, isRequired: false, transformFunction: null }, iconField: { classPropertyName: "iconField", publicName: "iconField", isSignal: true, isRequired: false, transformFunction: null }, toggleIcons: { classPropertyName: "toggleIcons", publicName: "toggleIcons", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, showEmptyNodeMassage: { classPropertyName: "showEmptyNodeMassage", publicName: "showEmptyNodeMassage", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: false, isRequired: false, transformFunction: null }, emptyTemplate: { classPropertyName: "emptyTemplate", publicName: "emptyTemplate", isSignal: false, isRequired: false, transformFunction: null }, expandOn: { classPropertyName: "expandOn", publicName: "expandOn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelectionChanged: "onSelectionChanged", onItemSelectedChanged: "onItemSelectedChanged", onNodeClick: "onNodeClick", onCollapsedChanged: "onCollapsedChanged", onNodedbClick: "onNodedbClick" }, host: { properties: { "class": "this.__hostClass" } }, providers: [
476
- { provide: AXComponent, useExisting: AXTreeViewComponent },
477
- { provide: AXTreeViewBase, useExisting: AXTreeViewComponent },
478
- ], usesInheritance: true, ngImport: i0, template: "@if (resolvedItems?.length) {\n @for (node of resolvedItems; track $index) {\n <ng-container [ngTemplateOutlet]=\"recursion\" [ngTemplateOutletContext]=\"{ $implicit: node }\"></ng-container>\n }\n} @else {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate || empty\"></ng-container>\n}\n\n<ng-template #recursion let-item>\n @if (item[visibleField()] !== false) {\n <ax-tree-view-item\n [item]=\"item\"\n [isExpanded]=\"item[expandedField()]\"\n [(isActive)]=\"item[activeField()]\"\n [isLoading]=\"isNodeLoading(item[valueField()])\"\n [executorChanges]=\"executorChanges()\"\n >\n @if (\n (showCheckbox() && selectionScope() === 'all' && item[hasCheckboxField()] !== false) ||\n (showCheckbox() &&\n selectionScope() === 'parent' &&\n item[childrenField()]?.length &&\n item[hasCheckboxField()]) !== false ||\n (showCheckbox() &&\n selectionScope() === 'children' &&\n !item[childrenField()]?.length &&\n item[hasCheckboxField()] !== false)\n ) {\n <ax-check-box\n [disabled]=\"item[disableField()]\"\n [indeterminate]=\"item[indeterminateField()]\"\n [(ngModel)]=\"item[selectedField()]\"\n (onValueChanged)=\"handleNodeSelectionClick($event, item)\"\n ></ax-check-box>\n }\n @if (item[iconField()]) {\n <ax-prefix>\n <ax-icon [icon]=\"item[iconField()]\"></ax-icon>\n </ax-prefix>\n }\n @if (item[textField()]) {\n <ax-text>{{ item[textField()] }}</ax-text>\n }\n\n @for (child of item?.[childrenField()]; track $index) {\n <ng-container [ngTemplateOutlet]=\"recursion\" [ngTemplateOutletContext]=\"{ $implicit: child }\"></ng-container>\n }\n </ax-tree-view-item>\n }\n</ng-template>\n\n<ng-template #empty>\n {{ '@acorex:common.general.no-result-found' | translate | async }}\n</ng-template>\n", styles: ["ax-tree-view{--ax-comp-tree-view-arrow-size: .875rem;--ax-comp-tree-view-text-size: .875rem;--ax-comp-tree-view-active-bg-color: var(--ax-sys-color-primary-surface);--ax-comp-tree-view-active-text-color: var(--ax-sys-color-on-primary-surface);--ax-comp-tree-view-hover-bg-color: var(--ax-sys-color-dark-surface);--ax-comp-tree-view-indicator-size: 2px}ax-tree-view:has(>ax-tree-view-item i) ax-tree-view-item .ax-tree-view-container:not(:has(i)){padding-inline-start:1.5rem}ax-tree-view.ax-look-with-line ax-tree-view-item{position:relative}ax-tree-view.ax-look-with-line ax-tree-view-item:before{content:\"\";position:absolute;top:0;inset-inline-start:.625rem;width:1px;height:100%;background-color:#ccc}ax-tree-view.ax-look-with-line ax-tree-view-item .ax-tree-view-container{padding-inline-start:1.25rem}ax-tree-view.ax-look-with-line ax-tree-view-item .ax-tree-view-container .ax-tree-view-icon-container{padding:.25rem}ax-tree-view ax-tree-view-item ax-check-box{margin-inline-start:.5rem}ax-tree-view ax-tree-view-item .ax-tree-view-container{display:flex;align-items:center;margin-bottom:.125rem}ax-tree-view ax-tree-view-item .ax-tree-view-container ax-text{font-size:var(--ax-comp-tree-view-text-size)}ax-tree-view ax-tree-view-item .ax-tree-view-container{cursor:pointer}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-checkbox-end-side{display:none!important}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-arrow{font-size:var(--ax-comp-tree-view-arrow-size)!important}ax-tree-view ax-tree-view-item .ax-tree-view-container ax-suffix:empty{display:none}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-icon-container{width:1.5rem;height:1.5rem;display:flex;align-items:center;justify-content:center}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items{display:flex;align-items:center}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix{display:flex;align-items:center;gap:.5rem;padding:.25rem .5rem;border-radius:.25rem;overflow-x:auto;margin-inline-start:.25rem}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix.ax-noselect-tree-view{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix.ax-state-tree-view-active{background-color:rgba(var(--ax-comp-tree-view-active-bg-color));color:rgba(var(--ax-comp-tree-view-active-text-color))}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix:hover:not(.ax-state-tree-view-active){background-color:rgba(var(--ax-comp-tree-view-hover-bg-color))}ax-tree-view ax-tree-view-item .ax-tree-view-child{padding-inline-start:1rem}ax-tree-view ax-tree-view-item .ax-tree-view-child .ax-tree-view-container:not(:has(.ax-tree-view-icon-container i)){padding-inline-start:1.5rem}ax-tree-view ax-tree-view-item .ax-tree-view-child.ax-tree-view-empty-child{padding-inline-start:2rem}ax-tree-view ax-tree-view-item .ax-state-disabled{cursor:not-allowed!important;opacity:.5!important}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXTreeViewItemComponent, selector: "ax-tree-view-item", inputs: ["item", "isExpanded", "isActive", "isLoading", "executorChanges"], outputs: ["isExpandedChange", "isActiveChange"] }, { kind: "component", type: AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2273
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2274
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", 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 }, selectionBehavior: { classPropertyName: "selectionBehavior", publicName: "selectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, dragArea: { classPropertyName: "dragArea", publicName: "dragArea", isSignal: true, isRequired: false, transformFunction: null }, dragBehavior: { classPropertyName: "dragBehavior", publicName: "dragBehavior", 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 }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, idField: { classPropertyName: "idField", publicName: "idField", isSignal: true, isRequired: false, transformFunction: null }, titleField: { classPropertyName: "titleField", publicName: "titleField", isSignal: true, isRequired: false, transformFunction: null }, tooltipField: { classPropertyName: "tooltipField", publicName: "tooltipField", isSignal: true, isRequired: false, transformFunction: null }, iconField: { classPropertyName: "iconField", publicName: "iconField", isSignal: true, isRequired: false, transformFunction: null }, expandedField: { classPropertyName: "expandedField", publicName: "expandedField", isSignal: true, isRequired: false, transformFunction: null }, selectedField: { classPropertyName: "selectedField", publicName: "selectedField", isSignal: true, isRequired: false, transformFunction: null }, indeterminateField: { classPropertyName: "indeterminateField", publicName: "indeterminateField", isSignal: true, isRequired: false, transformFunction: null }, disabledField: { classPropertyName: "disabledField", publicName: "disabledField", isSignal: true, isRequired: false, transformFunction: null }, hiddenField: { classPropertyName: "hiddenField", publicName: "hiddenField", isSignal: true, isRequired: false, transformFunction: null }, childrenField: { classPropertyName: "childrenField", publicName: "childrenField", isSignal: true, isRequired: false, transformFunction: null }, childrenCountField: { classPropertyName: "childrenCountField", publicName: "childrenCountField", isSignal: true, isRequired: false, transformFunction: null }, dataField: { classPropertyName: "dataField", publicName: "dataField", isSignal: true, isRequired: false, transformFunction: null }, inheritDisabled: { classPropertyName: "inheritDisabled", publicName: "inheritDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { datasource: "datasourceChange", onBeforeDrop: "onBeforeDrop", onNodeToggle: "onNodeToggle", onNodeSelect: "onNodeSelect", onNodeDoubleClick: "onNodeDoubleClick", onNodeClick: "onNodeClick", onSelectionChange: "onSelectionChange", 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 loading indicator -->\n@if (isLoading()) {\n <div class=\"ax-tree-view-root-loading\">\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-root-loading-spinner\"></i>\n <span class=\"ax-tree-view-root-loading-text\">{{ '@acorex:treeView.loading' | translate | async }}</span>\n </div>\n}\n\n<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragBehavior() !== '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-loading]=\"isLoading()\"\n role=\"group\"\n>\n @for (node of nodes(); track getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"isNodeEffectivelyDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-active]=\"activeNodeId() === getNodeId(node)\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"isNodeEffectivelyDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(getNodeId(node))\"\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]=\"\n isNodeEffectivelyDisabled(node) || (isLeafOnlyMode() && !isLeafNode(node)) ? 'true' : null\n \"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"onNodeClickHandle(node, $event)\"\n (dblclick)=\"onNodeDoubleClickHandle(node, $event)\"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (shouldShowDragHandle()) {\n <span\n class=\"ax-tree-view-drag-handle\"\n axDragHandle\n title=\"Drag to reorder\"\n (click)=\"$event.stopPropagation()\"\n >\n \u22EE\u22EE\n </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n (click)=\"$event.stopPropagation()\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\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]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (showChildrenBadge() && (getNodeChildrenCount(node) || getNodeChildren(node)?.length)) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node)?.length) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), 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]=\"dragBehavior() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"getNodeId(parent)\"\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 getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"isNodeEffectivelyDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-active]=\"activeNodeId() === getNodeId(node)\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"isNodeEffectivelyDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(getNodeId(node))\"\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]=\"\n isNodeEffectivelyDisabled(node) || (isLeafOnlyMode() && !isLeafNode(node)) ? 'true' : null\n \"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"onNodeClickHandle(node, $event)\"\n (dblclick)=\"onNodeDoubleClickHandle(node, $event)\"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (shouldShowDragHandle()) {\n <span\n class=\"ax-tree-view-drag-handle\"\n axDragHandle\n title=\"Drag to reorder\"\n (click)=\"$event.stopPropagation()\"\n >\n \u22EE\u22EE\n </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n (click)=\"$event.stopPropagation()\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\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]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (showChildrenBadge() && (getNodeChildrenCount(node) || getNodeChildren(node)?.length)) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node)?.length) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), 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-active-bg: rgba(var(--ax-sys-color-on-lighter-surface), .04);--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-badge-padding: .25rem;--ax-comp-tree-view-expand-toggle-padding: .25rem;--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-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);cursor:move}.ax-tree-view-node.ax-tree-view-node-active{background:var(--ax-comp-tree-view-node-active-bg)}.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)}.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-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);padding-inline-start:calc(var(--ax-comp-tree-view-drag-handle-padding) * 2)}.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-node-badge{padding:var(--ax-comp-tree-view-badge-padding);padding-inline-end:calc(var(--ax-comp-tree-view-badge-padding) * 1.5)}.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}.ax-tree-view-node-disabled .ax-tree-view-node-content{pointer-events:none}.ax-tree-view-node-disabled .ax-tree-view-expand-toggle{pointer-events:auto;cursor:pointer}.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)}.ax-tree-view-card .ax-tree-view-node.ax-tree-view-node-selected{border:1px solid rgba(var(--ax-sys-color-border-surface),1)}.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}.ax-tree-view-root-loading{display:flex;align-items:center;justify-content:center;gap:.5rem;padding:1.5rem;color:rgba(var(--ax-sys-color-on-lightest-surface),.6)}.ax-tree-view-root-loading-spinner{font-size:1.25rem;color:rgba(var(--ax-sys-color-primary-500),1)}.ax-tree-view-root-loading-text{font-size:.875rem}.ax-tree-view-drop-list.ax-tree-view-loading{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"] }, { 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" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
479
2275
  }
480
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewComponent, decorators: [{
2276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewComponent, decorators: [{
481
2277
  type: Component,
482
- args: [{ selector: 'ax-tree-view', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2278
+ args: [{ selector: 'ax-tree-view', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [AXTreeViewService], imports: [
2279
+ CommonModule,
2280
+ FormsModule,
2281
+ AXDragDirective,
2282
+ AXDragHandleDirective,
2283
+ AXDropListDirective,
2284
+ AXFocusTrapDirective,
483
2285
  NgTemplateOutlet,
484
- AXTreeViewItemComponent,
2286
+ AXButtonComponent,
485
2287
  AXCheckBoxComponent,
486
- FormsModule,
487
- AXDecoratorGenericComponent,
2288
+ AXBadgeComponent,
488
2289
  AXDecoratorIconComponent,
2290
+ AXTooltipDirective,
489
2291
  AXTranslatorPipe,
490
- AsyncPipe,
491
- ], providers: [
492
- { provide: AXComponent, useExisting: AXTreeViewComponent },
493
- { provide: AXTreeViewBase, useExisting: AXTreeViewComponent },
494
- ], template: "@if (resolvedItems?.length) {\n @for (node of resolvedItems; track $index) {\n <ng-container [ngTemplateOutlet]=\"recursion\" [ngTemplateOutletContext]=\"{ $implicit: node }\"></ng-container>\n }\n} @else {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate || empty\"></ng-container>\n}\n\n<ng-template #recursion let-item>\n @if (item[visibleField()] !== false) {\n <ax-tree-view-item\n [item]=\"item\"\n [isExpanded]=\"item[expandedField()]\"\n [(isActive)]=\"item[activeField()]\"\n [isLoading]=\"isNodeLoading(item[valueField()])\"\n [executorChanges]=\"executorChanges()\"\n >\n @if (\n (showCheckbox() && selectionScope() === 'all' && item[hasCheckboxField()] !== false) ||\n (showCheckbox() &&\n selectionScope() === 'parent' &&\n item[childrenField()]?.length &&\n item[hasCheckboxField()]) !== false ||\n (showCheckbox() &&\n selectionScope() === 'children' &&\n !item[childrenField()]?.length &&\n item[hasCheckboxField()] !== false)\n ) {\n <ax-check-box\n [disabled]=\"item[disableField()]\"\n [indeterminate]=\"item[indeterminateField()]\"\n [(ngModel)]=\"item[selectedField()]\"\n (onValueChanged)=\"handleNodeSelectionClick($event, item)\"\n ></ax-check-box>\n }\n @if (item[iconField()]) {\n <ax-prefix>\n <ax-icon [icon]=\"item[iconField()]\"></ax-icon>\n </ax-prefix>\n }\n @if (item[textField()]) {\n <ax-text>{{ item[textField()] }}</ax-text>\n }\n\n @for (child of item?.[childrenField()]; track $index) {\n <ng-container [ngTemplateOutlet]=\"recursion\" [ngTemplateOutletContext]=\"{ $implicit: child }\"></ng-container>\n }\n </ax-tree-view-item>\n }\n</ng-template>\n\n<ng-template #empty>\n {{ '@acorex:common.general.no-result-found' | translate | async }}\n</ng-template>\n", styles: ["ax-tree-view{--ax-comp-tree-view-arrow-size: .875rem;--ax-comp-tree-view-text-size: .875rem;--ax-comp-tree-view-active-bg-color: var(--ax-sys-color-primary-surface);--ax-comp-tree-view-active-text-color: var(--ax-sys-color-on-primary-surface);--ax-comp-tree-view-hover-bg-color: var(--ax-sys-color-dark-surface);--ax-comp-tree-view-indicator-size: 2px}ax-tree-view:has(>ax-tree-view-item i) ax-tree-view-item .ax-tree-view-container:not(:has(i)){padding-inline-start:1.5rem}ax-tree-view.ax-look-with-line ax-tree-view-item{position:relative}ax-tree-view.ax-look-with-line ax-tree-view-item:before{content:\"\";position:absolute;top:0;inset-inline-start:.625rem;width:1px;height:100%;background-color:#ccc}ax-tree-view.ax-look-with-line ax-tree-view-item .ax-tree-view-container{padding-inline-start:1.25rem}ax-tree-view.ax-look-with-line ax-tree-view-item .ax-tree-view-container .ax-tree-view-icon-container{padding:.25rem}ax-tree-view ax-tree-view-item ax-check-box{margin-inline-start:.5rem}ax-tree-view ax-tree-view-item .ax-tree-view-container{display:flex;align-items:center;margin-bottom:.125rem}ax-tree-view ax-tree-view-item .ax-tree-view-container ax-text{font-size:var(--ax-comp-tree-view-text-size)}ax-tree-view ax-tree-view-item .ax-tree-view-container{cursor:pointer}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-checkbox-end-side{display:none!important}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-arrow{font-size:var(--ax-comp-tree-view-arrow-size)!important}ax-tree-view ax-tree-view-item .ax-tree-view-container ax-suffix:empty{display:none}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-icon-container{width:1.5rem;height:1.5rem;display:flex;align-items:center;justify-content:center}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items{display:flex;align-items:center}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix{display:flex;align-items:center;gap:.5rem;padding:.25rem .5rem;border-radius:.25rem;overflow-x:auto;margin-inline-start:.25rem}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix.ax-noselect-tree-view{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix.ax-state-tree-view-active{background-color:rgba(var(--ax-comp-tree-view-active-bg-color));color:rgba(var(--ax-comp-tree-view-active-text-color))}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix:hover:not(.ax-state-tree-view-active){background-color:rgba(var(--ax-comp-tree-view-hover-bg-color))}ax-tree-view ax-tree-view-item .ax-tree-view-child{padding-inline-start:1rem}ax-tree-view ax-tree-view-item .ax-tree-view-child .ax-tree-view-container:not(:has(.ax-tree-view-icon-container i)){padding-inline-start:1.5rem}ax-tree-view ax-tree-view-item .ax-tree-view-child.ax-tree-view-empty-child{padding-inline-start:2rem}ax-tree-view ax-tree-view-item .ax-state-disabled{cursor:not-allowed!important;opacity:.5!important}\n"] }]
495
- }], propDecorators: { __hostClass: [{
496
- type: HostBinding,
497
- args: ['class']
498
- }], itemTemplate: [{
499
- type: Input
500
- }], emptyTemplate: [{
501
- type: Input
502
- }] } });
2292
+ ], host: {
2293
+ class: 'ax-tree-view',
2294
+ role: 'tree',
2295
+ tabindex: '0',
2296
+ '[class.ax-tree-view-default]': "look() === 'default'",
2297
+ '[class.ax-tree-view-card]': "look() === 'card'",
2298
+ '[class.ax-tree-view-with-line]': "look() === 'with-line'",
2299
+ '[class.ax-tree-view-rtl]': 'isRtl',
2300
+ '[style.--ax-tree-view-indent-size]': "indentSize() + 'px'",
2301
+ '[style.--ax-tree-view-line-offset]': "(indentSize() / 2) + 'px'",
2302
+ '[attr.aria-label]': '"Tree navigation"',
2303
+ '(keydown)': 'handleKeyDown($event)',
2304
+ '(focus)': 'onTreeFocus($event)',
2305
+ '(blur)': 'onTreeBlur($event)',
2306
+ }, template: "<!-- Root loading indicator -->\n@if (isLoading()) {\n <div class=\"ax-tree-view-root-loading\">\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-root-loading-spinner\"></i>\n <span class=\"ax-tree-view-root-loading-text\">{{ '@acorex:treeView.loading' | translate | async }}</span>\n </div>\n}\n\n<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragBehavior() !== '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-loading]=\"isLoading()\"\n role=\"group\"\n>\n @for (node of nodes(); track getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"isNodeEffectivelyDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-active]=\"activeNodeId() === getNodeId(node)\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"isNodeEffectivelyDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(getNodeId(node))\"\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]=\"\n isNodeEffectivelyDisabled(node) || (isLeafOnlyMode() && !isLeafNode(node)) ? 'true' : null\n \"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"onNodeClickHandle(node, $event)\"\n (dblclick)=\"onNodeDoubleClickHandle(node, $event)\"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (shouldShowDragHandle()) {\n <span\n class=\"ax-tree-view-drag-handle\"\n axDragHandle\n title=\"Drag to reorder\"\n (click)=\"$event.stopPropagation()\"\n >\n \u22EE\u22EE\n </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n (click)=\"$event.stopPropagation()\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\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]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (showChildrenBadge() && (getNodeChildrenCount(node) || getNodeChildren(node)?.length)) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node)?.length) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), 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]=\"dragBehavior() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"getNodeId(parent)\"\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 getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"isNodeEffectivelyDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-active]=\"activeNodeId() === getNodeId(node)\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"isNodeEffectivelyDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(getNodeId(node))\"\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]=\"\n isNodeEffectivelyDisabled(node) || (isLeafOnlyMode() && !isLeafNode(node)) ? 'true' : null\n \"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"onNodeClickHandle(node, $event)\"\n (dblclick)=\"onNodeDoubleClickHandle(node, $event)\"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (shouldShowDragHandle()) {\n <span\n class=\"ax-tree-view-drag-handle\"\n axDragHandle\n title=\"Drag to reorder\"\n (click)=\"$event.stopPropagation()\"\n >\n \u22EE\u22EE\n </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n (click)=\"$event.stopPropagation()\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\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]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (showChildrenBadge() && (getNodeChildrenCount(node) || getNodeChildren(node)?.length)) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node)?.length) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), 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-active-bg: rgba(var(--ax-sys-color-on-lighter-surface), .04);--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-badge-padding: .25rem;--ax-comp-tree-view-expand-toggle-padding: .25rem;--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-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);cursor:move}.ax-tree-view-node.ax-tree-view-node-active{background:var(--ax-comp-tree-view-node-active-bg)}.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)}.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-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);padding-inline-start:calc(var(--ax-comp-tree-view-drag-handle-padding) * 2)}.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-node-badge{padding:var(--ax-comp-tree-view-badge-padding);padding-inline-end:calc(var(--ax-comp-tree-view-badge-padding) * 1.5)}.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}.ax-tree-view-node-disabled .ax-tree-view-node-content{pointer-events:none}.ax-tree-view-node-disabled .ax-tree-view-expand-toggle{pointer-events:auto;cursor:pointer}.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)}.ax-tree-view-card .ax-tree-view-node.ax-tree-view-node-selected{border:1px solid rgba(var(--ax-sys-color-border-surface),1)}.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}.ax-tree-view-root-loading{display:flex;align-items:center;justify-content:center;gap:.5rem;padding:1.5rem;color:rgba(var(--ax-sys-color-on-lightest-surface),.6)}.ax-tree-view-root-loading-spinner{font-size:1.25rem;color:rgba(var(--ax-sys-color-primary-500),1)}.ax-tree-view-root-loading-text{font-size:.875rem}.ax-tree-view-drop-list.ax-tree-view-loading{display:none}\n"] }]
2307
+ }], 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 }] }], selectionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionBehavior", required: false }] }], dragArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragArea", required: false }] }], dragBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragBehavior", 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 }] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }], nodeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeTemplate", required: false }] }], idField: [{ type: i0.Input, args: [{ isSignal: true, alias: "idField", required: false }] }], titleField: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleField", required: false }] }], tooltipField: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipField", required: false }] }], iconField: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconField", required: false }] }], expandedField: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedField", required: false }] }], selectedField: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedField", required: false }] }], indeterminateField: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminateField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }], hiddenField: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenField", required: false }] }], childrenField: [{ type: i0.Input, args: [{ isSignal: true, alias: "childrenField", required: false }] }], childrenCountField: [{ type: i0.Input, args: [{ isSignal: true, alias: "childrenCountField", required: false }] }], dataField: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataField", required: false }] }], inheritDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "inheritDisabled", required: false }] }], onBeforeDrop: [{ type: i0.Output, args: ["onBeforeDrop"] }], onNodeToggle: [{ type: i0.Output, args: ["onNodeToggle"] }], onNodeSelect: [{ type: i0.Output, args: ["onNodeSelect"] }], onNodeDoubleClick: [{ type: i0.Output, args: ["onNodeDoubleClick"] }], onNodeClick: [{ type: i0.Output, args: ["onNodeClick"] }], onSelectionChange: [{ type: i0.Output, args: ["onSelectionChange"] }], onOrderChange: [{ type: i0.Output, args: ["onOrderChange"] }], onMoveChange: [{ type: i0.Output, args: ["onMoveChange"] }], onItemsChange: [{ type: i0.Output, args: ["onItemsChange"] }] } });
503
2308
 
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
2309
  class AXTreeViewModule {
516
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
517
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent, AXTreeViewItemComponent, CommonModule,
518
- AXCommonModule,
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] }); }
2310
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2311
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent] }); }
2312
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent] }); }
526
2313
  }
527
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXTreeViewModule, decorators: [{
2314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AXTreeViewModule, decorators: [{
528
2315
  type: NgModule,
529
2316
  args: [{
530
- imports: [...COMPONENT, ...MODULES],
531
- exports: [...COMPONENT],
2317
+ imports: [AXTreeViewComponent],
532
2318
  }]
533
2319
  }] });
534
2320
 
@@ -536,5 +2322,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
536
2322
  * Generated bundle index. Do not edit.
537
2323
  */
538
2324
 
539
- export { AXTreeItemClickBaseEvent, AXTreeViewBase, AXTreeViewComponent, AXTreeViewItemComponent, AXTreeViewModule };
2325
+ export { AXTreeViewComponent, AXTreeViewModule, AXTreeViewService };
540
2326
  //# sourceMappingURL=acorex-components-tree-view.mjs.map