@rt-tools/ui-kit-v2 0.23.3 → 0.24.0

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 (354) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-accordion.mjs +142 -71
  2. package/fesm2022/rt-tools-ui-kit-v2-accordion.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs +467 -231
  4. package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs +260 -91
  6. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-aside-section.mjs +80 -46
  8. package/fesm2022/rt-tools-ui-kit-v2-aside-section.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-aside.mjs +984 -697
  10. package/fesm2022/rt-tools-ui-kit-v2-aside.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +452 -210
  12. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-bar-list.mjs +143 -52
  14. package/fesm2022/rt-tools-ui-kit-v2-bar-list.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-bottom-sheet.mjs +158 -83
  16. package/fesm2022/rt-tools-ui-kit-v2-bottom-sheet.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs +335 -0
  18. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs.map +1 -0
  19. package/fesm2022/rt-tools-ui-kit-v2-button.mjs +437 -363
  20. package/fesm2022/rt-tools-ui-kit-v2-button.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs +430 -149
  22. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -1
  23. package/fesm2022/rt-tools-ui-kit-v2-card.mjs +155 -71
  24. package/fesm2022/rt-tools-ui-kit-v2-card.mjs.map +1 -1
  25. package/fesm2022/rt-tools-ui-kit-v2-carousel.mjs +155 -0
  26. package/fesm2022/rt-tools-ui-kit-v2-carousel.mjs.map +1 -0
  27. package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs +215 -110
  28. package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs.map +1 -1
  29. package/fesm2022/rt-tools-ui-kit-v2-collapsible-text.mjs +158 -75
  30. package/fesm2022/rt-tools-ui-kit-v2-collapsible-text.mjs.map +1 -1
  31. package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs +769 -0
  32. package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs.map +1 -0
  33. package/fesm2022/rt-tools-ui-kit-v2-confirm-popover.mjs +462 -253
  34. package/fesm2022/rt-tools-ui-kit-v2-confirm-popover.mjs.map +1 -1
  35. package/fesm2022/rt-tools-ui-kit-v2-container.mjs +1276 -1316
  36. package/fesm2022/rt-tools-ui-kit-v2-container.mjs.map +1 -1
  37. package/fesm2022/rt-tools-ui-kit-v2-copy-value.mjs +185 -80
  38. package/fesm2022/rt-tools-ui-kit-v2-copy-value.mjs.map +1 -1
  39. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +2321 -2619
  40. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  41. package/fesm2022/rt-tools-ui-kit-v2-counter-row.mjs +100 -44
  42. package/fesm2022/rt-tools-ui-kit-v2-counter-row.mjs.map +1 -1
  43. package/fesm2022/rt-tools-ui-kit-v2-counter.mjs +290 -155
  44. package/fesm2022/rt-tools-ui-kit-v2-counter.mjs.map +1 -1
  45. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +2097 -858
  46. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
  47. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +2247 -1176
  48. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
  49. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +1043 -705
  50. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
  51. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +810 -525
  52. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
  53. package/fesm2022/rt-tools-ui-kit-v2-delta-view.mjs +158 -138
  54. package/fesm2022/rt-tools-ui-kit-v2-delta-view.mjs.map +1 -1
  55. package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs +177 -67
  56. package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs.map +1 -1
  57. package/fesm2022/rt-tools-ui-kit-v2-dialog.mjs +550 -477
  58. package/fesm2022/rt-tools-ui-kit-v2-dialog.mjs.map +1 -1
  59. package/fesm2022/rt-tools-ui-kit-v2-dot-field.mjs +185 -168
  60. package/fesm2022/rt-tools-ui-kit-v2-dot-field.mjs.map +1 -1
  61. package/fesm2022/rt-tools-ui-kit-v2-download-link.mjs +116 -43
  62. package/fesm2022/rt-tools-ui-kit-v2-download-link.mjs.map +1 -1
  63. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +585 -0
  64. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -0
  65. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +755 -259
  66. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -1
  67. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +3266 -1115
  68. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  69. package/fesm2022/rt-tools-ui-kit-v2-empty-state.mjs +190 -55
  70. package/fesm2022/rt-tools-ui-kit-v2-empty-state.mjs.map +1 -1
  71. package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs +283 -103
  72. package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs.map +1 -1
  73. package/fesm2022/rt-tools-ui-kit-v2-field.mjs +393 -177
  74. package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
  75. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs +399 -207
  76. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs.map +1 -1
  77. package/fesm2022/rt-tools-ui-kit-v2-file-drop.mjs +288 -212
  78. package/fesm2022/rt-tools-ui-kit-v2-file-drop.mjs.map +1 -1
  79. package/fesm2022/rt-tools-ui-kit-v2-file-input.mjs +262 -116
  80. package/fesm2022/rt-tools-ui-kit-v2-file-input.mjs.map +1 -1
  81. package/fesm2022/rt-tools-ui-kit-v2-file-list.mjs +57 -31
  82. package/fesm2022/rt-tools-ui-kit-v2-file-list.mjs.map +1 -1
  83. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +258 -90
  84. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -1
  85. package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs +249 -215
  86. package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs.map +1 -1
  87. package/fesm2022/rt-tools-ui-kit-v2-header.mjs +199 -71
  88. package/fesm2022/rt-tools-ui-kit-v2-header.mjs.map +1 -1
  89. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs +231 -0
  90. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs.map +1 -0
  91. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +336 -0
  92. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -0
  93. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs +451 -162
  94. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs.map +1 -1
  95. package/fesm2022/rt-tools-ui-kit-v2-icon.mjs +931 -686
  96. package/fesm2022/rt-tools-ui-kit-v2-icon.mjs.map +1 -1
  97. package/fesm2022/rt-tools-ui-kit-v2-image-cropper.mjs +664 -491
  98. package/fesm2022/rt-tools-ui-kit-v2-image-cropper.mjs.map +1 -1
  99. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs +574 -222
  100. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs.map +1 -1
  101. package/fesm2022/rt-tools-ui-kit-v2-info-item.mjs +150 -36
  102. package/fesm2022/rt-tools-ui-kit-v2-info-item.mjs.map +1 -1
  103. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs +606 -0
  104. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs.map +1 -0
  105. package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +461 -355
  106. package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -1
  107. package/fesm2022/rt-tools-ui-kit-v2-input.mjs +338 -143
  108. package/fesm2022/rt-tools-ui-kit-v2-input.mjs.map +1 -1
  109. package/fesm2022/rt-tools-ui-kit-v2-live-badge.mjs +135 -48
  110. package/fesm2022/rt-tools-ui-kit-v2-live-badge.mjs.map +1 -1
  111. package/fesm2022/rt-tools-ui-kit-v2-logo.mjs +166 -92
  112. package/fesm2022/rt-tools-ui-kit-v2-logo.mjs.map +1 -1
  113. package/fesm2022/rt-tools-ui-kit-v2-markdown-text.mjs +118 -56
  114. package/fesm2022/rt-tools-ui-kit-v2-markdown-text.mjs.map +1 -1
  115. package/fesm2022/rt-tools-ui-kit-v2-menu.mjs +1054 -527
  116. package/fesm2022/rt-tools-ui-kit-v2-menu.mjs.map +1 -1
  117. package/fesm2022/rt-tools-ui-kit-v2-message.mjs +230 -104
  118. package/fesm2022/rt-tools-ui-kit-v2-message.mjs.map +1 -1
  119. package/fesm2022/rt-tools-ui-kit-v2-money-list.mjs +210 -70
  120. package/fesm2022/rt-tools-ui-kit-v2-money-list.mjs.map +1 -1
  121. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +532 -311
  122. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  123. package/fesm2022/rt-tools-ui-kit-v2-night-grid.mjs +125 -50
  124. package/fesm2022/rt-tools-ui-kit-v2-night-grid.mjs.map +1 -1
  125. package/fesm2022/rt-tools-ui-kit-v2-note.mjs +73 -43
  126. package/fesm2022/rt-tools-ui-kit-v2-note.mjs.map +1 -1
  127. package/fesm2022/rt-tools-ui-kit-v2-notifications-bell.mjs +161 -53
  128. package/fesm2022/rt-tools-ui-kit-v2-notifications-bell.mjs.map +1 -1
  129. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +491 -310
  130. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
  131. package/fesm2022/rt-tools-ui-kit-v2-pagination.mjs +374 -174
  132. package/fesm2022/rt-tools-ui-kit-v2-pagination.mjs.map +1 -1
  133. package/fesm2022/rt-tools-ui-kit-v2-photo-viewer.mjs +202 -159
  134. package/fesm2022/rt-tools-ui-kit-v2-photo-viewer.mjs.map +1 -1
  135. package/fesm2022/rt-tools-ui-kit-v2-popover.mjs +438 -309
  136. package/fesm2022/rt-tools-ui-kit-v2-popover.mjs.map +1 -1
  137. package/fesm2022/rt-tools-ui-kit-v2-profile-menu.mjs +295 -0
  138. package/fesm2022/rt-tools-ui-kit-v2-profile-menu.mjs.map +1 -0
  139. package/fesm2022/rt-tools-ui-kit-v2-prompt-suggestion.mjs +135 -45
  140. package/fesm2022/rt-tools-ui-kit-v2-prompt-suggestion.mjs.map +1 -1
  141. package/fesm2022/rt-tools-ui-kit-v2-radio-button.mjs +293 -171
  142. package/fesm2022/rt-tools-ui-kit-v2-radio-button.mjs.map +1 -1
  143. package/fesm2022/rt-tools-ui-kit-v2-radius.mjs +62 -55
  144. package/fesm2022/rt-tools-ui-kit-v2-radius.mjs.map +1 -1
  145. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +3379 -1466
  146. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  147. package/fesm2022/rt-tools-ui-kit-v2-ripple.mjs +114 -116
  148. package/fesm2022/rt-tools-ui-kit-v2-ripple.mjs.map +1 -1
  149. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs +406 -299
  150. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs.map +1 -1
  151. package/fesm2022/rt-tools-ui-kit-v2-section-nav.mjs +137 -36
  152. package/fesm2022/rt-tools-ui-kit-v2-section-nav.mjs.map +1 -1
  153. package/fesm2022/rt-tools-ui-kit-v2-select.mjs +687 -468
  154. package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
  155. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +2195 -1569
  156. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  157. package/fesm2022/rt-tools-ui-kit-v2-skeleton-wrapper.mjs +187 -48
  158. package/fesm2022/rt-tools-ui-kit-v2-skeleton-wrapper.mjs.map +1 -1
  159. package/fesm2022/rt-tools-ui-kit-v2-skeleton.mjs +242 -122
  160. package/fesm2022/rt-tools-ui-kit-v2-skeleton.mjs.map +1 -1
  161. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs +439 -0
  162. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs.map +1 -0
  163. package/fesm2022/rt-tools-ui-kit-v2-spinner.mjs +218 -69
  164. package/fesm2022/rt-tools-ui-kit-v2-spinner.mjs.map +1 -1
  165. package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs +292 -91
  166. package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs.map +1 -1
  167. package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs +225 -138
  168. package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs.map +1 -1
  169. package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs +150 -116
  170. package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs.map +1 -1
  171. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +2462 -1583
  172. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  173. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +647 -346
  174. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
  175. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +335 -121
  176. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  177. package/fesm2022/rt-tools-ui-kit-v2-textarea.mjs +217 -84
  178. package/fesm2022/rt-tools-ui-kit-v2-textarea.mjs.map +1 -1
  179. package/fesm2022/rt-tools-ui-kit-v2-theme-toggle.mjs +151 -72
  180. package/fesm2022/rt-tools-ui-kit-v2-theme-toggle.mjs.map +1 -1
  181. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +697 -226
  182. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
  183. package/fesm2022/rt-tools-ui-kit-v2-timeline.mjs +92 -38
  184. package/fesm2022/rt-tools-ui-kit-v2-timeline.mjs.map +1 -1
  185. package/fesm2022/rt-tools-ui-kit-v2-toast.mjs +752 -338
  186. package/fesm2022/rt-tools-ui-kit-v2-toast.mjs.map +1 -1
  187. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +310 -129
  188. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -1
  189. package/fesm2022/rt-tools-ui-kit-v2-toggle-switch.mjs +286 -158
  190. package/fesm2022/rt-tools-ui-kit-v2-toggle-switch.mjs.map +1 -1
  191. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +270 -134
  192. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -1
  193. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs +322 -202
  194. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs.map +1 -1
  195. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +908 -0
  196. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -0
  197. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +785 -0
  198. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -0
  199. package/fesm2022/rt-tools-ui-kit-v2-welcome-dialog.mjs +118 -63
  200. package/fesm2022/rt-tools-ui-kit-v2-welcome-dialog.mjs.map +1 -1
  201. package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +783 -262
  202. package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -1
  203. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +633 -294
  204. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
  205. package/fesm2022/rt-tools-ui-kit-v2.mjs +100 -103
  206. package/package.json +45 -1
  207. package/src/assets/icons/collapse-all.svg +9 -0
  208. package/src/assets/icons/expand-all.svg +9 -0
  209. package/src/assets/icons-material/collapse-all.fill.svg +5 -0
  210. package/src/assets/icons-material/collapse-all.svg +5 -0
  211. package/src/assets/icons-material/expand-all.fill.svg +5 -0
  212. package/src/assets/icons-material/expand-all.svg +5 -0
  213. package/src/assets/icons-material/undo.fill.svg +5 -0
  214. package/src/assets/icons-material/undo.svg +5 -0
  215. package/src/lib/components/bottom-sheet/CONTEXT.md +20 -2
  216. package/src/lib/components/bottom-sheet/rt-bottom-sheet.component.scss +36 -1
  217. package/src/lib/components/breadcrumbs/CONTEXT.md +37 -0
  218. package/src/lib/components/breadcrumbs/rt-breadcrumbs.component.scss +205 -0
  219. package/src/lib/components/carousel/CONTEXT.md +42 -0
  220. package/src/lib/components/carousel/rt-carousel.component.scss +117 -0
  221. package/src/lib/components/checkbox/CONTEXT.md +3 -0
  222. package/src/lib/components/checkbox/rt-checkbox.component.scss +9 -3
  223. package/src/lib/components/color-palette/CONTEXT.md +49 -0
  224. package/src/lib/components/color-palette/picker/rt-color-picker.component.scss +161 -0
  225. package/src/lib/components/color-palette/rt-color-palette.component.scss +132 -0
  226. package/src/lib/components/draggable-tree/CONTEXT.md +54 -0
  227. package/src/lib/components/draggable-tree/rt-draggable-tree.component.scss +219 -0
  228. package/src/lib/components/hybrid-tree/CONTEXT.md +31 -0
  229. package/src/lib/components/hybrid-tree-selector/CONTEXT.md +25 -0
  230. package/src/lib/components/icon-button/CONTEXT.md +3 -0
  231. package/src/lib/components/inline-edit/CONTEXT.md +41 -0
  232. package/src/lib/components/inline-edit/rt-inline-edit.component.scss +274 -0
  233. package/src/lib/components/popover/CONTEXT.md +3 -0
  234. package/src/lib/components/profile-menu/CONTEXT.md +32 -0
  235. package/src/lib/components/profile-menu/rt-profile-menu.component.scss +104 -0
  236. package/src/lib/components/speed-menu/CONTEXT.md +34 -0
  237. package/src/lib/components/speed-menu/rt-speed-menu.component.scss +118 -0
  238. package/src/lib/components/tag/rt-tag.component.scss +12 -0
  239. package/src/lib/components/thread-list/CONTEXT.md +1 -0
  240. package/src/lib/components/thread-list/rt-thread-list.component.scss +2 -1
  241. package/src/lib/components/toolbar/CONTEXT.md +9 -4
  242. package/src/lib/components/toolbar/rt-toolbar.component.scss +10 -2
  243. package/src/lib/components/tooltip/CONTEXT.md +8 -1
  244. package/src/lib/components/tree/CONTEXT.md +66 -0
  245. package/src/lib/components/tree/rt-tree.component.scss +313 -0
  246. package/src/lib/components/tree-selector/CONTEXT.md +47 -0
  247. package/src/lib/components/tree-selector/rt-tree-selector.component.scss +121 -0
  248. package/src/styles/_primitives.scss +4 -0
  249. package/src/styles/_semantic.scss +5 -0
  250. package/styles/tokens.css +43 -0
  251. package/types/rt-tools-ui-kit-v2-accordion.d.ts +31 -24
  252. package/types/rt-tools-ui-kit-v2-action-bar.d.ts +112 -91
  253. package/types/rt-tools-ui-kit-v2-ai-run-status.d.ts +64 -40
  254. package/types/rt-tools-ui-kit-v2-aside-section.d.ts +13 -10
  255. package/types/rt-tools-ui-kit-v2-aside.d.ts +268 -206
  256. package/types/rt-tools-ui-kit-v2-autocomplete.d.ts +102 -61
  257. package/types/rt-tools-ui-kit-v2-bar-list.d.ts +39 -27
  258. package/types/rt-tools-ui-kit-v2-bottom-sheet.d.ts +31 -20
  259. package/types/rt-tools-ui-kit-v2-breadcrumbs.d.ts +89 -0
  260. package/types/rt-tools-ui-kit-v2-button.d.ts +119 -59
  261. package/types/rt-tools-ui-kit-v2-calendar.d.ts +151 -86
  262. package/types/rt-tools-ui-kit-v2-card.d.ts +42 -21
  263. package/types/rt-tools-ui-kit-v2-carousel.d.ts +76 -0
  264. package/types/rt-tools-ui-kit-v2-checkbox.d.ts +48 -24
  265. package/types/rt-tools-ui-kit-v2-collapsible-text.d.ts +28 -20
  266. package/types/rt-tools-ui-kit-v2-color-palette.d.ts +208 -0
  267. package/types/rt-tools-ui-kit-v2-confirm-popover.d.ts +104 -47
  268. package/types/rt-tools-ui-kit-v2-container.d.ts +278 -221
  269. package/types/rt-tools-ui-kit-v2-copy-value.d.ts +39 -24
  270. package/types/rt-tools-ui-kit-v2-core.d.ts +862 -865
  271. package/types/rt-tools-ui-kit-v2-counter-row.d.ts +20 -12
  272. package/types/rt-tools-ui-kit-v2-counter.d.ts +70 -38
  273. package/types/rt-tools-ui-kit-v2-data-list.d.ts +429 -264
  274. package/types/rt-tools-ui-kit-v2-data-table.d.ts +500 -351
  275. package/types/rt-tools-ui-kit-v2-date-picker.d.ts +164 -138
  276. package/types/rt-tools-ui-kit-v2-date-range.d.ts +102 -84
  277. package/types/rt-tools-ui-kit-v2-delta-view.d.ts +16 -13
  278. package/types/rt-tools-ui-kit-v2-detail-list.d.ts +23 -16
  279. package/types/rt-tools-ui-kit-v2-dialog.d.ts +130 -101
  280. package/types/rt-tools-ui-kit-v2-dot-field.d.ts +10 -13
  281. package/types/rt-tools-ui-kit-v2-download-link.d.ts +22 -17
  282. package/types/rt-tools-ui-kit-v2-draggable-tree.d.ts +132 -0
  283. package/types/rt-tools-ui-kit-v2-dynamic-list.d.ts +185 -96
  284. package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +904 -404
  285. package/types/rt-tools-ui-kit-v2-empty-state.d.ts +48 -19
  286. package/types/rt-tools-ui-kit-v2-expansion-panel.d.ts +67 -39
  287. package/types/rt-tools-ui-kit-v2-field.d.ts +90 -49
  288. package/types/rt-tools-ui-kit-v2-file-card.d.ts +115 -75
  289. package/types/rt-tools-ui-kit-v2-file-drop.d.ts +75 -48
  290. package/types/rt-tools-ui-kit-v2-file-input.d.ts +53 -36
  291. package/types/rt-tools-ui-kit-v2-file-list.d.ts +5 -8
  292. package/types/rt-tools-ui-kit-v2-filter-control.d.ts +83 -49
  293. package/types/rt-tools-ui-kit-v2-form-control.d.ts +102 -74
  294. package/types/rt-tools-ui-kit-v2-header.d.ts +40 -22
  295. package/types/rt-tools-ui-kit-v2-hybrid-tree-selector.d.ts +14 -0
  296. package/types/rt-tools-ui-kit-v2-hybrid-tree.d.ts +72 -0
  297. package/types/rt-tools-ui-kit-v2-icon-button.d.ts +190 -90
  298. package/types/rt-tools-ui-kit-v2-icon.d.ts +160 -129
  299. package/types/rt-tools-ui-kit-v2-image-cropper.d.ts +151 -111
  300. package/types/rt-tools-ui-kit-v2-image-upload.d.ts +159 -85
  301. package/types/rt-tools-ui-kit-v2-info-item.d.ts +26 -13
  302. package/types/rt-tools-ui-kit-v2-inline-edit.d.ts +194 -0
  303. package/types/rt-tools-ui-kit-v2-input-number.d.ts +102 -53
  304. package/types/rt-tools-ui-kit-v2-input.d.ts +82 -43
  305. package/types/rt-tools-ui-kit-v2-live-badge.d.ts +30 -17
  306. package/types/rt-tools-ui-kit-v2-logo.d.ts +49 -32
  307. package/types/rt-tools-ui-kit-v2-markdown-text.d.ts +26 -17
  308. package/types/rt-tools-ui-kit-v2-menu.d.ts +272 -155
  309. package/types/rt-tools-ui-kit-v2-message.d.ts +59 -33
  310. package/types/rt-tools-ui-kit-v2-money-list.d.ts +36 -18
  311. package/types/rt-tools-ui-kit-v2-multiselect.d.ts +114 -80
  312. package/types/rt-tools-ui-kit-v2-night-grid.d.ts +34 -25
  313. package/types/rt-tools-ui-kit-v2-note.d.ts +12 -9
  314. package/types/rt-tools-ui-kit-v2-notifications-bell.d.ts +37 -16
  315. package/types/rt-tools-ui-kit-v2-page-header.d.ts +218 -150
  316. package/types/rt-tools-ui-kit-v2-pagination.d.ts +99 -63
  317. package/types/rt-tools-ui-kit-v2-photo-viewer.d.ts +61 -66
  318. package/types/rt-tools-ui-kit-v2-popover.d.ts +116 -55
  319. package/types/rt-tools-ui-kit-v2-profile-menu.d.ts +103 -0
  320. package/types/rt-tools-ui-kit-v2-prompt-suggestion.d.ts +26 -16
  321. package/types/rt-tools-ui-kit-v2-radio-button.d.ts +85 -43
  322. package/types/rt-tools-ui-kit-v2-radius.d.ts +18 -15
  323. package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +931 -590
  324. package/types/rt-tools-ui-kit-v2-ripple.d.ts +28 -25
  325. package/types/rt-tools-ui-kit-v2-scroll-area.d.ts +60 -58
  326. package/types/rt-tools-ui-kit-v2-section-nav.d.ts +32 -22
  327. package/types/rt-tools-ui-kit-v2-select.d.ts +182 -139
  328. package/types/rt-tools-ui-kit-v2-side-menu.d.ts +452 -349
  329. package/types/rt-tools-ui-kit-v2-skeleton-wrapper.d.ts +53 -20
  330. package/types/rt-tools-ui-kit-v2-skeleton.d.ts +55 -26
  331. package/types/rt-tools-ui-kit-v2-speed-menu.d.ts +131 -0
  332. package/types/rt-tools-ui-kit-v2-spinner.d.ts +56 -28
  333. package/types/rt-tools-ui-kit-v2-split-button.d.ts +96 -55
  334. package/types/rt-tools-ui-kit-v2-stat-tile.d.ts +88 -60
  335. package/types/rt-tools-ui-kit-v2-stepper.d.ts +50 -37
  336. package/types/rt-tools-ui-kit-v2-table.d.ts +631 -480
  337. package/types/rt-tools-ui-kit-v2-tabs.d.ts +198 -113
  338. package/types/rt-tools-ui-kit-v2-tag.d.ts +106 -53
  339. package/types/rt-tools-ui-kit-v2-textarea.d.ts +48 -25
  340. package/types/rt-tools-ui-kit-v2-theme-toggle.d.ts +28 -26
  341. package/types/rt-tools-ui-kit-v2-thread-list.d.ts +154 -94
  342. package/types/rt-tools-ui-kit-v2-timeline.d.ts +30 -28
  343. package/types/rt-tools-ui-kit-v2-toast.d.ts +174 -98
  344. package/types/rt-tools-ui-kit-v2-toggle-button-group.d.ts +132 -86
  345. package/types/rt-tools-ui-kit-v2-toggle-switch.d.ts +88 -50
  346. package/types/rt-tools-ui-kit-v2-toolbar.d.ts +30 -27
  347. package/types/rt-tools-ui-kit-v2-tooltip.d.ts +65 -41
  348. package/types/rt-tools-ui-kit-v2-tree-selector.d.ts +257 -0
  349. package/types/rt-tools-ui-kit-v2-tree.d.ts +282 -0
  350. package/types/rt-tools-ui-kit-v2-welcome-dialog.d.ts +21 -24
  351. package/types/rt-tools-ui-kit-v2-workspace-details.d.ts +195 -124
  352. package/types/rt-tools-ui-kit-v2-workspace.d.ts +122 -79
  353. package/types/rt-tools-ui-kit-v2.d.ts +100 -90
  354. package/fesm2022/rt-tools-ui-kit-v2.mjs.map +0 -1
@@ -1,1639 +1,2265 @@
1
- import { NgTemplateOutlet } from '@angular/common';
2
- import * as i0 from '@angular/core';
3
- import { InjectionToken, computed, inject, signal, DestroyRef, Injectable, makeEnvironmentProviders, Pipe, ErrorHandler, input, booleanAttribute, output, ViewEncapsulation, ChangeDetectionStrategy, Component, Injector, ElementRef, linkedSignal, viewChildren, afterNextRender, Directive, TemplateRef, Renderer2, viewChild, contentChild, numberAttribute, isDevMode, effect } from '@angular/core';
4
- import * as i1 from '@angular/forms';
5
- import { FormControl, ReactiveFormsModule } from '@angular/forms';
6
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
- import { Router, ActivatedRoute, RouterLink } from '@angular/router';
8
- import { LOCAL_STORAGE, PlatformService, WINDOW, BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';
9
- import { ERtStorageKeys, BreakpointsService, rtKitLabel } from '@rt-tools/ui-kit-v2/core';
10
- import { isKitIconName, materialPairOf, RtIconComponent } from '@rt-tools/ui-kit-v2/icon';
11
- import { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';
12
- import { RtInputComponent } from '@rt-tools/ui-kit-v2/input';
13
- import { RtScrollAreaComponent, RtScrollAreaContentDirective, RtScrollAreaFooterDirective, RtScrollAreaHeaderDirective } from '@rt-tools/ui-kit-v2/scroll-area';
14
- import { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';
15
- import { CdkDrag, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop';
16
- import { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from '@rt-tools/ui-kit-v2/expansion-panel';
17
- import { isRecord } from '@rt-tools/utils';
18
-
19
- /** Только строки и числа, без повторов, в прежнем порядке. */
1
+ import { NgTemplateOutlet } from "@angular/common";
2
+ import * as i0 from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, ErrorHandler, Injectable, InjectionToken, Injector, Pipe, Renderer2, TemplateRef, ViewEncapsulation, afterNextRender, booleanAttribute, computed, contentChild, effect, inject, input, isDevMode, linkedSignal, makeEnvironmentProviders, numberAttribute, output, signal, viewChild, viewChildren } from "@angular/core";
4
+ import * as i1 from "@angular/forms";
5
+ import { FormControl, ReactiveFormsModule } from "@angular/forms";
6
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
7
+ import { ActivatedRoute, Router, RouterLink } from "@angular/router";
8
+ import { BlockDirective, ElemDirective, LOCAL_STORAGE, ModDirective, PlatformService, WINDOW } from "@rt-tools/core";
9
+ import { BreakpointsService, ERtStorageKeys, rtKitLabel } from "@rt-tools/ui-kit-v2/core";
10
+ import { RtIconComponent, isKitIconName, materialPairOf } from "@rt-tools/ui-kit-v2/icon";
11
+ import { RtIconButtonComponent } from "@rt-tools/ui-kit-v2/icon-button";
12
+ import { RtInputComponent } from "@rt-tools/ui-kit-v2/input";
13
+ import { RtScrollAreaComponent, RtScrollAreaContentDirective, RtScrollAreaFooterDirective, RtScrollAreaHeaderDirective } from "@rt-tools/ui-kit-v2/scroll-area";
14
+ import { RtTooltipDirective } from "@rt-tools/ui-kit-v2/tooltip";
15
+ import { CdkDrag, CdkDragHandle, CdkDropList } from "@angular/cdk/drag-drop";
16
+ import { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from "@rt-tools/ui-kit-v2/expansion-panel";
17
+ import { isRecord } from "@rt-tools/utils";
20
18
  function normalizeSideMenuFavorites(values) {
21
- return values.reduce((kept, value) => {
22
- if ((typeof value === 'string' || typeof value === 'number') && !kept.includes(value)) {
23
- kept.push(value);
24
- }
25
- return kept;
26
- }, []);
19
+ return values.reduce((kept, value) => {
20
+ if ((typeof value === "string" || typeof value === "number") && !kept.includes(value)) kept.push(value);
21
+ return kept;
22
+ }, []);
27
23
  }
28
- /**
29
- * Перенос записи списка с места на место.
30
- *
31
- * Номер места вне списка у источника ничего не меняет — переносить нечего; у цели прижимается к
32
- * краю: рука, бросившая строку ниже последней, хотела поставить её последней.
33
- */
34
24
  function moveSideMenuFavorite(ids, from, to) {
35
- const next = [...ids];
36
- if (from < 0 || from >= next.length) {
37
- return next;
38
- }
39
- const moved = next.splice(from, 1)[0];
40
- const target = Math.min(Math.max(to, 0), next.length);
41
- next.splice(target, 0, moved);
42
- return next;
25
+ const next = [...ids];
26
+ if (from < 0 || from >= next.length) return next;
27
+ const moved = next.splice(from, 1)[0];
28
+ const target = Math.min(Math.max(to, 0), next.length);
29
+ next.splice(target, 0, moved);
30
+ return next;
43
31
  }
44
- /**
45
- * Перенос строки блока — места в блоке, а не в списке.
46
- *
47
- * Блок показывает только те номера, пункты которых в меню есть, поэтому место в блоке — не место в
48
- * списке. Видимые номера переставляются между собой и встают в те же ячейки списка, которые
49
- * занимали; скрытые остаются на своих местах: их пункт вернётся вместе с правом, и человек найдёт
50
- * его там, куда ставил. Видимый номер, которого в списке нет, пропускается: список могла поменять
51
- * другая вкладка, а место номера, которого нет, занять нечем.
52
- */
53
32
  function moveVisibleSideMenuFavorite(ids, visible, from, to) {
54
- const listed = new Set(ids);
55
- const reordered = moveSideMenuFavorite(normalizeSideMenuFavorites(visible), from, to).filter((id) => listed.has(id));
56
- const slots = new Set(reordered);
57
- let next = 0;
58
- return ids.map((id) => {
59
- if (!slots.has(id)) {
60
- return id;
61
- }
62
- const placed = reordered[next];
63
- next += 1;
64
- return placed;
65
- });
33
+ const listed = new Set(ids);
34
+ const reordered = moveSideMenuFavorite(normalizeSideMenuFavorites(visible), from, to).filter((id) => listed.has(id));
35
+ const slots = new Set(reordered);
36
+ let next = 0;
37
+ return ids.map((id) => {
38
+ if (!slots.has(id)) return id;
39
+ const placed = reordered[next];
40
+ next += 1;
41
+ return placed;
42
+ });
66
43
  }
67
- /**
68
- * Может ли пункт стоять в избранном: ссылка есть — может; папка без ссылки никуда не ведёт, а
69
- * пункт с флагом `favoriteDisabled` приложение исключило само.
70
- */
71
44
  function isSideMenuFavoriteCandidate(item) {
72
- return Boolean(item.link?.trim()) && !item.favoriteDisabled;
45
+ return Boolean(item.link?.trim()) && !item.favoriteDisabled;
73
46
  }
74
47
  function collectLinkedItems(items, byId) {
75
- for (const item of items) {
76
- if (isSideMenuFavoriteCandidate(item) && !byId.has(item.id)) {
77
- byId.set(item.id, item);
78
- }
79
- collectLinkedItems(item.submenu ?? [], byId);
80
- }
48
+ for (const item of items) {
49
+ if (isSideMenuFavoriteCandidate(item) && !byId.has(item.id)) byId.set(item.id, item);
50
+ collectLinkedItems(item.submenu ?? [], byId);
51
+ }
81
52
  }
82
- /**
83
- * Пункты меню, стоящие за номерами списка, в порядке списка.
84
- *
85
- * Пункт ищется в подменю пунктов полосы, вглубь папок, и берётся первый со ссылкой: избранное,
86
- * которое никуда не ведёт, — сломанное избранное. Номер без пункта пропускается, но из списка не
87
- * уходит — это решает сервис, а не меню.
88
- */
89
53
  function findSideMenuFavoriteItems(menu, ids) {
90
- const byId = new Map();
91
- collectLinkedItems(menu.flatMap((item) => item.submenu ?? []), byId);
92
- return ids.reduce((found, id) => {
93
- const item = byId.get(id);
94
- if (item) {
95
- found.push(item);
96
- }
97
- return found;
98
- }, []);
54
+ const byId = /* @__PURE__ */ new Map();
55
+ collectLinkedItems(menu.flatMap((item) => item.submenu ?? []), byId);
56
+ return ids.reduce((found, id) => {
57
+ const item = byId.get(id);
58
+ if (item) found.push(item);
59
+ return found;
60
+ }, []);
99
61
  }
100
- /**
101
- * Пункт полосы, чьё подменю сейчас показано, если избранное у него включено.
102
- *
103
- * Избранное у каждого пункта своё и по умолчанию выключено: включает его потребитель флагом
104
- * `favorites` пункта полосы. Показанный набор даёт меню — закреплённый или открытый наведением, —
105
- * и пустой набор не принадлежит никому: подменю без списка не показывает ничьё избранное.
106
- *
107
- * Раздел узнаётся сначала по самому набору, затем по номеру его первого пункта: приложение вправе
108
- * передать меню заново теми же пунктами в новых объектах — при смене языка или прав, — и набор,
109
- * который подменю ещё держит, перестаёт совпадать с пунктами полосы по ссылке. Номера пунктов
110
- * подменю единственны на всё меню — это обязанность потребителя.
111
- */
112
62
  function sideMenuFavoritesSection(menu, shown) {
113
- const first = shown[0];
114
- if (!first) {
115
- return null;
116
- }
117
- const section = menu.find((item) => item.submenu === shown) ??
118
- menu.find((item) => !!item.submenu?.some((child) => child.id === first.id));
119
- return section?.favorites ? section : null;
63
+ const first = shown[0];
64
+ if (!first) return null;
65
+ const section = menu.find((item) => item.submenu === shown) ?? menu.find((item) => !!item.submenu?.some((child) => child.id === first.id));
66
+ return section?.favorites ? section : null;
120
67
  }
121
- /**
122
- * Строку отпустили за пределами панели. Пока её тянули, уход указателя с панели не закрывал
123
- * подменю, открытое наведением, — после броска он должен сработать так, будто случился сейчас.
124
- */
125
68
  function isSideMenuDropOutside(panel, point) {
126
- return point.x < panel.left || point.x > panel.right || point.y < panel.top || point.y > panel.bottom;
69
+ return point.x < panel.left || point.x > panel.right || point.y < panel.top || point.y > panel.bottom;
127
70
  }
128
-
129
- /**
130
- * Отбор пунктов подменю по подстроке подписи.
131
- *
132
- * Отбор спускается внутрь папок на любую глубину: потребитель кладёт в папки то, что человек и ищет
133
- * по имени, и по одному верхнему уровню такой пункт не нашёлся бы.
134
- *
135
- * В выдаче стоят только строки, содержащие запрос. Папка остаётся путём к совпавшим детям, а её
136
- * состав отбирается тем же правилом: иначе одно совпадение по имени папки вытаскивало бы весь её
137
- * состав. Папка, совпавшая только по имени, остаётся одной строкой без детей.
138
- *
139
- * Отобранная папка — новый объект: правка на месте переписала бы набор потребителя, и стёртый
140
- * запрос вернул бы урезанное меню.
141
- */
142
71
  function filterSideMenuItems(items, query) {
143
- const needle = query.trim().toLowerCase();
144
- if (needle === '') {
145
- return [...items];
146
- }
147
- return items.reduce((kept, item) => {
148
- const isFolder = Boolean(item.submenu?.length);
149
- const inside = isFolder ? filterSideMenuItems(item.submenu ?? [], query) : [];
150
- if (inside.length) {
151
- kept.push({ ...item, submenu: inside });
152
- return kept;
153
- }
154
- if (item.name?.toLowerCase().includes(needle)) {
155
- kept.push(isFolder ? { ...item, submenu: [] } : item);
156
- }
157
- return kept;
158
- }, []);
72
+ const needle = query.trim().toLowerCase();
73
+ if (needle === "") return [...items];
74
+ return items.reduce((kept, item) => {
75
+ const isFolder = Boolean(item.submenu?.length);
76
+ const inside = isFolder ? filterSideMenuItems(item.submenu ?? [], query) : [];
77
+ if (inside.length) {
78
+ kept.push({
79
+ ...item,
80
+ submenu: inside
81
+ });
82
+ return kept;
83
+ }
84
+ if (item.name?.toLowerCase().includes(needle)) kept.push(isFolder ? {
85
+ ...item,
86
+ submenu: []
87
+ } : item);
88
+ return kept;
89
+ }, []);
159
90
  }
160
- /**
161
- * Папки отобранного списка — те, что должны стоять раскрытыми. Возвращаются номера, а не пункты:
162
- * отобранный список пересобирается на каждую букву запроса, и ссылка на пункт этого не переживает.
163
- */
164
91
  function sideMenuIdsToExpand(items) {
165
- return items.reduce((ids, item) => {
166
- if (item.submenu?.length) {
167
- ids.push(item.id, ...sideMenuIdsToExpand(item.submenu));
168
- }
169
- return ids;
170
- }, []);
92
+ return items.reduce((ids, item) => {
93
+ if (item.submenu?.length) ids.push(item.id, ...sideMenuIdsToExpand(item.submenu));
94
+ return ids;
95
+ }, []);
171
96
  }
172
- /**
173
- * Пределы ширины подменю в пикселях. Уже нижнего не помещается ни одна подпись пункта, шире
174
- * верхнего подменю закрывает содержимое страницы.
175
- */
176
97
  const RT_SIDE_MENU_WIDTH_MIN = 120;
177
98
  const RT_SIDE_MENU_WIDTH_MAX = 480;
178
- /** Шаг ширины с клавиатуры: стрелку держат нажатой, и крупный шаг проскакивал бы подпись целиком. */
179
99
  const RT_SIDE_MENU_WIDTH_STEP = 16;
180
- /** Приведение ширины к пределам. */
181
100
  function clampSideMenuWidth(width) {
182
- return Math.min(RT_SIDE_MENU_WIDTH_MAX, Math.max(RT_SIDE_MENU_WIDTH_MIN, width));
101
+ return Math.min(480, Math.max(120, width));
183
102
  }
184
- /**
185
- * Резка подписи на совпавшие и несовпавшие куски.
186
- *
187
- * Отмечаются все вхождения: отмеченное одно первое читается как «второго нет». Сравнение без учёта
188
- * регистра, а куски режутся из оригинала — приведение к нижнему регистру переписало бы названия.
189
- */
190
103
  function splitSideMenuTitle(name, query) {
191
- if (name === '') {
192
- return [];
193
- }
194
- const needle = query.trim().toLowerCase();
195
- if (needle === '') {
196
- return [{ text: name, matched: false }];
197
- }
198
- const haystack = name.toLowerCase();
199
- const parts = [];
200
- let from = 0;
201
- for (let at = haystack.indexOf(needle, from); at !== -1; at = haystack.indexOf(needle, from)) {
202
- if (at > from) {
203
- parts.push({ text: name.slice(from, at), matched: false });
204
- }
205
- parts.push({ text: name.slice(at, at + needle.length), matched: true });
206
- from = at + needle.length;
207
- }
208
- if (from < name.length) {
209
- parts.push({ text: name.slice(from), matched: false });
210
- }
211
- return parts;
104
+ if (name === "") return [];
105
+ const needle = query.trim().toLowerCase();
106
+ if (needle === "") return [{
107
+ text: name,
108
+ matched: false
109
+ }];
110
+ const haystack = name.toLowerCase();
111
+ const parts = [];
112
+ let from = 0;
113
+ for (let at = haystack.indexOf(needle, from); at !== -1; at = haystack.indexOf(needle, from)) {
114
+ if (at > from) parts.push({
115
+ text: name.slice(from, at),
116
+ matched: false
117
+ });
118
+ parts.push({
119
+ text: name.slice(at, at + needle.length),
120
+ matched: true
121
+ });
122
+ from = at + needle.length;
123
+ }
124
+ if (from < name.length) parts.push({
125
+ text: name.slice(from),
126
+ matched: false
127
+ });
128
+ return parts;
129
+ }
130
+ function splitTitleByWords(name, query) {
131
+ return query.split(/\s+/).filter((word) => word !== "").reduce((parts, word) => parts.flatMap((part) => part.matched ? [part] : splitSideMenuTitle(part.text, word)), splitSideMenuTitle(name, ""));
212
132
  }
213
- /**
214
- * Пункты подменю в том порядке, в каком они стоят на экране. Внутрь папки список спускается, только
215
- * если та раскрыта: ходьба стрелками идёт по видимому, а не по всему набору.
216
- */
217
133
  function walkSideMenuItems(items, expandedIds) {
218
- return items.reduce((walk, item) => {
219
- walk.push(item);
220
- if (item.submenu?.length && expandedIds.includes(item.id)) {
221
- walk.push(...walkSideMenuItems(item.submenu, expandedIds));
222
- }
223
- return walk;
224
- }, []);
134
+ return items.reduce((walk, item) => {
135
+ walk.push(item);
136
+ if (item.submenu?.length && expandedIds.includes(item.id)) walk.push(...walkSideMenuItems(item.submenu, expandedIds));
137
+ return walk;
138
+ }, []);
225
139
  }
226
- /**
227
- * Куда уходит подсветка на шаг стрелкой. Считается по номеру пункта: видимый список пересобирается
228
- * на каждую букву запроса. Без подсветки вниз берёт первый пункт, вверх — последний. У краёв ходьба
229
- * останавливается: заворот уводил бы взгляд через всю панель.
230
- */
231
140
  function stepSideMenuHighlight(walk, highlightedId, step) {
232
- if (walk.length === 0) {
233
- return null;
234
- }
235
- const at = walk.findIndex((item) => item.id === highlightedId);
236
- if (at === -1) {
237
- return step > 0 ? walk[0].id : walk[walk.length - 1].id;
238
- }
239
- return walk[Math.min(walk.length - 1, Math.max(0, at + step))].id;
141
+ if (walk.length === 0) return null;
142
+ const at = walk.findIndex((item) => item.id === highlightedId);
143
+ if (at === -1) return step > 0 ? walk[0].id : walk[walk.length - 1].id;
144
+ return walk[Math.min(walk.length - 1, Math.max(0, at + step))].id;
240
145
  }
241
- /**
242
- * Тяга ширины подменю указательными событиями: мышиных палец и перо не дают. Слушатели висят на
243
- * ручке, а держит их захват указателя — слушатель на документе терял бы движение над чужим кадром.
244
- * Указатель, отнятый средой, кончает тягу так же, как отпускание.
245
- *
246
- * Возвращает снятие слушателей. Пустое значение — тянуть нечем.
247
- */
248
146
  function startSideMenuWidthDrag(event, startWidth, listen, hooks) {
249
- const handle = event.currentTarget;
250
- // Тянет только основная кнопка: отпускание правой над меню среды ручке не приходит.
251
- if (!(handle instanceof HTMLElement) || event.button !== 0) {
252
- return null;
253
- }
254
- const pointerId = event.pointerId;
255
- const startX = event.clientX;
256
- // Захвата нет у среды, где идут проверки.
257
- if (typeof handle.setPointerCapture === 'function') {
258
- handle.setPointerCapture(pointerId);
259
- }
260
- const stopMove = listen(handle, 'pointermove', (moveEvent) => {
261
- if (moveEvent.pointerId === pointerId) {
262
- hooks.onWidth(clampSideMenuWidth(startWidth + moveEvent.clientX - startX));
263
- }
264
- });
265
- const stopUp = listen(handle, 'pointerup', () => hooks.onEnd());
266
- const stopCancel = listen(handle, 'pointercancel', () => hooks.onEnd());
267
- // Захват снимается и без отпускания — ручку убрали из разметки; тогда конец тяги — сама потеря.
268
- const stopLost = listen(handle, 'lostpointercapture', () => hooks.onEnd());
269
- return () => {
270
- stopMove();
271
- stopUp();
272
- stopCancel();
273
- stopLost();
274
- if (typeof handle.releasePointerCapture === 'function' && handle.hasPointerCapture(pointerId)) {
275
- handle.releasePointerCapture(pointerId);
276
- }
277
- };
147
+ const handle = event.currentTarget;
148
+ if (!(handle instanceof HTMLElement) || event.button !== 0) return null;
149
+ const pointerId = event.pointerId;
150
+ const startX = event.clientX;
151
+ if (typeof handle.setPointerCapture === "function") handle.setPointerCapture(pointerId);
152
+ const stopMove = listen(handle, "pointermove", (moveEvent) => {
153
+ if (moveEvent.pointerId === pointerId) hooks.onWidth(clampSideMenuWidth(startWidth + moveEvent.clientX - startX));
154
+ });
155
+ const stopUp = listen(handle, "pointerup", () => hooks.onEnd());
156
+ const stopCancel = listen(handle, "pointercancel", () => hooks.onEnd());
157
+ const stopLost = listen(handle, "lostpointercapture", () => hooks.onEnd());
158
+ return () => {
159
+ stopMove();
160
+ stopUp();
161
+ stopCancel();
162
+ stopLost();
163
+ if (typeof handle.releasePointerCapture === "function" && handle.hasPointerCapture(pointerId)) handle.releasePointerCapture(pointerId);
164
+ };
278
165
  }
279
- /**
280
- * Ширина, которой панель нарисована. Панели нет или раскладка не посчитана — нижний предел: с нуля
281
- * тяга уводила бы ширину в отрицательные числа.
282
- */
283
166
  function drawnSideMenuWidth(panel) {
284
- const width = panel?.getBoundingClientRect().width ?? 0;
285
- return width > 0 ? width : RT_SIDE_MENU_WIDTH_MIN;
167
+ const width = panel?.getBoundingClientRect().width ?? 0;
168
+ return width > 0 ? width : 120;
286
169
  }
287
- /**
288
- * Число, которое уходит наружу по концу тяги: большее из натянутого и нарисованного. Нижний предел
289
- * держит оформление, и панель не бывает уже его, даже если рука ушла левее.
290
- */
291
170
  function reportedSideMenuWidth(dragged, panel) {
292
- return Math.max(dragged, drawnSideMenuWidth(panel));
171
+ return Math.max(dragged, drawnSideMenuWidth(panel));
293
172
  }
294
- /**
295
- * Новая ширина по нажатой клавише. Пустое значение — клавиша не о ширине, и умолчание не отменяется:
296
- * иначе ручка съедала бы табуляцию. Влево значит уже при любом направлении письма: ручка стоит у
297
- * правого края панели.
298
- */
299
173
  function sideMenuWidthByKey(key, width) {
300
- switch (key) {
301
- case 'ArrowRight':
302
- return clampSideMenuWidth(width + RT_SIDE_MENU_WIDTH_STEP);
303
- case 'ArrowLeft':
304
- return clampSideMenuWidth(width - RT_SIDE_MENU_WIDTH_STEP);
305
- case 'Home':
306
- return RT_SIDE_MENU_WIDTH_MIN;
307
- case 'End':
308
- return RT_SIDE_MENU_WIDTH_MAX;
309
- default:
310
- return null;
311
- }
174
+ switch (key) {
175
+ case "ArrowRight": return clampSideMenuWidth(width + 16);
176
+ case "ArrowLeft": return clampSideMenuWidth(width - 16);
177
+ case "Home": return 120;
178
+ case "End": return 480;
179
+ default: return null;
180
+ }
312
181
  }
313
- /**
314
- * Что показывает закреплённое подменю: выбранный человеком раздел, а пока выбора нет — раздел
315
- * активного адреса. Выбор впереди активности: иначе до соседнего раздела не добраться.
316
- */
317
182
  function pinnedSideMenuItems(picked, activeIds, items) {
318
- if (picked?.length) {
319
- return picked;
320
- }
321
- return items.find((item) => activeIds.includes(item.id) && !!item.submenu?.length)?.submenu ?? [];
183
+ if (picked?.length) return picked;
184
+ return items.find((item) => activeIds.includes(item.id) && !!item.submenu?.length)?.submenu ?? [];
322
185
  }
323
- /**
324
- * Нажатие, которое браузер открывает сам по адресу ссылки: не левой кнопкой или с клавишей —
325
- * новая вкладка, окно, загрузка. Строка подменю такое нажатие не перехватывает.
326
- */
327
186
  function sideMenuPressOpensInBrowser(event) {
328
- const withKey = [event.ctrlKey, event.metaKey, event.shiftKey, event.altKey].some(Boolean);
329
- return event.button !== 0 || withKey;
187
+ const withKey = [
188
+ event.ctrlKey,
189
+ event.metaKey,
190
+ event.shiftKey,
191
+ event.altKey
192
+ ].some(Boolean);
193
+ return event.button !== 0 || withKey;
330
194
  }
331
-
332
- /** Номер меню, которому приложение своего не задало. */
333
- const RT_SIDE_MENU_DEFAULT_ID = 'main';
334
- /**
335
- * Номер меню, под которым лежат его настройки. Пустой — тот же, что не заданный: атрибут без
336
- * значения даёт пустую строку, и настройки легли бы под ключ, которого никто не называл.
337
- */
195
+ const RT_SIDE_MENU_DEFAULT_ID = "main";
338
196
  function normalizeSideMenuId(menuId) {
339
- return menuId?.trim() || RT_SIDE_MENU_DEFAULT_ID;
197
+ return menuId?.trim() || "main";
340
198
  }
341
199
  function isSubMenuMode(value) {
342
- return value === 'hover' || value === 'pinned';
200
+ return value === "hover" || value === "pinned";
343
201
  }
344
- /**
345
- * Настройки одного меню из того, что лежит под его номером. Недопустимое поле читается как
346
- * отсутствие значения, соседние поля остаются: сломанная ширина не стирает режим. Незнакомые поля
347
- * не удаляются. Ширина приводится к пределам подменю, списки избранного — к номерам без повторов.
348
- */
349
202
  function normalizeSideMenuSettings(value) {
350
- if (!isRecord(value)) {
351
- return {};
352
- }
353
- const settings = { ...value };
354
- const width = value['subMenuWidth'];
355
- const favorites = value['favorites'];
356
- const collapsed = value['favoritesCollapsed'];
357
- if (!isSubMenuMode(value['subMenuMode'])) {
358
- delete settings.subMenuMode;
359
- }
360
- if (typeof width === 'number' && Number.isFinite(width)) {
361
- settings.subMenuWidth = clampSideMenuWidth(width);
362
- }
363
- else {
364
- delete settings.subMenuWidth;
365
- }
366
- // Список — только строки и числа без повторов; не массив читается отсутствием списка.
367
- if (Array.isArray(favorites)) {
368
- settings.favorites = normalizeSideMenuFavorites(favorites);
369
- }
370
- else {
371
- delete settings.favorites;
372
- }
373
- if (Array.isArray(collapsed)) {
374
- settings.favoritesCollapsed = normalizeSideMenuFavorites(collapsed);
375
- }
376
- else {
377
- delete settings.favoritesCollapsed;
378
- }
379
- return settings;
203
+ if (!isRecord(value)) return {};
204
+ const settings = { ...value };
205
+ const width = value["subMenuWidth"];
206
+ const favorites = value["favorites"];
207
+ const collapsed = value["favoritesCollapsed"];
208
+ if (!isSubMenuMode(value["subMenuMode"])) delete settings.subMenuMode;
209
+ if (typeof width === "number" && Number.isFinite(width)) settings.subMenuWidth = clampSideMenuWidth(width);
210
+ else delete settings.subMenuWidth;
211
+ if (Array.isArray(favorites)) settings.favorites = normalizeSideMenuFavorites(favorites);
212
+ else delete settings.favorites;
213
+ if (Array.isArray(collapsed)) settings.favoritesCollapsed = normalizeSideMenuFavorites(collapsed);
214
+ else delete settings.favoritesCollapsed;
215
+ return settings;
380
216
  }
381
- /** Запись хранилища как объект. Не JSON, не объект, пустое хранилище — одинаково пустая запись. */
382
217
  function parseSideMenuSettingsRecord(raw) {
383
- if (raw === null) {
384
- return {};
385
- }
386
- try {
387
- const value = JSON.parse(raw);
388
- return isRecord(value) ? value : {};
389
- }
390
- catch {
391
- return {};
392
- }
218
+ if (raw === null) return {};
219
+ try {
220
+ const value = JSON.parse(raw);
221
+ return isRecord(value) ? value : {};
222
+ } catch {
223
+ return {};
224
+ }
393
225
  }
394
- /**
395
- * Настройки всех меню из записи. Меню, чьё значение не объект, пропускается: сломанный угол одного
396
- * меню не стирает соседние.
397
- */
398
226
  function readSideMenuSettings(record) {
399
- return Object.fromEntries(Object.entries(record)
400
- .filter(([, settings]) => isRecord(settings))
401
- .map(([menuId, settings]) => [menuId, normalizeSideMenuSettings(settings)]));
227
+ return Object.fromEntries(Object.entries(record).filter(([, settings]) => isRecord(settings)).map(([menuId, settings]) => [menuId, normalizeSideMenuSettings(settings)]));
402
228
  }
403
- /**
404
- * Запись с правкой полей одного меню. Остальные меню и поля уходят такими, какими лежали, даже
405
- * сломанные: кит правит только то, о чём его попросили.
406
- */
407
229
  function patchSideMenuSettings(record, menuId, patch) {
408
- const current = record[menuId];
409
- return { ...record, [menuId]: { ...(isRecord(current) ? current : {}), ...patch } };
230
+ const current = record[menuId];
231
+ return {
232
+ ...record,
233
+ [menuId]: {
234
+ ...isRecord(current) ? current : {},
235
+ ...patch
236
+ }
237
+ };
410
238
  }
411
- /** Запись без настроек одного меню. */
412
239
  function omitSideMenuSettings(record, menuId) {
413
- return Object.fromEntries(Object.entries(record).filter(([id]) => id !== menuId));
240
+ return Object.fromEntries(Object.entries(record).filter(([id]) => id !== menuId));
414
241
  }
415
-
416
- /** Корзина и вертикальная двойная стрелка — те же знаки, что у первого кита. */
417
- const DEFAULT_FAVORITES_ICONS = { remove: 'trash', drag: 'arrows-v' };
418
- const RT_SIDE_MENU_SETTINGS_CONFIG = new InjectionToken('RT_SIDE_MENU_SETTINGS_CONFIG');
419
- /** Сигнал на номер меню один: повторный вызов отдаёт тот же. */
242
+ const DEFAULT_FAVORITES_ICONS = {
243
+ remove: "trash",
244
+ drag: "arrows-v"
245
+ };
246
+ const RT_SIDE_MENU_SETTINGS_CONFIG = new InjectionToken("RT_SIDE_MENU_SETTINGS_CONFIG");
420
247
  function cached(cache, menuId, read) {
421
- const id = normalizeSideMenuId(menuId);
422
- let value = cache.get(id);
423
- if (!value) {
424
- value = computed(() => read(id));
425
- cache.set(id, value);
426
- }
427
- return value;
248
+ const id = normalizeSideMenuId(menuId);
249
+ let value = cache.get(id);
250
+ if (!value) {
251
+ value = computed(() => read(id));
252
+ cache.set(id, value);
253
+ }
254
+ return value;
428
255
  }
429
- /**
430
- * Настройки боковых меню в хранилище браузера: режим и ширина подменю, избранное и свёрнутые блоки
431
- * избранного под номером каждого меню.
432
- *
433
- * Перед каждой записью ключ читается заново: соседняя вкладка или приложение могли записать своё,
434
- * и запись из копии затёрла бы их. Правится одно поле одного меню, остальное уходит как лежало.
435
- * Изменения из других вкладок приходят событием `storage` и обновляют сигналы.
436
- *
437
- * Хранилище берётся токеном `@rt-tools/core` напрямую, а не общим сервисом хранилища: сервису
438
- * нужно знать, что запись не удалась, и держать правду в памяти до перезагрузки. Без хранилища, вне
439
- * браузера, закрытое или полное — настройки живут в памяти, и ни чтение, ни запись не бросают.
440
- *
441
- * Сервис ставится провайдером окружения — `provideRtSideMenuSettings()`, — а меню берёт его
442
- * необязательно: не поставлен — меню ничего не хранит, режим и ширина приходят входами.
443
- */
444
- class RtSideMenuSettingsService {
445
- #config;
446
- #storage;
447
- #key;
448
- #menus;
449
- #modes;
450
- #widths;
451
- #favorites;
452
- #collapsed;
453
- /** Последняя известная запись: ею сервис живёт, пока хранилище недоступно. */
454
- #record;
455
- /** Последняя запись в хранилище не удалась — оно полно, и правда лежит в памяти. */
456
- #unsaved;
457
- #settings;
458
- constructor() {
459
- this.#config = inject(RT_SIDE_MENU_SETTINGS_CONFIG, { optional: true }) ?? {};
460
- this.#storage = inject(LOCAL_STORAGE, { optional: true }) ?? null;
461
- this.#key = this.#config.storageKey?.trim() || ERtStorageKeys.SideMenu;
462
- this.#menus = new Map();
463
- this.#modes = new Map();
464
- this.#widths = new Map();
465
- this.#favorites = new Map();
466
- this.#collapsed = new Map();
467
- /** Последняя известная запись: ею сервис живёт, пока хранилище недоступно. */
468
- this.#record = this.#load() ?? {};
469
- /** Последняя запись в хранилище не удалась — оно полно, и правда лежит в памяти. */
470
- this.#unsaved = false;
471
- this.#settings = signal(readSideMenuSettings(this.#record), /* @ts-ignore */
472
- ...(ngDevMode ? [{ debugName: "#settings" }] : /* istanbul ignore next */ []));
473
- /** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */
474
- this.favoritesIcons = { ...DEFAULT_FAVORITES_ICONS, ...this.#config.icons };
475
- /** Номера меню, чьи настройки лежат в хранилище. */
476
- this.menuIds = computed(() => Object.keys(this.#settings()), /* @ts-ignore */
477
- ...(ngDevMode ? [{ debugName: "menuIds" }] : /* istanbul ignore next */ []));
478
- if (!inject(PlatformService).isPlatformBrowser) {
479
- return;
480
- }
481
- const view = inject(WINDOW);
482
- const onStorage = (event) => {
483
- if (event.key === this.#key || event.key === null) {
484
- this.#unsaved = false;
485
- this.#apply(this.#fresh());
486
- }
487
- };
488
- view.addEventListener('storage', onStorage);
489
- inject(DestroyRef).onDestroy(() => view.removeEventListener('storage', onStorage));
490
- }
491
- /** Настройки одного меню целиком, с незнакомыми полями; не сохранены — пустой объект. */
492
- settings(menuId) {
493
- return cached(this.#menus, menuId, (id) => this.#settings()[id] ?? {});
494
- }
495
- subMenuMode(menuId) {
496
- return cached(this.#modes, menuId, (id) => this.#settings()[id]?.subMenuMode ?? 'hover');
497
- }
498
- subMenuWidth(menuId) {
499
- return cached(this.#widths, menuId, (id) => this.#settings()[id]?.subMenuWidth ?? null);
500
- }
501
- /** Избранное меню в порядке списка; пусто — пустой список. */
502
- favoriteIds(menuId) {
503
- return cached(this.#favorites, menuId, (id) => this.#settings()[id]?.favorites ?? []);
504
- }
505
- /** Пункты полосы, чей блок избранного свёрнут; ничего не свёрнуто — пустой список. */
506
- favoritesCollapsed(menuId) {
507
- return cached(this.#collapsed, menuId, (id) => this.#settings()[id]?.favoritesCollapsed ?? []);
508
- }
509
- /** Сворачивает или разворачивает блок одного раздела; остальные разделы остаются как были. */
510
- setFavoritesCollapsed(menuId, sectionId, collapsed) {
511
- const current = this.#freshSettings(menuId).favoritesCollapsed ?? [];
512
- const others = current.filter((id) => id !== sectionId);
513
- this.#update(menuId, { favoritesCollapsed: collapsed ? [...others, sectionId] : others });
514
- }
515
- /** Звезда: нет в списке — встаёт последним, есть — уходит. */
516
- toggleFavorite(menuId, id) {
517
- this.#updateFavorites(menuId, (ids) => ids.includes(id) ? ids.filter((kept) => kept !== id) : [...ids, id]);
518
- }
519
- removeFavorite(menuId, id) {
520
- this.#updateFavorites(menuId, (ids) => ids.filter((kept) => kept !== id));
521
- }
522
- /** Перенос строки блока: места в блоке, скрытые номера остаются на своих. */
523
- moveVisibleSideMenuFavorite(menuId, visibleIds, from, to) {
524
- this.#updateFavorites(menuId, (ids) => moveVisibleSideMenuFavorite(ids, visibleIds, from, to));
525
- }
526
- /** Список целиком: приложение заводит избранное само, например по умолчанию для роли. */
527
- setFavorites(menuId, ids) {
528
- this.#updateFavorites(menuId, () => normalizeSideMenuFavorites(ids));
529
- }
530
- setSubMenuMode(menuId, mode) {
531
- this.#update(menuId, { subMenuMode: mode });
532
- }
533
- setSubMenuWidth(menuId, width) {
534
- this.#update(menuId, { subMenuWidth: clampSideMenuWidth(width) });
535
- }
536
- /** Настройки меню уходят только по вызову приложения: кит сам ничего не удаляет. */
537
- deleteSettings(menuId) {
538
- this.#commit(omitSideMenuSettings(this.#fresh(), normalizeSideMenuId(menuId)));
539
- }
540
- /** Прочитать хранилище → поправить поля одного меню → записать. */
541
- #update(menuId, patch) {
542
- const record = this.#fresh();
543
- const id = normalizeSideMenuId(menuId);
544
- this.#commit(patchSideMenuSettings(record, id, { ...normalizeSideMenuSettings(record[id]), ...patch }));
545
- }
546
- /** Список правится по свежей записи хранилища: соседняя вкладка могла его поменять. */
547
- #updateFavorites(menuId, next) {
548
- this.#update(menuId, { favorites: next([...(this.#freshSettings(menuId).favorites ?? [])]) });
549
- }
550
- #freshSettings(menuId) {
551
- return normalizeSideMenuSettings(this.#fresh()[normalizeSideMenuId(menuId)]);
552
- }
553
- #commit(record) {
554
- this.#apply(record);
555
- try {
556
- this.#storage?.setItem(this.#key, JSON.stringify(record));
557
- this.#unsaved = false;
558
- }
559
- catch {
560
- // хранилище закрыто или полно — настройки живут в памяти до перезагрузки
561
- this.#unsaved = true;
562
- }
563
- }
564
- #apply(record) {
565
- this.#record = record;
566
- this.#settings.set(readSideMenuSettings(record));
567
- }
568
- /** Запись из хранилища, а если оно недоступно или не приняло прошлую запись — последняя известная. */
569
- #fresh() {
570
- return this.#unsaved ? this.#record : (this.#load() ?? this.#record);
571
- }
572
- /** Пусто — хранилища нет или оно не отвечает. Сломанная запись читается пустой, но читается. */
573
- #load() {
574
- if (!this.#storage) {
575
- return null;
576
- }
577
- try {
578
- return parseSideMenuSettingsRecord(this.#storage.getItem(this.#key));
579
- }
580
- catch {
581
- return null;
582
- }
583
- }
584
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
585
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSettingsService }); }
586
- }
587
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSettingsService, decorators: [{
588
- type: Injectable
589
- }], ctorParameters: () => [] });
590
- /**
591
- * Сервис настроек бокового меню для приложения.
592
- *
593
- * ```ts
594
- * bootstrapApplication(App, { providers: [provideRtSideMenuSettings()] });
595
- * ```
596
- */
256
+ var RtSideMenuSettingsService = class RtSideMenuSettingsService {
257
+ #config;
258
+ #storage;
259
+ #key;
260
+ #menus;
261
+ #modes;
262
+ #widths;
263
+ #favorites;
264
+ #collapsed;
265
+ #record;
266
+ #unsaved;
267
+ #settings;
268
+ constructor() {
269
+ this.#config = inject(RT_SIDE_MENU_SETTINGS_CONFIG, { optional: true }) ?? {};
270
+ this.#storage = inject(LOCAL_STORAGE, { optional: true }) ?? null;
271
+ this.#key = this.#config.storageKey?.trim() || ERtStorageKeys.SideMenu;
272
+ this.#menus = /* @__PURE__ */ new Map();
273
+ this.#modes = /* @__PURE__ */ new Map();
274
+ this.#widths = /* @__PURE__ */ new Map();
275
+ this.#favorites = /* @__PURE__ */ new Map();
276
+ this.#collapsed = /* @__PURE__ */ new Map();
277
+ this.#record = this.#load() ?? {};
278
+ this.#unsaved = false;
279
+ this.#settings = signal(readSideMenuSettings(this.#record), ...ngDevMode ? [{ debugName: "#settings" }] : /* istanbul ignore next */ []);
280
+ this.favoritesIcons = {
281
+ ...DEFAULT_FAVORITES_ICONS,
282
+ ...this.#config.icons
283
+ };
284
+ this.menuIds = computed(() => Object.keys(this.#settings()), ...ngDevMode ? [{ debugName: "menuIds" }] : /* istanbul ignore next */ []);
285
+ if (!inject(PlatformService).isPlatformBrowser) return;
286
+ const view = inject(WINDOW);
287
+ const onStorage = (event) => {
288
+ if (event.key === this.#key || event.key === null) {
289
+ this.#unsaved = false;
290
+ this.#apply(this.#fresh());
291
+ }
292
+ };
293
+ view.addEventListener("storage", onStorage);
294
+ inject(DestroyRef).onDestroy(() => view.removeEventListener("storage", onStorage));
295
+ }
296
+ settings(menuId) {
297
+ return cached(this.#menus, menuId, (id) => this.#settings()[id] ?? {});
298
+ }
299
+ subMenuMode(menuId) {
300
+ return cached(this.#modes, menuId, (id) => this.#settings()[id]?.subMenuMode ?? "hover");
301
+ }
302
+ subMenuWidth(menuId) {
303
+ return cached(this.#widths, menuId, (id) => this.#settings()[id]?.subMenuWidth ?? null);
304
+ }
305
+ favoriteIds(menuId) {
306
+ return cached(this.#favorites, menuId, (id) => this.#settings()[id]?.favorites ?? []);
307
+ }
308
+ favoritesCollapsed(menuId) {
309
+ return cached(this.#collapsed, menuId, (id) => this.#settings()[id]?.favoritesCollapsed ?? []);
310
+ }
311
+ setFavoritesCollapsed(menuId, sectionId, collapsed) {
312
+ const others = (this.#freshSettings(menuId).favoritesCollapsed ?? []).filter((id) => id !== sectionId);
313
+ this.#update(menuId, { favoritesCollapsed: collapsed ? [...others, sectionId] : others });
314
+ }
315
+ toggleFavorite(menuId, id) {
316
+ this.#updateFavorites(menuId, (ids) => ids.includes(id) ? ids.filter((kept) => kept !== id) : [...ids, id]);
317
+ }
318
+ removeFavorite(menuId, id) {
319
+ this.#updateFavorites(menuId, (ids) => ids.filter((kept) => kept !== id));
320
+ }
321
+ moveVisibleSideMenuFavorite(menuId, visibleIds, from, to) {
322
+ this.#updateFavorites(menuId, (ids) => moveVisibleSideMenuFavorite(ids, visibleIds, from, to));
323
+ }
324
+ setFavorites(menuId, ids) {
325
+ this.#updateFavorites(menuId, () => normalizeSideMenuFavorites(ids));
326
+ }
327
+ setSubMenuMode(menuId, mode) {
328
+ this.#update(menuId, { subMenuMode: mode });
329
+ }
330
+ setSubMenuWidth(menuId, width) {
331
+ this.#update(menuId, { subMenuWidth: clampSideMenuWidth(width) });
332
+ }
333
+ deleteSettings(menuId) {
334
+ this.#commit(omitSideMenuSettings(this.#fresh(), normalizeSideMenuId(menuId)));
335
+ }
336
+ #update(menuId, patch) {
337
+ const record = this.#fresh();
338
+ const id = normalizeSideMenuId(menuId);
339
+ this.#commit(patchSideMenuSettings(record, id, {
340
+ ...normalizeSideMenuSettings(record[id]),
341
+ ...patch
342
+ }));
343
+ }
344
+ #updateFavorites(menuId, next) {
345
+ this.#update(menuId, { favorites: next([...this.#freshSettings(menuId).favorites ?? []]) });
346
+ }
347
+ #freshSettings(menuId) {
348
+ return normalizeSideMenuSettings(this.#fresh()[normalizeSideMenuId(menuId)]);
349
+ }
350
+ #commit(record) {
351
+ this.#apply(record);
352
+ try {
353
+ this.#storage?.setItem(this.#key, JSON.stringify(record));
354
+ this.#unsaved = false;
355
+ } catch {
356
+ this.#unsaved = true;
357
+ }
358
+ }
359
+ #apply(record) {
360
+ this.#record = record;
361
+ this.#settings.set(readSideMenuSettings(record));
362
+ }
363
+ #fresh() {
364
+ return this.#unsaved ? this.#record : this.#load() ?? this.#record;
365
+ }
366
+ #load() {
367
+ if (!this.#storage) return null;
368
+ try {
369
+ return parseSideMenuSettingsRecord(this.#storage.getItem(this.#key));
370
+ } catch {
371
+ return null;
372
+ }
373
+ }
374
+ static {
375
+ this.ɵfac = i0.ɵɵngDeclareFactory({
376
+ minVersion: "12.0.0",
377
+ version: "22.2.2",
378
+ ngImport: i0,
379
+ type: RtSideMenuSettingsService,
380
+ deps: [],
381
+ target: i0.ɵɵFactoryTarget.Injectable
382
+ });
383
+ }
384
+ static {
385
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
386
+ minVersion: "12.0.0",
387
+ version: "22.2.2",
388
+ ngImport: i0,
389
+ type: RtSideMenuSettingsService
390
+ });
391
+ }
392
+ };
393
+ i0.ɵɵngDeclareClassMetadata({
394
+ minVersion: "12.0.0",
395
+ version: "22.2.2",
396
+ ngImport: i0,
397
+ type: RtSideMenuSettingsService,
398
+ decorators: [{ type: Injectable }],
399
+ ctorParameters: () => []
400
+ });
597
401
  function provideRtSideMenuSettings(config = {}) {
598
- return makeEnvironmentProviders([{ provide: RT_SIDE_MENU_SETTINGS_CONFIG, useValue: config }, RtSideMenuSettingsService]);
402
+ return makeEnvironmentProviders([{
403
+ provide: RT_SIDE_MENU_SETTINGS_CONFIG,
404
+ useValue: config
405
+ }, RtSideMenuSettingsService]);
599
406
  }
600
-
601
- const RT_SIDE_MENU = new InjectionToken('RT_SIDE_MENU');
602
-
603
- /**
604
- * Адрес пункта так, как его строит ссылка роутера: относительно маршрута, на котором стоит меню.
605
- * Им строка подменю и переходит, и подписывает `href` — для средней кнопки и копирования ссылки.
606
- */
407
+ const RT_SIDE_MENU = new InjectionToken("RT_SIDE_MENU");
607
408
  function sideMenuLinkTree(router, route, link) {
608
- return router.createUrlTree([link], { relativeTo: route ?? undefined });
609
- }
610
- /** `href` строки подменю. Переход строка делает сама: ссылка роутера не дала бы потребителю его взять. */
611
- class RtSideMenuHrefPipe {
612
- #router = inject(Router);
613
- #route = inject(ActivatedRoute, { optional: true });
614
- transform(link) {
615
- return link ? this.#router.serializeUrl(sideMenuLinkTree(this.#router, this.#route, link)) : null;
616
- }
617
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHrefPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
618
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHrefPipe, isStandalone: true, name: "rtSideMenuHref" }); }
409
+ return router.createUrlTree([link], { relativeTo: route ?? void 0 });
619
410
  }
620
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHrefPipe, decorators: [{
621
- type: Pipe,
622
- args: [{
623
- name: 'rtSideMenuHref',
624
- }]
625
- }] });
626
-
627
- /**
628
- * Значок пункта по его `icon`: имя кита как есть, имя Material первого кита — его парой из перечня
629
- * кита, как у `rt-menu-item`. `null` — кит такой значок не рисует: пункт берёт свой шаблон меню или
630
- * стоит без значка.
631
- */
411
+ var RtSideMenuHrefPipe = class RtSideMenuHrefPipe {
412
+ #router = inject(Router);
413
+ #route = inject(ActivatedRoute, { optional: true });
414
+ transform(link) {
415
+ return link ? this.#router.serializeUrl(sideMenuLinkTree(this.#router, this.#route, link)) : null;
416
+ }
417
+ static {
418
+ this.ɵfac = i0.ɵɵngDeclareFactory({
419
+ minVersion: "12.0.0",
420
+ version: "22.2.2",
421
+ ngImport: i0,
422
+ type: RtSideMenuHrefPipe,
423
+ deps: [],
424
+ target: i0.ɵɵFactoryTarget.Pipe
425
+ });
426
+ }
427
+ static {
428
+ this.ɵpipe = i0.ɵɵngDeclarePipe({
429
+ minVersion: "14.0.0",
430
+ version: "22.2.2",
431
+ ngImport: i0,
432
+ type: RtSideMenuHrefPipe,
433
+ isStandalone: true,
434
+ name: "rtSideMenuHref"
435
+ });
436
+ }
437
+ };
438
+ i0.ɵɵngDeclareClassMetadata({
439
+ minVersion: "12.0.0",
440
+ version: "22.2.2",
441
+ ngImport: i0,
442
+ type: RtSideMenuHrefPipe,
443
+ decorators: [{
444
+ type: Pipe,
445
+ args: [{ name: "rtSideMenuHref" }]
446
+ }]
447
+ });
632
448
  function sideMenuIconName(icon) {
633
- if (!icon) {
634
- return null;
635
- }
636
- if (isKitIconName(icon)) {
637
- return icon;
638
- }
639
- return materialPairOf(icon);
449
+ if (!icon) return null;
450
+ if (isKitIconName(icon)) return icon;
451
+ return materialPairOf(icon);
640
452
  }
641
- /**
642
- * Имена значков меню, о которых оно должно предупредить: ни имени кита, ни пары в перечне. И значок
643
- * пункта, и значок кнопки строки закрывает свой шаблон меню.
644
- * Без предупреждения такой значок пуст, и пропуск не заметен. Обходит пункты на любую глубину;
645
- * каждое имя — один раз.
646
- */
647
453
  function unpairedSideMenuIcons(items, hasOwnIcon) {
648
- const found = new Set();
649
- const walk = (list) => {
650
- for (const item of list) {
651
- if (!hasOwnIcon && item.icon && sideMenuIconName(item.icon) === null) {
652
- found.add(item.icon);
653
- }
654
- if (!hasOwnIcon && item.iconButton?.icon && sideMenuIconName(item.iconButton.icon) === null) {
655
- found.add(item.iconButton.icon);
656
- }
657
- walk(item.submenu ?? []);
658
- }
659
- };
660
- walk(items);
661
- return [...found];
454
+ const found = /* @__PURE__ */ new Set();
455
+ const walk = (list) => {
456
+ for (const item of list) {
457
+ if (!hasOwnIcon && item.icon && sideMenuIconName(item.icon) === null) found.add(item.icon);
458
+ if (!hasOwnIcon && item.iconButton?.icon && sideMenuIconName(item.iconButton.icon) === null) found.add(item.iconButton.icon);
459
+ walk(item.submenu ?? []);
460
+ }
461
+ };
462
+ walk(items);
463
+ return [...found];
662
464
  }
663
- /**
664
- * Предупреждение разработчику приложения об именах из `unpairedSideMenuIcons`: имя без значка кита и
665
- * без пары рисует пункт без значка, и пропуск без предупреждения не заметен.
666
- */
667
465
  function warnUnpairedSideMenuIcons(menuId, items, hasOwnIcon) {
668
- const unpaired = unpairedSideMenuIcons(items, hasOwnIcon);
669
- if (unpaired.length) {
670
- const names = unpaired.map((name) => `«${name}»`).join(', ');
671
- // eslint-disable-next-line no-console -- предупреждение разработчику приложения: другого канала у кита нет
672
- console.warn(`rt-side-menu «${menuId}»: значков ${names} нет ни в наборе кита, ни в перечне имён Material. ` +
673
- 'Задайте имя кита или свой значок через <ng-template rtSideMenuIcon>.');
674
- }
675
- }
676
-
677
- /** Значок пункта, который рисует кит: имя кита или пара имени Material; `null` — такого нет. */
678
- class RtSideMenuIconPipe {
679
- transform(icon) {
680
- return sideMenuIconName(icon);
681
- }
682
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
683
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconPipe, isStandalone: true, name: "rtSideMenuIcon" }); }
684
- }
685
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconPipe, decorators: [{
686
- type: Pipe,
687
- args: [{
688
- name: 'rtSideMenuIcon',
689
- }]
690
- }] });
691
-
692
- /** Подпись пункта кусками: отмечено ровно то, чем она совпала с запросом поиска. */
693
- class RtSideMenuTitlePartsPipe {
694
- transform(name, query) {
695
- return splitSideMenuTitle(name ?? '', query);
696
- }
697
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuTitlePartsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
698
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuTitlePartsPipe, isStandalone: true, name: "rtSideMenuTitleParts" }); }
699
- }
700
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuTitlePartsPipe, decorators: [{
701
- type: Pipe,
702
- args: [{
703
- name: 'rtSideMenuTitleParts',
704
- }]
705
- }] });
706
-
707
- const BEM_BLOCK$2 = 'rt-side-menu-sub-item';
708
- /** Строка блока избранного и кнопка избранного в ней: фокус уходит на соседнюю до удаления строки. */
709
- const FAVORITE_ROW = '.rt-side-menu-favorites__row';
710
- const FAVORITE_BUTTON = '.rt-side-menu-sub-item__favorite button';
711
- /**
712
- * Строка подменю: пункт со ссылкой или папка со своими пунктами. Раскрытость, подсветку и запрос
713
- * строка берёт у меню — своего состояния у неё нет.
714
- *
715
- * В разделе, где приложение включило избранное, у пункта со ссылкой стоит звезда; в строке блока
716
- * избранного — кнопка «убрать» и слот `[rtSideMenuSubItemAction]`, куда блок кладёт ручку.
717
- */
718
- class RtSideMenuSubItemComponent {
719
- constructor() {
720
- this.#settings = inject(RtSideMenuSettingsService, { optional: true });
721
- this.#router = inject(Router);
722
- this.#route = inject(ActivatedRoute, { optional: true });
723
- this.#errors = inject(ErrorHandler);
724
- this.menuRef = inject(RT_SIDE_MENU);
725
- this.narrow = inject(BreakpointsService).narrow;
726
- /** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */
727
- this.favoritesIcons = this.#settings?.favoritesIcons ?? null;
728
- this.addLabel = rtKitLabel('sideMenuFavoriteAdd');
729
- this.removeLabel = rtKitLabel('sideMenuFavoriteRemove');
730
- /** Избранное включено у раздела, чьё подменю показано, и приложение поставило настройки меню. */
731
- this.favoritesOn = computed(() => !!this.#settings && sideMenuFavoritesSection(this.menuRef.menuItems(), this.menuRef.shownSubMenu()) !== null, /* @ts-ignore */
732
- ...(ngDevMode ? [{ debugName: "favoritesOn" }] : /* istanbul ignore next */ []));
733
- this.favoriteIds = computed(() => this.#settings?.favoriteIds(this.menuRef.menuId())() ?? [], /* @ts-ignore */
734
- ...(ngDevMode ? [{ debugName: "favoriteIds" }] : /* istanbul ignore next */ []));
735
- this.item = input.required(/* @ts-ignore */
736
- ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
737
- /** Строка стоит в блоке избранного: вместо звезды — «убрать», без номера и подсветки клавиатуры. */
738
- this.inFavorites = input(false, { ...(ngDevMode ? { debugName: "inFavorites" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
739
- this.clickSubMenuAction = output();
740
- this.clickSubMenuAdditionalAction = output();
741
- }
742
- #settings;
743
- #router;
744
- #route;
745
- #errors;
746
- /**
747
- * Нажатие строки уходит потребителю раньше перехода. Потребитель, взявший переход на себя,
748
- * отменяет умолчание события — тогда строка не переходит. Нажатие с клавишей или не левой
749
- * кнопкой остаётся браузеру: он открывает адрес строки сам.
750
- */
751
- onClickSubMenu(item, event) {
752
- this.clickSubMenuAction.emit({ item, event });
753
- if (event.defaultPrevented || !item.link || sideMenuPressOpensInBrowser(event)) {
754
- return;
755
- }
756
- event.preventDefault();
757
- // Отказ перехода уходит обработчику ошибок приложения, как у ссылки роутера.
758
- this.#router
759
- .navigateByUrl(sideMenuLinkTree(this.#router, this.#route, item.link))
760
- .catch((error) => this.#errors.handleError(error));
761
- }
762
- /** Кнопка потребителя в строке: ни перехода по ссылке строки, ни закрытия подменю. */
763
- onClickSubMenuAdditional(item, event) {
764
- event.preventDefault();
765
- event.stopPropagation();
766
- this.clickSubMenuAdditionalAction.emit({ data: item.iconButton?.data, event });
767
- }
768
- /** Звезда переключает избранное и больше ничего: ни перехода, ни закрытия подменю. */
769
- onToggleFavorite(item, event) {
770
- event.preventDefault();
771
- event.stopPropagation();
772
- this.#settings?.toggleFavorite(this.menuRef.menuId(), item.id);
773
- }
774
- /**
775
- * «Убрать» в строке блока. Фокус уходит на такую же кнопку соседней строки до того, как строка
776
- * исчезнет: иначе он падал бы на страницу, и человек с клавиатуры терял бы место.
777
- */
778
- onRemoveFavorite(item, event) {
779
- event.preventDefault();
780
- event.stopPropagation();
781
- const row = event.target instanceof Element ? event.target.closest(FAVORITE_ROW) : null;
782
- const neighbour = row?.nextElementSibling ?? row?.previousElementSibling;
783
- neighbour?.querySelector(FAVORITE_BUTTON)?.focus();
784
- this.#settings?.removeFavorite(this.menuRef.menuId(), item.id);
785
- }
786
- onToggleFolder(item) {
787
- this.menuRef.toggleFolder(item);
788
- }
789
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSubItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
790
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtSideMenuSubItemComponent, isStandalone: true, selector: "rt-side-menu-sub-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, inFavorites: { classPropertyName: "inFavorites", publicName: "inFavorites", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickSubMenuAction: "clickSubMenuAction", clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction" }, host: { classAttribute: "rt-side-menu-sub-item" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-side-menu-sub-item\">\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: item(), depth: 0 }\" />\n <ng-template #rowTpl let-row let-depth=\"depth\">\n @let isActive = menuRef.activeMenuIds().includes(row.id);\n @let isHighlighted = !inFavorites() && menuRef.highlightedMenuId() === row.id;\n @if (row.link) {\n <!--\n \u041D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430 \u043D\u0430 \u0441\u0430\u043C\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0435 \u2014 \u043F\u0440\u0438\u0437\u043D\u0430\u043A \u0434\u043B\u044F \u043F\u0440\u0430\u0432\u0438\u043B \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F: \u043F\u043E \u043D\u0435\u043C\u0443 \u043E\u043D\u043E \u043D\u0430\u0445\u043E\u0434\u0438\u0442 \u0441\u0442\u0440\u043E\u043A\u0443,\n \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 \u0442\u0443, \u0447\u0442\u043E \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0440\u0430\u0437\u0434\u0435\u043B\u0430. \u0421\u0442\u043E\u0438\u0442 \u0438 \u0443 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u043B\u043E\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E.\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0431\u0435\u0437 \u043D\u043E\u043C\u0435\u0440\u0430: \u043F\u043E \u043D\u043E\u043C\u0435\u0440\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443 \u0441\u043F\u0438\u0441\u043A\u0430. \u0418 \u0431\u0435\u0437\n \u0440\u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u044F \u0441\u0441\u044B\u043B\u043A\u0438: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u043D\u0430 \u0440\u0443\u0447\u043A\u0443 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0437\u0430\u0431\u0438\u0440\u0430\u043B \u0441\u0435\u0431\u0435,\n \u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u043B\u043E\u0441\u044C.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.data-item-id]=\"row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [attr.href]=\"row.link | rtSideMenuHref\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ active: isActive, highlighted: isHighlighted }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickSubMenu(row, $event)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n <!-- \u041A\u0443\u0441\u043E\u043A \u0431\u0435\u0437 \u043E\u0442\u043C\u0435\u0442\u043A\u0438 \u0442\u043E\u0436\u0435 \u0443\u0437\u0435\u043B: \u0433\u043E\u043B\u0430\u044F \u0438\u043D\u0442\u0435\u0440\u043F\u043E\u043B\u044F\u0446\u0438\u044F \u0440\u0430\u0437\u0440\u044B\u0432\u0430\u043B\u0430 \u0431\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u044C \u043F\u0440\u043E\u0431\u0435\u043B\u0430\u043C\u0438. -->\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <!--\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u043D\u0435\u0441\u0451\u0442 \u00AB\u0443\u0431\u0440\u0430\u0442\u044C\u00BB, \u0430 \u043D\u0435 \u0437\u0432\u0435\u0437\u0434\u0443: \u0432\u0441\u0451 \u0432 \u0431\u043B\u043E\u043A\u0435 \u0443\u0436\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C. \u0417\u0432\u0435\u0437\u0434\u0430 \u2014\n \u0443 \u043F\u0443\u043D\u043A\u0442\u0430 \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u043C \u0432 \u0440\u0430\u0437\u0434\u0435\u043B\u0435 \u0441 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u043C: \u043F\u043E\u043B\u0430\u044F \u2014 \u043D\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C,\n \u0437\u0430\u043A\u0440\u0430\u0448\u0435\u043D\u043D\u0430\u044F \u0446\u0432\u0435\u0442\u043E\u043C \u0442\u0435\u043C\u044B \u2014 \u0432 \u043D\u0451\u043C. \u0418\u043C\u044F \u043A\u043D\u043E\u043F\u043A\u0438 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\n [rtMod]=\"{ remove: true }\"\n [icon]=\"favoritesIcons.remove\"\n [ariaLabel]=\"removeLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : removeLabel()\"\n (clicked)=\"onRemoveFavorite(row, $event)\" />\n } @else if (favoritesOn() && !row.favoriteDisabled) {\n @let isFavorite = favoriteIds().includes(row.id);\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-star\"\n size=\"sm\"\n [rtMod]=\"{ on: isFavorite }\"\n [icon]=\"isFavorite ? 'star-fill' : 'star'\"\n [iconFill]=\"isFavorite\"\n [ariaLabel]=\"isFavorite ? removeLabel() : addLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : isFavorite ? removeLabel() : addLabel()\"\n (clicked)=\"onToggleFavorite(row, $event)\" />\n }\n\n <!-- \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0441\u0442\u0440\u043E\u043A\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432\u043B\u0430\u0434\u0435\u043B\u0435\u0446: \u0431\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u043A\u043B\u0430\u0434\u0451\u0442 \u0441\u044E\u0434\u0430 \u0440\u0443\u0447\u043A\u0443. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u0432\u0438\u0434\u043D\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0438 \u0441\u0442\u043E\u0438\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439: \u043A\u043D\u043E\u043F\u043A\u0438 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0432\u0441\u0442\u0430\u044E\u0442 \u043F\u0435\u0440\u0435\u0434 \u043D\u0435\u0439. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- \u0418\u043C\u044F \u0431\u0435\u0437 \u0437\u043D\u0430\u0447\u043A\u0430 \u043A\u0438\u0442\u0430 \u0431\u0435\u0440\u0451\u0442 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E, \u043A\u0430\u043A \u0437\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438. \u0411\u0435\u0437 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0430 \u043F\u0443\u0441\u0442\u0430 \u0438\n \u043D\u0435\u0437\u043D\u0430\u043A\u043E\u043C\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0437\u043D\u0430\u0447\u043A\u0443 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442: \u0437\u043D\u0430\u0447\u043A\u0430 \u0441 \u0442\u0430\u043A\u0438\u043C \u0438\u043C\u0435\u043D\u0435\u043C \u043D\u0435\u0442, \u0438 \u0436\u0434\u0430\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043A\u043E\u043C\u0443. -->\n @let buttonKitIcon = buttonIcon | rtSideMenuIcon;\n @let buttonOwnTpl = buttonKitIcon ? null : menuRef.ownIconTpl();\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"side-menu-sub-item-action\"\n size=\"sm\"\n [icon]=\"buttonKitIcon\"\n [iconFill]=\"menuRef.subItemIconFill()\"\n [ariaLabel]=\"row.name ?? ''\"\n (clicked)=\"onClickSubMenuAdditional(row, $event)\">\n @if (buttonOwnTpl) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-button-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"buttonOwnTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, icon: buttonIcon, slot: 'iconButton' }\" />\n </span>\n }\n </rt-icon-button>\n }\n </a>\n } @else if (row.submenu) {\n <!--\n \u041F\u0430\u043F\u043A\u0430 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430. \u0420\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0438 \u0441 \u043F\u0443\u0441\u0442\u044B\u043C \u0441\u043E\u0441\u0442\u0430\u0432\u043E\u043C: \u043E\u0442\u0431\u043E\u0440 \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0442\u0430\u043A\n \u0442\u0443, \u0447\u0442\u043E \u0441\u043E\u0432\u043F\u0430\u043B\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u044C\u044E. \u0428\u0435\u0432\u0440\u043E\u043D\u0430 \u0443 \u0442\u0430\u043A\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 \u043D\u0435\u0442 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E. \u0420\u0430\u0441\u043A\u0440\u044B\u0442\u0438\u0435\n \u0434\u0435\u0440\u0436\u0438\u0442 \u043C\u0435\u043D\u044E: \u043F\u0430\u043D\u0435\u043B\u044C \u0441\u043E\u043E\u0431\u0449\u0430\u0435\u0442 \u043E \u043D\u0430\u0436\u0430\u0442\u0438\u0438, \u043C\u0435\u043D\u044E \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u0430\u043F\u043A\u0443. \u041A\u043D\u043E\u043F\u043A\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430\n \u043E\u0442\u0434\u0430\u043D \u043D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430 \u2014 \u043F\u043E \u043D\u0435\u043C\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043C\u0435\u043D\u044E \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [attr.data-item-id]=\"row.id\"\n [hideToggle]=\"!row.submenu.length\"\n [expanded]=\"isOpen\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ highlighted: isHighlighted }\"\n (expandedChange)=\"onToggleFolder(row)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" qa-dataid=\"side-menu-folder\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div rtElem=\"children\" role=\"group\">\n @for (child of row.submenu; track child.id) {\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: child, depth: depth + 1 }\" />\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n </ng-template>\n\n <!-- \u0417\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438: \u0438\u043C\u044F \u043A\u0438\u0442\u0430 \u0438\u043B\u0438 \u043F\u0430\u0440\u0430 \u0438\u043C\u0435\u043D\u0438 Material, \u0430 \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" [fill]=\"menuRef.subItemIconFill()\" />\n } @else if (menuRef.ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: row, icon: row.icon, slot: 'icon' }\" />\n </span>\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-sub-item{--rt-side-menu-sub-item-radius: var(--rt-radius-full);--rt-side-menu-sub-item-transition-duration: var(--rt-duration-fast);--rt-side-menu-sub-item-match-bg: var(--rt-yellow-400);--rt-side-menu-sub-item-match-color: var(--rt-neutral-950);--rt-side-menu-sub-item-match-radius: var(--rt-radius-xs);--rt-side-menu-sub-item-min-height: var(--rt-control-height-md);--rt-side-menu-sub-item-font-size: var(--rt-text-sm);--rt-side-menu-sub-item-padding-start: var(--rt-space-md);--rt-side-menu-sub-item-padding-end: var(--rt-space-sm);--rt-side-menu-sub-item-color: var(--rt-color-text-primary);--rt-side-menu-sub-item-gap: var(--rt-space-sm);--rt-side-menu-sub-item-folder-padding-start: var(--rt-side-menu-sub-item-padding-start);--rt-side-menu-sub-item-chevron-color: var(--rt-color-text-muted);--rt-side-menu-sub-item-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-button-radius: var(--rt-radius-md);--rt-side-menu-sub-item-button-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-action-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-color: var(--rt-color-text-primary);display:block;letter-spacing:var(--rt-side-menu-sub-item-letter-spacing, inherit)}.rt-side-menu-sub-item__row{--rt-side-menu-sub-item-depth: 0;display:flex;width:100%;min-height:var(--rt-side-menu-sub-item-min-height);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-sub-item-radius);background:transparent;color:var(--rt-side-menu-sub-item-color);cursor:pointer;font:inherit;font-size:var(--rt-side-menu-sub-item-font-size);gap:var(--rt-side-menu-sub-item-gap);padding-block:var(--rt-space-2);padding-inline:calc(var(--rt-side-menu-sub-item-padding-start) * (1 + var(--rt-side-menu-sub-item-depth))) var(--rt-side-menu-sub-item-padding-end);text-align:start;text-decoration:none;transition:background-color var(--rt-side-menu-sub-item-transition-duration) ease}.rt-side-menu-sub-item__row:hover,.rt-side-menu-sub-item__row:active,.rt-side-menu-sub-item__row--highlighted{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__row:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu-sub-item__row--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu-sub-item__icon{--rt-icon-step-sm: var(--rt-side-menu-sub-item-icon-size);flex-shrink:0;color:inherit}.rt-side-menu-sub-item__own-icon{--rt-side-menu-sub-item-own-icon-size: var(--rt-size-4);display:inline-flex;width:var(--rt-side-menu-sub-item-own-icon-size);height:var(--rt-side-menu-sub-item-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu-sub-item__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu-sub-item__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-sub-item__part{color:inherit}.rt-side-menu-sub-item__match{border-radius:var(--rt-side-menu-sub-item-match-radius);background:var(--rt-side-menu-sub-item-match-bg);color:var(--rt-side-menu-sub-item-match-color)}.rt-side-menu-sub-item__action{--rt-side-menu-sub-item-action-ring-width: var(--rt-border-width-thin);--rt-side-menu-sub-item-action-radius: var(--rt-radius-full);flex-shrink:0;border-radius:var(--rt-side-menu-sub-item-action-radius);outline:var(--rt-side-menu-sub-item-action-ring-width) solid transparent;outline-offset:calc(var(--rt-side-menu-sub-item-action-ring-width) * -1)}.rt-side-menu-sub-item__action:hover{outline-color:var(--rt-color-border-strong)}.rt-side-menu-sub-item__action>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-action-size)}.rt-side-menu-sub-item__action,.rt-side-menu-sub-item__favorite{--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size)}.rt-side-menu-sub-item__action>.rt-icon-button,.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-sub-item__favorite{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);flex-shrink:0;opacity:0}.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size)}.rt-side-menu-sub-item__favorite--on{--rt-icon-button-color: var(--rt-color-action-primary);opacity:1}@media(hover:hover){.rt-side-menu-sub-item__favorite--remove:hover{--rt-icon-button-color: var(--rt-color-state-danger)}}.rt-side-menu-sub-item__favorite--remove:has(:focus-visible){--rt-icon-button-color: var(--rt-color-state-danger)}@media(hover:none){.rt-side-menu-sub-item__favorite{opacity:1}}.rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{opacity:1}.rt-side-menu-sub-item__folder.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-side-menu-sub-item-transition-duration);--rt-expansion-panel-header-radius: var(--rt-side-menu-sub-item-radius);--rt-expansion-panel-header-min-height: var(--rt-side-menu-sub-item-min-height);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: calc( var(--rt-side-menu-sub-item-folder-padding-start) * (1 + var(--rt-side-menu-sub-item-depth)) );--rt-expansion-panel-header-padding-end: var(--rt-side-menu-sub-item-padding-end);--rt-expansion-panel-header-gap: var(--rt-side-menu-sub-item-gap);--rt-expansion-panel-chevron-color: var(--rt-side-menu-sub-item-chevron-color);--rt-expansion-panel-header-font-size: var(--rt-side-menu-sub-item-font-size);--rt-expansion-panel-header-color: var(--rt-side-menu-sub-item-color);--rt-expansion-panel-header-bg: transparent;--rt-expansion-panel-header-hover-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__folder--highlighted.rt-expansion-panel{--rt-expansion-panel-header-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__children{display:flex;flex-direction:column}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__favorite:not(.rt-side-menu-sub-item__favorite--on){overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{overflow:visible;width:auto;margin-inline-start:0}}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtExpansionPanelComponent, selector: "rt-expansion-panel", inputs: ["expanded", "disabled", "hideToggle", "appearance", "headerId", "ariaLabel"], outputs: ["expandedChange"] }, { kind: "directive", type: RtExpansionPanelContentDirective, selector: "ng-template[rtExpansionPanelContent]" }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "directive", type: RtTooltipDirective, selector: "[rtTooltip]", inputs: ["rtTooltip", "rtTooltipPlacement", "rtTooltipWhenTruncated"], exportAs: ["rtTooltip"] }, { kind: "pipe", type: RtSideMenuTitlePartsPipe, name: "rtSideMenuTitleParts" }, { kind: "pipe", type: RtSideMenuIconPipe, name: "rtSideMenuIcon" }, { kind: "pipe", type: RtSideMenuHrefPipe, name: "rtSideMenuHref" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
466
+ const unpaired = unpairedSideMenuIcons(items, hasOwnIcon);
467
+ if (unpaired.length) {
468
+ const names = unpaired.map((name) => `«${name}»`).join(", ");
469
+ console.warn(`rt-side-menu «${menuId}»: значков ${names} нет ни в наборе кита, ни в перечне имён Material. Задайте имя кита или свой значок через <ng-template rtSideMenuIcon>.`);
470
+ }
791
471
  }
792
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSubItemComponent, decorators: [{
793
- type: Component,
794
- args: [{ selector: 'rt-side-menu-sub-item', host: { class: BEM_BLOCK$2 }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
795
- NgTemplateOutlet,
796
- BlockDirective,
797
- ElemDirective,
798
- ModDirective,
799
- RtExpansionPanelComponent,
800
- RtExpansionPanelContentDirective,
801
- RtIconComponent,
802
- RtIconButtonComponent,
803
- RtTooltipDirective,
804
- RtSideMenuTitlePartsPipe,
805
- RtSideMenuIconPipe,
806
- RtSideMenuHrefPipe,
807
- ], template: "<ng-container rtBlock=\"rt-side-menu-sub-item\">\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: item(), depth: 0 }\" />\n <ng-template #rowTpl let-row let-depth=\"depth\">\n @let isActive = menuRef.activeMenuIds().includes(row.id);\n @let isHighlighted = !inFavorites() && menuRef.highlightedMenuId() === row.id;\n @if (row.link) {\n <!--\n \u041D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430 \u043D\u0430 \u0441\u0430\u043C\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0435 \u2014 \u043F\u0440\u0438\u0437\u043D\u0430\u043A \u0434\u043B\u044F \u043F\u0440\u0430\u0432\u0438\u043B \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F: \u043F\u043E \u043D\u0435\u043C\u0443 \u043E\u043D\u043E \u043D\u0430\u0445\u043E\u0434\u0438\u0442 \u0441\u0442\u0440\u043E\u043A\u0443,\n \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 \u0442\u0443, \u0447\u0442\u043E \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0440\u0430\u0437\u0434\u0435\u043B\u0430. \u0421\u0442\u043E\u0438\u0442 \u0438 \u0443 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u043B\u043E\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E.\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0431\u0435\u0437 \u043D\u043E\u043C\u0435\u0440\u0430: \u043F\u043E \u043D\u043E\u043C\u0435\u0440\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443 \u0441\u043F\u0438\u0441\u043A\u0430. \u0418 \u0431\u0435\u0437\n \u0440\u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u044F \u0441\u0441\u044B\u043B\u043A\u0438: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u043D\u0430 \u0440\u0443\u0447\u043A\u0443 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0437\u0430\u0431\u0438\u0440\u0430\u043B \u0441\u0435\u0431\u0435,\n \u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u043B\u043E\u0441\u044C.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.data-item-id]=\"row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [attr.href]=\"row.link | rtSideMenuHref\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ active: isActive, highlighted: isHighlighted }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickSubMenu(row, $event)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n <!-- \u041A\u0443\u0441\u043E\u043A \u0431\u0435\u0437 \u043E\u0442\u043C\u0435\u0442\u043A\u0438 \u0442\u043E\u0436\u0435 \u0443\u0437\u0435\u043B: \u0433\u043E\u043B\u0430\u044F \u0438\u043D\u0442\u0435\u0440\u043F\u043E\u043B\u044F\u0446\u0438\u044F \u0440\u0430\u0437\u0440\u044B\u0432\u0430\u043B\u0430 \u0431\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u044C \u043F\u0440\u043E\u0431\u0435\u043B\u0430\u043C\u0438. -->\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <!--\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u043D\u0435\u0441\u0451\u0442 \u00AB\u0443\u0431\u0440\u0430\u0442\u044C\u00BB, \u0430 \u043D\u0435 \u0437\u0432\u0435\u0437\u0434\u0443: \u0432\u0441\u0451 \u0432 \u0431\u043B\u043E\u043A\u0435 \u0443\u0436\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C. \u0417\u0432\u0435\u0437\u0434\u0430 \u2014\n \u0443 \u043F\u0443\u043D\u043A\u0442\u0430 \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u043C \u0432 \u0440\u0430\u0437\u0434\u0435\u043B\u0435 \u0441 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u043C: \u043F\u043E\u043B\u0430\u044F \u2014 \u043D\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C,\n \u0437\u0430\u043A\u0440\u0430\u0448\u0435\u043D\u043D\u0430\u044F \u0446\u0432\u0435\u0442\u043E\u043C \u0442\u0435\u043C\u044B \u2014 \u0432 \u043D\u0451\u043C. \u0418\u043C\u044F \u043A\u043D\u043E\u043F\u043A\u0438 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\n [rtMod]=\"{ remove: true }\"\n [icon]=\"favoritesIcons.remove\"\n [ariaLabel]=\"removeLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : removeLabel()\"\n (clicked)=\"onRemoveFavorite(row, $event)\" />\n } @else if (favoritesOn() && !row.favoriteDisabled) {\n @let isFavorite = favoriteIds().includes(row.id);\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-star\"\n size=\"sm\"\n [rtMod]=\"{ on: isFavorite }\"\n [icon]=\"isFavorite ? 'star-fill' : 'star'\"\n [iconFill]=\"isFavorite\"\n [ariaLabel]=\"isFavorite ? removeLabel() : addLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : isFavorite ? removeLabel() : addLabel()\"\n (clicked)=\"onToggleFavorite(row, $event)\" />\n }\n\n <!-- \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0441\u0442\u0440\u043E\u043A\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432\u043B\u0430\u0434\u0435\u043B\u0435\u0446: \u0431\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u043A\u043B\u0430\u0434\u0451\u0442 \u0441\u044E\u0434\u0430 \u0440\u0443\u0447\u043A\u0443. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u0432\u0438\u0434\u043D\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0438 \u0441\u0442\u043E\u0438\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439: \u043A\u043D\u043E\u043F\u043A\u0438 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0432\u0441\u0442\u0430\u044E\u0442 \u043F\u0435\u0440\u0435\u0434 \u043D\u0435\u0439. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- \u0418\u043C\u044F \u0431\u0435\u0437 \u0437\u043D\u0430\u0447\u043A\u0430 \u043A\u0438\u0442\u0430 \u0431\u0435\u0440\u0451\u0442 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E, \u043A\u0430\u043A \u0437\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438. \u0411\u0435\u0437 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0430 \u043F\u0443\u0441\u0442\u0430 \u0438\n \u043D\u0435\u0437\u043D\u0430\u043A\u043E\u043C\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0437\u043D\u0430\u0447\u043A\u0443 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442: \u0437\u043D\u0430\u0447\u043A\u0430 \u0441 \u0442\u0430\u043A\u0438\u043C \u0438\u043C\u0435\u043D\u0435\u043C \u043D\u0435\u0442, \u0438 \u0436\u0434\u0430\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043A\u043E\u043C\u0443. -->\n @let buttonKitIcon = buttonIcon | rtSideMenuIcon;\n @let buttonOwnTpl = buttonKitIcon ? null : menuRef.ownIconTpl();\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"side-menu-sub-item-action\"\n size=\"sm\"\n [icon]=\"buttonKitIcon\"\n [iconFill]=\"menuRef.subItemIconFill()\"\n [ariaLabel]=\"row.name ?? ''\"\n (clicked)=\"onClickSubMenuAdditional(row, $event)\">\n @if (buttonOwnTpl) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-button-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"buttonOwnTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, icon: buttonIcon, slot: 'iconButton' }\" />\n </span>\n }\n </rt-icon-button>\n }\n </a>\n } @else if (row.submenu) {\n <!--\n \u041F\u0430\u043F\u043A\u0430 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430. \u0420\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0438 \u0441 \u043F\u0443\u0441\u0442\u044B\u043C \u0441\u043E\u0441\u0442\u0430\u0432\u043E\u043C: \u043E\u0442\u0431\u043E\u0440 \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0442\u0430\u043A\n \u0442\u0443, \u0447\u0442\u043E \u0441\u043E\u0432\u043F\u0430\u043B\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u044C\u044E. \u0428\u0435\u0432\u0440\u043E\u043D\u0430 \u0443 \u0442\u0430\u043A\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 \u043D\u0435\u0442 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E. \u0420\u0430\u0441\u043A\u0440\u044B\u0442\u0438\u0435\n \u0434\u0435\u0440\u0436\u0438\u0442 \u043C\u0435\u043D\u044E: \u043F\u0430\u043D\u0435\u043B\u044C \u0441\u043E\u043E\u0431\u0449\u0430\u0435\u0442 \u043E \u043D\u0430\u0436\u0430\u0442\u0438\u0438, \u043C\u0435\u043D\u044E \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u0430\u043F\u043A\u0443. \u041A\u043D\u043E\u043F\u043A\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430\n \u043E\u0442\u0434\u0430\u043D \u043D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430 \u2014 \u043F\u043E \u043D\u0435\u043C\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043C\u0435\u043D\u044E \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [attr.data-item-id]=\"row.id\"\n [hideToggle]=\"!row.submenu.length\"\n [expanded]=\"isOpen\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ highlighted: isHighlighted }\"\n (expandedChange)=\"onToggleFolder(row)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" qa-dataid=\"side-menu-folder\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div rtElem=\"children\" role=\"group\">\n @for (child of row.submenu; track child.id) {\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: child, depth: depth + 1 }\" />\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n </ng-template>\n\n <!-- \u0417\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438: \u0438\u043C\u044F \u043A\u0438\u0442\u0430 \u0438\u043B\u0438 \u043F\u0430\u0440\u0430 \u0438\u043C\u0435\u043D\u0438 Material, \u0430 \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" [fill]=\"menuRef.subItemIconFill()\" />\n } @else if (menuRef.ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: row, icon: row.icon, slot: 'icon' }\" />\n </span>\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-sub-item{--rt-side-menu-sub-item-radius: var(--rt-radius-full);--rt-side-menu-sub-item-transition-duration: var(--rt-duration-fast);--rt-side-menu-sub-item-match-bg: var(--rt-yellow-400);--rt-side-menu-sub-item-match-color: var(--rt-neutral-950);--rt-side-menu-sub-item-match-radius: var(--rt-radius-xs);--rt-side-menu-sub-item-min-height: var(--rt-control-height-md);--rt-side-menu-sub-item-font-size: var(--rt-text-sm);--rt-side-menu-sub-item-padding-start: var(--rt-space-md);--rt-side-menu-sub-item-padding-end: var(--rt-space-sm);--rt-side-menu-sub-item-color: var(--rt-color-text-primary);--rt-side-menu-sub-item-gap: var(--rt-space-sm);--rt-side-menu-sub-item-folder-padding-start: var(--rt-side-menu-sub-item-padding-start);--rt-side-menu-sub-item-chevron-color: var(--rt-color-text-muted);--rt-side-menu-sub-item-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-button-radius: var(--rt-radius-md);--rt-side-menu-sub-item-button-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-action-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-color: var(--rt-color-text-primary);display:block;letter-spacing:var(--rt-side-menu-sub-item-letter-spacing, inherit)}.rt-side-menu-sub-item__row{--rt-side-menu-sub-item-depth: 0;display:flex;width:100%;min-height:var(--rt-side-menu-sub-item-min-height);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-sub-item-radius);background:transparent;color:var(--rt-side-menu-sub-item-color);cursor:pointer;font:inherit;font-size:var(--rt-side-menu-sub-item-font-size);gap:var(--rt-side-menu-sub-item-gap);padding-block:var(--rt-space-2);padding-inline:calc(var(--rt-side-menu-sub-item-padding-start) * (1 + var(--rt-side-menu-sub-item-depth))) var(--rt-side-menu-sub-item-padding-end);text-align:start;text-decoration:none;transition:background-color var(--rt-side-menu-sub-item-transition-duration) ease}.rt-side-menu-sub-item__row:hover,.rt-side-menu-sub-item__row:active,.rt-side-menu-sub-item__row--highlighted{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__row:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu-sub-item__row--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu-sub-item__icon{--rt-icon-step-sm: var(--rt-side-menu-sub-item-icon-size);flex-shrink:0;color:inherit}.rt-side-menu-sub-item__own-icon{--rt-side-menu-sub-item-own-icon-size: var(--rt-size-4);display:inline-flex;width:var(--rt-side-menu-sub-item-own-icon-size);height:var(--rt-side-menu-sub-item-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu-sub-item__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu-sub-item__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-sub-item__part{color:inherit}.rt-side-menu-sub-item__match{border-radius:var(--rt-side-menu-sub-item-match-radius);background:var(--rt-side-menu-sub-item-match-bg);color:var(--rt-side-menu-sub-item-match-color)}.rt-side-menu-sub-item__action{--rt-side-menu-sub-item-action-ring-width: var(--rt-border-width-thin);--rt-side-menu-sub-item-action-radius: var(--rt-radius-full);flex-shrink:0;border-radius:var(--rt-side-menu-sub-item-action-radius);outline:var(--rt-side-menu-sub-item-action-ring-width) solid transparent;outline-offset:calc(var(--rt-side-menu-sub-item-action-ring-width) * -1)}.rt-side-menu-sub-item__action:hover{outline-color:var(--rt-color-border-strong)}.rt-side-menu-sub-item__action>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-action-size)}.rt-side-menu-sub-item__action,.rt-side-menu-sub-item__favorite{--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size)}.rt-side-menu-sub-item__action>.rt-icon-button,.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-sub-item__favorite{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);flex-shrink:0;opacity:0}.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size)}.rt-side-menu-sub-item__favorite--on{--rt-icon-button-color: var(--rt-color-action-primary);opacity:1}@media(hover:hover){.rt-side-menu-sub-item__favorite--remove:hover{--rt-icon-button-color: var(--rt-color-state-danger)}}.rt-side-menu-sub-item__favorite--remove:has(:focus-visible){--rt-icon-button-color: var(--rt-color-state-danger)}@media(hover:none){.rt-side-menu-sub-item__favorite{opacity:1}}.rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{opacity:1}.rt-side-menu-sub-item__folder.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-side-menu-sub-item-transition-duration);--rt-expansion-panel-header-radius: var(--rt-side-menu-sub-item-radius);--rt-expansion-panel-header-min-height: var(--rt-side-menu-sub-item-min-height);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: calc( var(--rt-side-menu-sub-item-folder-padding-start) * (1 + var(--rt-side-menu-sub-item-depth)) );--rt-expansion-panel-header-padding-end: var(--rt-side-menu-sub-item-padding-end);--rt-expansion-panel-header-gap: var(--rt-side-menu-sub-item-gap);--rt-expansion-panel-chevron-color: var(--rt-side-menu-sub-item-chevron-color);--rt-expansion-panel-header-font-size: var(--rt-side-menu-sub-item-font-size);--rt-expansion-panel-header-color: var(--rt-side-menu-sub-item-color);--rt-expansion-panel-header-bg: transparent;--rt-expansion-panel-header-hover-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__folder--highlighted.rt-expansion-panel{--rt-expansion-panel-header-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__children{display:flex;flex-direction:column}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__favorite:not(.rt-side-menu-sub-item__favorite--on){overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{overflow:visible;width:auto;margin-inline-start:0}}}\n"] }]
808
- }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], inFavorites: [{ type: i0.Input, args: [{ isSignal: true, alias: "inFavorites", required: false }] }], clickSubMenuAction: [{ type: i0.Output, args: ["clickSubMenuAction"] }], clickSubMenuAdditionalAction: [{ type: i0.Output, args: ["clickSubMenuAdditionalAction"] }] } });
809
-
810
- const BEM_BLOCK$1 = 'rt-side-menu-favorites';
811
- /** Панель подменю: уход указателя с неё закрывает подменю, открытое наведением. */
812
- const SUB_MENU_PANEL = '.rt-side-menu__panel';
813
- /**
814
- * Блок избранного вверху подменю: выбранные человеком разделы в порядке его списка. Перенос блока
815
- * первого кита на части второго, без Material: блок — раскрывающаяся панель кита, как папка
816
- * подменю, строки тянутся за ручку перетаскиванием CDK.
817
- *
818
- * Строки собираются из пунктов самого меню, а не из хранилища: подпись следует языку пунктов, адрес
819
- * — объявлению. Номер без пункта в меню не показывается, но из списка не уходит. Строку рисует тот же
820
- * подпункт, что и в списке.
821
- *
822
- * Избранное у каждого пункта полосы своё и по умолчанию выключено: блок стоит только в подменю пункта
823
- * с флагом `favorites`. Без настроек меню (`provideRtSideMenuSettings()`) блока нет вовсе. Показать
824
- * нечего — блок места не занимает.
825
- */
826
- class RtSideMenuFavoritesComponent {
827
- #menu;
828
- #settings;
829
- #injector;
830
- #host;
831
- /** Строку тянут: подменю держится открытым, пока её не бросят. */
832
- #dragging;
833
- #section;
834
- constructor() {
835
- this.#menu = inject(RT_SIDE_MENU);
836
- this.#settings = inject(RtSideMenuSettingsService, { optional: true });
837
- this.#injector = inject(Injector);
838
- this.#host = inject(ElementRef);
839
- /** Строку тянут: подменю держится открытым, пока её не бросят. */
840
- this.#dragging = signal(false, /* @ts-ignore */
841
- ...(ngDevMode ? [{ debugName: "#dragging" }] : /* istanbul ignore next */ []));
842
- this.#section = computed(() => this.#settings ? sideMenuFavoritesSection(this.#menu.menuItems(), this.#menu.shownSubMenu()) : null, /* @ts-ignore */
843
- ...(ngDevMode ? [{ debugName: "#section" }] : /* istanbul ignore next */ []));
844
- /** В поиске что-то набрано: блок показывает совпавшие строки раскрытым, сохранённое не трогая. */
845
- this.searching = computed(() => this.#menu.subMenuQuery().trim() !== '', /* @ts-ignore */
846
- ...(ngDevMode ? [{ debugName: "searching" }] : /* istanbul ignore next */ []));
847
- this.rows = computed(() => {
848
- const section = this.#section();
849
- if (!this.#settings || !section || (this.searching() && !this.#menu.isFavoritesSearchShown())) {
850
- return [];
851
- }
852
- const found = findSideMenuFavoriteItems([section], this.#settings.favoriteIds(this.#menu.menuId())());
853
- return filterSideMenuItems(found, this.#menu.subMenuQuery());
854
- }, /* @ts-ignore */
855
- ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
856
- /**
857
- * Блок раздела свёрнут: заголовок и черта остаются, строк нет. Состояние своё у каждого раздела и
858
- * лежит в настройках меню. Поиск его не меняет: на время поиска блок стоит раскрытым.
859
- */
860
- this.collapsed = computed(() => {
861
- const section = this.#section();
862
- return !!section && !!this.#settings?.favoritesCollapsed(this.#menu.menuId())().includes(section.id);
863
- }, /* @ts-ignore */
864
- ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
865
- /**
866
- * Раскрытие панели блока. Начинается заново с сохранённого выбора при каждой его смене и при
867
- * входе в поиск и выходе из него: свёрнутый в поиске блок — выбор на время поиска, он не
868
- * сохраняется и с концом поиска возвращается к сохранённому.
869
- */
870
- this.open = linkedSignal({ ...(ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {}), source: () => ({ collapsed: this.collapsed(), searching: this.searching() }),
871
- computation: (state) => !state.collapsed || state.searching });
872
- /** Заголовок показывает число строк: всегда, у свёрнутого блока или никогда — по входу меню. */
873
- this.countShown = computed(() => {
874
- const mode = this.#menu.favoritesCount();
875
- return mode === 'always' || (mode === 'collapsed' && !this.open());
876
- }, /* @ts-ignore */
877
- ...(ngDevMode ? [{ debugName: "countShown" }] : /* istanbul ignore next */ []));
878
- /** Ручки строк по порядку: стрелка возвращает фокус на ручку переставленной строки. */
879
- this.handles = viewChildren('handle', { ...(ngDevMode ? { debugName: "handles" } : /* istanbul ignore next */ {}), read: ElementRef });
880
- this.dragIcon = this.#settings?.favoritesIcons.drag ?? 'arrows-v';
881
- /** Узкий экран: подсказка у ручки не показывается — наводиться там нечем. */
882
- this.narrow = inject(BreakpointsService).narrow;
883
- this.titleLabel = rtKitLabel('sideMenuFavorites');
884
- this.dragLabel = rtKitLabel('sideMenuFavoriteDrag');
885
- this.expandLabel = rtKitLabel('sideMenuFavoritesExpand');
886
- this.collapseLabel = rtKitLabel('sideMenuFavoritesCollapse');
887
- this.clickSubMenuAction = output();
888
- this.clickSubMenuAdditionalAction = output();
889
- // Блок уничтожили посреди тяги — указатель увёл подменю на другой раздел. CDK закрывает тягу
890
- // без события конца, и удержание осталось бы навсегда.
891
- inject(DestroyRef).onDestroy(() => this.#release());
892
- }
893
- /** Заголовок нажат: выбор ложится в настройки меню. Во время поиска он не сохраняется. */
894
- onToggle(expanded) {
895
- const section = this.#section();
896
- if (this.#settings && section && !this.searching()) {
897
- this.#settings.setFavoritesCollapsed(this.#menu.menuId(), section.id, !expanded);
898
- }
899
- }
900
- /** Строка в руке: подменю, открытое наведением, не закрывается, когда рука выходит за панель. */
901
- onDragStart() {
902
- this.#dragging.set(true);
903
- this.#menu.holdSubMenu(true);
904
- }
905
- /** Строка брошена за панелью: уход указателя, пропущенный за время тяги, срабатывает сейчас. */
906
- onDragEnd(event) {
907
- this.#release();
908
- const panel = this.#host.nativeElement.closest(SUB_MENU_PANEL);
909
- if (panel && isSideMenuDropOutside(panel.getBoundingClientRect(), event.dropPoint)) {
910
- this.#menu.toggleSubMenu();
911
- }
912
- }
913
- /** Брошенная вне блока строка ничего не меняет: убирает из избранного кнопка, а не перетаскивание. */
914
- onDrop(event) {
915
- if (event.isPointerOverContainer) {
916
- this.#move(event.previousIndex, event.currentIndex);
917
- }
918
- }
919
- /** Стрелка на ручке переставляет строку на соседнее место, и фокус едет вместе с ручкой. */
920
- onHandleKey(event, index, step) {
921
- // Стрелки не уходят в меню: там они ведут подсветку по списку раздела.
922
- event.preventDefault();
923
- event.stopPropagation();
924
- const target = index + step;
925
- if (target < 0 || target >= this.rows().length) {
926
- return;
927
- }
928
- this.#move(index, target);
929
- afterNextRender(() => this.handles()[target]?.nativeElement.querySelector('button')?.focus(), {
930
- injector: this.#injector,
931
- });
932
- }
933
- /** Нажатие ручки — не переход по строке: она стоит внутри ссылки пункта. */
934
- onHandleClick(event) {
935
- event.preventDefault();
936
- event.stopPropagation();
937
- }
938
- #release() {
939
- if (!this.#dragging()) {
940
- return;
941
- }
942
- this.#dragging.set(false);
943
- this.#menu.holdSubMenu(false);
944
- }
945
- /** Место в блоке переводится в место списка по свежей записи хранилища: скрытые номера остаются на своих. */
946
- #move(from, to) {
947
- if (!this.#settings || from === to) {
948
- return;
949
- }
950
- const visible = this.rows().map((item) => item.id);
951
- this.#settings.moveVisibleSideMenuFavorite(this.#menu.menuId(), visible, from, to);
952
- }
953
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFavoritesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
954
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtSideMenuFavoritesComponent, isStandalone: true, selector: "rt-side-menu-favorites", outputs: { clickSubMenuAction: "clickSubMenuAction", clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction" }, host: { classAttribute: "rt-side-menu-favorites" }, viewQueries: [{ propertyName: "handles", predicate: ["handle"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n \u0411\u043B\u043E\u043A \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430, \u043A\u0430\u043A \u043F\u0430\u043F\u043A\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0441\u043E \u0437\u0432\u0435\u0437\u0434\u043E\u0439 \u0432\u043C\u0435\u0441\u0442\u043E \u0437\u043D\u0430\u0447\u043A\u0430 \u043F\u0430\u043F\u043A\u0438. \u0418\u043C\u044F\n \u043A\u043D\u043E\u043F\u043A\u0438 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \u043F\u043E \u0432\u0445\u043E\u0434\u0443 \u043C\u0435\u043D\u044E.\n -->\n <rt-expansion-panel\n rtElem=\"panel\"\n appearance=\"plain\"\n [ariaLabel]=\"open() ? collapseLabel() : expandLabel()\"\n [(expanded)]=\"open\"\n (expandedChange)=\"onToggle($event)\">\n <rt-icon rtElem=\"star\" size=\"sm\" name=\"star-fill\" [fill]=\"true\" />\n <span rtElem=\"title\" qa-dataid=\"side-menu-favorites-title\">\n {{ titleLabel() }}\n @if (countShown()) {\n ({{ rows().length }})\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div\n rtElem=\"list\"\n role=\"group\"\n qa-dataid=\"side-menu-favorites\"\n cdkDropList\n cdkDropListLockAxis=\"y\"\n [cdkDropListData]=\"rows()\"\n [attr.aria-label]=\"titleLabel()\"\n (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of rows(); track item.id) {\n <div\n rtElem=\"row\"\n qa-dataid=\"side-menu-favorite-row\"\n cdkDrag\n [attr.data-id]=\"item.id\"\n (cdkDragStarted)=\"onDragStart()\"\n (cdkDragEnded)=\"onDragEnd($event)\">\n <!--\n \u0420\u0443\u0447\u043A\u0430 \u0441\u0442\u043E\u0438\u0442 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438, \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439 \u043F\u0435\u0440\u0435\u0434 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F. \u0421\u0442\u0440\u043E\u043A\u0443 \u0442\u044F\u043D\u0443\u0442\n \u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430 \u0440\u0443\u0447\u043A\u0443: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u2014 \u0441\u0441\u044B\u043B\u043A\u0430. \u0421 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044B \u0441\u0442\u0440\u043E\u043A\u0443 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\n \u0441\u0442\u0440\u0435\u043B\u043A\u0438 \u043D\u0430 \u0440\u0443\u0447\u043A\u0435: \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043A\u043B\u0430\u0432\u0438\u0448 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442.\n -->\n <rt-side-menu-sub-item\n rtElem=\"item\"\n [item]=\"item\"\n [inFavorites]=\"true\"\n (clickSubMenuAction)=\"clickSubMenuAction.emit($event)\"\n (clickSubMenuAdditionalAction)=\"clickSubMenuAdditionalAction.emit($event)\">\n <rt-icon-button\n #handle\n rtSideMenuSubItemAction\n cdkDragHandle\n rtElem=\"handle\"\n qa-dataid=\"side-menu-favorite-handle\"\n size=\"sm\"\n aria-keyshortcuts=\"ArrowUp ArrowDown\"\n [icon]=\"dragIcon\"\n [ariaLabel]=\"dragLabel()\"\n [tooltip]=\"narrow() ? '' : dragLabel()\"\n (clicked)=\"onHandleClick($event)\"\n (keydown.arrowup)=\"onHandleKey($event, $index, -1)\"\n (keydown.arrowdown)=\"onHandleKey($event, $index, 1)\" />\n </rt-side-menu-sub-item>\n </div>\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-favorites{--rt-side-menu-favorites-radius: var(--rt-radius-full);--rt-side-menu-favorites-divider-width: var(--rt-border-width-thin);display:block}.rt-side-menu-favorites:has(.rt-side-menu-favorites__panel){border-bottom:var(--rt-side-menu-favorites-divider-width) solid var(--rt-color-border-subtle);margin-block-end:var(--rt-space-sm);padding-block-end:var(--rt-space-sm)}.rt-side-menu-favorites__panel.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-duration-fast);--rt-expansion-panel-header-radius: var(--rt-side-menu-favorites-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: var(--rt-space-1);--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm)}.rt-side-menu-favorites__star{flex-shrink:0;color:var(--rt-color-action-primary)}.rt-side-menu-favorites__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-favorites__list{display:flex;flex-direction:column}.rt-side-menu-favorites__item{min-width:0;flex:1 1 auto}.rt-side-menu-favorites__row{display:flex;align-items:center}.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-favorites-preview-radius: var(--rt-radius-full);--rt-side-menu-favorites-preview-shadow: var(--rt-shadow-md);border-radius:var(--rt-side-menu-favorites-preview-radius);box-shadow:var(--rt-side-menu-favorites-preview-shadow)}.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__item{min-width:0;flex:1 1 auto;border-radius:var(--rt-side-menu-favorites-preview-radius);background:var(--rt-color-bg-surface)}.rt-side-menu-favorites__row.cdk-drag-placeholder{opacity:0}.rt-side-menu-favorites__handle{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size);flex-shrink:0;cursor:grab;opacity:0}.rt-side-menu-favorites__handle>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size);--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-favorites__handle:active{cursor:grabbing}@media(hover:none){.rt-side-menu-favorites__handle{opacity:1}}.rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__handle{opacity:1}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__handle{overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle{overflow:visible;width:auto;margin-inline-start:0}}}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type:
955
- // directives
956
- BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: RtExpansionPanelContentDirective, selector: "ng-template[rtExpansionPanelContent]" }, { kind: "component", type:
957
- // components
958
- RtExpansionPanelComponent, selector: "rt-expansion-panel", inputs: ["expanded", "disabled", "hideToggle", "appearance", "headerId", "ariaLabel"], outputs: ["expandedChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtSideMenuSubItemComponent, selector: "rt-side-menu-sub-item", inputs: ["item", "inFavorites"], outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
959
- }
960
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFavoritesComponent, decorators: [{
961
- type: Component,
962
- args: [{ selector: 'rt-side-menu-favorites', host: { class: BEM_BLOCK$1 }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
963
- CdkDrag,
964
- CdkDragHandle,
965
- CdkDropList,
966
- // directives
967
- BlockDirective,
968
- ElemDirective,
969
- ModDirective,
970
- RtExpansionPanelContentDirective,
971
- // components
972
- RtExpansionPanelComponent,
973
- RtIconButtonComponent,
974
- RtIconComponent,
975
- RtSideMenuSubItemComponent,
976
- ], template: "<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n \u0411\u043B\u043E\u043A \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430, \u043A\u0430\u043A \u043F\u0430\u043F\u043A\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0441\u043E \u0437\u0432\u0435\u0437\u0434\u043E\u0439 \u0432\u043C\u0435\u0441\u0442\u043E \u0437\u043D\u0430\u0447\u043A\u0430 \u043F\u0430\u043F\u043A\u0438. \u0418\u043C\u044F\n \u043A\u043D\u043E\u043F\u043A\u0438 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \u043F\u043E \u0432\u0445\u043E\u0434\u0443 \u043C\u0435\u043D\u044E.\n -->\n <rt-expansion-panel\n rtElem=\"panel\"\n appearance=\"plain\"\n [ariaLabel]=\"open() ? collapseLabel() : expandLabel()\"\n [(expanded)]=\"open\"\n (expandedChange)=\"onToggle($event)\">\n <rt-icon rtElem=\"star\" size=\"sm\" name=\"star-fill\" [fill]=\"true\" />\n <span rtElem=\"title\" qa-dataid=\"side-menu-favorites-title\">\n {{ titleLabel() }}\n @if (countShown()) {\n ({{ rows().length }})\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div\n rtElem=\"list\"\n role=\"group\"\n qa-dataid=\"side-menu-favorites\"\n cdkDropList\n cdkDropListLockAxis=\"y\"\n [cdkDropListData]=\"rows()\"\n [attr.aria-label]=\"titleLabel()\"\n (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of rows(); track item.id) {\n <div\n rtElem=\"row\"\n qa-dataid=\"side-menu-favorite-row\"\n cdkDrag\n [attr.data-id]=\"item.id\"\n (cdkDragStarted)=\"onDragStart()\"\n (cdkDragEnded)=\"onDragEnd($event)\">\n <!--\n \u0420\u0443\u0447\u043A\u0430 \u0441\u0442\u043E\u0438\u0442 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438, \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439 \u043F\u0435\u0440\u0435\u0434 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F. \u0421\u0442\u0440\u043E\u043A\u0443 \u0442\u044F\u043D\u0443\u0442\n \u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430 \u0440\u0443\u0447\u043A\u0443: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u2014 \u0441\u0441\u044B\u043B\u043A\u0430. \u0421 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044B \u0441\u0442\u0440\u043E\u043A\u0443 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\n \u0441\u0442\u0440\u0435\u043B\u043A\u0438 \u043D\u0430 \u0440\u0443\u0447\u043A\u0435: \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043A\u043B\u0430\u0432\u0438\u0448 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442.\n -->\n <rt-side-menu-sub-item\n rtElem=\"item\"\n [item]=\"item\"\n [inFavorites]=\"true\"\n (clickSubMenuAction)=\"clickSubMenuAction.emit($event)\"\n (clickSubMenuAdditionalAction)=\"clickSubMenuAdditionalAction.emit($event)\">\n <rt-icon-button\n #handle\n rtSideMenuSubItemAction\n cdkDragHandle\n rtElem=\"handle\"\n qa-dataid=\"side-menu-favorite-handle\"\n size=\"sm\"\n aria-keyshortcuts=\"ArrowUp ArrowDown\"\n [icon]=\"dragIcon\"\n [ariaLabel]=\"dragLabel()\"\n [tooltip]=\"narrow() ? '' : dragLabel()\"\n (clicked)=\"onHandleClick($event)\"\n (keydown.arrowup)=\"onHandleKey($event, $index, -1)\"\n (keydown.arrowdown)=\"onHandleKey($event, $index, 1)\" />\n </rt-side-menu-sub-item>\n </div>\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-favorites{--rt-side-menu-favorites-radius: var(--rt-radius-full);--rt-side-menu-favorites-divider-width: var(--rt-border-width-thin);display:block}.rt-side-menu-favorites:has(.rt-side-menu-favorites__panel){border-bottom:var(--rt-side-menu-favorites-divider-width) solid var(--rt-color-border-subtle);margin-block-end:var(--rt-space-sm);padding-block-end:var(--rt-space-sm)}.rt-side-menu-favorites__panel.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-duration-fast);--rt-expansion-panel-header-radius: var(--rt-side-menu-favorites-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: var(--rt-space-1);--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm)}.rt-side-menu-favorites__star{flex-shrink:0;color:var(--rt-color-action-primary)}.rt-side-menu-favorites__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-favorites__list{display:flex;flex-direction:column}.rt-side-menu-favorites__item{min-width:0;flex:1 1 auto}.rt-side-menu-favorites__row{display:flex;align-items:center}.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-favorites-preview-radius: var(--rt-radius-full);--rt-side-menu-favorites-preview-shadow: var(--rt-shadow-md);border-radius:var(--rt-side-menu-favorites-preview-radius);box-shadow:var(--rt-side-menu-favorites-preview-shadow)}.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__item{min-width:0;flex:1 1 auto;border-radius:var(--rt-side-menu-favorites-preview-radius);background:var(--rt-color-bg-surface)}.rt-side-menu-favorites__row.cdk-drag-placeholder{opacity:0}.rt-side-menu-favorites__handle{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size);flex-shrink:0;cursor:grab;opacity:0}.rt-side-menu-favorites__handle>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size);--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-favorites__handle:active{cursor:grabbing}@media(hover:none){.rt-side-menu-favorites__handle{opacity:1}}.rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__handle{opacity:1}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__handle{overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle{overflow:visible;width:auto;margin-inline-start:0}}}\n"] }]
977
- }], ctorParameters: () => [], propDecorators: { handles: [{ type: i0.ViewChildren, args: ['handle', { ...{
978
- read: ElementRef,
979
- }, isSignal: true }] }], clickSubMenuAction: [{ type: i0.Output, args: ["clickSubMenuAction"] }], clickSubMenuAdditionalAction: [{ type: i0.Output, args: ["clickSubMenuAdditionalAction"] }] } });
980
-
981
- /**
982
- * Отложенное закрытие подменю, открытого наведением. Уход указателя ставит закрытие, возврат на
983
- * панель или наведение на пункт с подменю его снимают. Нулевая задержка закрывает сразу, как
984
- * раньше: без входа задержки меню ведёт себя прежним образом.
985
- */
986
- class RtSideMenuCloseTimer {
987
- #handle = null;
988
- constructor(destroyRef) {
989
- destroyRef.onDestroy(() => this.cancel());
990
- }
991
- /** Закрыть через `delay` миллисекунд; поставленное раньше закрытие заменяется новым. */
992
- run(delay, close) {
993
- this.cancel();
994
- if (delay <= 0) {
995
- close();
996
- return;
997
- }
998
- this.#handle = setTimeout(() => {
999
- this.#handle = null;
1000
- close();
1001
- }, delay);
1002
- }
1003
- cancel() {
1004
- if (this.#handle !== null) {
1005
- clearTimeout(this.#handle);
1006
- this.#handle = null;
1007
- }
1008
- }
1009
- }
1010
-
1011
- /**
1012
- * Ходьба по подменю с клавиатуры. Проверяется вызовом, без подъёма компонента.
1013
- *
1014
- * Клавиши слушает поле поиска: фокус из поля не уходит, и между стрелками работает набор текста.
1015
- * Съедаются только клавиши ходьбы — на остальные ответ отрицательный, иначе поле перестанет
1016
- * принимать буквы. Подсветка держится по номеру пункта: видимый список пересобирается на каждую
1017
- * букву запроса.
1018
- */
1019
- class RtSubMenuKeyboard {
1020
- #highlightedId;
1021
- /**
1022
- * Папки, раскрытые и свёрнутые стрелками. Два списка: раскрытость приходит ещё и от поиска и от
1023
- * активного адреса, и свернуть такую папку можно, только помня об этом отдельно.
1024
- */
1025
- #openedIds;
1026
- #closedIds;
1027
- #hooks;
1028
- constructor(hooks) {
1029
- this.#highlightedId = signal(null, /* @ts-ignore */
1030
- ...(ngDevMode ? [{ debugName: "#highlightedId" }] : /* istanbul ignore next */ []));
1031
- /**
1032
- * Папки, раскрытые и свёрнутые стрелками. Два списка: раскрытость приходит ещё и от поиска и от
1033
- * активного адреса, и свернуть такую папку можно, только помня об этом отдельно.
1034
- */
1035
- this.#openedIds = signal([], /* @ts-ignore */
1036
- ...(ngDevMode ? [{ debugName: "#openedIds" }] : /* istanbul ignore next */ []));
1037
- this.#closedIds = signal([], /* @ts-ignore */
1038
- ...(ngDevMode ? [{ debugName: "#closedIds" }] : /* istanbul ignore next */ []));
1039
- this.highlightedId = this.#highlightedId.asReadonly();
1040
- this.openedIds = this.#openedIds.asReadonly();
1041
- this.closedIds = this.#closedIds.asReadonly();
1042
- this.#hooks = hooks;
1043
- }
1044
- /** Всё, что клавиатура успела наделать, снимается: набран новый запрос или подменю закрылось. */
1045
- reset() {
1046
- this.#highlightedId.set(null);
1047
- this.#openedIds.set([]);
1048
- this.#closedIds.set([]);
1049
- }
1050
- /** Нажата клавиша в поле поиска. Отвечает, съедена ли она: только съеденная отменяет умолчание. */
1051
- press(key, items, expandedIds) {
1052
- const walk = walkSideMenuItems(items, expandedIds);
1053
- const current = walk.find((item) => item.id === this.#highlightedId()) ?? null;
1054
- switch (key) {
1055
- case 'ArrowDown':
1056
- case 'ArrowUp':
1057
- this.#highlightedId.set(stepSideMenuHighlight(walk, this.#highlightedId(), key === 'ArrowDown' ? 1 : -1));
1058
- return true;
1059
- case 'ArrowRight':
1060
- return this.setFolderOpen(current, true);
1061
- case 'ArrowLeft':
1062
- return this.setFolderOpen(current, false);
1063
- case 'Enter':
1064
- return this.#enter(current);
1065
- case 'Escape':
1066
- this.reset();
1067
- this.#hooks.clearQuery();
1068
- return true;
1069
- default:
1070
- return false;
1071
- }
1072
- }
1073
- /** Enter открывает подсвеченное: у пункта со ссылкой это переход, у папки — раскрытие. */
1074
- #enter(item) {
1075
- if (item === null) {
1076
- return false;
1077
- }
1078
- if (item.link) {
1079
- this.#hooks.open(item);
1080
- return true;
1081
- }
1082
- return this.setFolderOpen(item, true);
1083
- }
1084
- /** Раскрыть или свернуть папку: стрелкой, Enter или нажатием её заголовка. */
1085
- setFolderOpen(item, open) {
1086
- if (item === null || !item.submenu?.length) {
1087
- return false;
1088
- }
1089
- const without = (ids) => ids.filter((id) => id !== item.id);
1090
- this.#openedIds.update((ids) => (open ? [...without(ids), item.id] : without(ids)));
1091
- this.#closedIds.update((ids) => (open ? without(ids) : [...without(ids), item.id]));
1092
- return true;
1093
- }
1094
- }
1095
-
1096
- /**
1097
- * Тяга ширины закреплённого подменю: натянутая ширина и снятие слушателей. Механика событий —
1098
- * `startSideMenuWidthDrag` в логике рядом, здесь держится только то, что живёт дольше одного события.
1099
- */
1100
- class RtSideMenuResize {
1101
- /** Натянутая ширина. Пустая — тяги нет, и ширину ставит потребитель или оформление. */
1102
- #draggedWidth;
1103
- #listen;
1104
- #hooks;
1105
- #stop;
1106
- /**
1107
- * Меню, разрушенное посреди тяги, кончает её здесь же: иначе накрытие чужих кадров, снимаемое по
1108
- * концу, осталось бы стоять.
1109
- */
1110
- constructor(listen, hooks, destroyRef) {
1111
- /** Натянутая ширина. Пустая — тяги нет, и ширину ставит потребитель или оформление. */
1112
- this.#draggedWidth = signal(null, /* @ts-ignore */
1113
- ...(ngDevMode ? [{ debugName: "#draggedWidth" }] : /* istanbul ignore next */ []));
1114
- this.#stop = null;
1115
- this.draggedWidth = this.#draggedWidth.asReadonly();
1116
- /**
1117
- * Ширина для диктора: только то, что кит знает сам. Не названо ничего и ничего не тянули — числа
1118
- * нет: выдуманное назвало бы ширину, которой панель не нарисована.
1119
- */
1120
- this.valueNow = computed(() => {
1121
- const width = this.#draggedWidth() ?? this.#hooks.namedWidth();
1122
- return width === null ? null : clampSideMenuWidth(width);
1123
- }, /* @ts-ignore */
1124
- ...(ngDevMode ? [{ debugName: "valueNow" }] : /* istanbul ignore next */ []));
1125
- this.#listen = listen;
1126
- this.#hooks = hooks;
1127
- destroyRef?.onDestroy(() => this.finish());
1128
- }
1129
- /** Тяга идёт. Второе нажатие при начатой тяге ничего не начинает: указатель уже захвачен. */
1130
- isRunning() {
1131
- return this.#stop !== null;
1132
- }
1133
- /**
1134
- * Нажата клавиша на ручке. Просьба о ширине уходит сразу: жеста нет, и держать её не до чего.
1135
- * Отрицательный ответ — клавиша не о ширине, и умолчание у неё не отменяется.
1136
- */
1137
- pressKey(key, from) {
1138
- const width = sideMenuWidthByKey(key, from);
1139
- if (width === null) {
1140
- return false;
1141
- }
1142
- this.#hooks.askWidth(width);
1143
- return true;
1144
- }
1145
- start(event, startWidth) {
1146
- if (this.isRunning()) {
1147
- return;
1148
- }
1149
- const stop = startSideMenuWidthDrag(event, startWidth, this.#listen, {
1150
- onWidth: (width) => this.#draggedWidth.set(width),
1151
- onEnd: () => this.finish(),
1152
- });
1153
- if (stop !== null) {
1154
- this.#stop = stop;
1155
- this.#hooks.started();
1156
- }
1157
- }
1158
- /** Конец тяги. Наружу уходит то число, которым панель нарисована: нижний предел держит оформление. */
1159
- finish() {
1160
- if (!this.isRunning()) {
1161
- return;
1162
- }
1163
- // Замер идёт до сброса натянутой ширины: сбросив её, панель перерисуют, и мерить будет нечего.
1164
- const width = this.#draggedWidth();
1165
- const drawn = width === null ? null : reportedSideMenuWidth(width, this.#hooks.panel());
1166
- this.#stop?.();
1167
- this.#stop = null;
1168
- this.#draggedWidth.set(null);
1169
- if (drawn !== null) {
1170
- this.#hooks.askWidth(drawn);
1171
- }
1172
- this.#hooks.ended();
1173
- }
1174
- }
1175
-
1176
- /**
1177
- * Входы вида бокового меню: подписи и заливка значков, поле поиска, подсказка прокрутки панели. Ни
1178
- * один не меняет поведения — по ним приложение даёт меню вид первого кита, а без них меню рисуется
1179
- * как раньше. Вынесены из класса меню, чтобы тот оставался в пределе длины файла.
1180
- */
1181
- class RtSideMenuLookBase {
1182
- constructor() {
1183
- /** Подсказки строк подменю — у подписей и у кнопок строк. Доступные имена остаются. */
1184
- this.subMenuTooltipsShown = input(true, { ...(ngDevMode ? { debugName: "subMenuTooltipsShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1185
- /**
1186
- * Подписи под значками полосы. Выключено — имя пункта уходит в подсказку справа от значка, а
1187
- * подсказка подчиняется `subMenuTooltipsShown`; доступное имя пункт держит всегда.
1188
- */
1189
- this.railTitlesShown = input(true, { ...(ngDevMode ? { debugName: "railTitlesShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1190
- /** Залитые значки полосы — залитый рисунок материального набора и заливка шрифта лигатуры. */
1191
- this.railIconFill = input(false, { ...(ngDevMode ? { debugName: "railIconFill" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1192
- /** Залитые значки строк и папок подменю — так же, как `railIconFill` у полосы. */
1193
- this.subItemIconFill = input(false, { ...(ngDevMode ? { debugName: "subItemIconFill" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1194
- /** Подсказка прокрутки внизу панели подменю, как у полосы: длинный список не обрывается молча. */
1195
- this.panelScrollHintShown = input(false, { ...(ngDevMode ? { debugName: "panelScrollHintShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1196
- /** Размер поля поиска подменю — ступень `rt-input`. */
1197
- this.searchSize = input('sm', /* @ts-ignore */
1198
- ...(ngDevMode ? [{ debugName: "searchSize" }] : /* istanbul ignore next */ []));
1199
- /** Вид поля поиска подменю — вид `rt-input`. */
1200
- this.searchAppearance = input('outline', /* @ts-ignore */
1201
- ...(ngDevMode ? [{ debugName: "searchAppearance" }] : /* istanbul ignore next */ []));
1202
- }
1203
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuLookBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1204
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: RtSideMenuLookBase, isStandalone: true, inputs: { subMenuTooltipsShown: { classPropertyName: "subMenuTooltipsShown", publicName: "subMenuTooltipsShown", isSignal: true, isRequired: false, transformFunction: null }, railTitlesShown: { classPropertyName: "railTitlesShown", publicName: "railTitlesShown", isSignal: true, isRequired: false, transformFunction: null }, railIconFill: { classPropertyName: "railIconFill", publicName: "railIconFill", isSignal: true, isRequired: false, transformFunction: null }, subItemIconFill: { classPropertyName: "subItemIconFill", publicName: "subItemIconFill", isSignal: true, isRequired: false, transformFunction: null }, panelScrollHintShown: { classPropertyName: "panelScrollHintShown", publicName: "panelScrollHintShown", isSignal: true, isRequired: false, transformFunction: null }, searchSize: { classPropertyName: "searchSize", publicName: "searchSize", isSignal: true, isRequired: false, transformFunction: null }, searchAppearance: { classPropertyName: "searchAppearance", publicName: "searchAppearance", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
1205
- }
1206
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuLookBase, decorators: [{
1207
- type: Directive
1208
- }], propDecorators: { subMenuTooltipsShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuTooltipsShown", required: false }] }], railTitlesShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "railTitlesShown", required: false }] }], railIconFill: [{ type: i0.Input, args: [{ isSignal: true, alias: "railIconFill", required: false }] }], subItemIconFill: [{ type: i0.Input, args: [{ isSignal: true, alias: "subItemIconFill", required: false }] }], panelScrollHintShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelScrollHintShown", required: false }] }], searchSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchSize", required: false }] }], searchAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchAppearance", required: false }] }] } });
1209
-
1210
- /** Шапка бокового меню: над полосой пунктов. */
1211
- class RtSideMenuHeaderDirective {
1212
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1213
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtSideMenuHeaderDirective, isStandalone: true, selector: "[rtSideMenuHeader]", ngImport: i0 }); }
1214
- }
1215
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHeaderDirective, decorators: [{
1216
- type: Directive,
1217
- args: [{
1218
- selector: '[rtSideMenuHeader]',
1219
- }]
1220
- }] });
1221
- /** Подвал бокового меню: под полосой пунктов. */
1222
- class RtSideMenuFooterDirective {
1223
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1224
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtSideMenuFooterDirective, isStandalone: true, selector: "[rtSideMenuFooter]", ngImport: i0 }); }
1225
- }
1226
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFooterDirective, decorators: [{
1227
- type: Directive,
1228
- args: [{
1229
- selector: '[rtSideMenuFooter]',
1230
- }]
1231
- }] });
1232
- /**
1233
- * Помечает `<ng-template>` своего значка пунктов меню — для имён, которых нет ни в наборе кита, ни
1234
- * в перечне имён Material. Шаблон один на меню и рисует и значок строки, и значок кнопки строки; кит
1235
- * ставит его на место значка размером значка и цветом строки. Рисунок выбирают по `icon` контекста.
1236
- * Значок, который кит рисует сам, шаблон не перебивает.
1237
- *
1238
- * ```html
1239
- * <rt-side-menu [menuItems]="items">
1240
- * <ng-template rtSideMenuIcon let-icon="icon"><img [src]="'/icons/' + icon + '.svg'" alt="" /></ng-template>
1241
- * </rt-side-menu>
1242
- * ```
1243
- */
1244
- class RtSideMenuIconDirective {
1245
- constructor() {
1246
- this.templateRef = inject(TemplateRef);
1247
- }
1248
- // eslint-disable-next-line @typescript-eslint/no-unused-vars -- второй довод стража контекста шаблона стоит только в типе-предикате; убрать его нечем, подпись задаёт каркас
1249
- static ngTemplateContextGuard(_directive, context) {
1250
- return true;
1251
- }
1252
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1253
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtSideMenuIconDirective, isStandalone: true, selector: "ng-template[rtSideMenuIcon]", ngImport: i0 }); }
1254
- }
1255
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconDirective, decorators: [{
1256
- type: Directive,
1257
- args: [{
1258
- selector: 'ng-template[rtSideMenuIcon]',
1259
- }]
1260
- }] });
1261
-
1262
- const BEM_BLOCK = 'rt-side-menu';
1263
- /**
1264
- * Боковая навигация: полоса пунктов и подменю раздела рядом. Перенос бокового меню первого кита на
1265
- * части второго; `rt-menu` — другое семейство, выпадающее меню действий.
1266
- */
1267
- class RtSideMenuComponent extends RtSideMenuLookBase {
1268
- #breakpoints;
1269
- #renderer;
1270
- /** Настройки меню, если приложение их включило: режим и ширина хранятся там под `menuId`. */
1271
- #settings;
1272
- /** Тяга ширины: просьба уходит одна, на отпускании — в настройки меню и наружу. */
1273
- #resize;
1274
- /** Закрытие подменю, отложенное входом `subMenuCloseDelay`. */
1275
- #closeTimer;
1276
- /** Ходьба с клавиатуры открывает пункт настоящим нажатием его строки — той же дорогой, что и мышь. */
1277
- #keyboard;
1278
- /** Подменю открыл указатель. У закреплённого режима открытость считается иначе. */
1279
- #hoverOpened;
1280
- /** Человек в поле поиска: подменю держится открытым до нажатия снаружи. */
1281
- #searchHeld;
1282
- /** Строку избранного тянут: уход указателя за панель подменю не закрывает. */
1283
- #dragHeld;
1284
- /**
1285
- * Режим и ширина в работе: вход приложения, иначе сохранённые под номером меню. Без кнопки
1286
- * закрепления сохранённый режим не читается: открепить меню, закреплённое раньше, было бы нечем.
1287
- */
1288
- #mode;
1289
- #width;
1290
- #pinnedSubMenu;
1291
- constructor() {
1292
- super();
1293
- this.#breakpoints = inject(BreakpointsService);
1294
- this.#renderer = inject(Renderer2);
1295
- /** Настройки меню, если приложение их включило: режим и ширина хранятся там под `menuId`. */
1296
- this.#settings = inject(RtSideMenuSettingsService, { optional: true });
1297
- /** Тяга ширины: просьба уходит одна, на отпускании — в настройки меню и наружу. */
1298
- this.#resize = new RtSideMenuResize((target, name, handler) => this.#renderer.listen(target, name, handler), {
1299
- askWidth: (width) => {
1300
- this.#settings?.setSubMenuWidth(this.menuId(), width);
1301
- this.subMenuWidthChange.emit(width);
1302
- },
1303
- started: () => this.subMenuResizeStart.emit(),
1304
- ended: () => this.subMenuResizeEnd.emit(),
1305
- panel: () => this.panelRef()?.nativeElement ?? null,
1306
- namedWidth: () => this.#width(),
1307
- }, inject(DestroyRef));
1308
- /** Закрытие подменю, отложенное входом `subMenuCloseDelay`. */
1309
- this.#closeTimer = new RtSideMenuCloseTimer(inject(DestroyRef));
1310
- /** Ходьба с клавиатуры открывает пункт настоящим нажатием его строки — той же дорогой, что и мышь. */
1311
- this.#keyboard = new RtSubMenuKeyboard({
1312
- open: (item) => this.#pressRow(item),
1313
- clearQuery: () => this.searchControl.setValue(''),
1314
- });
1315
- /** Подменю открыл указатель. У закреплённого режима открытость считается иначе. */
1316
- this.#hoverOpened = signal(false, /* @ts-ignore */
1317
- ...(ngDevMode ? [{ debugName: "#hoverOpened" }] : /* istanbul ignore next */ []));
1318
- /** Человек в поле поиска: подменю держится открытым до нажатия снаружи. */
1319
- this.#searchHeld = signal(false, /* @ts-ignore */
1320
- ...(ngDevMode ? [{ debugName: "#searchHeld" }] : /* istanbul ignore next */ []));
1321
- /** Строку избранного тянут: уход указателя за панель подменю не закрывает. */
1322
- this.#dragHeld = signal(false, /* @ts-ignore */
1323
- ...(ngDevMode ? [{ debugName: "#dragHeld" }] : /* istanbul ignore next */ []));
1324
- /**
1325
- * Режим и ширина в работе: вход приложения, иначе сохранённые под номером меню. Без кнопки
1326
- * закрепления сохранённый режим не читается: открепить меню, закреплённое раньше, было бы нечем.
1327
- */
1328
- this.#mode = computed(() => this.subMenuMode() ?? (this.pinShown() ? this.#settings?.subMenuMode(this.menuId())() : undefined) ?? 'hover', /* @ts-ignore */
1329
- ...(ngDevMode ? [{ debugName: "#mode" }] : /* istanbul ignore next */ []));
1330
- this.#width = computed(() => this.subMenuWidth() ?? this.#settings?.subMenuWidth(this.menuId())() ?? null, /* @ts-ignore */
1331
- ...(ngDevMode ? [{ debugName: "#width" }] : /* istanbul ignore next */ []));
1332
- this.#pinnedSubMenu = computed(() => this.isPinned() ? pinnedSideMenuItems(this.selectedSubMenu(), this.activeMenuIds(), this.menuItems()) : [], /* @ts-ignore */
1333
- ...(ngDevMode ? [{ debugName: "#pinnedSubMenu" }] : /* istanbul ignore next */ []));
1334
- this.searchControl = new FormControl('');
1335
- this.panelRef = viewChild('panel', /* @ts-ignore */
1336
- ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
1337
- this.headerTpl = contentChild(RtSideMenuHeaderDirective, { ...(ngDevMode ? { debugName: "headerTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
1338
- this.footerTpl = contentChild(RtSideMenuFooterDirective, { ...(ngDevMode ? { debugName: "footerTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
1339
- this.searchLabel = rtKitLabel('sideMenuSearch');
1340
- this.pinLabel = rtKitLabel('sideMenuPin');
1341
- this.unpinLabel = rtKitLabel('sideMenuUnpin');
1342
- this.nothingFoundLabel = rtKitLabel('sideMenuNothingFound');
1343
- this.resizeLabel = rtKitLabel('sideMenuResize');
1344
- this.backLabel = rtKitLabel('sideMenuBack');
1345
- /** Экран узкий: замер кита, и другого источника у этого признака нет. */
1346
- this.narrow = this.#breakpoints.narrow;
1347
- this.resizeValueNow = this.#resize.valueNow;
1348
- this.resizeValueMin = RT_SIDE_MENU_WIDTH_MIN;
1349
- this.resizeValueMax = RT_SIDE_MENU_WIDTH_MAX;
1350
- /** Натянутая ширина панели. Своего выбора нет — свойство не ставится, и ширину берёт оформление. */
1351
- this.panelWidthStyle = computed(() => {
1352
- const width = this.#resize.draggedWidth() ?? this.#width();
1353
- return width === null ? null : `${clampSideMenuWidth(width)}px`;
1354
- }, /* @ts-ignore */
1355
- ...(ngDevMode ? [{ debugName: "panelWidthStyle" }] : /* istanbul ignore next */ []));
1356
- /** Закрепление действует только на широком экране: на узком подменю занимает экран целиком. */
1357
- this.isPinned = computed(() => this.#mode() === 'pinned' && !this.narrow(), /* @ts-ignore */
1358
- ...(ngDevMode ? [{ debugName: "isPinned" }] : /* istanbul ignore next */ []));
1359
- /** Подменю держится набором запроса: уходом указателя не закрывается. */
1360
- this.isSearchHeld = computed(() => this.#searchHeld() && !this.isPinned(), /* @ts-ignore */
1361
- ...(ngDevMode ? [{ debugName: "isSearchHeld" }] : /* istanbul ignore next */ []));
1362
- /** Закреплённое подменю открыто, пока ему есть что показать: указатель на это не влияет. */
1363
- this.subMenuOpened = computed(() => this.isPinned() ? this.#pinnedSubMenu().length > 0 : this.#hoverOpened(), /* @ts-ignore */
1364
- ...(ngDevMode ? [{ debugName: "subMenuOpened" }] : /* istanbul ignore next */ []));
1365
- this.visibleSubMenuItems = computed(() => filterSideMenuItems(this.shownSubMenu(), this.subMenuQuery()), /* @ts-ignore */
1366
- ...(ngDevMode ? [{ debugName: "visibleSubMenuItems" }] : /* istanbul ignore next */ []));
1367
- this.isNothingFound = computed(() => this.subMenuQuery().trim() !== '' && this.visibleSubMenuItems().length === 0, /* @ts-ignore */
1368
- ...(ngDevMode ? [{ debugName: "isNothingFound" }] : /* istanbul ignore next */ []));
1369
- /** Пункт полосы подсвечен активным: выбранный человеком, а без выбора — пункт текущего адреса. */
1370
- this.activeRailIds = computed(() => {
1371
- const selected = this.selectedItem();
1372
- return selected ? [selected.id] : this.activeMenuIds();
1373
- }, /* @ts-ignore */
1374
- ...(ngDevMode ? [{ debugName: "activeRailIds" }] : /* istanbul ignore next */ []));
1375
- /** Набор, который подменю наполняет сейчас, до отбора поиском. */
1376
- this.shownSubMenu = computed(() => this.isPinned() ? this.#pinnedSubMenu() : (this.selectedSubMenu() ?? []), /* @ts-ignore */
1377
- ...(ngDevMode ? [{ debugName: "shownSubMenu" }] : /* istanbul ignore next */ []));
1378
- this.selectedItem = signal(null, /* @ts-ignore */
1379
- ...(ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []));
1380
- this.selectedSubMenu = signal(null, /* @ts-ignore */
1381
- ...(ngDevMode ? [{ debugName: "selectedSubMenu" }] : /* istanbul ignore next */ []));
1382
- /** Что набрано в поиске: строка берёт запрос отсюда, чтобы отметить совпавшее. */
1383
- this.subMenuQuery = signal('', /* @ts-ignore */
1384
- ...(ngDevMode ? [{ debugName: "subMenuQuery" }] : /* istanbul ignore next */ []));
1385
- this.highlightedMenuId = this.#keyboard.highlightedId;
1386
- /**
1387
- * Какие папки раскрыты. Пустой запрос оставляет раздел текущего адреса; непустой добавляет
1388
- * папки с совпадением — иначе найденное лежит за закрытым заголовком.
1389
- */
1390
- this.expandedMenuIds = computed(() => {
1391
- const found = this.subMenuQuery().trim() === '' ? [] : sideMenuIdsToExpand(this.visibleSubMenuItems());
1392
- const closed = this.#keyboard.closedIds();
1393
- return [...this.activeMenuIds(), ...found, ...this.#keyboard.openedIds()].filter((id) => !closed.includes(id));
1394
- }, /* @ts-ignore */
1395
- ...(ngDevMode ? [{ debugName: "expandedMenuIds" }] : /* istanbul ignore next */ []));
1396
- this.activeMenuIds = input.required(/* @ts-ignore */
1397
- ...(ngDevMode ? [{ debugName: "activeMenuIds" }] : /* istanbul ignore next */ []));
1398
- this.menuItems = input([], /* @ts-ignore */
1399
- ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
1400
- /** Режим подменю от приложения. Задан — важнее сохранённого; нет ни того, ни другого — наведение. */
1401
- this.subMenuMode = input(undefined, /* @ts-ignore */
1402
- ...(ngDevMode ? [{ debugName: "subMenuMode" }] : /* istanbul ignore next */ []));
1403
- /** Ширина закреплённого подменю в пикселях. Пусто — сохранённая, её нет — оформление. */
1404
- this.subMenuWidth = input(undefined, /* @ts-ignore */
1405
- ...(ngDevMode ? [{ debugName: "subMenuWidth" }] : /* istanbul ignore next */ []));
1406
- /** Номер, под которым меню хранит свои настройки: у двух меню приложения — два номера. */
1407
- this.menuId = input(RT_SIDE_MENU_DEFAULT_ID, { ...(ngDevMode ? { debugName: "menuId" } : /* istanbul ignore next */ {}), transform: normalizeSideMenuId });
1408
- /** Число строк в заголовке блока избранного: всегда, у свёрнутого блока или никогда. */
1409
- this.favoritesCount = input('collapsed', /* @ts-ignore */
1410
- ...(ngDevMode ? [{ debugName: "favoritesCount" }] : /* istanbul ignore next */ []));
1411
- /** Место под кнопки избранного, ждущие наведения: держать всегда или отдавать подписи. */
1412
- this.favoriteActionsReserve = input('none', /* @ts-ignore */
1413
- ...(ngDevMode ? [{ debugName: "favoriteActionsReserve" }] : /* istanbul ignore next */ []));
1414
- /** Поиск показывает совпавшие строки избранного; выключено — на время поиска блока нет. */
1415
- this.isFavoritesSearchShown = input(true, { ...(ngDevMode ? { debugName: "isFavoritesSearchShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1416
- /** Кнопка закрепления в шапке подменю; выключено — подменю только всплывает, если режим не задан входом. */
1417
- this.pinShown = input(true, { ...(ngDevMode ? { debugName: "pinShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1418
- /**
1419
- * Задержка закрытия подменю, открытого наведением, в миллисекундах: уход указателя закрывает его
1420
- * не сразу, а возврат на панель держит открытым. Ноль — закрытие сразу, как раньше.
1421
- */
1422
- this.subMenuCloseDelay = input(0, { ...(ngDevMode ? { debugName: "subMenuCloseDelay" } : /* istanbul ignore next */ {}), transform: (value) => numberAttribute(value, 0) });
1423
- this.subMenuModeChange = output();
1424
- this.subMenuWidthChange = output();
1425
- /** Начало и конец тяги: по ним потребитель накрывает чужие кадры и снимает накрытие. */
1426
- this.subMenuResizeStart = output();
1427
- this.subMenuResizeEnd = output();
1428
- this.closeMobileMenuAction = output();
1429
- this.clickSubMenuAction = output();
1430
- this.clickSubMenuAdditionalAction = output();
1431
- /** Свой значок пунктов, чьё имя кит не рисует; подпункты читают его через токен меню. */
1432
- this.ownIconTpl = contentChild(RtSideMenuIconDirective, { ...(ngDevMode ? { debugName: "ownIconTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
1433
- this.searchControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((query) => {
1434
- this.subMenuQuery.set(query ?? '');
1435
- this.onSearchHold();
1436
- // Видимый список пересобрался: подсветка и раскрытое стрелками к нему больше не относятся.
1437
- this.#keyboard.reset();
1438
- });
1439
- // Имя без значка кита и без пары рисует пункт без значка, и пропуск без предупреждения не заметен.
1440
- if (isDevMode()) {
1441
- effect(() => warnUnpairedSideMenuIcons(this.menuId(), this.menuItems(), this.ownIconTpl() !== undefined));
1442
- }
1443
- }
1444
- onClickMenu(item) {
1445
- if (this.isPinned()) {
1446
- this.#pickPinnedSubMenu(item);
1447
- this.closeMobileMenu();
1448
- return;
1449
- }
1450
- this.selectedItem.set(item);
1451
- if (item.submenu) {
1452
- this.selectedSubMenu.set(item.submenu);
1453
- this.#hoverOpened.set(true);
1454
- }
1455
- else if (this.selectedSubMenu()) {
1456
- this.closeSubMenu();
1457
- }
1458
- else {
1459
- // У пункта нет подменю, и открытого подменю тоже нет — закрывать нечего.
1460
- }
1461
- if (item.link) {
1462
- this.closeMobileMenu();
1463
- }
1464
- }
1465
- onClickSubMenu({ item, event }) {
1466
- if (!item.link) {
1467
- return;
1468
- }
1469
- this.clickSubMenuAction.emit({ item, event });
1470
- if (!this.isPinned()) {
1471
- this.closeSubMenu();
1472
- }
1473
- this.closeMobileMenu();
1474
- }
1475
- onClickSubMenuAdditional(payload) {
1476
- this.clickSubMenuAdditionalAction.emit(payload);
1477
- }
1478
- onBackToMainMenu() {
1479
- this.selectedItem.set(null);
1480
- this.selectedSubMenu.set(null);
1481
- this.searchControl.setValue('', { emitEvent: false });
1482
- this.subMenuQuery.set('');
1483
- }
1484
- /**
1485
- * Наведение на пункт полосы открывает его подменю сразу; уход указателя с панели — без пункта —
1486
- * и пункт без подменю закрывают его через задержку.
1487
- */
1488
- toggleSubMenu(item) {
1489
- if (this.isPinned() || (item === undefined && this.#isHeld())) {
1490
- return;
1491
- }
1492
- if (item?.submenu) {
1493
- this.#closeTimer.cancel();
1494
- this.selectedSubMenu.set(item.submenu);
1495
- this.#hoverOpened.set(true);
1496
- }
1497
- else if (this.selectedItem()?.submenu) {
1498
- this.#closeTimer.cancel();
1499
- this.selectedSubMenu.set(this.selectedItem()?.submenu ?? null);
1500
- }
1501
- else {
1502
- // Ушедший с панели указатель мог за задержку взяться за поиск или строку избранного.
1503
- this.#closeTimer.run(this.subMenuCloseDelay(), () => {
1504
- if (item !== undefined || !this.#isHeld()) {
1505
- this.closeSubMenu();
1506
- }
1507
- });
1508
- }
1509
- }
1510
- /** Указатель вернулся на панель: отложенное закрытие снимается. */
1511
- cancelSubMenuClose() {
1512
- this.#closeTimer.cancel();
1513
- }
1514
- closeSubMenu() {
1515
- this.#closeTimer.cancel();
1516
- this.selectedItem.set(null);
1517
- this.selectedSubMenu.set(null);
1518
- this.searchControl.setValue('', { emitEvent: false });
1519
- this.subMenuQuery.set('');
1520
- this.#hoverOpened.set(false);
1521
- this.#searchHeld.set(false);
1522
- this.#keyboard.reset();
1523
- }
1524
- holdSubMenu(held) {
1525
- this.#dragHeld.set(held);
1526
- }
1527
- toggleFolder(item) {
1528
- this.#keyboard.setFolderOpen(item, !this.expandedMenuIds().includes(item.id));
1529
- }
1530
- /** Человек взялся за поле поиска: дальше подменю закрывает нажатие снаружи, а не уход указателя. */
1531
- onSearchHold() {
1532
- if (!this.isPinned()) {
1533
- this.#searchHeld.set(true);
1534
- }
1535
- }
1536
- /** Клавиши поля поиска раздаются списку; умолчание отменяется только у съеденной. */
1537
- onSearchKeydown(event) {
1538
- if (this.#keyboard.press(event.key, this.visibleSubMenuItems(), this.expandedMenuIds())) {
1539
- event.preventDefault();
1540
- event.stopPropagation();
1541
- }
1542
- }
1543
- /** Нажат переключатель режима: выбор уходит в настройки меню и наружу. */
1544
- onSubMenuModeToggle() {
1545
- const mode = this.isPinned() ? 'hover' : 'pinned';
1546
- this.#settings?.setSubMenuMode(this.menuId(), mode);
1547
- this.subMenuModeChange.emit(mode);
1548
- }
1549
- onResizeStart(event) {
1550
- if (!this.isPinned() || this.#resize.isRunning() || event.button !== 0) {
1551
- return;
1552
- }
1553
- // Иначе указатель выделяет подписи пунктов, и тяга выглядит выделением текста.
1554
- event.preventDefault();
1555
- this.#resize.start(event, this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null));
1556
- }
1557
- onResizeKeydown(event) {
1558
- const from = this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null);
1559
- if (this.isPinned() && this.#resize.pressKey(event.key, from)) {
1560
- event.preventDefault();
1561
- }
1562
- }
1563
- closeMobileMenu() {
1564
- if (this.narrow()) {
1565
- this.closeMobileMenuAction.emit();
1566
- }
1567
- }
1568
- #isHeld() {
1569
- return this.#searchHeld() || this.#dragHeld();
1570
- }
1571
- /**
1572
- * Нажат пункт полосы, пока подменю закреплено: нажатие — выбор раздела. Пункт со своим адресом и
1573
- * без разделов выбор снимает: у страницы нет разделов, и панель прежнего раздела врала бы.
1574
- */
1575
- #pickPinnedSubMenu(item) {
1576
- if (item.submenu?.length) {
1577
- this.selectedItem.set(item);
1578
- this.selectedSubMenu.set(item.submenu);
1579
- }
1580
- else if (item.link) {
1581
- this.selectedItem.set(null);
1582
- this.selectedSubMenu.set(null);
1583
- }
1584
- else {
1585
- return;
1586
- }
1587
- this.searchControl.setValue('', { emitEvent: false });
1588
- this.subMenuQuery.set('');
1589
- }
1590
- /**
1591
- * Строка ищется по номеру среди узлов панели с id и нажимается как мышью: у ссылки номер стоит
1592
- * на ней самой, у папки — на кнопке заголовка её раскрывающейся панели.
1593
- */
1594
- #pressRow(item) {
1595
- const rows = Array.from(this.panelRef()?.nativeElement.querySelectorAll('[id]') ?? []);
1596
- rows.find((row) => row.id === String(item.id))?.click();
1597
- }
1598
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1599
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtSideMenuComponent, isStandalone: true, selector: "rt-side-menu", inputs: { activeMenuIds: { classPropertyName: "activeMenuIds", publicName: "activeMenuIds", isSignal: true, isRequired: true, transformFunction: null }, menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, subMenuMode: { classPropertyName: "subMenuMode", publicName: "subMenuMode", isSignal: true, isRequired: false, transformFunction: null }, subMenuWidth: { classPropertyName: "subMenuWidth", publicName: "subMenuWidth", isSignal: true, isRequired: false, transformFunction: null }, menuId: { classPropertyName: "menuId", publicName: "menuId", isSignal: true, isRequired: false, transformFunction: null }, favoritesCount: { classPropertyName: "favoritesCount", publicName: "favoritesCount", isSignal: true, isRequired: false, transformFunction: null }, favoriteActionsReserve: { classPropertyName: "favoriteActionsReserve", publicName: "favoriteActionsReserve", isSignal: true, isRequired: false, transformFunction: null }, isFavoritesSearchShown: { classPropertyName: "isFavoritesSearchShown", publicName: "isFavoritesSearchShown", isSignal: true, isRequired: false, transformFunction: null }, pinShown: { classPropertyName: "pinShown", publicName: "pinShown", isSignal: true, isRequired: false, transformFunction: null }, subMenuCloseDelay: { classPropertyName: "subMenuCloseDelay", publicName: "subMenuCloseDelay", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { subMenuModeChange: "subMenuModeChange", subMenuWidthChange: "subMenuWidthChange", subMenuResizeStart: "subMenuResizeStart", subMenuResizeEnd: "subMenuResizeEnd", closeMobileMenuAction: "closeMobileMenuAction", clickSubMenuAction: "clickSubMenuAction", clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction" }, host: { properties: { "class.rt-side-menu--pinned": "isPinned()", "style.--rt-side-menu-panel-dragged-width": "panelWidthStyle()", "class.rt-side-menu--favorite-actions-none": "favoriteActionsReserve() === 'none'" }, classAttribute: "rt-side-menu" }, providers: [{ provide: RT_SIDE_MENU, useExisting: RtSideMenuComponent }], queries: [{ propertyName: "headerTpl", first: true, predicate: RtSideMenuHeaderDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "footerTpl", first: true, predicate: RtSideMenuFooterDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "ownIconTpl", first: true, predicate: RtSideMenuIconDirective, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- \u0423\u0437\u043A\u0438\u0439 \u044D\u043A\u0440\u0430\u043D: \u043E\u0434\u0438\u043D \u0441\u0442\u043E\u043B\u0431\u0435\u0446 \u2014 \u0433\u043B\u0430\u0432\u043D\u044B\u0435 \u043F\u0443\u043D\u043A\u0442\u044B \u0438\u043B\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u044E \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430 \u0441 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043D\u0430\u0437\u0430\u0434. -->\n <rt-scroll-area rtElem=\"mobile\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n @if (selectedSubMenu()?.length) {\n <div rtElem=\"mobile-sub\">\n <button type=\"button\" rtElem=\"back\" qa-dataid=\"side-menu-back\" (click)=\"onBackToMainMenu()\">\n <rt-icon rtElem=\"back-icon\" size=\"sm\" name=\"arrow-left\" />\n <span rtElem=\"back-label\">{{ backLabel() }}</span>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </div>\n } @else {\n <nav rtElem=\"mobile-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"mobileItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n }\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n } @else {\n <rt-scroll-area rtElem=\"rail\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n <nav rtElem=\"rail-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"railItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n\n @if (subMenuOpened()) {\n @if (isSearchHeld()) {\n <!-- \u041F\u043E\u0434\u043B\u043E\u0436\u043A\u0430 \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0439 \u043F\u0430\u043D\u0435\u043B\u0438: \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0432\u0437\u044F\u0442\u043E\u0435 \u043F\u043E\u043B\u0435\u043C \u043F\u043E\u0438\u0441\u043A\u0430, \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0441\u043D\u0430\u0440\u0443\u0436\u0438. -->\n <div\n rtElem=\"backdrop\"\n qa-dataid=\"side-menu-backdrop\"\n tabindex=\"-1\"\n (click)=\"closeSubMenu()\"\n (keydown.escape)=\"closeSubMenu()\"></div>\n }\n\n <div\n #panel\n rtElem=\"panel\"\n qa-dataid=\"side-menu-panel\"\n [rtMod]=\"{ pinned: isPinned(), held: isSearchHeld() }\"\n (mouseenter)=\"cancelSubMenuClose()\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (focusin)=\"onSearchHold()\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (pinShown()) {\n <rt-icon-button\n rtElem=\"pin\"\n qa-dataid=\"side-menu-pin\"\n size=\"sm\"\n icon=\"pin\"\n [iconFill]=\"isPinned()\"\n [active]=\"isPinned()\"\n [ariaLabel]=\"isPinned() ? unpinLabel() : pinLabel()\"\n [tooltip]=\"isPinned() ? unpinLabel() : pinLabel()\"\n (clicked)=\"onSubMenuModeToggle()\" />\n }\n </div>\n\n <rt-scroll-area rtElem=\"panel-body\" [isScrollHintShown]=\"panelScrollHintShown()\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- \u0420\u0443\u0447\u043A\u0430 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0443 \u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0434\u043C\u0435\u043D\u044E: \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0435 \u0443\u0445\u043E\u0434\u0438\u0442 \u0440\u0430\u043D\u044C\u0448\u0435, \u0447\u0435\u043C \u0434\u043E \u043A\u0440\u0430\u044F \u0434\u043E\u0435\u0434\u0435\u0442 \u0440\u0443\u043A\u0430. -->\n <div\n rtElem=\"resizer\"\n qa-dataid=\"side-menu-resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"resizeLabel()\"\n [attr.aria-valuenow]=\"resizeValueNow()\"\n [attr.aria-valuemin]=\"resizeValueNow() === null ? null : resizeValueMin\"\n [attr.aria-valuemax]=\"resizeValueNow() === null ? null : resizeValueMax\"\n (pointerdown)=\"onResizeStart($event)\"\n (keydown)=\"onResizeKeydown($event)\"></div>\n }\n }\n }\n <ng-template #subMenuTpl>\n @if (narrow()) {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (keydown)=\"onSearchKeydown($event)\" />\n }\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"side-menu-empty\">{{ nothingFoundLabel() }}</div>\n }\n\n <div rtElem=\"sub-list\">\n <!-- \u0411\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430: \u0441\u0430\u043C \u0440\u0435\u0448\u0430\u0435\u0442, \u0435\u0441\u0442\u044C \u043B\u0438 \u0435\u043C\u0443 \u0447\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C, \u0438 \u0431\u0435\u0437 \u0441\u0442\u0440\u043E\u043A \u043C\u0435\u0441\u0442\u0430 \u043D\u0435 \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442. -->\n <rt-side-menu-favorites\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n\n @for (item of visibleSubMenuItems(); track item.id) {\n <rt-side-menu-sub-item\n [item]=\"item\"\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n }\n </div>\n </ng-template>\n\n <ng-template #railItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n <!-- \u0411\u0435\u0437 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439 \u043F\u043E\u0434 \u0437\u043D\u0430\u0447\u043A\u0430\u043C\u0438 \u0438\u043C\u044F \u043F\u0443\u043D\u043A\u0442\u0430 \u2014 \u0432 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0435 \u0441\u043F\u0440\u0430\u0432\u0430; \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u0435 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438 \u0433\u0430\u0441\u044F\u0442 \u0438 \u0435\u0451. -->\n @let railTip = railTitlesShown() || !subMenuTooltipsShown() ? '' : item.name;\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n [attr.aria-expanded]=\"item.submenu?.length ? subMenuOpened() && selectedItem() === item : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #railItemBodyTpl let-item>\n @if (item.icon) {\n <!-- \u0418\u043C\u044F Material \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430 \u0440\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0441\u0432\u043E\u0435\u0439 \u043F\u0430\u0440\u043E\u0439; \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0438\u043C \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u043C \u043C\u0435\u043D\u044E. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" [fill]=\"railIconFill()\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"own\"\n [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n </span>\n }\n\n @if (item.name && railTitlesShown()) {\n <span rtElem=\"rail-title\">{{ item.name }}</span>\n }\n </ng-template>\n\n <ng-template #mobileItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link && !item.submenu?.length) {\n <a\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #mobileItemBodyTpl let-item>\n @if (item.icon) {\n @let kitIcon = item.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"mobile-icon\" [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n }\n\n <span rtElem=\"mobile-title\" [rtTooltip]=\"item.name\">{{ item.name }}</span>\n\n @if (item.submenu?.length) {\n <!-- \u0421\u0442\u0440\u0435\u043B\u043A\u0430 \u0440\u0430\u0437\u0434\u0435\u043B\u0430, \u043A\u0430\u043A \u0443 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0443\u0432\u043E\u0434\u0438\u0442 \u0432 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0430 \u043D\u0435 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu,.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-item-color: currentcolor;--rt-side-menu-item-bg-hover: var(--rt-color-bg-hover);--rt-side-menu-item-bg-active: var(--rt-color-action-primary-subtle);--rt-side-menu-item-color-active: var(--rt-color-action-primary-on-surface)}.rt-side-menu{--rt-side-menu-rail-width: 5.5rem;--rt-side-menu-panel-width: 15rem;--rt-side-menu-panel-held-width: 30rem;--rt-side-menu-mobile-width: 15rem;--rt-side-menu-panel-radius: var(--rt-radius-lg);--rt-side-menu-panel-shadow: var(--rt-shadow-nav-panel);--rt-side-menu-item-radius: var(--rt-radius-full);--rt-side-menu-resizer-border-width: var(--rt-border-width-thick);--rt-side-menu-panel-border-width: var(--rt-border-width-thin);--rt-side-menu-transition-duration: var(--rt-duration-fast);--rt-side-menu-rail-icon-size: var(--rt-icon-step-md);--rt-side-menu-rail-gap: var(--rt-space-md);--rt-side-menu-rail-bg: var(--rt-color-bg-surface);--rt-side-menu-rail-border-width: var(--rt-side-menu-panel-border-width);--rt-side-menu-panel-bg: var(--rt-color-bg-surface);--rt-side-menu-panel-head-padding: var(--rt-space-sm);--rt-side-menu-sub-list-padding-inline: var(--rt-space-2);--rt-side-menu-empty-padding: var(--rt-space-md);--rt-side-menu-panel-dragged-width: 0px;--rt-side-menu-panel-shown-width: max(var(--rt-side-menu-panel-width), var(--rt-side-menu-panel-dragged-width));position:relative;display:flex;height:100%;color:var(--rt-color-text-primary)}.rt-side-menu__rail{max-width:var(--rt-side-menu-rail-width);height:100%;box-sizing:border-box;flex:0 0 var(--rt-side-menu-rail-width);border-right:var(--rt-side-menu-rail-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-rail-bg)}.rt-side-menu__rail .rt-scroll-area__body{padding-inline:var(--rt-space-1)}.rt-side-menu__rail-list{display:flex;flex-direction:column;align-items:center;gap:var(--rt-side-menu-rail-gap);padding-block:var(--rt-space-sm)}.rt-side-menu__rail-item{display:flex;width:100%;flex-direction:column;align-items:center;padding:0;border:0;background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;gap:var(--rt-space-1);text-decoration:none}.rt-side-menu__rail-item:focus-visible{outline:none}.rt-side-menu__rail-item:focus-visible .rt-side-menu__rail-icon{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color)}.rt-side-menu__rail-item:hover .rt-side-menu__rail-icon,.rt-side-menu__rail-item:active .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__rail-item--active .rt-side-menu__rail-icon,.rt-side-menu__rail-item--active:hover .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__rail-icon{--rt-icon-step-md: var(--rt-side-menu-rail-icon-size);display:flex;width:3.5rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--rt-side-menu-item-radius);transition:background-color var(--rt-side-menu-transition-duration) ease}.rt-side-menu__rail-title{max-width:100%;font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);overflow-wrap:anywhere;text-align:center}.rt-side-menu__backdrop{position:fixed;z-index:var(--rt-z-dropdown);inset:0;inset-inline-start:var(--rt-side-menu-rail-width)}.rt-side-menu__panel{position:absolute;z-index:var(--rt-z-dropdown);top:0;left:var(--rt-side-menu-rail-width);display:flex;width:var(--rt-side-menu-panel-shown-width);height:100%;box-sizing:border-box;flex-direction:column;border-radius:0 var(--rt-side-menu-panel-radius) var(--rt-side-menu-panel-radius) 0;border-left:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-panel-bg);box-shadow:var(--rt-side-menu-panel-shadow)}.rt-side-menu__panel--held{width:max(var(--rt-side-menu-panel-held-width),var(--rt-side-menu-panel-shown-width))}.rt-side-menu__panel--pinned{--rt-side-menu-panel-radius: 0;--rt-side-menu-panel-shadow: var(--rt-shadow-none);position:relative;left:0;flex-shrink:0}.rt-side-menu__panel-head{display:flex;align-items:center;padding:var(--rt-side-menu-panel-head-padding);gap:var(--rt-space-2)}.rt-side-menu__search{min-width:0;flex:1 1 auto}.rt-side-menu__pin{flex-shrink:0}.rt-side-menu__panel-body{min-height:0;flex:1 1 auto}.rt-side-menu__sub-list{display:flex;flex-direction:column;padding-block-end:var(--rt-space-sm);padding-inline:var(--rt-side-menu-sub-list-padding-inline)}.rt-side-menu__empty{padding:var(--rt-side-menu-empty-padding);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}.rt-side-menu__resizer{position:absolute;z-index:var(--rt-z-dropdown);top:0;bottom:0;left:calc(var(--rt-side-menu-rail-width) + var(--rt-side-menu-panel-shown-width) - var(--rt-space-2));width:var(--rt-space-sm);cursor:col-resize}.rt-side-menu__resizer:before{position:absolute;top:0;bottom:0;left:50%;border-left:var(--rt-side-menu-resizer-border-width) solid transparent;content:\"\"}.rt-side-menu__resizer:hover:before,.rt-side-menu__resizer:focus-visible:before{border-left-color:var(--rt-color-border-strong)}.rt-side-menu__resizer:focus-visible{outline:none}.rt-side-menu__mobile{min-width:0;max-width:100%;height:100%;flex:0 0 var(--rt-side-menu-mobile-width);background:var(--rt-color-bg-surface)}.rt-side-menu__mobile .rt-scroll-area__body{padding-inline:0}.rt-side-menu__mobile-list,.rt-side-menu__mobile-sub{display:flex;flex-direction:column;padding:var(--rt-space-sm);gap:var(--rt-space-1)}.rt-side-menu__mobile-item,.rt-side-menu__back{display:flex;width:100%;min-height:var(--rt-control-height-lg);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-item-radius);background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:calc(var(--rt-space-sm) + var(--rt-space-xs));padding-inline:var(--rt-space-md);text-align:start;text-decoration:none}.rt-side-menu__mobile-item:hover,.rt-side-menu__back:hover{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__mobile-item:focus-visible,.rt-side-menu__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu__mobile-item--active,.rt-side-menu__back--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__own-icon{--rt-side-menu-own-icon-size: var(--rt-size-5);display:inline-flex;width:var(--rt-side-menu-own-icon-size);height:var(--rt-side-menu-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu__mobile-icon,.rt-side-menu__back-icon,.rt-side-menu__mobile-chevron{flex-shrink:0}.rt-side-menu__mobile-title,.rt-side-menu__back-label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "glyphStrategy", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtInputComponent, selector: "rt-input", inputs: ["appearance", "type", "placeholder", "iconLeft", "iconRight", "passwordToggle", "autocomplete"] }, { kind: "component", type: RtScrollAreaComponent, selector: "rt-scroll-area", inputs: ["isScrollHintShown"] }, { kind: "directive", type: RtScrollAreaContentDirective, selector: "[rtScrollAreaContent]" }, { kind: "directive", type: RtScrollAreaFooterDirective, selector: "[rtScrollAreaFooter]" }, { kind: "directive", type: RtScrollAreaHeaderDirective, selector: "[rtScrollAreaHeader]" }, { kind: "directive", type: RtTooltipDirective, selector: "[rtTooltip]", inputs: ["rtTooltip", "rtTooltipPlacement", "rtTooltipWhenTruncated"], exportAs: ["rtTooltip"] }, { kind: "component", type: RtSideMenuFavoritesComponent, selector: "rt-side-menu-favorites", outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"] }, { kind: "component", type: RtSideMenuSubItemComponent, selector: "rt-side-menu-sub-item", inputs: ["item", "inFavorites"], outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"] }, { kind: "pipe", type: RtSideMenuIconPipe, name: "rtSideMenuIcon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1600
- }
1601
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuComponent, decorators: [{
1602
- type: Component,
1603
- args: [{ selector: 'rt-side-menu', host: {
1604
- class: BEM_BLOCK,
1605
- // Раскладка хоста меняется только у закреплённого режима: панель встаёт в поток рядом с полосой.
1606
- '[class.rt-side-menu--pinned]': 'isPinned()',
1607
- // Натянутая ширина подменю приходит своим свойством: панель берёт наибольшее из него и ширины оформления.
1608
- '[style.--rt-side-menu-panel-dragged-width]': 'panelWidthStyle()',
1609
- // Кнопки избранного, ждущие наведения, в покое ширины не занимают.
1610
- '[class.rt-side-menu--favorite-actions-none]': "favoriteActionsReserve() === 'none'",
1611
- }, providers: [{ provide: RT_SIDE_MENU, useExisting: RtSideMenuComponent }], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
1612
- NgTemplateOutlet,
1613
- ReactiveFormsModule,
1614
- RouterLink,
1615
- BlockDirective,
1616
- ElemDirective,
1617
- ModDirective,
1618
- RtIconComponent,
1619
- RtIconButtonComponent,
1620
- RtInputComponent,
1621
- RtScrollAreaComponent,
1622
- RtScrollAreaContentDirective,
1623
- RtScrollAreaFooterDirective,
1624
- RtScrollAreaHeaderDirective,
1625
- RtTooltipDirective,
1626
- RtSideMenuFavoritesComponent,
1627
- RtSideMenuIconPipe,
1628
- RtSideMenuSubItemComponent,
1629
- ], template: "<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- \u0423\u0437\u043A\u0438\u0439 \u044D\u043A\u0440\u0430\u043D: \u043E\u0434\u0438\u043D \u0441\u0442\u043E\u043B\u0431\u0435\u0446 \u2014 \u0433\u043B\u0430\u0432\u043D\u044B\u0435 \u043F\u0443\u043D\u043A\u0442\u044B \u0438\u043B\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u044E \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430 \u0441 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043D\u0430\u0437\u0430\u0434. -->\n <rt-scroll-area rtElem=\"mobile\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n @if (selectedSubMenu()?.length) {\n <div rtElem=\"mobile-sub\">\n <button type=\"button\" rtElem=\"back\" qa-dataid=\"side-menu-back\" (click)=\"onBackToMainMenu()\">\n <rt-icon rtElem=\"back-icon\" size=\"sm\" name=\"arrow-left\" />\n <span rtElem=\"back-label\">{{ backLabel() }}</span>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </div>\n } @else {\n <nav rtElem=\"mobile-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"mobileItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n }\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n } @else {\n <rt-scroll-area rtElem=\"rail\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n <nav rtElem=\"rail-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"railItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n\n @if (subMenuOpened()) {\n @if (isSearchHeld()) {\n <!-- \u041F\u043E\u0434\u043B\u043E\u0436\u043A\u0430 \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0439 \u043F\u0430\u043D\u0435\u043B\u0438: \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0432\u0437\u044F\u0442\u043E\u0435 \u043F\u043E\u043B\u0435\u043C \u043F\u043E\u0438\u0441\u043A\u0430, \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0441\u043D\u0430\u0440\u0443\u0436\u0438. -->\n <div\n rtElem=\"backdrop\"\n qa-dataid=\"side-menu-backdrop\"\n tabindex=\"-1\"\n (click)=\"closeSubMenu()\"\n (keydown.escape)=\"closeSubMenu()\"></div>\n }\n\n <div\n #panel\n rtElem=\"panel\"\n qa-dataid=\"side-menu-panel\"\n [rtMod]=\"{ pinned: isPinned(), held: isSearchHeld() }\"\n (mouseenter)=\"cancelSubMenuClose()\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (focusin)=\"onSearchHold()\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (pinShown()) {\n <rt-icon-button\n rtElem=\"pin\"\n qa-dataid=\"side-menu-pin\"\n size=\"sm\"\n icon=\"pin\"\n [iconFill]=\"isPinned()\"\n [active]=\"isPinned()\"\n [ariaLabel]=\"isPinned() ? unpinLabel() : pinLabel()\"\n [tooltip]=\"isPinned() ? unpinLabel() : pinLabel()\"\n (clicked)=\"onSubMenuModeToggle()\" />\n }\n </div>\n\n <rt-scroll-area rtElem=\"panel-body\" [isScrollHintShown]=\"panelScrollHintShown()\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- \u0420\u0443\u0447\u043A\u0430 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0443 \u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0434\u043C\u0435\u043D\u044E: \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0435 \u0443\u0445\u043E\u0434\u0438\u0442 \u0440\u0430\u043D\u044C\u0448\u0435, \u0447\u0435\u043C \u0434\u043E \u043A\u0440\u0430\u044F \u0434\u043E\u0435\u0434\u0435\u0442 \u0440\u0443\u043A\u0430. -->\n <div\n rtElem=\"resizer\"\n qa-dataid=\"side-menu-resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"resizeLabel()\"\n [attr.aria-valuenow]=\"resizeValueNow()\"\n [attr.aria-valuemin]=\"resizeValueNow() === null ? null : resizeValueMin\"\n [attr.aria-valuemax]=\"resizeValueNow() === null ? null : resizeValueMax\"\n (pointerdown)=\"onResizeStart($event)\"\n (keydown)=\"onResizeKeydown($event)\"></div>\n }\n }\n }\n <ng-template #subMenuTpl>\n @if (narrow()) {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (keydown)=\"onSearchKeydown($event)\" />\n }\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"side-menu-empty\">{{ nothingFoundLabel() }}</div>\n }\n\n <div rtElem=\"sub-list\">\n <!-- \u0411\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430: \u0441\u0430\u043C \u0440\u0435\u0448\u0430\u0435\u0442, \u0435\u0441\u0442\u044C \u043B\u0438 \u0435\u043C\u0443 \u0447\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C, \u0438 \u0431\u0435\u0437 \u0441\u0442\u0440\u043E\u043A \u043C\u0435\u0441\u0442\u0430 \u043D\u0435 \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442. -->\n <rt-side-menu-favorites\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n\n @for (item of visibleSubMenuItems(); track item.id) {\n <rt-side-menu-sub-item\n [item]=\"item\"\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n }\n </div>\n </ng-template>\n\n <ng-template #railItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n <!-- \u0411\u0435\u0437 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439 \u043F\u043E\u0434 \u0437\u043D\u0430\u0447\u043A\u0430\u043C\u0438 \u0438\u043C\u044F \u043F\u0443\u043D\u043A\u0442\u0430 \u2014 \u0432 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0435 \u0441\u043F\u0440\u0430\u0432\u0430; \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u0435 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0438 \u0433\u0430\u0441\u044F\u0442 \u0438 \u0435\u0451. -->\n @let railTip = railTitlesShown() || !subMenuTooltipsShown() ? '' : item.name;\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n [attr.aria-expanded]=\"item.submenu?.length ? subMenuOpened() && selectedItem() === item : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #railItemBodyTpl let-item>\n @if (item.icon) {\n <!-- \u0418\u043C\u044F Material \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430 \u0440\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0441\u0432\u043E\u0435\u0439 \u043F\u0430\u0440\u043E\u0439; \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0438\u043C \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u043C \u043C\u0435\u043D\u044E. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" [fill]=\"railIconFill()\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"own\"\n [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n </span>\n }\n\n @if (item.name && railTitlesShown()) {\n <span rtElem=\"rail-title\">{{ item.name }}</span>\n }\n </ng-template>\n\n <ng-template #mobileItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link && !item.submenu?.length) {\n <a\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #mobileItemBodyTpl let-item>\n @if (item.icon) {\n @let kitIcon = item.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"mobile-icon\" [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n }\n\n <span rtElem=\"mobile-title\" [rtTooltip]=\"item.name\">{{ item.name }}</span>\n\n @if (item.submenu?.length) {\n <!-- \u0421\u0442\u0440\u0435\u043B\u043A\u0430 \u0440\u0430\u0437\u0434\u0435\u043B\u0430, \u043A\u0430\u043A \u0443 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0443\u0432\u043E\u0434\u0438\u0442 \u0432 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0430 \u043D\u0435 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu,.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-item-color: currentcolor;--rt-side-menu-item-bg-hover: var(--rt-color-bg-hover);--rt-side-menu-item-bg-active: var(--rt-color-action-primary-subtle);--rt-side-menu-item-color-active: var(--rt-color-action-primary-on-surface)}.rt-side-menu{--rt-side-menu-rail-width: 5.5rem;--rt-side-menu-panel-width: 15rem;--rt-side-menu-panel-held-width: 30rem;--rt-side-menu-mobile-width: 15rem;--rt-side-menu-panel-radius: var(--rt-radius-lg);--rt-side-menu-panel-shadow: var(--rt-shadow-nav-panel);--rt-side-menu-item-radius: var(--rt-radius-full);--rt-side-menu-resizer-border-width: var(--rt-border-width-thick);--rt-side-menu-panel-border-width: var(--rt-border-width-thin);--rt-side-menu-transition-duration: var(--rt-duration-fast);--rt-side-menu-rail-icon-size: var(--rt-icon-step-md);--rt-side-menu-rail-gap: var(--rt-space-md);--rt-side-menu-rail-bg: var(--rt-color-bg-surface);--rt-side-menu-rail-border-width: var(--rt-side-menu-panel-border-width);--rt-side-menu-panel-bg: var(--rt-color-bg-surface);--rt-side-menu-panel-head-padding: var(--rt-space-sm);--rt-side-menu-sub-list-padding-inline: var(--rt-space-2);--rt-side-menu-empty-padding: var(--rt-space-md);--rt-side-menu-panel-dragged-width: 0px;--rt-side-menu-panel-shown-width: max(var(--rt-side-menu-panel-width), var(--rt-side-menu-panel-dragged-width));position:relative;display:flex;height:100%;color:var(--rt-color-text-primary)}.rt-side-menu__rail{max-width:var(--rt-side-menu-rail-width);height:100%;box-sizing:border-box;flex:0 0 var(--rt-side-menu-rail-width);border-right:var(--rt-side-menu-rail-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-rail-bg)}.rt-side-menu__rail .rt-scroll-area__body{padding-inline:var(--rt-space-1)}.rt-side-menu__rail-list{display:flex;flex-direction:column;align-items:center;gap:var(--rt-side-menu-rail-gap);padding-block:var(--rt-space-sm)}.rt-side-menu__rail-item{display:flex;width:100%;flex-direction:column;align-items:center;padding:0;border:0;background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;gap:var(--rt-space-1);text-decoration:none}.rt-side-menu__rail-item:focus-visible{outline:none}.rt-side-menu__rail-item:focus-visible .rt-side-menu__rail-icon{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color)}.rt-side-menu__rail-item:hover .rt-side-menu__rail-icon,.rt-side-menu__rail-item:active .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__rail-item--active .rt-side-menu__rail-icon,.rt-side-menu__rail-item--active:hover .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__rail-icon{--rt-icon-step-md: var(--rt-side-menu-rail-icon-size);display:flex;width:3.5rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--rt-side-menu-item-radius);transition:background-color var(--rt-side-menu-transition-duration) ease}.rt-side-menu__rail-title{max-width:100%;font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);overflow-wrap:anywhere;text-align:center}.rt-side-menu__backdrop{position:fixed;z-index:var(--rt-z-dropdown);inset:0;inset-inline-start:var(--rt-side-menu-rail-width)}.rt-side-menu__panel{position:absolute;z-index:var(--rt-z-dropdown);top:0;left:var(--rt-side-menu-rail-width);display:flex;width:var(--rt-side-menu-panel-shown-width);height:100%;box-sizing:border-box;flex-direction:column;border-radius:0 var(--rt-side-menu-panel-radius) var(--rt-side-menu-panel-radius) 0;border-left:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-panel-bg);box-shadow:var(--rt-side-menu-panel-shadow)}.rt-side-menu__panel--held{width:max(var(--rt-side-menu-panel-held-width),var(--rt-side-menu-panel-shown-width))}.rt-side-menu__panel--pinned{--rt-side-menu-panel-radius: 0;--rt-side-menu-panel-shadow: var(--rt-shadow-none);position:relative;left:0;flex-shrink:0}.rt-side-menu__panel-head{display:flex;align-items:center;padding:var(--rt-side-menu-panel-head-padding);gap:var(--rt-space-2)}.rt-side-menu__search{min-width:0;flex:1 1 auto}.rt-side-menu__pin{flex-shrink:0}.rt-side-menu__panel-body{min-height:0;flex:1 1 auto}.rt-side-menu__sub-list{display:flex;flex-direction:column;padding-block-end:var(--rt-space-sm);padding-inline:var(--rt-side-menu-sub-list-padding-inline)}.rt-side-menu__empty{padding:var(--rt-side-menu-empty-padding);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}.rt-side-menu__resizer{position:absolute;z-index:var(--rt-z-dropdown);top:0;bottom:0;left:calc(var(--rt-side-menu-rail-width) + var(--rt-side-menu-panel-shown-width) - var(--rt-space-2));width:var(--rt-space-sm);cursor:col-resize}.rt-side-menu__resizer:before{position:absolute;top:0;bottom:0;left:50%;border-left:var(--rt-side-menu-resizer-border-width) solid transparent;content:\"\"}.rt-side-menu__resizer:hover:before,.rt-side-menu__resizer:focus-visible:before{border-left-color:var(--rt-color-border-strong)}.rt-side-menu__resizer:focus-visible{outline:none}.rt-side-menu__mobile{min-width:0;max-width:100%;height:100%;flex:0 0 var(--rt-side-menu-mobile-width);background:var(--rt-color-bg-surface)}.rt-side-menu__mobile .rt-scroll-area__body{padding-inline:0}.rt-side-menu__mobile-list,.rt-side-menu__mobile-sub{display:flex;flex-direction:column;padding:var(--rt-space-sm);gap:var(--rt-space-1)}.rt-side-menu__mobile-item,.rt-side-menu__back{display:flex;width:100%;min-height:var(--rt-control-height-lg);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-item-radius);background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:calc(var(--rt-space-sm) + var(--rt-space-xs));padding-inline:var(--rt-space-md);text-align:start;text-decoration:none}.rt-side-menu__mobile-item:hover,.rt-side-menu__back:hover{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__mobile-item:focus-visible,.rt-side-menu__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu__mobile-item--active,.rt-side-menu__back--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__own-icon{--rt-side-menu-own-icon-size: var(--rt-size-5);display:inline-flex;width:var(--rt-side-menu-own-icon-size);height:var(--rt-side-menu-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu__mobile-icon,.rt-side-menu__back-icon,.rt-side-menu__mobile-chevron{flex-shrink:0}.rt-side-menu__mobile-title,.rt-side-menu__back-label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}}\n"] }]
1630
- }], ctorParameters: () => [], propDecorators: { panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], headerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtSideMenuHeaderDirective), { ...{ read: TemplateRef }, isSignal: true }] }], footerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtSideMenuFooterDirective), { ...{ read: TemplateRef }, isSignal: true }] }], activeMenuIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeMenuIds", required: true }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], subMenuMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuMode", required: false }] }], subMenuWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuWidth", required: false }] }], menuId: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuId", required: false }] }], favoritesCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "favoritesCount", required: false }] }], favoriteActionsReserve: [{ type: i0.Input, args: [{ isSignal: true, alias: "favoriteActionsReserve", required: false }] }], isFavoritesSearchShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "isFavoritesSearchShown", required: false }] }], pinShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinShown", required: false }] }], subMenuCloseDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuCloseDelay", required: false }] }], subMenuModeChange: [{ type: i0.Output, args: ["subMenuModeChange"] }], subMenuWidthChange: [{ type: i0.Output, args: ["subMenuWidthChange"] }], subMenuResizeStart: [{ type: i0.Output, args: ["subMenuResizeStart"] }], subMenuResizeEnd: [{ type: i0.Output, args: ["subMenuResizeEnd"] }], closeMobileMenuAction: [{ type: i0.Output, args: ["closeMobileMenuAction"] }], clickSubMenuAction: [{ type: i0.Output, args: ["clickSubMenuAction"] }], clickSubMenuAdditionalAction: [{ type: i0.Output, args: ["clickSubMenuAdditionalAction"] }], ownIconTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtSideMenuIconDirective), { ...{
1631
- read: TemplateRef,
1632
- }, isSignal: true }] }] } });
1633
-
1634
- /**
1635
- * Generated bundle index. Do not edit.
1636
- */
472
+ var RtSideMenuIconPipe = class RtSideMenuIconPipe {
473
+ transform(icon) {
474
+ return sideMenuIconName(icon);
475
+ }
476
+ static {
477
+ this.ɵfac = i0.ɵɵngDeclareFactory({
478
+ minVersion: "12.0.0",
479
+ version: "22.2.2",
480
+ ngImport: i0,
481
+ type: RtSideMenuIconPipe,
482
+ deps: [],
483
+ target: i0.ɵɵFactoryTarget.Pipe
484
+ });
485
+ }
486
+ static {
487
+ this.ɵpipe = i0.ɵɵngDeclarePipe({
488
+ minVersion: "14.0.0",
489
+ version: "22.2.2",
490
+ ngImport: i0,
491
+ type: RtSideMenuIconPipe,
492
+ isStandalone: true,
493
+ name: "rtSideMenuIcon"
494
+ });
495
+ }
496
+ };
497
+ i0.ɵɵngDeclareClassMetadata({
498
+ minVersion: "12.0.0",
499
+ version: "22.2.2",
500
+ ngImport: i0,
501
+ type: RtSideMenuIconPipe,
502
+ decorators: [{
503
+ type: Pipe,
504
+ args: [{ name: "rtSideMenuIcon" }]
505
+ }]
506
+ });
507
+ var RtSideMenuTitlePartsPipe = class RtSideMenuTitlePartsPipe {
508
+ transform(name, query) {
509
+ return splitSideMenuTitle(name ?? "", query);
510
+ }
511
+ static {
512
+ this.ɵfac = i0.ɵɵngDeclareFactory({
513
+ minVersion: "12.0.0",
514
+ version: "22.2.2",
515
+ ngImport: i0,
516
+ type: RtSideMenuTitlePartsPipe,
517
+ deps: [],
518
+ target: i0.ɵɵFactoryTarget.Pipe
519
+ });
520
+ }
521
+ static {
522
+ this.ɵpipe = i0.ɵɵngDeclarePipe({
523
+ minVersion: "14.0.0",
524
+ version: "22.2.2",
525
+ ngImport: i0,
526
+ type: RtSideMenuTitlePartsPipe,
527
+ isStandalone: true,
528
+ name: "rtSideMenuTitleParts"
529
+ });
530
+ }
531
+ };
532
+ i0.ɵɵngDeclareClassMetadata({
533
+ minVersion: "12.0.0",
534
+ version: "22.2.2",
535
+ ngImport: i0,
536
+ type: RtSideMenuTitlePartsPipe,
537
+ decorators: [{
538
+ type: Pipe,
539
+ args: [{ name: "rtSideMenuTitleParts" }]
540
+ }]
541
+ });
542
+ const BEM_BLOCK$2 = "rt-side-menu-sub-item";
543
+ const FAVORITE_ROW = ".rt-side-menu-favorites__row";
544
+ const FAVORITE_BUTTON = ".rt-side-menu-sub-item__favorite button";
545
+ var RtSideMenuSubItemComponent = class RtSideMenuSubItemComponent {
546
+ constructor() {
547
+ this.#settings = inject(RtSideMenuSettingsService, { optional: true });
548
+ this.#router = inject(Router);
549
+ this.#route = inject(ActivatedRoute, { optional: true });
550
+ this.#errors = inject(ErrorHandler);
551
+ this.menuRef = inject(RT_SIDE_MENU);
552
+ this.narrow = inject(BreakpointsService).narrow;
553
+ this.favoritesIcons = this.#settings?.favoritesIcons ?? null;
554
+ this.addLabel = rtKitLabel("sideMenuFavoriteAdd");
555
+ this.removeLabel = rtKitLabel("sideMenuFavoriteRemove");
556
+ this.favoritesOn = computed(() => !!this.#settings && sideMenuFavoritesSection(this.menuRef.menuItems(), this.menuRef.shownSubMenu()) !== null, ...ngDevMode ? [{ debugName: "favoritesOn" }] : /* istanbul ignore next */ []);
557
+ this.favoriteIds = computed(() => this.#settings?.favoriteIds(this.menuRef.menuId())() ?? [], ...ngDevMode ? [{ debugName: "favoriteIds" }] : /* istanbul ignore next */ []);
558
+ this.item = input.required(...ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []);
559
+ this.inFavorites = input(false, {
560
+ ...ngDevMode ? { debugName: "inFavorites" } : /* istanbul ignore next */ {},
561
+ transform: booleanAttribute
562
+ });
563
+ this.clickSubMenuAction = output();
564
+ this.clickSubMenuAdditionalAction = output();
565
+ }
566
+ #settings;
567
+ #router;
568
+ #route;
569
+ #errors;
570
+ onClickSubMenu(item, event) {
571
+ this.clickSubMenuAction.emit({
572
+ item,
573
+ event
574
+ });
575
+ if (event.defaultPrevented || !item.link || sideMenuPressOpensInBrowser(event)) return;
576
+ event.preventDefault();
577
+ this.#router.navigateByUrl(sideMenuLinkTree(this.#router, this.#route, item.link)).catch((error) => this.#errors.handleError(error));
578
+ }
579
+ onClickSubMenuAdditional(item, event) {
580
+ event.preventDefault();
581
+ event.stopPropagation();
582
+ this.clickSubMenuAdditionalAction.emit({
583
+ data: item.iconButton?.data,
584
+ event
585
+ });
586
+ }
587
+ onToggleFavorite(item, event) {
588
+ event.preventDefault();
589
+ event.stopPropagation();
590
+ this.#settings?.toggleFavorite(this.menuRef.menuId(), item.id);
591
+ }
592
+ onRemoveFavorite(item, event) {
593
+ event.preventDefault();
594
+ event.stopPropagation();
595
+ const row = event.target instanceof Element ? event.target.closest(FAVORITE_ROW) : null;
596
+ (row?.nextElementSibling ?? row?.previousElementSibling)?.querySelector(FAVORITE_BUTTON)?.focus();
597
+ this.#settings?.removeFavorite(this.menuRef.menuId(), item.id);
598
+ }
599
+ onToggleFolder(item) {
600
+ this.menuRef.toggleFolder(item);
601
+ }
602
+ static {
603
+ this.ɵfac = i0.ɵɵngDeclareFactory({
604
+ minVersion: "12.0.0",
605
+ version: "22.2.2",
606
+ ngImport: i0,
607
+ type: RtSideMenuSubItemComponent,
608
+ deps: [],
609
+ target: i0.ɵɵFactoryTarget.Component
610
+ });
611
+ }
612
+ static {
613
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
614
+ minVersion: "17.0.0",
615
+ version: "22.2.2",
616
+ type: RtSideMenuSubItemComponent,
617
+ isStandalone: true,
618
+ selector: "rt-side-menu-sub-item",
619
+ inputs: {
620
+ item: {
621
+ classPropertyName: "item",
622
+ publicName: "item",
623
+ isSignal: true,
624
+ isRequired: true,
625
+ transformFunction: null
626
+ },
627
+ inFavorites: {
628
+ classPropertyName: "inFavorites",
629
+ publicName: "inFavorites",
630
+ isSignal: true,
631
+ isRequired: false,
632
+ transformFunction: null
633
+ }
634
+ },
635
+ outputs: {
636
+ clickSubMenuAction: "clickSubMenuAction",
637
+ clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction"
638
+ },
639
+ host: { classAttribute: "rt-side-menu-sub-item" },
640
+ ngImport: i0,
641
+ template: "<ng-container rtBlock=\"rt-side-menu-sub-item\">\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: item(), depth: 0 }\" />\n <ng-template #rowTpl let-row let-depth=\"depth\">\n @let isActive = menuRef.activeMenuIds().includes(row.id);\n @let isHighlighted = !inFavorites() && menuRef.highlightedMenuId() === row.id;\n @if (row.link) {\n <!--\n Номер пункта на самой строке — признак для правил приложения: по нему оно находит строку,\n например ту, что открывает страницу раздела. Стоит и у строки блока избранного.\n Строка блока избранного без номера: по номеру клавиатура нажимает строку списка. И без\n родного перетаскивания ссылки: нажатие на ручку внутри строки браузер забирал себе,\n и перетаскивание CDK не начиналось.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.data-item-id]=\"row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [attr.href]=\"row.link | rtSideMenuHref\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ active: isActive, highlighted: isHighlighted }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickSubMenu(row, $event)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n <!-- Кусок без отметки тоже узел: голая интерполяция разрывала бы подпись пробелами. -->\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <!--\n Строка блока несёт «убрать», а не звезду: всё в блоке уже в избранном. Звезда —\n у пункта со своим адресом в разделе с избранным: полая — не в избранном,\n закрашенная цветом темы — в нём. Имя кнопки называет следующее нажатие.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\n [rtMod]=\"{ remove: true }\"\n [icon]=\"favoritesIcons.remove\"\n [ariaLabel]=\"removeLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : removeLabel()\"\n (clicked)=\"onRemoveFavorite(row, $event)\" />\n } @else if (favoritesOn() && !row.favoriteDisabled) {\n @let isFavorite = favoriteIds().includes(row.id);\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-star\"\n size=\"sm\"\n [rtMod]=\"{ on: isFavorite }\"\n [icon]=\"isFavorite ? 'star-fill' : 'star'\"\n [iconFill]=\"isFavorite\"\n [ariaLabel]=\"isFavorite ? removeLabel() : addLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : isFavorite ? removeLabel() : addLabel()\"\n (clicked)=\"onToggleFavorite(row, $event)\" />\n }\n\n <!-- Действия, которые строке передаёт владелец: блок избранного кладёт сюда ручку. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- Кнопка потребителя видна всегда и стоит последней: кнопки избранного встают перед ней. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- Имя без значка кита берёт свой шаблон меню, как значок строки. Без шаблона кнопка пуста и\n незнакомого имени значку не передаёт: значка с таким именем нет, и ждать его некому. -->\n @let buttonKitIcon = buttonIcon | rtSideMenuIcon;\n @let buttonOwnTpl = buttonKitIcon ? null : menuRef.ownIconTpl();\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"side-menu-sub-item-action\"\n size=\"sm\"\n [icon]=\"buttonKitIcon\"\n [iconFill]=\"menuRef.subItemIconFill()\"\n [ariaLabel]=\"row.name ?? ''\"\n (clicked)=\"onClickSubMenuAdditional(row, $event)\">\n @if (buttonOwnTpl) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-button-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"buttonOwnTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, icon: buttonIcon, slot: 'iconButton' }\" />\n </span>\n }\n </rt-icon-button>\n }\n </a>\n } @else if (row.submenu) {\n <!--\n Папка — раскрывающаяся панель кита. Рисуется и с пустым составом: отбор оставляет так\n ту, что совпала подписью. Шеврона у такой папки нет — раскрывать нечего. Раскрытие\n держит меню: панель сообщает о нажатии, меню переключает папку. Кнопке заголовка\n отдан номер пункта — по нему клавиатура меню нажимает строку.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [attr.data-item-id]=\"row.id\"\n [hideToggle]=\"!row.submenu.length\"\n [expanded]=\"isOpen\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ highlighted: isHighlighted }\"\n (expandedChange)=\"onToggleFolder(row)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" qa-dataid=\"side-menu-folder\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div rtElem=\"children\" role=\"group\">\n @for (child of row.submenu; track child.id) {\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: child, depth: depth + 1 }\" />\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n </ng-template>\n\n <!-- Значок строки: имя кита или пара имени Material, а имя, которого кит не рисует, — свой шаблон меню. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" [fill]=\"menuRef.subItemIconFill()\" />\n } @else if (menuRef.ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: row, icon: row.icon, slot: 'icon' }\" />\n </span>\n }\n </ng-template>\n</ng-container>\n",
642
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-sub-item{--rt-side-menu-sub-item-radius: var(--rt-radius-full);--rt-side-menu-sub-item-transition-duration: var(--rt-duration-fast);--rt-side-menu-sub-item-match-bg: var(--rt-yellow-400);--rt-side-menu-sub-item-match-color: var(--rt-neutral-950);--rt-side-menu-sub-item-match-radius: var(--rt-radius-xs);--rt-side-menu-sub-item-min-height: var(--rt-control-height-md);--rt-side-menu-sub-item-font-size: var(--rt-text-sm);--rt-side-menu-sub-item-padding-start: var(--rt-space-md);--rt-side-menu-sub-item-padding-end: var(--rt-space-sm);--rt-side-menu-sub-item-color: var(--rt-color-text-primary);--rt-side-menu-sub-item-gap: var(--rt-space-sm);--rt-side-menu-sub-item-folder-padding-start: var(--rt-side-menu-sub-item-padding-start);--rt-side-menu-sub-item-chevron-color: var(--rt-color-text-muted);--rt-side-menu-sub-item-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-button-radius: var(--rt-radius-md);--rt-side-menu-sub-item-button-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-action-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-color: var(--rt-color-text-primary);display:block;letter-spacing:var(--rt-side-menu-sub-item-letter-spacing, inherit)}.rt-side-menu-sub-item__row{--rt-side-menu-sub-item-depth: 0;display:flex;width:100%;min-height:var(--rt-side-menu-sub-item-min-height);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-sub-item-radius);background:transparent;color:var(--rt-side-menu-sub-item-color);cursor:pointer;font:inherit;font-size:var(--rt-side-menu-sub-item-font-size);gap:var(--rt-side-menu-sub-item-gap);padding-block:var(--rt-space-2);padding-inline:calc(var(--rt-side-menu-sub-item-padding-start) * (1 + var(--rt-side-menu-sub-item-depth))) var(--rt-side-menu-sub-item-padding-end);text-align:start;text-decoration:none;transition:background-color var(--rt-side-menu-sub-item-transition-duration) ease}.rt-side-menu-sub-item__row:hover,.rt-side-menu-sub-item__row:active,.rt-side-menu-sub-item__row--highlighted{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__row:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu-sub-item__row--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu-sub-item__icon{--rt-icon-step-sm: var(--rt-side-menu-sub-item-icon-size);flex-shrink:0;color:inherit}.rt-side-menu-sub-item__own-icon{--rt-side-menu-sub-item-own-icon-size: var(--rt-size-4);display:inline-flex;width:var(--rt-side-menu-sub-item-own-icon-size);height:var(--rt-side-menu-sub-item-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu-sub-item__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu-sub-item__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-sub-item__part{color:inherit}.rt-side-menu-sub-item__match{border-radius:var(--rt-side-menu-sub-item-match-radius);background:var(--rt-side-menu-sub-item-match-bg);color:var(--rt-side-menu-sub-item-match-color)}.rt-side-menu-sub-item__action{--rt-side-menu-sub-item-action-ring-width: var(--rt-border-width-thin);--rt-side-menu-sub-item-action-radius: var(--rt-radius-full);flex-shrink:0;border-radius:var(--rt-side-menu-sub-item-action-radius);outline:var(--rt-side-menu-sub-item-action-ring-width) solid transparent;outline-offset:calc(var(--rt-side-menu-sub-item-action-ring-width) * -1)}.rt-side-menu-sub-item__action:hover{outline-color:var(--rt-color-border-strong)}.rt-side-menu-sub-item__action>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-action-size)}.rt-side-menu-sub-item__action,.rt-side-menu-sub-item__favorite{--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size)}.rt-side-menu-sub-item__action>.rt-icon-button,.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-sub-item__favorite{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);flex-shrink:0;opacity:0}.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size)}.rt-side-menu-sub-item__favorite--on{--rt-icon-button-color: var(--rt-color-action-primary);opacity:1}@media(hover:hover){.rt-side-menu-sub-item__favorite--remove:hover{--rt-icon-button-color: var(--rt-color-state-danger)}}.rt-side-menu-sub-item__favorite--remove:has(:focus-visible){--rt-icon-button-color: var(--rt-color-state-danger)}@media(hover:none){.rt-side-menu-sub-item__favorite{opacity:1}}.rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{opacity:1}.rt-side-menu-sub-item__folder.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-side-menu-sub-item-transition-duration);--rt-expansion-panel-header-radius: var(--rt-side-menu-sub-item-radius);--rt-expansion-panel-header-min-height: var(--rt-side-menu-sub-item-min-height);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: calc( var(--rt-side-menu-sub-item-folder-padding-start) * (1 + var(--rt-side-menu-sub-item-depth)) );--rt-expansion-panel-header-padding-end: var(--rt-side-menu-sub-item-padding-end);--rt-expansion-panel-header-gap: var(--rt-side-menu-sub-item-gap);--rt-expansion-panel-chevron-color: var(--rt-side-menu-sub-item-chevron-color);--rt-expansion-panel-header-font-size: var(--rt-side-menu-sub-item-font-size);--rt-expansion-panel-header-color: var(--rt-side-menu-sub-item-color);--rt-expansion-panel-header-bg: transparent;--rt-expansion-panel-header-hover-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__folder--highlighted.rt-expansion-panel{--rt-expansion-panel-header-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__children{display:flex;flex-direction:column}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__favorite:not(.rt-side-menu-sub-item__favorite--on){overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{overflow:visible;width:auto;margin-inline-start:0}}}\n"],
643
+ dependencies: [
644
+ {
645
+ kind: "directive",
646
+ type: NgTemplateOutlet,
647
+ selector: "[ngTemplateOutlet]",
648
+ inputs: [
649
+ "ngTemplateOutletContext",
650
+ "ngTemplateOutlet",
651
+ "ngTemplateOutletInjector"
652
+ ]
653
+ },
654
+ {
655
+ kind: "directive",
656
+ type: BlockDirective,
657
+ selector: "[rtBlock]",
658
+ inputs: ["rtMod"]
659
+ },
660
+ {
661
+ kind: "directive",
662
+ type: ElemDirective,
663
+ selector: "[rtElem]",
664
+ inputs: ["rtMod"]
665
+ },
666
+ {
667
+ kind: "directive",
668
+ type: ModDirective,
669
+ selector: "[rtMod]"
670
+ },
671
+ {
672
+ kind: "component",
673
+ type: RtExpansionPanelComponent,
674
+ selector: "rt-expansion-panel",
675
+ inputs: [
676
+ "expanded",
677
+ "disabled",
678
+ "hideToggle",
679
+ "appearance",
680
+ "headerId",
681
+ "ariaLabel"
682
+ ],
683
+ outputs: ["expandedChange"]
684
+ },
685
+ {
686
+ kind: "directive",
687
+ type: RtExpansionPanelContentDirective,
688
+ selector: "ng-template[rtExpansionPanelContent]"
689
+ },
690
+ {
691
+ kind: "component",
692
+ type: RtIconComponent,
693
+ selector: "rt-icon",
694
+ inputs: [
695
+ "name",
696
+ "glyph",
697
+ "glyphStrategy",
698
+ "size",
699
+ "color",
700
+ "fill",
701
+ "spin",
702
+ "rotate"
703
+ ]
704
+ },
705
+ {
706
+ kind: "component",
707
+ type: RtIconButtonComponent,
708
+ selector: "rt-icon-button",
709
+ inputs: [
710
+ "icon",
711
+ "glyph",
712
+ "ariaLabel",
713
+ "variant",
714
+ "iconColor",
715
+ "iconFill",
716
+ "size",
717
+ "iconSize",
718
+ "type",
719
+ "tooltip",
720
+ "tooltipHideOnClick",
721
+ "tabIndex",
722
+ "loading",
723
+ "disabled",
724
+ "active",
725
+ "expanded",
726
+ "indicator"
727
+ ],
728
+ outputs: ["clicked"]
729
+ },
730
+ {
731
+ kind: "directive",
732
+ type: RtTooltipDirective,
733
+ selector: "[rtTooltip]",
734
+ inputs: [
735
+ "rtTooltip",
736
+ "rtTooltipPlacement",
737
+ "rtTooltipWhenTruncated",
738
+ "rtTooltipHideOnClick"
739
+ ],
740
+ exportAs: ["rtTooltip"]
741
+ },
742
+ {
743
+ kind: "pipe",
744
+ type: RtSideMenuTitlePartsPipe,
745
+ name: "rtSideMenuTitleParts"
746
+ },
747
+ {
748
+ kind: "pipe",
749
+ type: RtSideMenuIconPipe,
750
+ name: "rtSideMenuIcon"
751
+ },
752
+ {
753
+ kind: "pipe",
754
+ type: RtSideMenuHrefPipe,
755
+ name: "rtSideMenuHref"
756
+ }
757
+ ],
758
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
759
+ encapsulation: i0.ViewEncapsulation.None
760
+ });
761
+ }
762
+ };
763
+ i0.ɵɵngDeclareClassMetadata({
764
+ minVersion: "12.0.0",
765
+ version: "22.2.2",
766
+ ngImport: i0,
767
+ type: RtSideMenuSubItemComponent,
768
+ decorators: [{
769
+ type: Component,
770
+ args: [{
771
+ selector: "rt-side-menu-sub-item",
772
+ host: { class: BEM_BLOCK$2 },
773
+ changeDetection: ChangeDetectionStrategy.OnPush,
774
+ encapsulation: ViewEncapsulation.None,
775
+ imports: [
776
+ NgTemplateOutlet,
777
+ BlockDirective,
778
+ ElemDirective,
779
+ ModDirective,
780
+ RtExpansionPanelComponent,
781
+ RtExpansionPanelContentDirective,
782
+ RtIconComponent,
783
+ RtIconButtonComponent,
784
+ RtTooltipDirective,
785
+ RtSideMenuTitlePartsPipe,
786
+ RtSideMenuIconPipe,
787
+ RtSideMenuHrefPipe
788
+ ],
789
+ template: "<ng-container rtBlock=\"rt-side-menu-sub-item\">\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: item(), depth: 0 }\" />\n <ng-template #rowTpl let-row let-depth=\"depth\">\n @let isActive = menuRef.activeMenuIds().includes(row.id);\n @let isHighlighted = !inFavorites() && menuRef.highlightedMenuId() === row.id;\n @if (row.link) {\n <!--\n Номер пункта на самой строке — признак для правил приложения: по нему оно находит строку,\n например ту, что открывает страницу раздела. Стоит и у строки блока избранного.\n Строка блока избранного без номера: по номеру клавиатура нажимает строку списка. И без\n родного перетаскивания ссылки: нажатие на ручку внутри строки браузер забирал себе,\n и перетаскивание CDK не начиналось.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.data-item-id]=\"row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [attr.href]=\"row.link | rtSideMenuHref\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ active: isActive, highlighted: isHighlighted }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickSubMenu(row, $event)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n <!-- Кусок без отметки тоже узел: голая интерполяция разрывала бы подпись пробелами. -->\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <!--\n Строка блока несёт «убрать», а не звезду: всё в блоке уже в избранном. Звезда —\n у пункта со своим адресом в разделе с избранным: полая — не в избранном,\n закрашенная цветом темы — в нём. Имя кнопки называет следующее нажатие.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\n [rtMod]=\"{ remove: true }\"\n [icon]=\"favoritesIcons.remove\"\n [ariaLabel]=\"removeLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : removeLabel()\"\n (clicked)=\"onRemoveFavorite(row, $event)\" />\n } @else if (favoritesOn() && !row.favoriteDisabled) {\n @let isFavorite = favoriteIds().includes(row.id);\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-star\"\n size=\"sm\"\n [rtMod]=\"{ on: isFavorite }\"\n [icon]=\"isFavorite ? 'star-fill' : 'star'\"\n [iconFill]=\"isFavorite\"\n [ariaLabel]=\"isFavorite ? removeLabel() : addLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : isFavorite ? removeLabel() : addLabel()\"\n (clicked)=\"onToggleFavorite(row, $event)\" />\n }\n\n <!-- Действия, которые строке передаёт владелец: блок избранного кладёт сюда ручку. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- Кнопка потребителя видна всегда и стоит последней: кнопки избранного встают перед ней. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- Имя без значка кита берёт свой шаблон меню, как значок строки. Без шаблона кнопка пуста и\n незнакомого имени значку не передаёт: значка с таким именем нет, и ждать его некому. -->\n @let buttonKitIcon = buttonIcon | rtSideMenuIcon;\n @let buttonOwnTpl = buttonKitIcon ? null : menuRef.ownIconTpl();\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"side-menu-sub-item-action\"\n size=\"sm\"\n [icon]=\"buttonKitIcon\"\n [iconFill]=\"menuRef.subItemIconFill()\"\n [ariaLabel]=\"row.name ?? ''\"\n (clicked)=\"onClickSubMenuAdditional(row, $event)\">\n @if (buttonOwnTpl) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-button-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"buttonOwnTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, icon: buttonIcon, slot: 'iconButton' }\" />\n </span>\n }\n </rt-icon-button>\n }\n </a>\n } @else if (row.submenu) {\n <!--\n Папка — раскрывающаяся панель кита. Рисуется и с пустым составом: отбор оставляет так\n ту, что совпала подписью. Шеврона у такой папки нет — раскрывать нечего. Раскрытие\n держит меню: панель сообщает о нажатии, меню переключает папку. Кнопке заголовка\n отдан номер пункта — по нему клавиатура меню нажимает строку.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [attr.data-item-id]=\"row.id\"\n [hideToggle]=\"!row.submenu.length\"\n [expanded]=\"isOpen\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ highlighted: isHighlighted }\"\n (expandedChange)=\"onToggleFolder(row)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" qa-dataid=\"side-menu-folder\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div rtElem=\"children\" role=\"group\">\n @for (child of row.submenu; track child.id) {\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: child, depth: depth + 1 }\" />\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n </ng-template>\n\n <!-- Значок строки: имя кита или пара имени Material, а имя, которого кит не рисует, — свой шаблон меню. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" [fill]=\"menuRef.subItemIconFill()\" />\n } @else if (menuRef.ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: row, icon: row.icon, slot: 'icon' }\" />\n </span>\n }\n </ng-template>\n</ng-container>\n",
790
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-sub-item{--rt-side-menu-sub-item-radius: var(--rt-radius-full);--rt-side-menu-sub-item-transition-duration: var(--rt-duration-fast);--rt-side-menu-sub-item-match-bg: var(--rt-yellow-400);--rt-side-menu-sub-item-match-color: var(--rt-neutral-950);--rt-side-menu-sub-item-match-radius: var(--rt-radius-xs);--rt-side-menu-sub-item-min-height: var(--rt-control-height-md);--rt-side-menu-sub-item-font-size: var(--rt-text-sm);--rt-side-menu-sub-item-padding-start: var(--rt-space-md);--rt-side-menu-sub-item-padding-end: var(--rt-space-sm);--rt-side-menu-sub-item-color: var(--rt-color-text-primary);--rt-side-menu-sub-item-gap: var(--rt-space-sm);--rt-side-menu-sub-item-folder-padding-start: var(--rt-side-menu-sub-item-padding-start);--rt-side-menu-sub-item-chevron-color: var(--rt-color-text-muted);--rt-side-menu-sub-item-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-button-radius: var(--rt-radius-md);--rt-side-menu-sub-item-button-icon-size: var(--rt-icon-step-sm);--rt-side-menu-sub-item-action-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-size: var(--rt-control-height-sm);--rt-side-menu-sub-item-favorite-color: var(--rt-color-text-primary);display:block;letter-spacing:var(--rt-side-menu-sub-item-letter-spacing, inherit)}.rt-side-menu-sub-item__row{--rt-side-menu-sub-item-depth: 0;display:flex;width:100%;min-height:var(--rt-side-menu-sub-item-min-height);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-sub-item-radius);background:transparent;color:var(--rt-side-menu-sub-item-color);cursor:pointer;font:inherit;font-size:var(--rt-side-menu-sub-item-font-size);gap:var(--rt-side-menu-sub-item-gap);padding-block:var(--rt-space-2);padding-inline:calc(var(--rt-side-menu-sub-item-padding-start) * (1 + var(--rt-side-menu-sub-item-depth))) var(--rt-side-menu-sub-item-padding-end);text-align:start;text-decoration:none;transition:background-color var(--rt-side-menu-sub-item-transition-duration) ease}.rt-side-menu-sub-item__row:hover,.rt-side-menu-sub-item__row:active,.rt-side-menu-sub-item__row--highlighted{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__row:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu-sub-item__row--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu-sub-item__icon{--rt-icon-step-sm: var(--rt-side-menu-sub-item-icon-size);flex-shrink:0;color:inherit}.rt-side-menu-sub-item__own-icon{--rt-side-menu-sub-item-own-icon-size: var(--rt-size-4);display:inline-flex;width:var(--rt-side-menu-sub-item-own-icon-size);height:var(--rt-side-menu-sub-item-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu-sub-item__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu-sub-item__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-sub-item__part{color:inherit}.rt-side-menu-sub-item__match{border-radius:var(--rt-side-menu-sub-item-match-radius);background:var(--rt-side-menu-sub-item-match-bg);color:var(--rt-side-menu-sub-item-match-color)}.rt-side-menu-sub-item__action{--rt-side-menu-sub-item-action-ring-width: var(--rt-border-width-thin);--rt-side-menu-sub-item-action-radius: var(--rt-radius-full);flex-shrink:0;border-radius:var(--rt-side-menu-sub-item-action-radius);outline:var(--rt-side-menu-sub-item-action-ring-width) solid transparent;outline-offset:calc(var(--rt-side-menu-sub-item-action-ring-width) * -1)}.rt-side-menu-sub-item__action:hover{outline-color:var(--rt-color-border-strong)}.rt-side-menu-sub-item__action>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-action-size)}.rt-side-menu-sub-item__action,.rt-side-menu-sub-item__favorite{--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size)}.rt-side-menu-sub-item__action>.rt-icon-button,.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-sub-item__favorite{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);flex-shrink:0;opacity:0}.rt-side-menu-sub-item__favorite>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size)}.rt-side-menu-sub-item__favorite--on{--rt-icon-button-color: var(--rt-color-action-primary);opacity:1}@media(hover:hover){.rt-side-menu-sub-item__favorite--remove:hover{--rt-icon-button-color: var(--rt-color-state-danger)}}.rt-side-menu-sub-item__favorite--remove:has(:focus-visible){--rt-icon-button-color: var(--rt-color-state-danger)}@media(hover:none){.rt-side-menu-sub-item__favorite{opacity:1}}.rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{opacity:1}.rt-side-menu-sub-item__folder.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-side-menu-sub-item-transition-duration);--rt-expansion-panel-header-radius: var(--rt-side-menu-sub-item-radius);--rt-expansion-panel-header-min-height: var(--rt-side-menu-sub-item-min-height);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: calc( var(--rt-side-menu-sub-item-folder-padding-start) * (1 + var(--rt-side-menu-sub-item-depth)) );--rt-expansion-panel-header-padding-end: var(--rt-side-menu-sub-item-padding-end);--rt-expansion-panel-header-gap: var(--rt-side-menu-sub-item-gap);--rt-expansion-panel-chevron-color: var(--rt-side-menu-sub-item-chevron-color);--rt-expansion-panel-header-font-size: var(--rt-side-menu-sub-item-font-size);--rt-expansion-panel-header-color: var(--rt-side-menu-sub-item-color);--rt-expansion-panel-header-bg: transparent;--rt-expansion-panel-header-hover-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__folder--highlighted.rt-expansion-panel{--rt-expansion-panel-header-bg: var(--rt-side-menu-item-bg-hover)}.rt-side-menu-sub-item__children{display:flex;flex-direction:column}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__favorite:not(.rt-side-menu-sub-item__favorite--on){overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{overflow:visible;width:auto;margin-inline-start:0}}}\n"]
791
+ }]
792
+ }],
793
+ propDecorators: {
794
+ item: [{
795
+ type: i0.Input,
796
+ args: [{
797
+ isSignal: true,
798
+ alias: "item",
799
+ required: true
800
+ }]
801
+ }],
802
+ inFavorites: [{
803
+ type: i0.Input,
804
+ args: [{
805
+ isSignal: true,
806
+ alias: "inFavorites",
807
+ required: false
808
+ }]
809
+ }],
810
+ clickSubMenuAction: [{
811
+ type: i0.Output,
812
+ args: ["clickSubMenuAction"]
813
+ }],
814
+ clickSubMenuAdditionalAction: [{
815
+ type: i0.Output,
816
+ args: ["clickSubMenuAdditionalAction"]
817
+ }]
818
+ }
819
+ });
820
+ const BEM_BLOCK$1 = "rt-side-menu-favorites";
821
+ const SUB_MENU_PANEL = ".rt-side-menu__panel";
822
+ var RtSideMenuFavoritesComponent = class RtSideMenuFavoritesComponent {
823
+ #menu;
824
+ #settings;
825
+ #injector;
826
+ #host;
827
+ #dragging;
828
+ #section;
829
+ constructor() {
830
+ this.#menu = inject(RT_SIDE_MENU);
831
+ this.#settings = inject(RtSideMenuSettingsService, { optional: true });
832
+ this.#injector = inject(Injector);
833
+ this.#host = inject(ElementRef);
834
+ this.#dragging = signal(false, ...ngDevMode ? [{ debugName: "#dragging" }] : /* istanbul ignore next */ []);
835
+ this.#section = computed(() => this.#settings ? sideMenuFavoritesSection(this.#menu.menuItems(), this.#menu.shownSubMenu()) : null, ...ngDevMode ? [{ debugName: "#section" }] : /* istanbul ignore next */ []);
836
+ this.searching = computed(() => this.#menu.subMenuQuery().trim() !== "", ...ngDevMode ? [{ debugName: "searching" }] : /* istanbul ignore next */ []);
837
+ this.rows = computed(() => {
838
+ const section = this.#section();
839
+ if (!this.#settings || !section || this.searching() && !this.#menu.isFavoritesSearchShown()) return [];
840
+ return filterSideMenuItems(findSideMenuFavoriteItems([section], this.#settings.favoriteIds(this.#menu.menuId())()), this.#menu.subMenuQuery());
841
+ }, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
842
+ this.collapsed = computed(() => {
843
+ const section = this.#section();
844
+ return !!section && !!this.#settings?.favoritesCollapsed(this.#menu.menuId())().includes(section.id);
845
+ }, ...ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []);
846
+ this.open = linkedSignal({
847
+ ...ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {},
848
+ source: () => ({
849
+ collapsed: this.collapsed(),
850
+ searching: this.searching()
851
+ }),
852
+ computation: (state) => !state.collapsed || state.searching
853
+ });
854
+ this.countShown = computed(() => {
855
+ const mode = this.#menu.favoritesCount();
856
+ return mode === "always" || mode === "collapsed" && !this.open();
857
+ }, ...ngDevMode ? [{ debugName: "countShown" }] : /* istanbul ignore next */ []);
858
+ this.handles = viewChildren("handle", {
859
+ ...ngDevMode ? { debugName: "handles" } : /* istanbul ignore next */ {},
860
+ read: ElementRef
861
+ });
862
+ this.dragIcon = this.#settings?.favoritesIcons.drag ?? "arrows-v";
863
+ this.narrow = inject(BreakpointsService).narrow;
864
+ this.titleLabel = rtKitLabel("sideMenuFavorites");
865
+ this.dragLabel = rtKitLabel("sideMenuFavoriteDrag");
866
+ this.expandLabel = rtKitLabel("sideMenuFavoritesExpand");
867
+ this.collapseLabel = rtKitLabel("sideMenuFavoritesCollapse");
868
+ this.clickSubMenuAction = output();
869
+ this.clickSubMenuAdditionalAction = output();
870
+ inject(DestroyRef).onDestroy(() => this.#release());
871
+ }
872
+ onToggle(expanded) {
873
+ const section = this.#section();
874
+ if (this.#settings && section && !this.searching()) this.#settings.setFavoritesCollapsed(this.#menu.menuId(), section.id, !expanded);
875
+ }
876
+ onDragStart() {
877
+ this.#dragging.set(true);
878
+ this.#menu.holdSubMenu(true);
879
+ }
880
+ onDragEnd(event) {
881
+ this.#release();
882
+ const panel = this.#host.nativeElement.closest(SUB_MENU_PANEL);
883
+ if (panel && isSideMenuDropOutside(panel.getBoundingClientRect(), event.dropPoint)) this.#menu.toggleSubMenu();
884
+ }
885
+ onDrop(event) {
886
+ if (event.isPointerOverContainer) this.#move(event.previousIndex, event.currentIndex);
887
+ }
888
+ onHandleKey(event, index, step) {
889
+ event.preventDefault();
890
+ event.stopPropagation();
891
+ const target = index + step;
892
+ if (target < 0 || target >= this.rows().length) return;
893
+ this.#move(index, target);
894
+ afterNextRender(() => this.handles()[target]?.nativeElement.querySelector("button")?.focus(), { injector: this.#injector });
895
+ }
896
+ onHandleClick(event) {
897
+ event.preventDefault();
898
+ event.stopPropagation();
899
+ }
900
+ #release() {
901
+ if (!this.#dragging()) return;
902
+ this.#dragging.set(false);
903
+ this.#menu.holdSubMenu(false);
904
+ }
905
+ #move(from, to) {
906
+ if (!this.#settings || from === to) return;
907
+ const visible = this.rows().map((item) => item.id);
908
+ this.#settings.moveVisibleSideMenuFavorite(this.#menu.menuId(), visible, from, to);
909
+ }
910
+ static {
911
+ this.ɵfac = i0.ɵɵngDeclareFactory({
912
+ minVersion: "12.0.0",
913
+ version: "22.2.2",
914
+ ngImport: i0,
915
+ type: RtSideMenuFavoritesComponent,
916
+ deps: [],
917
+ target: i0.ɵɵFactoryTarget.Component
918
+ });
919
+ }
920
+ static {
921
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
922
+ minVersion: "17.0.0",
923
+ version: "22.2.2",
924
+ type: RtSideMenuFavoritesComponent,
925
+ isStandalone: true,
926
+ selector: "rt-side-menu-favorites",
927
+ outputs: {
928
+ clickSubMenuAction: "clickSubMenuAction",
929
+ clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction"
930
+ },
931
+ host: { classAttribute: "rt-side-menu-favorites" },
932
+ viewQueries: [{
933
+ propertyName: "handles",
934
+ predicate: ["handle"],
935
+ descendants: true,
936
+ read: ElementRef,
937
+ isSignal: true
938
+ }],
939
+ ngImport: i0,
940
+ template: "<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n Блок — раскрывающаяся панель кита, как папка подменю, со звездой вместо значка папки. Имя\n кнопки заголовка называет следующее нажатие; число строк — по входу меню.\n -->\n <rt-expansion-panel\n rtElem=\"panel\"\n appearance=\"plain\"\n [ariaLabel]=\"open() ? collapseLabel() : expandLabel()\"\n [(expanded)]=\"open\"\n (expandedChange)=\"onToggle($event)\">\n <rt-icon rtElem=\"star\" size=\"sm\" name=\"star-fill\" [fill]=\"true\" />\n <span rtElem=\"title\" qa-dataid=\"side-menu-favorites-title\">\n {{ titleLabel() }}\n @if (countShown()) {\n ({{ rows().length }})\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div\n rtElem=\"list\"\n role=\"group\"\n qa-dataid=\"side-menu-favorites\"\n cdkDropList\n cdkDropListLockAxis=\"y\"\n [cdkDropListData]=\"rows()\"\n [attr.aria-label]=\"titleLabel()\"\n (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of rows(); track item.id) {\n <div\n rtElem=\"row\"\n qa-dataid=\"side-menu-favorite-row\"\n cdkDrag\n [attr.data-id]=\"item.id\"\n (cdkDragStarted)=\"onDragStart()\"\n (cdkDragEnded)=\"onDragEnd($event)\">\n <!--\n Ручка стоит внутри строки, последней перед кнопкой потребителя. Строку тянут\n только за ручку: остальная строка — ссылка. С клавиатуры строку переставляют\n стрелки на ручке: перетаскивание CDK клавиш не знает.\n -->\n <rt-side-menu-sub-item\n rtElem=\"item\"\n [item]=\"item\"\n [inFavorites]=\"true\"\n (clickSubMenuAction)=\"clickSubMenuAction.emit($event)\"\n (clickSubMenuAdditionalAction)=\"clickSubMenuAdditionalAction.emit($event)\">\n <rt-icon-button\n #handle\n rtSideMenuSubItemAction\n cdkDragHandle\n rtElem=\"handle\"\n qa-dataid=\"side-menu-favorite-handle\"\n size=\"sm\"\n aria-keyshortcuts=\"ArrowUp ArrowDown\"\n [icon]=\"dragIcon\"\n [ariaLabel]=\"dragLabel()\"\n [tooltip]=\"narrow() ? '' : dragLabel()\"\n (clicked)=\"onHandleClick($event)\"\n (keydown.arrowup)=\"onHandleKey($event, $index, -1)\"\n (keydown.arrowdown)=\"onHandleKey($event, $index, 1)\" />\n </rt-side-menu-sub-item>\n </div>\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n</ng-container>\n",
941
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-favorites{--rt-side-menu-favorites-radius: var(--rt-radius-full);--rt-side-menu-favorites-divider-width: var(--rt-border-width-thin);display:block}.rt-side-menu-favorites:has(.rt-side-menu-favorites__panel){border-bottom:var(--rt-side-menu-favorites-divider-width) solid var(--rt-color-border-subtle);margin-block-end:var(--rt-space-sm);padding-block-end:var(--rt-space-sm)}.rt-side-menu-favorites__panel.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-duration-fast);--rt-expansion-panel-header-radius: var(--rt-side-menu-favorites-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: var(--rt-space-1);--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm)}.rt-side-menu-favorites__star{flex-shrink:0;color:var(--rt-color-action-primary)}.rt-side-menu-favorites__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-favorites__list{display:flex;flex-direction:column}.rt-side-menu-favorites__item{min-width:0;flex:1 1 auto}.rt-side-menu-favorites__row{display:flex;align-items:center}.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-favorites-preview-radius: var(--rt-radius-full);--rt-side-menu-favorites-preview-shadow: var(--rt-shadow-md);border-radius:var(--rt-side-menu-favorites-preview-radius);box-shadow:var(--rt-side-menu-favorites-preview-shadow)}.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__item{min-width:0;flex:1 1 auto;border-radius:var(--rt-side-menu-favorites-preview-radius);background:var(--rt-color-bg-surface)}.rt-side-menu-favorites__row.cdk-drag-placeholder{opacity:0}.rt-side-menu-favorites__handle{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size);flex-shrink:0;cursor:grab;opacity:0}.rt-side-menu-favorites__handle>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size);--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-favorites__handle:active{cursor:grabbing}@media(hover:none){.rt-side-menu-favorites__handle{opacity:1}}.rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__handle{opacity:1}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__handle{overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle{overflow:visible;width:auto;margin-inline-start:0}}}\n"],
942
+ dependencies: [
943
+ {
944
+ kind: "directive",
945
+ type: CdkDrag,
946
+ selector: "[cdkDrag]",
947
+ inputs: [
948
+ "cdkDragData",
949
+ "cdkDragLockAxis",
950
+ "cdkDragRootElement",
951
+ "cdkDragBoundary",
952
+ "cdkDragStartDelay",
953
+ "cdkDragFreeDragPosition",
954
+ "cdkDragDisabled",
955
+ "cdkDragConstrainPosition",
956
+ "cdkDragPreviewClass",
957
+ "cdkDragPreviewContainer",
958
+ "cdkDragScale"
959
+ ],
960
+ outputs: [
961
+ "cdkDragStarted",
962
+ "cdkDragReleased",
963
+ "cdkDragEnded",
964
+ "cdkDragEntered",
965
+ "cdkDragExited",
966
+ "cdkDragDropped",
967
+ "cdkDragMoved"
968
+ ],
969
+ exportAs: ["cdkDrag"]
970
+ },
971
+ {
972
+ kind: "directive",
973
+ type: CdkDragHandle,
974
+ selector: "[cdkDragHandle]",
975
+ inputs: ["cdkDragHandleDisabled"]
976
+ },
977
+ {
978
+ kind: "directive",
979
+ type: CdkDropList,
980
+ selector: "[cdkDropList], cdk-drop-list",
981
+ inputs: [
982
+ "cdkDropListConnectedTo",
983
+ "cdkDropListData",
984
+ "cdkDropListOrientation",
985
+ "id",
986
+ "cdkDropListLockAxis",
987
+ "cdkDropListDisabled",
988
+ "cdkDropListSortingDisabled",
989
+ "cdkDropListEnterPredicate",
990
+ "cdkDropListSortPredicate",
991
+ "cdkDropListAutoScrollDisabled",
992
+ "cdkDropListAutoScrollStep",
993
+ "cdkDropListElementContainer",
994
+ "cdkDropListHasAnchor"
995
+ ],
996
+ outputs: [
997
+ "cdkDropListDropped",
998
+ "cdkDropListEntered",
999
+ "cdkDropListExited",
1000
+ "cdkDropListSorted"
1001
+ ],
1002
+ exportAs: ["cdkDropList"]
1003
+ },
1004
+ {
1005
+ kind: "directive",
1006
+ type: BlockDirective,
1007
+ selector: "[rtBlock]",
1008
+ inputs: ["rtMod"]
1009
+ },
1010
+ {
1011
+ kind: "directive",
1012
+ type: ElemDirective,
1013
+ selector: "[rtElem]",
1014
+ inputs: ["rtMod"]
1015
+ },
1016
+ {
1017
+ kind: "directive",
1018
+ type: RtExpansionPanelContentDirective,
1019
+ selector: "ng-template[rtExpansionPanelContent]"
1020
+ },
1021
+ {
1022
+ kind: "component",
1023
+ type: RtExpansionPanelComponent,
1024
+ selector: "rt-expansion-panel",
1025
+ inputs: [
1026
+ "expanded",
1027
+ "disabled",
1028
+ "hideToggle",
1029
+ "appearance",
1030
+ "headerId",
1031
+ "ariaLabel"
1032
+ ],
1033
+ outputs: ["expandedChange"]
1034
+ },
1035
+ {
1036
+ kind: "component",
1037
+ type: RtIconButtonComponent,
1038
+ selector: "rt-icon-button",
1039
+ inputs: [
1040
+ "icon",
1041
+ "glyph",
1042
+ "ariaLabel",
1043
+ "variant",
1044
+ "iconColor",
1045
+ "iconFill",
1046
+ "size",
1047
+ "iconSize",
1048
+ "type",
1049
+ "tooltip",
1050
+ "tooltipHideOnClick",
1051
+ "tabIndex",
1052
+ "loading",
1053
+ "disabled",
1054
+ "active",
1055
+ "expanded",
1056
+ "indicator"
1057
+ ],
1058
+ outputs: ["clicked"]
1059
+ },
1060
+ {
1061
+ kind: "component",
1062
+ type: RtIconComponent,
1063
+ selector: "rt-icon",
1064
+ inputs: [
1065
+ "name",
1066
+ "glyph",
1067
+ "glyphStrategy",
1068
+ "size",
1069
+ "color",
1070
+ "fill",
1071
+ "spin",
1072
+ "rotate"
1073
+ ]
1074
+ },
1075
+ {
1076
+ kind: "component",
1077
+ type: RtSideMenuSubItemComponent,
1078
+ selector: "rt-side-menu-sub-item",
1079
+ inputs: ["item", "inFavorites"],
1080
+ outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"]
1081
+ }
1082
+ ],
1083
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
1084
+ encapsulation: i0.ViewEncapsulation.None
1085
+ });
1086
+ }
1087
+ };
1088
+ i0.ɵɵngDeclareClassMetadata({
1089
+ minVersion: "12.0.0",
1090
+ version: "22.2.2",
1091
+ ngImport: i0,
1092
+ type: RtSideMenuFavoritesComponent,
1093
+ decorators: [{
1094
+ type: Component,
1095
+ args: [{
1096
+ selector: "rt-side-menu-favorites",
1097
+ host: { class: BEM_BLOCK$1 },
1098
+ changeDetection: ChangeDetectionStrategy.OnPush,
1099
+ encapsulation: ViewEncapsulation.None,
1100
+ imports: [
1101
+ CdkDrag,
1102
+ CdkDragHandle,
1103
+ CdkDropList,
1104
+ BlockDirective,
1105
+ ElemDirective,
1106
+ ModDirective,
1107
+ RtExpansionPanelContentDirective,
1108
+ RtExpansionPanelComponent,
1109
+ RtIconButtonComponent,
1110
+ RtIconComponent,
1111
+ RtSideMenuSubItemComponent
1112
+ ],
1113
+ template: "<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n Блок — раскрывающаяся панель кита, как папка подменю, со звездой вместо значка папки. Имя\n кнопки заголовка называет следующее нажатие; число строк — по входу меню.\n -->\n <rt-expansion-panel\n rtElem=\"panel\"\n appearance=\"plain\"\n [ariaLabel]=\"open() ? collapseLabel() : expandLabel()\"\n [(expanded)]=\"open\"\n (expandedChange)=\"onToggle($event)\">\n <rt-icon rtElem=\"star\" size=\"sm\" name=\"star-fill\" [fill]=\"true\" />\n <span rtElem=\"title\" qa-dataid=\"side-menu-favorites-title\">\n {{ titleLabel() }}\n @if (countShown()) {\n ({{ rows().length }})\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div\n rtElem=\"list\"\n role=\"group\"\n qa-dataid=\"side-menu-favorites\"\n cdkDropList\n cdkDropListLockAxis=\"y\"\n [cdkDropListData]=\"rows()\"\n [attr.aria-label]=\"titleLabel()\"\n (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of rows(); track item.id) {\n <div\n rtElem=\"row\"\n qa-dataid=\"side-menu-favorite-row\"\n cdkDrag\n [attr.data-id]=\"item.id\"\n (cdkDragStarted)=\"onDragStart()\"\n (cdkDragEnded)=\"onDragEnd($event)\">\n <!--\n Ручка стоит внутри строки, последней перед кнопкой потребителя. Строку тянут\n только за ручку: остальная строка — ссылка. С клавиатуры строку переставляют\n стрелки на ручке: перетаскивание CDK клавиш не знает.\n -->\n <rt-side-menu-sub-item\n rtElem=\"item\"\n [item]=\"item\"\n [inFavorites]=\"true\"\n (clickSubMenuAction)=\"clickSubMenuAction.emit($event)\"\n (clickSubMenuAdditionalAction)=\"clickSubMenuAdditionalAction.emit($event)\">\n <rt-icon-button\n #handle\n rtSideMenuSubItemAction\n cdkDragHandle\n rtElem=\"handle\"\n qa-dataid=\"side-menu-favorite-handle\"\n size=\"sm\"\n aria-keyshortcuts=\"ArrowUp ArrowDown\"\n [icon]=\"dragIcon\"\n [ariaLabel]=\"dragLabel()\"\n [tooltip]=\"narrow() ? '' : dragLabel()\"\n (clicked)=\"onHandleClick($event)\"\n (keydown.arrowup)=\"onHandleKey($event, $index, -1)\"\n (keydown.arrowdown)=\"onHandleKey($event, $index, 1)\" />\n </rt-side-menu-sub-item>\n </div>\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n</ng-container>\n",
1114
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-favorites{--rt-side-menu-favorites-radius: var(--rt-radius-full);--rt-side-menu-favorites-divider-width: var(--rt-border-width-thin);display:block}.rt-side-menu-favorites:has(.rt-side-menu-favorites__panel){border-bottom:var(--rt-side-menu-favorites-divider-width) solid var(--rt-color-border-subtle);margin-block-end:var(--rt-space-sm);padding-block-end:var(--rt-space-sm)}.rt-side-menu-favorites__panel.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-duration-fast);--rt-expansion-panel-header-radius: var(--rt-side-menu-favorites-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: var(--rt-space-1);--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm)}.rt-side-menu-favorites__star{flex-shrink:0;color:var(--rt-color-action-primary)}.rt-side-menu-favorites__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-favorites__list{display:flex;flex-direction:column}.rt-side-menu-favorites__item{min-width:0;flex:1 1 auto}.rt-side-menu-favorites__row{display:flex;align-items:center}.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-favorites-preview-radius: var(--rt-radius-full);--rt-side-menu-favorites-preview-shadow: var(--rt-shadow-md);border-radius:var(--rt-side-menu-favorites-preview-radius);box-shadow:var(--rt-side-menu-favorites-preview-shadow)}.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__item{min-width:0;flex:1 1 auto;border-radius:var(--rt-side-menu-favorites-preview-radius);background:var(--rt-color-bg-surface)}.rt-side-menu-favorites__row.cdk-drag-placeholder{opacity:0}.rt-side-menu-favorites__handle{--rt-icon-button-color: var(--rt-side-menu-sub-item-favorite-color);--rt-icon-step-sm: var(--rt-side-menu-sub-item-button-icon-size);flex-shrink:0;cursor:grab;opacity:0}.rt-side-menu-favorites__handle>.rt-icon-button{--rt-icon-button-size-step: var(--rt-side-menu-sub-item-favorite-size);--rt-icon-button-radius: var(--rt-side-menu-sub-item-button-radius)}.rt-side-menu-favorites__handle:active{cursor:grabbing}@media(hover:none){.rt-side-menu-favorites__handle{opacity:1}}.rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__handle{opacity:1}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__handle{overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle{overflow:visible;width:auto;margin-inline-start:0}}}\n"]
1115
+ }]
1116
+ }],
1117
+ ctorParameters: () => [],
1118
+ propDecorators: {
1119
+ handles: [{
1120
+ type: i0.ViewChildren,
1121
+ args: ["handle", {
1122
+ read: ElementRef,
1123
+ isSignal: true
1124
+ }]
1125
+ }],
1126
+ clickSubMenuAction: [{
1127
+ type: i0.Output,
1128
+ args: ["clickSubMenuAction"]
1129
+ }],
1130
+ clickSubMenuAdditionalAction: [{
1131
+ type: i0.Output,
1132
+ args: ["clickSubMenuAdditionalAction"]
1133
+ }]
1134
+ }
1135
+ });
1136
+ var RtSideMenuCloseTimer = class {
1137
+ #handle = null;
1138
+ constructor(destroyRef) {
1139
+ destroyRef.onDestroy(() => this.cancel());
1140
+ }
1141
+ run(delay, close) {
1142
+ this.cancel();
1143
+ if (delay <= 0) {
1144
+ close();
1145
+ return;
1146
+ }
1147
+ this.#handle = setTimeout(() => {
1148
+ this.#handle = null;
1149
+ close();
1150
+ }, delay);
1151
+ }
1152
+ cancel() {
1153
+ if (this.#handle !== null) {
1154
+ clearTimeout(this.#handle);
1155
+ this.#handle = null;
1156
+ }
1157
+ }
1158
+ };
1159
+ var RtSubMenuKeyboard = class {
1160
+ #highlightedId;
1161
+ #openedIds;
1162
+ #closedIds;
1163
+ #hooks;
1164
+ constructor(hooks) {
1165
+ this.#highlightedId = signal(null, ...ngDevMode ? [{ debugName: "#highlightedId" }] : /* istanbul ignore next */ []);
1166
+ this.#openedIds = signal([], ...ngDevMode ? [{ debugName: "#openedIds" }] : /* istanbul ignore next */ []);
1167
+ this.#closedIds = signal([], ...ngDevMode ? [{ debugName: "#closedIds" }] : /* istanbul ignore next */ []);
1168
+ this.highlightedId = this.#highlightedId.asReadonly();
1169
+ this.openedIds = this.#openedIds.asReadonly();
1170
+ this.closedIds = this.#closedIds.asReadonly();
1171
+ this.#hooks = hooks;
1172
+ }
1173
+ reset() {
1174
+ this.#highlightedId.set(null);
1175
+ this.#openedIds.set([]);
1176
+ this.#closedIds.set([]);
1177
+ }
1178
+ press(key, items, expandedIds) {
1179
+ const walk = walkSideMenuItems(items, expandedIds);
1180
+ const current = walk.find((item) => item.id === this.#highlightedId()) ?? null;
1181
+ switch (key) {
1182
+ case "ArrowDown":
1183
+ case "ArrowUp":
1184
+ this.#highlightedId.set(stepSideMenuHighlight(walk, this.#highlightedId(), key === "ArrowDown" ? 1 : -1));
1185
+ return true;
1186
+ case "ArrowRight": return this.setFolderOpen(current, true);
1187
+ case "ArrowLeft": return this.setFolderOpen(current, false);
1188
+ case "Enter": return this.#enter(current);
1189
+ case "Escape":
1190
+ this.reset();
1191
+ this.#hooks.clearQuery();
1192
+ return true;
1193
+ default: return false;
1194
+ }
1195
+ }
1196
+ #enter(item) {
1197
+ if (item === null) return false;
1198
+ if (item.link) {
1199
+ this.#hooks.open(item);
1200
+ return true;
1201
+ }
1202
+ return this.setFolderOpen(item, true);
1203
+ }
1204
+ setFolderOpen(item, open) {
1205
+ if (item === null || !item.submenu?.length) return false;
1206
+ const without = (ids) => ids.filter((id) => id !== item.id);
1207
+ this.#openedIds.update((ids) => open ? [...without(ids), item.id] : without(ids));
1208
+ this.#closedIds.update((ids) => open ? without(ids) : [...without(ids), item.id]);
1209
+ return true;
1210
+ }
1211
+ };
1212
+ var RtSideMenuResize = class {
1213
+ #draggedWidth;
1214
+ #listen;
1215
+ #hooks;
1216
+ #stop;
1217
+ constructor(listen, hooks, destroyRef) {
1218
+ this.#draggedWidth = signal(null, ...ngDevMode ? [{ debugName: "#draggedWidth" }] : /* istanbul ignore next */ []);
1219
+ this.#stop = null;
1220
+ this.draggedWidth = this.#draggedWidth.asReadonly();
1221
+ this.valueNow = computed(() => {
1222
+ const width = this.#draggedWidth() ?? this.#hooks.namedWidth();
1223
+ return width === null ? null : clampSideMenuWidth(width);
1224
+ }, ...ngDevMode ? [{ debugName: "valueNow" }] : /* istanbul ignore next */ []);
1225
+ this.#listen = listen;
1226
+ this.#hooks = hooks;
1227
+ destroyRef?.onDestroy(() => this.finish());
1228
+ }
1229
+ isRunning() {
1230
+ return this.#stop !== null;
1231
+ }
1232
+ pressKey(key, from) {
1233
+ const width = sideMenuWidthByKey(key, from);
1234
+ if (width === null) return false;
1235
+ this.#hooks.askWidth(width);
1236
+ return true;
1237
+ }
1238
+ start(event, startWidth) {
1239
+ if (this.isRunning()) return;
1240
+ const stop = startSideMenuWidthDrag(event, startWidth, this.#listen, {
1241
+ onWidth: (width) => this.#draggedWidth.set(width),
1242
+ onEnd: () => this.finish()
1243
+ });
1244
+ if (stop !== null) {
1245
+ this.#stop = stop;
1246
+ this.#hooks.started();
1247
+ }
1248
+ }
1249
+ finish() {
1250
+ if (!this.isRunning()) return;
1251
+ const width = this.#draggedWidth();
1252
+ const drawn = width === null ? null : reportedSideMenuWidth(width, this.#hooks.panel());
1253
+ this.#stop?.();
1254
+ this.#stop = null;
1255
+ this.#draggedWidth.set(null);
1256
+ if (drawn !== null) this.#hooks.askWidth(drawn);
1257
+ this.#hooks.ended();
1258
+ }
1259
+ };
1260
+ var RtSideMenuLookBase = class RtSideMenuLookBase {
1261
+ constructor() {
1262
+ this.subMenuTooltipsShown = input(true, {
1263
+ ...ngDevMode ? { debugName: "subMenuTooltipsShown" } : /* istanbul ignore next */ {},
1264
+ transform: booleanAttribute
1265
+ });
1266
+ this.railTitlesShown = input(true, {
1267
+ ...ngDevMode ? { debugName: "railTitlesShown" } : /* istanbul ignore next */ {},
1268
+ transform: booleanAttribute
1269
+ });
1270
+ this.railIconFill = input(false, {
1271
+ ...ngDevMode ? { debugName: "railIconFill" } : /* istanbul ignore next */ {},
1272
+ transform: booleanAttribute
1273
+ });
1274
+ this.subItemIconFill = input(false, {
1275
+ ...ngDevMode ? { debugName: "subItemIconFill" } : /* istanbul ignore next */ {},
1276
+ transform: booleanAttribute
1277
+ });
1278
+ this.panelScrollHintShown = input(false, {
1279
+ ...ngDevMode ? { debugName: "panelScrollHintShown" } : /* istanbul ignore next */ {},
1280
+ transform: booleanAttribute
1281
+ });
1282
+ this.searchSize = input("sm", ...ngDevMode ? [{ debugName: "searchSize" }] : /* istanbul ignore next */ []);
1283
+ this.searchAppearance = input("outline", ...ngDevMode ? [{ debugName: "searchAppearance" }] : /* istanbul ignore next */ []);
1284
+ }
1285
+ static {
1286
+ this.ɵfac = i0.ɵɵngDeclareFactory({
1287
+ minVersion: "12.0.0",
1288
+ version: "22.2.2",
1289
+ ngImport: i0,
1290
+ type: RtSideMenuLookBase,
1291
+ deps: [],
1292
+ target: i0.ɵɵFactoryTarget.Directive
1293
+ });
1294
+ }
1295
+ static {
1296
+ this.ɵdir = i0.ɵɵngDeclareDirective({
1297
+ minVersion: "17.1.0",
1298
+ version: "22.2.2",
1299
+ type: RtSideMenuLookBase,
1300
+ isStandalone: true,
1301
+ inputs: {
1302
+ subMenuTooltipsShown: {
1303
+ classPropertyName: "subMenuTooltipsShown",
1304
+ publicName: "subMenuTooltipsShown",
1305
+ isSignal: true,
1306
+ isRequired: false,
1307
+ transformFunction: null
1308
+ },
1309
+ railTitlesShown: {
1310
+ classPropertyName: "railTitlesShown",
1311
+ publicName: "railTitlesShown",
1312
+ isSignal: true,
1313
+ isRequired: false,
1314
+ transformFunction: null
1315
+ },
1316
+ railIconFill: {
1317
+ classPropertyName: "railIconFill",
1318
+ publicName: "railIconFill",
1319
+ isSignal: true,
1320
+ isRequired: false,
1321
+ transformFunction: null
1322
+ },
1323
+ subItemIconFill: {
1324
+ classPropertyName: "subItemIconFill",
1325
+ publicName: "subItemIconFill",
1326
+ isSignal: true,
1327
+ isRequired: false,
1328
+ transformFunction: null
1329
+ },
1330
+ panelScrollHintShown: {
1331
+ classPropertyName: "panelScrollHintShown",
1332
+ publicName: "panelScrollHintShown",
1333
+ isSignal: true,
1334
+ isRequired: false,
1335
+ transformFunction: null
1336
+ },
1337
+ searchSize: {
1338
+ classPropertyName: "searchSize",
1339
+ publicName: "searchSize",
1340
+ isSignal: true,
1341
+ isRequired: false,
1342
+ transformFunction: null
1343
+ },
1344
+ searchAppearance: {
1345
+ classPropertyName: "searchAppearance",
1346
+ publicName: "searchAppearance",
1347
+ isSignal: true,
1348
+ isRequired: false,
1349
+ transformFunction: null
1350
+ }
1351
+ },
1352
+ ngImport: i0
1353
+ });
1354
+ }
1355
+ };
1356
+ i0.ɵɵngDeclareClassMetadata({
1357
+ minVersion: "12.0.0",
1358
+ version: "22.2.2",
1359
+ ngImport: i0,
1360
+ type: RtSideMenuLookBase,
1361
+ decorators: [{ type: Directive }],
1362
+ propDecorators: {
1363
+ subMenuTooltipsShown: [{
1364
+ type: i0.Input,
1365
+ args: [{
1366
+ isSignal: true,
1367
+ alias: "subMenuTooltipsShown",
1368
+ required: false
1369
+ }]
1370
+ }],
1371
+ railTitlesShown: [{
1372
+ type: i0.Input,
1373
+ args: [{
1374
+ isSignal: true,
1375
+ alias: "railTitlesShown",
1376
+ required: false
1377
+ }]
1378
+ }],
1379
+ railIconFill: [{
1380
+ type: i0.Input,
1381
+ args: [{
1382
+ isSignal: true,
1383
+ alias: "railIconFill",
1384
+ required: false
1385
+ }]
1386
+ }],
1387
+ subItemIconFill: [{
1388
+ type: i0.Input,
1389
+ args: [{
1390
+ isSignal: true,
1391
+ alias: "subItemIconFill",
1392
+ required: false
1393
+ }]
1394
+ }],
1395
+ panelScrollHintShown: [{
1396
+ type: i0.Input,
1397
+ args: [{
1398
+ isSignal: true,
1399
+ alias: "panelScrollHintShown",
1400
+ required: false
1401
+ }]
1402
+ }],
1403
+ searchSize: [{
1404
+ type: i0.Input,
1405
+ args: [{
1406
+ isSignal: true,
1407
+ alias: "searchSize",
1408
+ required: false
1409
+ }]
1410
+ }],
1411
+ searchAppearance: [{
1412
+ type: i0.Input,
1413
+ args: [{
1414
+ isSignal: true,
1415
+ alias: "searchAppearance",
1416
+ required: false
1417
+ }]
1418
+ }]
1419
+ }
1420
+ });
1421
+ var RtSideMenuHeaderDirective = class RtSideMenuHeaderDirective {
1422
+ static {
1423
+ this.ɵfac = i0.ɵɵngDeclareFactory({
1424
+ minVersion: "12.0.0",
1425
+ version: "22.2.2",
1426
+ ngImport: i0,
1427
+ type: RtSideMenuHeaderDirective,
1428
+ deps: [],
1429
+ target: i0.ɵɵFactoryTarget.Directive
1430
+ });
1431
+ }
1432
+ static {
1433
+ this.ɵdir = i0.ɵɵngDeclareDirective({
1434
+ minVersion: "14.0.0",
1435
+ version: "22.2.2",
1436
+ type: RtSideMenuHeaderDirective,
1437
+ isStandalone: true,
1438
+ selector: "[rtSideMenuHeader]",
1439
+ ngImport: i0
1440
+ });
1441
+ }
1442
+ };
1443
+ i0.ɵɵngDeclareClassMetadata({
1444
+ minVersion: "12.0.0",
1445
+ version: "22.2.2",
1446
+ ngImport: i0,
1447
+ type: RtSideMenuHeaderDirective,
1448
+ decorators: [{
1449
+ type: Directive,
1450
+ args: [{ selector: "[rtSideMenuHeader]" }]
1451
+ }]
1452
+ });
1453
+ var RtSideMenuFooterDirective = class RtSideMenuFooterDirective {
1454
+ static {
1455
+ this.ɵfac = i0.ɵɵngDeclareFactory({
1456
+ minVersion: "12.0.0",
1457
+ version: "22.2.2",
1458
+ ngImport: i0,
1459
+ type: RtSideMenuFooterDirective,
1460
+ deps: [],
1461
+ target: i0.ɵɵFactoryTarget.Directive
1462
+ });
1463
+ }
1464
+ static {
1465
+ this.ɵdir = i0.ɵɵngDeclareDirective({
1466
+ minVersion: "14.0.0",
1467
+ version: "22.2.2",
1468
+ type: RtSideMenuFooterDirective,
1469
+ isStandalone: true,
1470
+ selector: "[rtSideMenuFooter]",
1471
+ ngImport: i0
1472
+ });
1473
+ }
1474
+ };
1475
+ i0.ɵɵngDeclareClassMetadata({
1476
+ minVersion: "12.0.0",
1477
+ version: "22.2.2",
1478
+ ngImport: i0,
1479
+ type: RtSideMenuFooterDirective,
1480
+ decorators: [{
1481
+ type: Directive,
1482
+ args: [{ selector: "[rtSideMenuFooter]" }]
1483
+ }]
1484
+ });
1485
+ var RtSideMenuIconDirective = class RtSideMenuIconDirective {
1486
+ constructor() {
1487
+ this.templateRef = inject(TemplateRef);
1488
+ }
1489
+ static ngTemplateContextGuard(_directive, context) {
1490
+ return true;
1491
+ }
1492
+ static {
1493
+ this.ɵfac = i0.ɵɵngDeclareFactory({
1494
+ minVersion: "12.0.0",
1495
+ version: "22.2.2",
1496
+ ngImport: i0,
1497
+ type: RtSideMenuIconDirective,
1498
+ deps: [],
1499
+ target: i0.ɵɵFactoryTarget.Directive
1500
+ });
1501
+ }
1502
+ static {
1503
+ this.ɵdir = i0.ɵɵngDeclareDirective({
1504
+ minVersion: "14.0.0",
1505
+ version: "22.2.2",
1506
+ type: RtSideMenuIconDirective,
1507
+ isStandalone: true,
1508
+ selector: "ng-template[rtSideMenuIcon]",
1509
+ ngImport: i0
1510
+ });
1511
+ }
1512
+ };
1513
+ i0.ɵɵngDeclareClassMetadata({
1514
+ minVersion: "12.0.0",
1515
+ version: "22.2.2",
1516
+ ngImport: i0,
1517
+ type: RtSideMenuIconDirective,
1518
+ decorators: [{
1519
+ type: Directive,
1520
+ args: [{ selector: "ng-template[rtSideMenuIcon]" }]
1521
+ }]
1522
+ });
1523
+ const BEM_BLOCK = "rt-side-menu";
1524
+ var RtSideMenuComponent = class RtSideMenuComponent extends RtSideMenuLookBase {
1525
+ #breakpoints;
1526
+ #renderer;
1527
+ #settings;
1528
+ #resize;
1529
+ #closeTimer;
1530
+ #keyboard;
1531
+ #hoverOpened;
1532
+ #searchHeld;
1533
+ #dragHeld;
1534
+ #mode;
1535
+ #width;
1536
+ #pinnedSubMenu;
1537
+ constructor() {
1538
+ super();
1539
+ this.#breakpoints = inject(BreakpointsService);
1540
+ this.#renderer = inject(Renderer2);
1541
+ this.#settings = inject(RtSideMenuSettingsService, { optional: true });
1542
+ this.#resize = new RtSideMenuResize((target, name, handler) => this.#renderer.listen(target, name, handler), {
1543
+ askWidth: (width) => {
1544
+ this.#settings?.setSubMenuWidth(this.menuId(), width);
1545
+ this.subMenuWidthChange.emit(width);
1546
+ },
1547
+ started: () => this.subMenuResizeStart.emit(),
1548
+ ended: () => this.subMenuResizeEnd.emit(),
1549
+ panel: () => this.panelRef()?.nativeElement ?? null,
1550
+ namedWidth: () => this.#width()
1551
+ }, inject(DestroyRef));
1552
+ this.#closeTimer = new RtSideMenuCloseTimer(inject(DestroyRef));
1553
+ this.#keyboard = new RtSubMenuKeyboard({
1554
+ open: (item) => this.#pressRow(item),
1555
+ clearQuery: () => this.searchControl.setValue("")
1556
+ });
1557
+ this.#hoverOpened = signal(false, ...ngDevMode ? [{ debugName: "#hoverOpened" }] : /* istanbul ignore next */ []);
1558
+ this.#searchHeld = signal(false, ...ngDevMode ? [{ debugName: "#searchHeld" }] : /* istanbul ignore next */ []);
1559
+ this.#dragHeld = signal(false, ...ngDevMode ? [{ debugName: "#dragHeld" }] : /* istanbul ignore next */ []);
1560
+ this.#mode = computed(() => this.subMenuMode() ?? (this.pinShown() ? this.#settings?.subMenuMode(this.menuId())() : void 0) ?? "hover", ...ngDevMode ? [{ debugName: "#mode" }] : /* istanbul ignore next */ []);
1561
+ this.#width = computed(() => this.subMenuWidth() ?? this.#settings?.subMenuWidth(this.menuId())() ?? null, ...ngDevMode ? [{ debugName: "#width" }] : /* istanbul ignore next */ []);
1562
+ this.#pinnedSubMenu = computed(() => this.isPinned() ? pinnedSideMenuItems(this.selectedSubMenu(), this.activeMenuIds(), this.menuItems()) : [], ...ngDevMode ? [{ debugName: "#pinnedSubMenu" }] : /* istanbul ignore next */ []);
1563
+ this.searchControl = new FormControl("");
1564
+ this.panelRef = viewChild("panel", ...ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []);
1565
+ this.headerTpl = contentChild(RtSideMenuHeaderDirective, {
1566
+ ...ngDevMode ? { debugName: "headerTpl" } : /* istanbul ignore next */ {},
1567
+ read: TemplateRef
1568
+ });
1569
+ this.footerTpl = contentChild(RtSideMenuFooterDirective, {
1570
+ ...ngDevMode ? { debugName: "footerTpl" } : /* istanbul ignore next */ {},
1571
+ read: TemplateRef
1572
+ });
1573
+ this.searchLabel = rtKitLabel("sideMenuSearch");
1574
+ this.pinLabel = rtKitLabel("sideMenuPin");
1575
+ this.unpinLabel = rtKitLabel("sideMenuUnpin");
1576
+ this.nothingFoundLabel = rtKitLabel("sideMenuNothingFound");
1577
+ this.resizeLabel = rtKitLabel("sideMenuResize");
1578
+ this.backLabel = rtKitLabel("sideMenuBack");
1579
+ this.narrow = this.#breakpoints.narrow;
1580
+ this.resizeValueNow = this.#resize.valueNow;
1581
+ this.resizeValueMin = 120;
1582
+ this.resizeValueMax = 480;
1583
+ this.panelWidthStyle = computed(() => {
1584
+ const width = this.#resize.draggedWidth() ?? this.#width();
1585
+ return width === null ? null : `${clampSideMenuWidth(width)}px`;
1586
+ }, ...ngDevMode ? [{ debugName: "panelWidthStyle" }] : /* istanbul ignore next */ []);
1587
+ this.isPinned = computed(() => this.#mode() === "pinned" && !this.narrow(), ...ngDevMode ? [{ debugName: "isPinned" }] : /* istanbul ignore next */ []);
1588
+ this.isSearchHeld = computed(() => this.#searchHeld() && !this.isPinned(), ...ngDevMode ? [{ debugName: "isSearchHeld" }] : /* istanbul ignore next */ []);
1589
+ this.subMenuOpened = computed(() => this.isPinned() ? this.#pinnedSubMenu().length > 0 : this.#hoverOpened(), ...ngDevMode ? [{ debugName: "subMenuOpened" }] : /* istanbul ignore next */ []);
1590
+ this.visibleSubMenuItems = computed(() => filterSideMenuItems(this.shownSubMenu(), this.subMenuQuery()), ...ngDevMode ? [{ debugName: "visibleSubMenuItems" }] : /* istanbul ignore next */ []);
1591
+ this.isNothingFound = computed(() => this.subMenuQuery().trim() !== "" && this.visibleSubMenuItems().length === 0, ...ngDevMode ? [{ debugName: "isNothingFound" }] : /* istanbul ignore next */ []);
1592
+ this.activeRailIds = computed(() => {
1593
+ const selected = this.selectedItem();
1594
+ return selected ? [selected.id] : this.activeMenuIds();
1595
+ }, ...ngDevMode ? [{ debugName: "activeRailIds" }] : /* istanbul ignore next */ []);
1596
+ this.shownSubMenu = computed(() => this.isPinned() ? this.#pinnedSubMenu() : this.selectedSubMenu() ?? [], ...ngDevMode ? [{ debugName: "shownSubMenu" }] : /* istanbul ignore next */ []);
1597
+ this.selectedItem = signal(null, ...ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []);
1598
+ this.selectedSubMenu = signal(null, ...ngDevMode ? [{ debugName: "selectedSubMenu" }] : /* istanbul ignore next */ []);
1599
+ this.subMenuQuery = signal("", ...ngDevMode ? [{ debugName: "subMenuQuery" }] : /* istanbul ignore next */ []);
1600
+ this.highlightedMenuId = this.#keyboard.highlightedId;
1601
+ this.expandedMenuIds = computed(() => {
1602
+ const found = this.subMenuQuery().trim() === "" ? [] : sideMenuIdsToExpand(this.visibleSubMenuItems());
1603
+ const closed = this.#keyboard.closedIds();
1604
+ return [
1605
+ ...this.activeMenuIds(),
1606
+ ...found,
1607
+ ...this.#keyboard.openedIds()
1608
+ ].filter((id) => !closed.includes(id));
1609
+ }, ...ngDevMode ? [{ debugName: "expandedMenuIds" }] : /* istanbul ignore next */ []);
1610
+ this.activeMenuIds = input.required(...ngDevMode ? [{ debugName: "activeMenuIds" }] : /* istanbul ignore next */ []);
1611
+ this.menuItems = input([], ...ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []);
1612
+ this.subMenuMode = input(void 0, ...ngDevMode ? [{ debugName: "subMenuMode" }] : /* istanbul ignore next */ []);
1613
+ this.subMenuWidth = input(void 0, ...ngDevMode ? [{ debugName: "subMenuWidth" }] : /* istanbul ignore next */ []);
1614
+ this.menuId = input(RT_SIDE_MENU_DEFAULT_ID, {
1615
+ ...ngDevMode ? { debugName: "menuId" } : /* istanbul ignore next */ {},
1616
+ transform: normalizeSideMenuId
1617
+ });
1618
+ this.favoritesCount = input("collapsed", ...ngDevMode ? [{ debugName: "favoritesCount" }] : /* istanbul ignore next */ []);
1619
+ this.favoriteActionsReserve = input("none", ...ngDevMode ? [{ debugName: "favoriteActionsReserve" }] : /* istanbul ignore next */ []);
1620
+ this.isFavoritesSearchShown = input(true, {
1621
+ ...ngDevMode ? { debugName: "isFavoritesSearchShown" } : /* istanbul ignore next */ {},
1622
+ transform: booleanAttribute
1623
+ });
1624
+ this.pinShown = input(true, {
1625
+ ...ngDevMode ? { debugName: "pinShown" } : /* istanbul ignore next */ {},
1626
+ transform: booleanAttribute
1627
+ });
1628
+ this.subMenuCloseDelay = input(0, {
1629
+ ...ngDevMode ? { debugName: "subMenuCloseDelay" } : /* istanbul ignore next */ {},
1630
+ transform: (value) => numberAttribute(value, 0)
1631
+ });
1632
+ this.subMenuModeChange = output();
1633
+ this.subMenuWidthChange = output();
1634
+ this.subMenuResizeStart = output();
1635
+ this.subMenuResizeEnd = output();
1636
+ this.closeMobileMenuAction = output();
1637
+ this.clickSubMenuAction = output();
1638
+ this.clickSubMenuAdditionalAction = output();
1639
+ this.ownIconTpl = contentChild(RtSideMenuIconDirective, {
1640
+ ...ngDevMode ? { debugName: "ownIconTpl" } : /* istanbul ignore next */ {},
1641
+ read: TemplateRef
1642
+ });
1643
+ this.searchControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((query) => {
1644
+ this.subMenuQuery.set(query ?? "");
1645
+ this.onSearchHold();
1646
+ this.#keyboard.reset();
1647
+ });
1648
+ if (isDevMode()) effect(() => warnUnpairedSideMenuIcons(this.menuId(), this.menuItems(), this.ownIconTpl() !== void 0));
1649
+ }
1650
+ onClickMenu(item) {
1651
+ if (this.isPinned()) {
1652
+ this.#pickPinnedSubMenu(item);
1653
+ this.closeMobileMenu();
1654
+ return;
1655
+ }
1656
+ this.selectedItem.set(item);
1657
+ if (item.submenu) {
1658
+ this.selectedSubMenu.set(item.submenu);
1659
+ this.#hoverOpened.set(true);
1660
+ } else if (this.selectedSubMenu()) this.closeSubMenu();
1661
+ if (item.link) this.closeMobileMenu();
1662
+ }
1663
+ onClickSubMenu({ item, event }) {
1664
+ if (!item.link) return;
1665
+ this.clickSubMenuAction.emit({
1666
+ item,
1667
+ event
1668
+ });
1669
+ if (!this.isPinned()) this.closeSubMenu();
1670
+ this.closeMobileMenu();
1671
+ }
1672
+ onClickSubMenuAdditional(payload) {
1673
+ this.clickSubMenuAdditionalAction.emit(payload);
1674
+ }
1675
+ onBackToMainMenu() {
1676
+ this.selectedItem.set(null);
1677
+ this.selectedSubMenu.set(null);
1678
+ this.searchControl.setValue("", { emitEvent: false });
1679
+ this.subMenuQuery.set("");
1680
+ }
1681
+ toggleSubMenu(item) {
1682
+ if (this.isPinned() || item === void 0 && this.#isHeld()) return;
1683
+ if (item?.submenu) {
1684
+ this.#closeTimer.cancel();
1685
+ this.selectedSubMenu.set(item.submenu);
1686
+ this.#hoverOpened.set(true);
1687
+ } else if (this.selectedItem()?.submenu) {
1688
+ this.#closeTimer.cancel();
1689
+ this.selectedSubMenu.set(this.selectedItem()?.submenu ?? null);
1690
+ } else this.#closeTimer.run(this.subMenuCloseDelay(), () => {
1691
+ if (item !== void 0 || !this.#isHeld()) this.closeSubMenu();
1692
+ });
1693
+ }
1694
+ cancelSubMenuClose() {
1695
+ this.#closeTimer.cancel();
1696
+ }
1697
+ closeSubMenu() {
1698
+ this.#closeTimer.cancel();
1699
+ this.selectedItem.set(null);
1700
+ this.selectedSubMenu.set(null);
1701
+ this.searchControl.setValue("", { emitEvent: false });
1702
+ this.subMenuQuery.set("");
1703
+ this.#hoverOpened.set(false);
1704
+ this.#searchHeld.set(false);
1705
+ this.#keyboard.reset();
1706
+ }
1707
+ holdSubMenu(held) {
1708
+ this.#dragHeld.set(held);
1709
+ }
1710
+ toggleFolder(item) {
1711
+ this.#keyboard.setFolderOpen(item, !this.expandedMenuIds().includes(item.id));
1712
+ }
1713
+ onSearchHold() {
1714
+ if (!this.isPinned()) this.#searchHeld.set(true);
1715
+ }
1716
+ onSearchKeydown(event) {
1717
+ if (this.#keyboard.press(event.key, this.visibleSubMenuItems(), this.expandedMenuIds())) {
1718
+ event.preventDefault();
1719
+ event.stopPropagation();
1720
+ }
1721
+ }
1722
+ onSubMenuModeToggle() {
1723
+ const mode = this.isPinned() ? "hover" : "pinned";
1724
+ this.#settings?.setSubMenuMode(this.menuId(), mode);
1725
+ this.subMenuModeChange.emit(mode);
1726
+ }
1727
+ onResizeStart(event) {
1728
+ if (!this.isPinned() || this.#resize.isRunning() || event.button !== 0) return;
1729
+ event.preventDefault();
1730
+ this.#resize.start(event, this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null));
1731
+ }
1732
+ onResizeKeydown(event) {
1733
+ const from = this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null);
1734
+ if (this.isPinned() && this.#resize.pressKey(event.key, from)) event.preventDefault();
1735
+ }
1736
+ closeMobileMenu() {
1737
+ if (this.narrow()) this.closeMobileMenuAction.emit();
1738
+ }
1739
+ #isHeld() {
1740
+ return this.#searchHeld() || this.#dragHeld();
1741
+ }
1742
+ #pickPinnedSubMenu(item) {
1743
+ if (item.submenu?.length) {
1744
+ this.selectedItem.set(item);
1745
+ this.selectedSubMenu.set(item.submenu);
1746
+ } else if (item.link) {
1747
+ this.selectedItem.set(null);
1748
+ this.selectedSubMenu.set(null);
1749
+ } else return;
1750
+ this.searchControl.setValue("", { emitEvent: false });
1751
+ this.subMenuQuery.set("");
1752
+ }
1753
+ #pressRow(item) {
1754
+ Array.from(this.panelRef()?.nativeElement.querySelectorAll("[id]") ?? []).find((row) => row.id === String(item.id))?.click();
1755
+ }
1756
+ static {
1757
+ this.ɵfac = i0.ɵɵngDeclareFactory({
1758
+ minVersion: "12.0.0",
1759
+ version: "22.2.2",
1760
+ ngImport: i0,
1761
+ type: RtSideMenuComponent,
1762
+ deps: [],
1763
+ target: i0.ɵɵFactoryTarget.Component
1764
+ });
1765
+ }
1766
+ static {
1767
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
1768
+ minVersion: "17.0.0",
1769
+ version: "22.2.2",
1770
+ type: RtSideMenuComponent,
1771
+ isStandalone: true,
1772
+ selector: "rt-side-menu",
1773
+ inputs: {
1774
+ activeMenuIds: {
1775
+ classPropertyName: "activeMenuIds",
1776
+ publicName: "activeMenuIds",
1777
+ isSignal: true,
1778
+ isRequired: true,
1779
+ transformFunction: null
1780
+ },
1781
+ menuItems: {
1782
+ classPropertyName: "menuItems",
1783
+ publicName: "menuItems",
1784
+ isSignal: true,
1785
+ isRequired: false,
1786
+ transformFunction: null
1787
+ },
1788
+ subMenuMode: {
1789
+ classPropertyName: "subMenuMode",
1790
+ publicName: "subMenuMode",
1791
+ isSignal: true,
1792
+ isRequired: false,
1793
+ transformFunction: null
1794
+ },
1795
+ subMenuWidth: {
1796
+ classPropertyName: "subMenuWidth",
1797
+ publicName: "subMenuWidth",
1798
+ isSignal: true,
1799
+ isRequired: false,
1800
+ transformFunction: null
1801
+ },
1802
+ menuId: {
1803
+ classPropertyName: "menuId",
1804
+ publicName: "menuId",
1805
+ isSignal: true,
1806
+ isRequired: false,
1807
+ transformFunction: null
1808
+ },
1809
+ favoritesCount: {
1810
+ classPropertyName: "favoritesCount",
1811
+ publicName: "favoritesCount",
1812
+ isSignal: true,
1813
+ isRequired: false,
1814
+ transformFunction: null
1815
+ },
1816
+ favoriteActionsReserve: {
1817
+ classPropertyName: "favoriteActionsReserve",
1818
+ publicName: "favoriteActionsReserve",
1819
+ isSignal: true,
1820
+ isRequired: false,
1821
+ transformFunction: null
1822
+ },
1823
+ isFavoritesSearchShown: {
1824
+ classPropertyName: "isFavoritesSearchShown",
1825
+ publicName: "isFavoritesSearchShown",
1826
+ isSignal: true,
1827
+ isRequired: false,
1828
+ transformFunction: null
1829
+ },
1830
+ pinShown: {
1831
+ classPropertyName: "pinShown",
1832
+ publicName: "pinShown",
1833
+ isSignal: true,
1834
+ isRequired: false,
1835
+ transformFunction: null
1836
+ },
1837
+ subMenuCloseDelay: {
1838
+ classPropertyName: "subMenuCloseDelay",
1839
+ publicName: "subMenuCloseDelay",
1840
+ isSignal: true,
1841
+ isRequired: false,
1842
+ transformFunction: null
1843
+ }
1844
+ },
1845
+ outputs: {
1846
+ subMenuModeChange: "subMenuModeChange",
1847
+ subMenuWidthChange: "subMenuWidthChange",
1848
+ subMenuResizeStart: "subMenuResizeStart",
1849
+ subMenuResizeEnd: "subMenuResizeEnd",
1850
+ closeMobileMenuAction: "closeMobileMenuAction",
1851
+ clickSubMenuAction: "clickSubMenuAction",
1852
+ clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction"
1853
+ },
1854
+ host: {
1855
+ properties: {
1856
+ "class.rt-side-menu--pinned": "isPinned()",
1857
+ "style.--rt-side-menu-panel-dragged-width": "panelWidthStyle()",
1858
+ "class.rt-side-menu--favorite-actions-none": "favoriteActionsReserve() === 'none'"
1859
+ },
1860
+ classAttribute: "rt-side-menu"
1861
+ },
1862
+ providers: [{
1863
+ provide: RT_SIDE_MENU,
1864
+ useExisting: RtSideMenuComponent
1865
+ }],
1866
+ queries: [
1867
+ {
1868
+ propertyName: "headerTpl",
1869
+ first: true,
1870
+ predicate: RtSideMenuHeaderDirective,
1871
+ descendants: true,
1872
+ read: TemplateRef,
1873
+ isSignal: true
1874
+ },
1875
+ {
1876
+ propertyName: "footerTpl",
1877
+ first: true,
1878
+ predicate: RtSideMenuFooterDirective,
1879
+ descendants: true,
1880
+ read: TemplateRef,
1881
+ isSignal: true
1882
+ },
1883
+ {
1884
+ propertyName: "ownIconTpl",
1885
+ first: true,
1886
+ predicate: RtSideMenuIconDirective,
1887
+ descendants: true,
1888
+ read: TemplateRef,
1889
+ isSignal: true
1890
+ }
1891
+ ],
1892
+ viewQueries: [{
1893
+ propertyName: "panelRef",
1894
+ first: true,
1895
+ predicate: ["panel"],
1896
+ descendants: true,
1897
+ isSignal: true
1898
+ }],
1899
+ usesInheritance: true,
1900
+ ngImport: i0,
1901
+ template: "<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- Узкий экран: один столбец — главные пункты или подменю выбранного раздела с кнопкой назад. -->\n <rt-scroll-area rtElem=\"mobile\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n @if (selectedSubMenu()?.length) {\n <div rtElem=\"mobile-sub\">\n <button type=\"button\" rtElem=\"back\" qa-dataid=\"side-menu-back\" (click)=\"onBackToMainMenu()\">\n <rt-icon rtElem=\"back-icon\" size=\"sm\" name=\"arrow-left\" />\n <span rtElem=\"back-label\">{{ backLabel() }}</span>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </div>\n } @else {\n <nav rtElem=\"mobile-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"mobileItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n }\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n } @else {\n <rt-scroll-area rtElem=\"rail\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n <nav rtElem=\"rail-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"railItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n\n @if (subMenuOpened()) {\n @if (isSearchHeld()) {\n <!-- Подложка незакреплённой панели: подменю, взятое полем поиска, закрывает нажатие снаружи. -->\n <div\n rtElem=\"backdrop\"\n qa-dataid=\"side-menu-backdrop\"\n tabindex=\"-1\"\n (click)=\"closeSubMenu()\"\n (keydown.escape)=\"closeSubMenu()\"></div>\n }\n\n <div\n #panel\n rtElem=\"panel\"\n qa-dataid=\"side-menu-panel\"\n [rtMod]=\"{ pinned: isPinned(), held: isSearchHeld() }\"\n (mouseenter)=\"cancelSubMenuClose()\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (focusin)=\"onSearchHold()\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (pinShown()) {\n <rt-icon-button\n rtElem=\"pin\"\n qa-dataid=\"side-menu-pin\"\n size=\"sm\"\n icon=\"pin\"\n [iconFill]=\"isPinned()\"\n [active]=\"isPinned()\"\n [ariaLabel]=\"isPinned() ? unpinLabel() : pinLabel()\"\n [tooltip]=\"isPinned() ? unpinLabel() : pinLabel()\"\n (clicked)=\"onSubMenuModeToggle()\" />\n }\n </div>\n\n <rt-scroll-area rtElem=\"panel-body\" [isScrollHintShown]=\"panelScrollHintShown()\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- Ручка есть только у закреплённого подменю: незакреплённое уходит раньше, чем до края доедет рука. -->\n <div\n rtElem=\"resizer\"\n qa-dataid=\"side-menu-resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"resizeLabel()\"\n [attr.aria-valuenow]=\"resizeValueNow()\"\n [attr.aria-valuemin]=\"resizeValueNow() === null ? null : resizeValueMin\"\n [attr.aria-valuemax]=\"resizeValueNow() === null ? null : resizeValueMax\"\n (pointerdown)=\"onResizeStart($event)\"\n (keydown)=\"onResizeKeydown($event)\"></div>\n }\n }\n }\n <ng-template #subMenuTpl>\n @if (narrow()) {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (keydown)=\"onSearchKeydown($event)\" />\n }\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"side-menu-empty\">{{ nothingFoundLabel() }}</div>\n }\n\n <div rtElem=\"sub-list\">\n <!-- Блок избранного раздела: сам решает, есть ли ему что показать, и без строк места не занимает. -->\n <rt-side-menu-favorites\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n\n @for (item of visibleSubMenuItems(); track item.id) {\n <rt-side-menu-sub-item\n [item]=\"item\"\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n }\n </div>\n </ng-template>\n\n <ng-template #railItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n <!-- Без подписей под значками имя пункта — в подсказке справа; выключенные подсказки гасят и её. -->\n @let railTip = railTitlesShown() || !subMenuTooltipsShown() ? '' : item.name;\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n [attr.aria-expanded]=\"item.submenu?.length ? subMenuOpened() && selectedItem() === item : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #railItemBodyTpl let-item>\n @if (item.icon) {\n <!-- Имя Material первого кита рисуется своей парой; имя, которого кит не рисует, — своим шаблоном меню. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" [fill]=\"railIconFill()\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"own\"\n [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n </span>\n }\n\n @if (item.name && railTitlesShown()) {\n <span rtElem=\"rail-title\">{{ item.name }}</span>\n }\n </ng-template>\n\n <ng-template #mobileItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link && !item.submenu?.length) {\n <a\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #mobileItemBodyTpl let-item>\n @if (item.icon) {\n @let kitIcon = item.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"mobile-icon\" [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n }\n\n <span rtElem=\"mobile-title\" [rtTooltip]=\"item.name\">{{ item.name }}</span>\n\n @if (item.submenu?.length) {\n <!-- Стрелка раздела, как у первого кита: нажатие уводит в подменю, а не раскрывает на месте. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n",
1902
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu,.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-item-color: currentcolor;--rt-side-menu-item-bg-hover: var(--rt-color-bg-hover);--rt-side-menu-item-bg-active: var(--rt-color-action-primary-subtle);--rt-side-menu-item-color-active: var(--rt-color-action-primary-on-surface)}.rt-side-menu{--rt-side-menu-rail-width: 5.5rem;--rt-side-menu-panel-width: 15rem;--rt-side-menu-panel-held-width: 30rem;--rt-side-menu-mobile-width: 15rem;--rt-side-menu-panel-radius: var(--rt-radius-lg);--rt-side-menu-panel-shadow: var(--rt-shadow-nav-panel);--rt-side-menu-item-radius: var(--rt-radius-full);--rt-side-menu-resizer-border-width: var(--rt-border-width-thick);--rt-side-menu-panel-border-width: var(--rt-border-width-thin);--rt-side-menu-transition-duration: var(--rt-duration-fast);--rt-side-menu-rail-icon-size: var(--rt-icon-step-md);--rt-side-menu-rail-gap: var(--rt-space-md);--rt-side-menu-rail-bg: var(--rt-color-bg-surface);--rt-side-menu-rail-border-width: var(--rt-side-menu-panel-border-width);--rt-side-menu-panel-bg: var(--rt-color-bg-surface);--rt-side-menu-panel-head-padding: var(--rt-space-sm);--rt-side-menu-sub-list-padding-inline: var(--rt-space-2);--rt-side-menu-empty-padding: var(--rt-space-md);--rt-side-menu-panel-dragged-width: 0px;--rt-side-menu-panel-shown-width: max(var(--rt-side-menu-panel-width), var(--rt-side-menu-panel-dragged-width));position:relative;display:flex;height:100%;color:var(--rt-color-text-primary)}.rt-side-menu__rail{max-width:var(--rt-side-menu-rail-width);height:100%;box-sizing:border-box;flex:0 0 var(--rt-side-menu-rail-width);border-right:var(--rt-side-menu-rail-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-rail-bg)}.rt-side-menu__rail .rt-scroll-area__body{padding-inline:var(--rt-space-1)}.rt-side-menu__rail-list{display:flex;flex-direction:column;align-items:center;gap:var(--rt-side-menu-rail-gap);padding-block:var(--rt-space-sm)}.rt-side-menu__rail-item{display:flex;width:100%;flex-direction:column;align-items:center;padding:0;border:0;background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;gap:var(--rt-space-1);text-decoration:none}.rt-side-menu__rail-item:focus-visible{outline:none}.rt-side-menu__rail-item:focus-visible .rt-side-menu__rail-icon{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color)}.rt-side-menu__rail-item:hover .rt-side-menu__rail-icon,.rt-side-menu__rail-item:active .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__rail-item--active .rt-side-menu__rail-icon,.rt-side-menu__rail-item--active:hover .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__rail-icon{--rt-icon-step-md: var(--rt-side-menu-rail-icon-size);display:flex;width:3.5rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--rt-side-menu-item-radius);transition:background-color var(--rt-side-menu-transition-duration) ease}.rt-side-menu__rail-title{max-width:100%;font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);overflow-wrap:anywhere;text-align:center}.rt-side-menu__backdrop{position:fixed;z-index:var(--rt-z-dropdown);inset:0;inset-inline-start:var(--rt-side-menu-rail-width)}.rt-side-menu__panel{position:absolute;z-index:var(--rt-z-dropdown);top:0;left:var(--rt-side-menu-rail-width);display:flex;width:var(--rt-side-menu-panel-shown-width);height:100%;box-sizing:border-box;flex-direction:column;border-radius:0 var(--rt-side-menu-panel-radius) var(--rt-side-menu-panel-radius) 0;border-left:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-panel-bg);box-shadow:var(--rt-side-menu-panel-shadow)}.rt-side-menu__panel--held{width:max(var(--rt-side-menu-panel-held-width),var(--rt-side-menu-panel-shown-width))}.rt-side-menu__panel--pinned{--rt-side-menu-panel-radius: 0;--rt-side-menu-panel-shadow: var(--rt-shadow-none);position:relative;left:0;flex-shrink:0}.rt-side-menu__panel-head{display:flex;align-items:center;padding:var(--rt-side-menu-panel-head-padding);gap:var(--rt-space-2)}.rt-side-menu__search{min-width:0;flex:1 1 auto}.rt-side-menu__pin{flex-shrink:0}.rt-side-menu__panel-body{min-height:0;flex:1 1 auto}.rt-side-menu__sub-list{display:flex;flex-direction:column;padding-block-end:var(--rt-space-sm);padding-inline:var(--rt-side-menu-sub-list-padding-inline)}.rt-side-menu__empty{padding:var(--rt-side-menu-empty-padding);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}.rt-side-menu__resizer{position:absolute;z-index:var(--rt-z-dropdown);top:0;bottom:0;left:calc(var(--rt-side-menu-rail-width) + var(--rt-side-menu-panel-shown-width) - var(--rt-space-2));width:var(--rt-space-sm);cursor:col-resize}.rt-side-menu__resizer:before{position:absolute;top:0;bottom:0;left:50%;border-left:var(--rt-side-menu-resizer-border-width) solid transparent;content:\"\"}.rt-side-menu__resizer:hover:before,.rt-side-menu__resizer:focus-visible:before{border-left-color:var(--rt-color-border-strong)}.rt-side-menu__resizer:focus-visible{outline:none}.rt-side-menu__mobile{min-width:0;max-width:100%;height:100%;flex:0 0 var(--rt-side-menu-mobile-width);background:var(--rt-color-bg-surface)}.rt-side-menu__mobile .rt-scroll-area__body{padding-inline:0}.rt-side-menu__mobile-list,.rt-side-menu__mobile-sub{display:flex;flex-direction:column;padding:var(--rt-space-sm);gap:var(--rt-space-1)}.rt-side-menu__mobile-item,.rt-side-menu__back{display:flex;width:100%;min-height:var(--rt-control-height-lg);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-item-radius);background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:calc(var(--rt-space-sm) + var(--rt-space-xs));padding-inline:var(--rt-space-md);text-align:start;text-decoration:none}.rt-side-menu__mobile-item:hover,.rt-side-menu__back:hover{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__mobile-item:focus-visible,.rt-side-menu__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu__mobile-item--active,.rt-side-menu__back--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__own-icon{--rt-side-menu-own-icon-size: var(--rt-size-5);display:inline-flex;width:var(--rt-side-menu-own-icon-size);height:var(--rt-side-menu-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu__mobile-icon,.rt-side-menu__back-icon,.rt-side-menu__mobile-chevron{flex-shrink:0}.rt-side-menu__mobile-title,.rt-side-menu__back-label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}}\n"],
1903
+ dependencies: [
1904
+ {
1905
+ kind: "directive",
1906
+ type: NgTemplateOutlet,
1907
+ selector: "[ngTemplateOutlet]",
1908
+ inputs: [
1909
+ "ngTemplateOutletContext",
1910
+ "ngTemplateOutlet",
1911
+ "ngTemplateOutletInjector"
1912
+ ]
1913
+ },
1914
+ {
1915
+ kind: "ngmodule",
1916
+ type: ReactiveFormsModule
1917
+ },
1918
+ {
1919
+ kind: "directive",
1920
+ type: i1.NgControlStatus,
1921
+ selector: "[formControlName],[ngModel],[formControl]"
1922
+ },
1923
+ {
1924
+ kind: "directive",
1925
+ type: i1.FormControlDirective,
1926
+ selector: "[formControl]",
1927
+ inputs: [
1928
+ "formControl",
1929
+ "disabled",
1930
+ "ngModel"
1931
+ ],
1932
+ outputs: ["ngModelChange"],
1933
+ exportAs: ["ngForm"]
1934
+ },
1935
+ {
1936
+ kind: "directive",
1937
+ type: RouterLink,
1938
+ selector: "[routerLink]",
1939
+ inputs: [
1940
+ "target",
1941
+ "queryParams",
1942
+ "fragment",
1943
+ "queryParamsHandling",
1944
+ "state",
1945
+ "info",
1946
+ "relativeTo",
1947
+ "preserveFragment",
1948
+ "skipLocationChange",
1949
+ "replaceUrl",
1950
+ "browserUrl",
1951
+ "routerLink"
1952
+ ]
1953
+ },
1954
+ {
1955
+ kind: "directive",
1956
+ type: BlockDirective,
1957
+ selector: "[rtBlock]",
1958
+ inputs: ["rtMod"]
1959
+ },
1960
+ {
1961
+ kind: "directive",
1962
+ type: ElemDirective,
1963
+ selector: "[rtElem]",
1964
+ inputs: ["rtMod"]
1965
+ },
1966
+ {
1967
+ kind: "directive",
1968
+ type: ModDirective,
1969
+ selector: "[rtMod]"
1970
+ },
1971
+ {
1972
+ kind: "component",
1973
+ type: RtIconComponent,
1974
+ selector: "rt-icon",
1975
+ inputs: [
1976
+ "name",
1977
+ "glyph",
1978
+ "glyphStrategy",
1979
+ "size",
1980
+ "color",
1981
+ "fill",
1982
+ "spin",
1983
+ "rotate"
1984
+ ]
1985
+ },
1986
+ {
1987
+ kind: "component",
1988
+ type: RtIconButtonComponent,
1989
+ selector: "rt-icon-button",
1990
+ inputs: [
1991
+ "icon",
1992
+ "glyph",
1993
+ "ariaLabel",
1994
+ "variant",
1995
+ "iconColor",
1996
+ "iconFill",
1997
+ "size",
1998
+ "iconSize",
1999
+ "type",
2000
+ "tooltip",
2001
+ "tooltipHideOnClick",
2002
+ "tabIndex",
2003
+ "loading",
2004
+ "disabled",
2005
+ "active",
2006
+ "expanded",
2007
+ "indicator"
2008
+ ],
2009
+ outputs: ["clicked"]
2010
+ },
2011
+ {
2012
+ kind: "component",
2013
+ type: RtInputComponent,
2014
+ selector: "rt-input",
2015
+ inputs: [
2016
+ "appearance",
2017
+ "type",
2018
+ "placeholder",
2019
+ "iconLeft",
2020
+ "iconRight",
2021
+ "passwordToggle",
2022
+ "autocomplete"
2023
+ ]
2024
+ },
2025
+ {
2026
+ kind: "component",
2027
+ type: RtScrollAreaComponent,
2028
+ selector: "rt-scroll-area",
2029
+ inputs: ["isScrollHintShown"]
2030
+ },
2031
+ {
2032
+ kind: "directive",
2033
+ type: RtScrollAreaContentDirective,
2034
+ selector: "[rtScrollAreaContent]"
2035
+ },
2036
+ {
2037
+ kind: "directive",
2038
+ type: RtScrollAreaFooterDirective,
2039
+ selector: "[rtScrollAreaFooter]"
2040
+ },
2041
+ {
2042
+ kind: "directive",
2043
+ type: RtScrollAreaHeaderDirective,
2044
+ selector: "[rtScrollAreaHeader]"
2045
+ },
2046
+ {
2047
+ kind: "directive",
2048
+ type: RtTooltipDirective,
2049
+ selector: "[rtTooltip]",
2050
+ inputs: [
2051
+ "rtTooltip",
2052
+ "rtTooltipPlacement",
2053
+ "rtTooltipWhenTruncated",
2054
+ "rtTooltipHideOnClick"
2055
+ ],
2056
+ exportAs: ["rtTooltip"]
2057
+ },
2058
+ {
2059
+ kind: "component",
2060
+ type: RtSideMenuFavoritesComponent,
2061
+ selector: "rt-side-menu-favorites",
2062
+ outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"]
2063
+ },
2064
+ {
2065
+ kind: "component",
2066
+ type: RtSideMenuSubItemComponent,
2067
+ selector: "rt-side-menu-sub-item",
2068
+ inputs: ["item", "inFavorites"],
2069
+ outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"]
2070
+ },
2071
+ {
2072
+ kind: "pipe",
2073
+ type: RtSideMenuIconPipe,
2074
+ name: "rtSideMenuIcon"
2075
+ }
2076
+ ],
2077
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
2078
+ encapsulation: i0.ViewEncapsulation.None
2079
+ });
2080
+ }
2081
+ };
2082
+ i0.ɵɵngDeclareClassMetadata({
2083
+ minVersion: "12.0.0",
2084
+ version: "22.2.2",
2085
+ ngImport: i0,
2086
+ type: RtSideMenuComponent,
2087
+ decorators: [{
2088
+ type: Component,
2089
+ args: [{
2090
+ selector: "rt-side-menu",
2091
+ host: {
2092
+ class: BEM_BLOCK,
2093
+ "[class.rt-side-menu--pinned]": "isPinned()",
2094
+ "[style.--rt-side-menu-panel-dragged-width]": "panelWidthStyle()",
2095
+ "[class.rt-side-menu--favorite-actions-none]": "favoriteActionsReserve() === 'none'"
2096
+ },
2097
+ providers: [{
2098
+ provide: RT_SIDE_MENU,
2099
+ useExisting: RtSideMenuComponent
2100
+ }],
2101
+ changeDetection: ChangeDetectionStrategy.OnPush,
2102
+ encapsulation: ViewEncapsulation.None,
2103
+ imports: [
2104
+ NgTemplateOutlet,
2105
+ ReactiveFormsModule,
2106
+ RouterLink,
2107
+ BlockDirective,
2108
+ ElemDirective,
2109
+ ModDirective,
2110
+ RtIconComponent,
2111
+ RtIconButtonComponent,
2112
+ RtInputComponent,
2113
+ RtScrollAreaComponent,
2114
+ RtScrollAreaContentDirective,
2115
+ RtScrollAreaFooterDirective,
2116
+ RtScrollAreaHeaderDirective,
2117
+ RtTooltipDirective,
2118
+ RtSideMenuFavoritesComponent,
2119
+ RtSideMenuIconPipe,
2120
+ RtSideMenuSubItemComponent
2121
+ ],
2122
+ template: "<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- Узкий экран: один столбец — главные пункты или подменю выбранного раздела с кнопкой назад. -->\n <rt-scroll-area rtElem=\"mobile\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n @if (selectedSubMenu()?.length) {\n <div rtElem=\"mobile-sub\">\n <button type=\"button\" rtElem=\"back\" qa-dataid=\"side-menu-back\" (click)=\"onBackToMainMenu()\">\n <rt-icon rtElem=\"back-icon\" size=\"sm\" name=\"arrow-left\" />\n <span rtElem=\"back-label\">{{ backLabel() }}</span>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </div>\n } @else {\n <nav rtElem=\"mobile-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"mobileItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n }\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n } @else {\n <rt-scroll-area rtElem=\"rail\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n <nav rtElem=\"rail-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"railItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n\n @if (subMenuOpened()) {\n @if (isSearchHeld()) {\n <!-- Подложка незакреплённой панели: подменю, взятое полем поиска, закрывает нажатие снаружи. -->\n <div\n rtElem=\"backdrop\"\n qa-dataid=\"side-menu-backdrop\"\n tabindex=\"-1\"\n (click)=\"closeSubMenu()\"\n (keydown.escape)=\"closeSubMenu()\"></div>\n }\n\n <div\n #panel\n rtElem=\"panel\"\n qa-dataid=\"side-menu-panel\"\n [rtMod]=\"{ pinned: isPinned(), held: isSearchHeld() }\"\n (mouseenter)=\"cancelSubMenuClose()\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (focusin)=\"onSearchHold()\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (pinShown()) {\n <rt-icon-button\n rtElem=\"pin\"\n qa-dataid=\"side-menu-pin\"\n size=\"sm\"\n icon=\"pin\"\n [iconFill]=\"isPinned()\"\n [active]=\"isPinned()\"\n [ariaLabel]=\"isPinned() ? unpinLabel() : pinLabel()\"\n [tooltip]=\"isPinned() ? unpinLabel() : pinLabel()\"\n (clicked)=\"onSubMenuModeToggle()\" />\n }\n </div>\n\n <rt-scroll-area rtElem=\"panel-body\" [isScrollHintShown]=\"panelScrollHintShown()\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- Ручка есть только у закреплённого подменю: незакреплённое уходит раньше, чем до края доедет рука. -->\n <div\n rtElem=\"resizer\"\n qa-dataid=\"side-menu-resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"resizeLabel()\"\n [attr.aria-valuenow]=\"resizeValueNow()\"\n [attr.aria-valuemin]=\"resizeValueNow() === null ? null : resizeValueMin\"\n [attr.aria-valuemax]=\"resizeValueNow() === null ? null : resizeValueMax\"\n (pointerdown)=\"onResizeStart($event)\"\n (keydown)=\"onResizeKeydown($event)\"></div>\n }\n }\n }\n <ng-template #subMenuTpl>\n @if (narrow()) {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (keydown)=\"onSearchKeydown($event)\" />\n }\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"side-menu-empty\">{{ nothingFoundLabel() }}</div>\n }\n\n <div rtElem=\"sub-list\">\n <!-- Блок избранного раздела: сам решает, есть ли ему что показать, и без строк места не занимает. -->\n <rt-side-menu-favorites\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n\n @for (item of visibleSubMenuItems(); track item.id) {\n <rt-side-menu-sub-item\n [item]=\"item\"\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n }\n </div>\n </ng-template>\n\n <ng-template #railItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n <!-- Без подписей под значками имя пункта — в подсказке справа; выключенные подсказки гасят и её. -->\n @let railTip = railTitlesShown() || !subMenuTooltipsShown() ? '' : item.name;\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\n [attr.aria-expanded]=\"item.submenu?.length ? subMenuOpened() && selectedItem() === item : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #railItemBodyTpl let-item>\n @if (item.icon) {\n <!-- Имя Material первого кита рисуется своей парой; имя, которого кит не рисует, — своим шаблоном меню. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" [fill]=\"railIconFill()\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"own\"\n [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n </span>\n }\n\n @if (item.name && railTitlesShown()) {\n <span rtElem=\"rail-title\">{{ item.name }}</span>\n }\n </ng-template>\n\n <ng-template #mobileItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link && !item.submenu?.length) {\n <a\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #mobileItemBodyTpl let-item>\n @if (item.icon) {\n @let kitIcon = item.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"mobile-icon\" [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n }\n\n <span rtElem=\"mobile-title\" [rtTooltip]=\"item.name\">{{ item.name }}</span>\n\n @if (item.submenu?.length) {\n <!-- Стрелка раздела, как у первого кита: нажатие уводит в подменю, а не раскрывает на месте. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n",
2123
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu,.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-item-color: currentcolor;--rt-side-menu-item-bg-hover: var(--rt-color-bg-hover);--rt-side-menu-item-bg-active: var(--rt-color-action-primary-subtle);--rt-side-menu-item-color-active: var(--rt-color-action-primary-on-surface)}.rt-side-menu{--rt-side-menu-rail-width: 5.5rem;--rt-side-menu-panel-width: 15rem;--rt-side-menu-panel-held-width: 30rem;--rt-side-menu-mobile-width: 15rem;--rt-side-menu-panel-radius: var(--rt-radius-lg);--rt-side-menu-panel-shadow: var(--rt-shadow-nav-panel);--rt-side-menu-item-radius: var(--rt-radius-full);--rt-side-menu-resizer-border-width: var(--rt-border-width-thick);--rt-side-menu-panel-border-width: var(--rt-border-width-thin);--rt-side-menu-transition-duration: var(--rt-duration-fast);--rt-side-menu-rail-icon-size: var(--rt-icon-step-md);--rt-side-menu-rail-gap: var(--rt-space-md);--rt-side-menu-rail-bg: var(--rt-color-bg-surface);--rt-side-menu-rail-border-width: var(--rt-side-menu-panel-border-width);--rt-side-menu-panel-bg: var(--rt-color-bg-surface);--rt-side-menu-panel-head-padding: var(--rt-space-sm);--rt-side-menu-sub-list-padding-inline: var(--rt-space-2);--rt-side-menu-empty-padding: var(--rt-space-md);--rt-side-menu-panel-dragged-width: 0px;--rt-side-menu-panel-shown-width: max(var(--rt-side-menu-panel-width), var(--rt-side-menu-panel-dragged-width));position:relative;display:flex;height:100%;color:var(--rt-color-text-primary)}.rt-side-menu__rail{max-width:var(--rt-side-menu-rail-width);height:100%;box-sizing:border-box;flex:0 0 var(--rt-side-menu-rail-width);border-right:var(--rt-side-menu-rail-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-rail-bg)}.rt-side-menu__rail .rt-scroll-area__body{padding-inline:var(--rt-space-1)}.rt-side-menu__rail-list{display:flex;flex-direction:column;align-items:center;gap:var(--rt-side-menu-rail-gap);padding-block:var(--rt-space-sm)}.rt-side-menu__rail-item{display:flex;width:100%;flex-direction:column;align-items:center;padding:0;border:0;background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;gap:var(--rt-space-1);text-decoration:none}.rt-side-menu__rail-item:focus-visible{outline:none}.rt-side-menu__rail-item:focus-visible .rt-side-menu__rail-icon{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color)}.rt-side-menu__rail-item:hover .rt-side-menu__rail-icon,.rt-side-menu__rail-item:active .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__rail-item--active .rt-side-menu__rail-icon,.rt-side-menu__rail-item--active:hover .rt-side-menu__rail-icon{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__rail-icon{--rt-icon-step-md: var(--rt-side-menu-rail-icon-size);display:flex;width:3.5rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--rt-side-menu-item-radius);transition:background-color var(--rt-side-menu-transition-duration) ease}.rt-side-menu__rail-title{max-width:100%;font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);overflow-wrap:anywhere;text-align:center}.rt-side-menu__backdrop{position:fixed;z-index:var(--rt-z-dropdown);inset:0;inset-inline-start:var(--rt-side-menu-rail-width)}.rt-side-menu__panel{position:absolute;z-index:var(--rt-z-dropdown);top:0;left:var(--rt-side-menu-rail-width);display:flex;width:var(--rt-side-menu-panel-shown-width);height:100%;box-sizing:border-box;flex-direction:column;border-radius:0 var(--rt-side-menu-panel-radius) var(--rt-side-menu-panel-radius) 0;border-left:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-side-menu-panel-bg);box-shadow:var(--rt-side-menu-panel-shadow)}.rt-side-menu__panel--held{width:max(var(--rt-side-menu-panel-held-width),var(--rt-side-menu-panel-shown-width))}.rt-side-menu__panel--pinned{--rt-side-menu-panel-radius: 0;--rt-side-menu-panel-shadow: var(--rt-shadow-none);position:relative;left:0;flex-shrink:0}.rt-side-menu__panel-head{display:flex;align-items:center;padding:var(--rt-side-menu-panel-head-padding);gap:var(--rt-space-2)}.rt-side-menu__search{min-width:0;flex:1 1 auto}.rt-side-menu__pin{flex-shrink:0}.rt-side-menu__panel-body{min-height:0;flex:1 1 auto}.rt-side-menu__sub-list{display:flex;flex-direction:column;padding-block-end:var(--rt-space-sm);padding-inline:var(--rt-side-menu-sub-list-padding-inline)}.rt-side-menu__empty{padding:var(--rt-side-menu-empty-padding);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}.rt-side-menu__resizer{position:absolute;z-index:var(--rt-z-dropdown);top:0;bottom:0;left:calc(var(--rt-side-menu-rail-width) + var(--rt-side-menu-panel-shown-width) - var(--rt-space-2));width:var(--rt-space-sm);cursor:col-resize}.rt-side-menu__resizer:before{position:absolute;top:0;bottom:0;left:50%;border-left:var(--rt-side-menu-resizer-border-width) solid transparent;content:\"\"}.rt-side-menu__resizer:hover:before,.rt-side-menu__resizer:focus-visible:before{border-left-color:var(--rt-color-border-strong)}.rt-side-menu__resizer:focus-visible{outline:none}.rt-side-menu__mobile{min-width:0;max-width:100%;height:100%;flex:0 0 var(--rt-side-menu-mobile-width);background:var(--rt-color-bg-surface)}.rt-side-menu__mobile .rt-scroll-area__body{padding-inline:0}.rt-side-menu__mobile-list,.rt-side-menu__mobile-sub{display:flex;flex-direction:column;padding:var(--rt-space-sm);gap:var(--rt-space-1)}.rt-side-menu__mobile-item,.rt-side-menu__back{display:flex;width:100%;min-height:var(--rt-control-height-lg);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-item-radius);background:transparent;color:var(--rt-side-menu-item-color);cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:calc(var(--rt-space-sm) + var(--rt-space-xs));padding-inline:var(--rt-space-md);text-align:start;text-decoration:none}.rt-side-menu__mobile-item:hover,.rt-side-menu__back:hover{background:var(--rt-side-menu-item-bg-hover)}.rt-side-menu__mobile-item:focus-visible,.rt-side-menu__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu__mobile-item--active,.rt-side-menu__back--active{background:var(--rt-side-menu-item-bg-active);color:var(--rt-side-menu-item-color-active)}.rt-side-menu__own-icon{--rt-side-menu-own-icon-size: var(--rt-size-5);display:inline-flex;width:var(--rt-side-menu-own-icon-size);height:var(--rt-side-menu-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu__mobile-icon,.rt-side-menu__back-icon,.rt-side-menu__mobile-chevron{flex-shrink:0}.rt-side-menu__mobile-title,.rt-side-menu__back-label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}}\n"]
2124
+ }]
2125
+ }],
2126
+ ctorParameters: () => [],
2127
+ propDecorators: {
2128
+ panelRef: [{
2129
+ type: i0.ViewChild,
2130
+ args: ["panel", { isSignal: true }]
2131
+ }],
2132
+ headerTpl: [{
2133
+ type: i0.ContentChild,
2134
+ args: [i0.forwardRef(() => RtSideMenuHeaderDirective), {
2135
+ read: TemplateRef,
2136
+ isSignal: true
2137
+ }]
2138
+ }],
2139
+ footerTpl: [{
2140
+ type: i0.ContentChild,
2141
+ args: [i0.forwardRef(() => RtSideMenuFooterDirective), {
2142
+ read: TemplateRef,
2143
+ isSignal: true
2144
+ }]
2145
+ }],
2146
+ activeMenuIds: [{
2147
+ type: i0.Input,
2148
+ args: [{
2149
+ isSignal: true,
2150
+ alias: "activeMenuIds",
2151
+ required: true
2152
+ }]
2153
+ }],
2154
+ menuItems: [{
2155
+ type: i0.Input,
2156
+ args: [{
2157
+ isSignal: true,
2158
+ alias: "menuItems",
2159
+ required: false
2160
+ }]
2161
+ }],
2162
+ subMenuMode: [{
2163
+ type: i0.Input,
2164
+ args: [{
2165
+ isSignal: true,
2166
+ alias: "subMenuMode",
2167
+ required: false
2168
+ }]
2169
+ }],
2170
+ subMenuWidth: [{
2171
+ type: i0.Input,
2172
+ args: [{
2173
+ isSignal: true,
2174
+ alias: "subMenuWidth",
2175
+ required: false
2176
+ }]
2177
+ }],
2178
+ menuId: [{
2179
+ type: i0.Input,
2180
+ args: [{
2181
+ isSignal: true,
2182
+ alias: "menuId",
2183
+ required: false
2184
+ }]
2185
+ }],
2186
+ favoritesCount: [{
2187
+ type: i0.Input,
2188
+ args: [{
2189
+ isSignal: true,
2190
+ alias: "favoritesCount",
2191
+ required: false
2192
+ }]
2193
+ }],
2194
+ favoriteActionsReserve: [{
2195
+ type: i0.Input,
2196
+ args: [{
2197
+ isSignal: true,
2198
+ alias: "favoriteActionsReserve",
2199
+ required: false
2200
+ }]
2201
+ }],
2202
+ isFavoritesSearchShown: [{
2203
+ type: i0.Input,
2204
+ args: [{
2205
+ isSignal: true,
2206
+ alias: "isFavoritesSearchShown",
2207
+ required: false
2208
+ }]
2209
+ }],
2210
+ pinShown: [{
2211
+ type: i0.Input,
2212
+ args: [{
2213
+ isSignal: true,
2214
+ alias: "pinShown",
2215
+ required: false
2216
+ }]
2217
+ }],
2218
+ subMenuCloseDelay: [{
2219
+ type: i0.Input,
2220
+ args: [{
2221
+ isSignal: true,
2222
+ alias: "subMenuCloseDelay",
2223
+ required: false
2224
+ }]
2225
+ }],
2226
+ subMenuModeChange: [{
2227
+ type: i0.Output,
2228
+ args: ["subMenuModeChange"]
2229
+ }],
2230
+ subMenuWidthChange: [{
2231
+ type: i0.Output,
2232
+ args: ["subMenuWidthChange"]
2233
+ }],
2234
+ subMenuResizeStart: [{
2235
+ type: i0.Output,
2236
+ args: ["subMenuResizeStart"]
2237
+ }],
2238
+ subMenuResizeEnd: [{
2239
+ type: i0.Output,
2240
+ args: ["subMenuResizeEnd"]
2241
+ }],
2242
+ closeMobileMenuAction: [{
2243
+ type: i0.Output,
2244
+ args: ["closeMobileMenuAction"]
2245
+ }],
2246
+ clickSubMenuAction: [{
2247
+ type: i0.Output,
2248
+ args: ["clickSubMenuAction"]
2249
+ }],
2250
+ clickSubMenuAdditionalAction: [{
2251
+ type: i0.Output,
2252
+ args: ["clickSubMenuAdditionalAction"]
2253
+ }],
2254
+ ownIconTpl: [{
2255
+ type: i0.ContentChild,
2256
+ args: [i0.forwardRef(() => RtSideMenuIconDirective), {
2257
+ read: TemplateRef,
2258
+ isSignal: true
2259
+ }]
2260
+ }]
2261
+ }
2262
+ });
2263
+ export { RT_SIDE_MENU_DEFAULT_ID, RT_SIDE_MENU_SETTINGS_CONFIG, RT_SIDE_MENU_WIDTH_MAX, RT_SIDE_MENU_WIDTH_MIN, RT_SIDE_MENU_WIDTH_STEP, RtSideMenuComponent, RtSideMenuFooterDirective, RtSideMenuHeaderDirective, RtSideMenuIconDirective, RtSideMenuSettingsService, RtSideMenuSubItemComponent, clampSideMenuWidth, drawnSideMenuWidth, filterSideMenuItems, normalizeSideMenuId, normalizeSideMenuSettings, omitSideMenuSettings, parseSideMenuSettingsRecord, patchSideMenuSettings, pinnedSideMenuItems, provideRtSideMenuSettings, readSideMenuSettings, reportedSideMenuWidth, sideMenuIconName, sideMenuIdsToExpand, sideMenuPressOpensInBrowser, sideMenuWidthByKey, splitSideMenuTitle, splitTitleByWords, startSideMenuWidthDrag, stepSideMenuHighlight, unpairedSideMenuIcons, walkSideMenuItems, warnUnpairedSideMenuIcons };
1637
2264
 
1638
- export { RT_SIDE_MENU_DEFAULT_ID, RT_SIDE_MENU_SETTINGS_CONFIG, RT_SIDE_MENU_WIDTH_MAX, RT_SIDE_MENU_WIDTH_MIN, RT_SIDE_MENU_WIDTH_STEP, RtSideMenuComponent, RtSideMenuFooterDirective, RtSideMenuHeaderDirective, RtSideMenuIconDirective, RtSideMenuSettingsService, RtSideMenuSubItemComponent, clampSideMenuWidth, drawnSideMenuWidth, filterSideMenuItems, normalizeSideMenuId, normalizeSideMenuSettings, omitSideMenuSettings, parseSideMenuSettingsRecord, patchSideMenuSettings, pinnedSideMenuItems, provideRtSideMenuSettings, readSideMenuSettings, reportedSideMenuWidth, sideMenuIconName, sideMenuIdsToExpand, sideMenuPressOpensInBrowser, sideMenuWidthByKey, splitSideMenuTitle, startSideMenuWidthDrag, stepSideMenuHighlight, unpairedSideMenuIcons, walkSideMenuItems, warnUnpairedSideMenuIcons };
1639
- //# sourceMappingURL=rt-tools-ui-kit-v2-side-menu.mjs.map
2265
+ //# sourceMappingURL=rt-tools-ui-kit-v2-side-menu.mjs.map