@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 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-pagination.mjs","sources":["../../../projects/ui-kit-v2/src/lib/components/pagination/rt-pagination.logic.ts","../../../projects/ui-kit-v2/src/lib/components/pagination/rt-pagination.component.ts","../../../projects/ui-kit-v2/src/lib/components/pagination/rt-pagination.component.html","../../../projects/ui-kit-v2/src/lib/components/pagination/rt-tools-ui-kit-v2-pagination.ts"],"sourcesContent":["import { IPageModel } from '@rt-tools/utils';\n\nimport { IRtPagination } from './rt-pagination.model';\n\n/** Сколько всего страниц. Пустой список — одна страница, листать нечего. */\nexport function lastPageOf(page: IPageModel): number {\n if (page.pageSize <= 0) {\n return 1;\n }\n\n return Math.max(1, Math.ceil(page.totalCount / page.pageSize));\n}\n\n/** Номер первой записи открытой страницы. */\nexport function rangeFromOf(page: IPageModel): number {\n return (page.pageNumber - 1) * page.pageSize + 1;\n}\n\n/** Номер последней записи открытой страницы — на последней странице она короче. */\nexport function rangeToOf(page: IPageModel): number {\n return Math.min(page.pageNumber * page.pageSize, page.totalCount);\n}\n\n/**\n * Полоса номеров страниц с разрывами «…»: первая, последняя и соседи открытой.\n * Пуста при единственной странице — показывать нечего.\n */\nexport function pageItemsOf(page: IPageModel, neighbours: number): ReadonlyArray<IRtPagination.PageItem> {\n const lastPage: number = lastPageOf(page);\n if (lastPage <= 1) {\n return [];\n }\n\n const items: IRtPagination.PageItem[] = [];\n let previous: number = 0;\n for (let candidate: number = 1; candidate <= lastPage; candidate++) {\n const isEdge: boolean = candidate === 1 || candidate === lastPage;\n const isNeighbour: boolean = candidate >= page.pageNumber - neighbours && candidate <= page.pageNumber + neighbours;\n if (!isEdge && !isNeighbour) {\n continue;\n }\n if (previous && candidate - previous > 1) {\n items.push('gap');\n }\n items.push(candidate);\n previous = candidate;\n }\n\n return items;\n}\n\n/** Номера с `from` по `to` включительно. */\nfunction pagesBetween(from: number, to: number): number[] {\n return Array.from({ length: to - from + 1 }, (_value: unknown, index: number): number => from + index);\n}\n\n/**\n * Полоса номеров первого кита — семь мест: до шести страниц видны все, дальше края по три\n * (`1 2 3 … 11 12 13`) или первая, соседи открытой и последняя (`1 … 6 7 8 … 13`); у края разрыв\n * съезжает на одно место за открытой страницей. Одна страница — один номер.\n */\nexport function pageSlotsOf(page: IPageModel): ReadonlyArray<IRtPagination.PageItem> {\n const lastPage: number = lastPageOf(page);\n const current: number = page.pageNumber;\n\n if (lastPage <= 6) {\n return pagesBetween(1, lastPage);\n }\n if (current < 3 || current > lastPage - 2) {\n return [...pagesBetween(1, 3), 'gap', ...pagesBetween(lastPage - 2, lastPage)];\n }\n if (current === 3) {\n return [...pagesBetween(1, 4), 'gap', ...pagesBetween(lastPage - 1, lastPage)];\n }\n if (current === 4) {\n return [...pagesBetween(1, 5), 'gap', lastPage];\n }\n if (current <= lastPage - 4) {\n return [1, 'gap', current - 1, current, current + 1, 'gap', lastPage];\n }\n if (current === lastPage - 3) {\n return [1, 'gap', ...pagesBetween(lastPage - 4, lastPage)];\n }\n\n return [1, 2, 'gap', ...pagesBetween(lastPage - 3, lastPage)];\n}\n","import {\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n ChangeDetectionStrategy,\n Component,\n InputSignal,\n InputSignalWithTransform,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\nimport { IPageModel } from '@rt-tools/utils';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap, TRtKitLabelParams, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtSelectComponent } from '@rt-tools/ui-kit-v2/select';\nimport { IRtSelect } from '@rt-tools/ui-kit-v2/select';\nimport { lastPageOf, pageItemsOf, pageSlotsOf, rangeFromOf, rangeToOf } from './rt-pagination.logic';\nimport { IRtPagination } from './rt-pagination.model';\n\nconst BEM_BLOCK: string = 'rt-pagination';\nconst NEIGHBOURS: number = 1; // соседей вокруг открытой страницы\n\n@Component({\n selector: 'rt-pagination',\n templateUrl: './rt-pagination.component.html',\n styleUrl: './rt-pagination.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n FormsModule,\n\n // standalone components / directives\n RtIconButtonComponent,\n RtSelectComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[style.display]': \"isVisible() ? null : 'none'\",\n },\n})\nexport class RtPaginationComponent {\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly perPageSelectOptions: Signal<ReadonlyArray<IRtSelect.Option<number>>> = computed(\n (): ReadonlyArray<IRtSelect.Option<number>> =>\n this.perPageOptions().map((n: number): IRtSelect.Option<number> => ({ label: String(n), value: n }))\n );\n\n /**\n * Диапазон записей и номер страницы — с подстановками, поэтому карте подписей\n * они не годятся: числа приходят из модели страницы.\n */\n protected readonly rangeLabel: Signal<string> = rtKitLabel(\n 'uiRangeOf',\n computed((): TRtKitLabelParams => ({ from: this.rangeFrom(), to: this.rangeTo(), total: this.pageModel().totalCount }))\n );\n\n protected readonly pageLabel: Signal<string> = rtKitLabel(\n 'uiPageOf',\n computed((): TRtKitLabelParams => ({ page: this.pageModel().pageNumber, last: this.lastPage() }))\n );\n\n protected readonly isFirst: Signal<boolean> = computed((): boolean => this.pageModel().pageNumber <= 1);\n\n protected readonly isLast: Signal<boolean> = computed((): boolean => this.pageModel().pageNumber >= this.lastPage());\n\n public readonly pageModel: InputSignal<IPageModel> = input.required<IPageModel>();\n\n public readonly perPageOptions: InputSignal<ReadonlyArray<number>> = input<ReadonlyArray<number>>([20, 50, 100]);\n\n /** Значок стрелки «назад»: вид первого кита рисует стрелку, а не шеврон. */\n public readonly prevIcon: InputSignal<IRtIcon.Name> = input<IRtIcon.Name>('chevron-left');\n\n /** Значок стрелки «вперёд». */\n public readonly nextIcon: InputSignal<IRtIcon.Name> = input<IRtIcon.Name>('chevron-right');\n\n /** Счёт номеров: список под материальным набором просит семь мест первого кита. */\n public readonly numbering: InputSignal<IRtPagination.Numbering> = input<IRtPagination.Numbering>('neighbours');\n\n public readonly loading: InputSignalWithTransform<boolean, unknown> = input(false, {\n transform: booleanAttribute,\n });\n\n public readonly pageChange: OutputEmitterRef<number> = output<number>();\n\n public readonly perPageChange: OutputEmitterRef<number> = output<number>();\n\n /**\n * Пагинацию показываем, только когда есть что листать/менять: всё не влезает\n * на одну страницу даже при минимальной опции размера (`total > наименьший\n * perPageOption`). Иначе и навигация, и селектор размера бесполезны — прячем\n * весь бар (включая пустой и пустой-по-фильтру список).\n */\n public readonly isVisible: Signal<boolean> = computed((): boolean => {\n const options: ReadonlyArray<number> = this.perPageOptions();\n const smallest: number = options.length > 0 ? Math.min(...options) : 0;\n return this.pageModel().totalCount > smallest;\n });\n\n /** Сколько всего страниц при нынешнем размере страницы. */\n public readonly lastPage: Signal<number> = computed((): number => lastPageOf(this.pageModel()));\n\n /**\n * Границы диапазона записей открытой страницы. Собираются числами, а сама\n * подпись — в шаблоне: только там подстановка проходит через словарь и\n * переживает смену языка.\n */\n public readonly rangeFrom: Signal<number> = computed((): number => rangeFromOf(this.pageModel()));\n\n public readonly rangeTo: Signal<number> = computed((): number => rangeToOf(this.pageModel()));\n\n /** Полоса номеров страниц с разрывами «…». По соседям пуста при единственной странице. */\n public readonly pageItems: Signal<ReadonlyArray<IRtPagination.PageItem>> = computed((): ReadonlyArray<IRtPagination.PageItem> =>\n this.numbering() === 'seven' ? pageSlotsOf(this.pageModel()) : pageItemsOf(this.pageModel(), NEIGHBOURS)\n );\n\n public goTo(pageNumber: number): void {\n if (this.loading() || pageNumber === this.pageModel().pageNumber || pageNumber < 1 || pageNumber > this.lastPage()) {\n return;\n }\n this.pageChange.emit(pageNumber);\n }\n\n protected onPerPage(value: number | null): void {\n if (value === null || this.loading() || value === this.pageModel().pageSize) {\n return;\n }\n this.perPageChange.emit(value);\n }\n}\n","<ng-container rtBlock=\"rt-pagination\">\n <!--\n Ветки «ничего не найдено» здесь нет намеренно: пустой набор прячет весь\n бар целиком (`isVisible`), так что до этой строки дело не доходит. Пока\n такая ветка тут висела, она читалась как рабочее состояние, а показать\n её было нельзя ни при каких входах.\n -->\n <span rtElem=\"range\" qa-dataid=\"pagination-range\">\n {{ rangeLabel() }}\n </span>\n\n @if (pageItems().length) {\n <nav rtElem=\"compact\" qa-dataid=\"pagination-compact\" [attr.aria-label]=\"t().uiPagination\">\n <rt-icon-button\n qa-dataid=\"pagination-compact-prev\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"prevIcon()\"\n [ariaLabel]=\"t().uiPrevPage\"\n [tooltip]=\"t().uiBack\"\n [disabled]=\"isFirst() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber - 1)\" />\n\n <span rtElem=\"compact-label\" qa-dataid=\"pagination-compact-label\">\n {{ pageLabel() }}\n </span>\n\n <rt-icon-button\n qa-dataid=\"pagination-compact-next\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"nextIcon()\"\n [ariaLabel]=\"t().uiNextPage\"\n [tooltip]=\"t().uiForward\"\n [disabled]=\"isLast() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber + 1)\" />\n </nav>\n\n <nav rtElem=\"nav\" qa-dataid=\"pagination-nav\" [attr.aria-label]=\"t().uiPagination\">\n <rt-icon-button\n qa-dataid=\"pagination-prev\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"prevIcon()\"\n [ariaLabel]=\"t().uiPrevPage\"\n [tooltip]=\"t().uiBack\"\n [disabled]=\"isFirst() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber - 1)\" />\n\n @for (item of pageItems(); track $index) {\n @if (item === 'gap') {\n <span rtElem=\"gap\" aria-hidden=\"true\">…</span>\n } @else {\n <button\n rtElem=\"page\"\n qa-dataid=\"pagination-page\"\n type=\"button\"\n [attr.data-page]=\"item\"\n [rtMod]=\"{ current: item === pageModel().pageNumber }\"\n [attr.aria-current]=\"item === pageModel().pageNumber ? 'page' : null\"\n [disabled]=\"loading()\"\n (click)=\"goTo(item)\">\n {{ item }}\n </button>\n }\n }\n\n <rt-icon-button\n qa-dataid=\"pagination-next\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"nextIcon()\"\n [ariaLabel]=\"t().uiNextPage\"\n [tooltip]=\"t().uiForward\"\n [disabled]=\"isLast() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber + 1)\" />\n </nav>\n }\n\n <div rtElem=\"per-page\">\n <span rtElem=\"per-page-label\">{{ t().uiPerPage }}</span>\n <rt-select\n qa-dataid=\"pagination-per-page\"\n [ariaLabel]=\"t().uiItemsPerPage\"\n [options]=\"perPageSelectOptions()\"\n [clearable]=\"false\"\n [ngModel]=\"pageModel().pageSize\"\n [ngModelOptions]=\"{ standalone: true }\"\n (selectionChange)=\"onPerPage($event)\" />\n </div>\n</ng-container>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-pagination.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/pagination/rt-pagination.logic.ts","../../../projects/ui-kit-v2/src/lib/components/pagination/rt-pagination.component.ts","../../../projects/ui-kit-v2/src/lib/components/pagination/rt-pagination.component.html"],"sourcesContent":["import { IPageModel } from '@rt-tools/utils';\n\nimport { IRtPagination } from './rt-pagination.model';\n\n/** Сколько всего страниц. Пустой список — одна страница, листать нечего. */\nexport function lastPageOf(page: IPageModel): number {\n if (page.pageSize <= 0) {\n return 1;\n }\n\n return Math.max(1, Math.ceil(page.totalCount / page.pageSize));\n}\n\n/** Номер первой записи открытой страницы. */\nexport function rangeFromOf(page: IPageModel): number {\n return (page.pageNumber - 1) * page.pageSize + 1;\n}\n\n/** Номер последней записи открытой страницы — на последней странице она короче. */\nexport function rangeToOf(page: IPageModel): number {\n return Math.min(page.pageNumber * page.pageSize, page.totalCount);\n}\n\n/**\n * Полоса номеров страниц с разрывами «…»: первая, последняя и соседи открытой.\n * Пуста при единственной странице — показывать нечего.\n */\nexport function pageItemsOf(page: IPageModel, neighbours: number): ReadonlyArray<IRtPagination.PageItem> {\n const lastPage: number = lastPageOf(page);\n if (lastPage <= 1) {\n return [];\n }\n\n const items: IRtPagination.PageItem[] = [];\n let previous: number = 0;\n for (let candidate: number = 1; candidate <= lastPage; candidate++) {\n const isEdge: boolean = candidate === 1 || candidate === lastPage;\n const isNeighbour: boolean = candidate >= page.pageNumber - neighbours && candidate <= page.pageNumber + neighbours;\n if (!isEdge && !isNeighbour) {\n continue;\n }\n if (previous && candidate - previous > 1) {\n items.push('gap');\n }\n items.push(candidate);\n previous = candidate;\n }\n\n return items;\n}\n\n/** Номера с `from` по `to` включительно. */\nfunction pagesBetween(from: number, to: number): number[] {\n return Array.from({ length: to - from + 1 }, (_value: unknown, index: number): number => from + index);\n}\n\n/**\n * Полоса номеров первого кита — семь мест: до шести страниц видны все, дальше края по три\n * (`1 2 3 … 11 12 13`) или первая, соседи открытой и последняя (`1 … 6 7 8 … 13`); у края разрыв\n * съезжает на одно место за открытой страницей. Одна страница — один номер.\n */\nexport function pageSlotsOf(page: IPageModel): ReadonlyArray<IRtPagination.PageItem> {\n const lastPage: number = lastPageOf(page);\n const current: number = page.pageNumber;\n\n if (lastPage <= 6) {\n return pagesBetween(1, lastPage);\n }\n if (current < 3 || current > lastPage - 2) {\n return [...pagesBetween(1, 3), 'gap', ...pagesBetween(lastPage - 2, lastPage)];\n }\n if (current === 3) {\n return [...pagesBetween(1, 4), 'gap', ...pagesBetween(lastPage - 1, lastPage)];\n }\n if (current === 4) {\n return [...pagesBetween(1, 5), 'gap', lastPage];\n }\n if (current <= lastPage - 4) {\n return [1, 'gap', current - 1, current, current + 1, 'gap', lastPage];\n }\n if (current === lastPage - 3) {\n return [1, 'gap', ...pagesBetween(lastPage - 4, lastPage)];\n }\n\n return [1, 2, 'gap', ...pagesBetween(lastPage - 3, lastPage)];\n}\n","import {\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n ChangeDetectionStrategy,\n Component,\n InputSignal,\n InputSignalWithTransform,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\nimport { IPageModel } from '@rt-tools/utils';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap, TRtKitLabelParams, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtSelectComponent } from '@rt-tools/ui-kit-v2/select';\nimport { IRtSelect } from '@rt-tools/ui-kit-v2/select';\nimport { lastPageOf, pageItemsOf, pageSlotsOf, rangeFromOf, rangeToOf } from './rt-pagination.logic';\nimport { IRtPagination } from './rt-pagination.model';\n\nconst BEM_BLOCK: string = 'rt-pagination';\nconst NEIGHBOURS: number = 1; // соседей вокруг открытой страницы\n\n@Component({\n selector: 'rt-pagination',\n templateUrl: './rt-pagination.component.html',\n styleUrl: './rt-pagination.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n FormsModule,\n\n // standalone components / directives\n RtIconButtonComponent,\n RtSelectComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[style.display]': \"isVisible() ? null : 'none'\",\n },\n})\nexport class RtPaginationComponent {\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly perPageSelectOptions: Signal<ReadonlyArray<IRtSelect.Option<number>>> = computed(\n (): ReadonlyArray<IRtSelect.Option<number>> =>\n this.perPageOptions().map((n: number): IRtSelect.Option<number> => ({ label: String(n), value: n }))\n );\n\n /**\n * Диапазон записей и номер страницы — с подстановками, поэтому карте подписей\n * они не годятся: числа приходят из модели страницы.\n */\n protected readonly rangeLabel: Signal<string> = rtKitLabel(\n 'uiRangeOf',\n computed((): TRtKitLabelParams => ({ from: this.rangeFrom(), to: this.rangeTo(), total: this.pageModel().totalCount }))\n );\n\n protected readonly pageLabel: Signal<string> = rtKitLabel(\n 'uiPageOf',\n computed((): TRtKitLabelParams => ({ page: this.pageModel().pageNumber, last: this.lastPage() }))\n );\n\n protected readonly isFirst: Signal<boolean> = computed((): boolean => this.pageModel().pageNumber <= 1);\n\n protected readonly isLast: Signal<boolean> = computed((): boolean => this.pageModel().pageNumber >= this.lastPage());\n\n public readonly pageModel: InputSignal<IPageModel> = input.required<IPageModel>();\n\n public readonly perPageOptions: InputSignal<ReadonlyArray<number>> = input<ReadonlyArray<number>>([20, 50, 100]);\n\n /** Значок стрелки «назад»: вид первого кита рисует стрелку, а не шеврон. */\n public readonly prevIcon: InputSignal<IRtIcon.Name> = input<IRtIcon.Name>('chevron-left');\n\n /** Значок стрелки «вперёд». */\n public readonly nextIcon: InputSignal<IRtIcon.Name> = input<IRtIcon.Name>('chevron-right');\n\n /** Счёт номеров: список под материальным набором просит семь мест первого кита. */\n public readonly numbering: InputSignal<IRtPagination.Numbering> = input<IRtPagination.Numbering>('neighbours');\n\n public readonly loading: InputSignalWithTransform<boolean, unknown> = input(false, {\n transform: booleanAttribute,\n });\n\n public readonly pageChange: OutputEmitterRef<number> = output<number>();\n\n public readonly perPageChange: OutputEmitterRef<number> = output<number>();\n\n /**\n * Пагинацию показываем, только когда есть что листать/менять: всё не влезает\n * на одну страницу даже при минимальной опции размера (`total > наименьший\n * perPageOption`). Иначе и навигация, и селектор размера бесполезны — прячем\n * весь бар (включая пустой и пустой-по-фильтру список).\n */\n public readonly isVisible: Signal<boolean> = computed((): boolean => {\n const options: ReadonlyArray<number> = this.perPageOptions();\n const smallest: number = options.length > 0 ? Math.min(...options) : 0;\n return this.pageModel().totalCount > smallest;\n });\n\n /** Сколько всего страниц при нынешнем размере страницы. */\n public readonly lastPage: Signal<number> = computed((): number => lastPageOf(this.pageModel()));\n\n /**\n * Границы диапазона записей открытой страницы. Собираются числами, а сама\n * подпись — в шаблоне: только там подстановка проходит через словарь и\n * переживает смену языка.\n */\n public readonly rangeFrom: Signal<number> = computed((): number => rangeFromOf(this.pageModel()));\n\n public readonly rangeTo: Signal<number> = computed((): number => rangeToOf(this.pageModel()));\n\n /** Полоса номеров страниц с разрывами «…». По соседям пуста при единственной странице. */\n public readonly pageItems: Signal<ReadonlyArray<IRtPagination.PageItem>> = computed((): ReadonlyArray<IRtPagination.PageItem> =>\n this.numbering() === 'seven' ? pageSlotsOf(this.pageModel()) : pageItemsOf(this.pageModel(), NEIGHBOURS)\n );\n\n public goTo(pageNumber: number): void {\n if (this.loading() || pageNumber === this.pageModel().pageNumber || pageNumber < 1 || pageNumber > this.lastPage()) {\n return;\n }\n this.pageChange.emit(pageNumber);\n }\n\n protected onPerPage(value: number | null): void {\n if (value === null || this.loading() || value === this.pageModel().pageSize) {\n return;\n }\n this.perPageChange.emit(value);\n }\n}\n","<ng-container rtBlock=\"rt-pagination\">\n <!--\n Ветки «ничего не найдено» здесь нет намеренно: пустой набор прячет весь\n бар целиком (`isVisible`), так что до этой строки дело не доходит. Пока\n такая ветка тут висела, она читалась как рабочее состояние, а показать\n её было нельзя ни при каких входах.\n -->\n <span rtElem=\"range\" qa-dataid=\"pagination-range\">\n {{ rangeLabel() }}\n </span>\n\n @if (pageItems().length) {\n <nav rtElem=\"compact\" qa-dataid=\"pagination-compact\" [attr.aria-label]=\"t().uiPagination\">\n <rt-icon-button\n qa-dataid=\"pagination-compact-prev\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"prevIcon()\"\n [ariaLabel]=\"t().uiPrevPage\"\n [tooltip]=\"t().uiBack\"\n [disabled]=\"isFirst() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber - 1)\" />\n\n <span rtElem=\"compact-label\" qa-dataid=\"pagination-compact-label\">\n {{ pageLabel() }}\n </span>\n\n <rt-icon-button\n qa-dataid=\"pagination-compact-next\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"nextIcon()\"\n [ariaLabel]=\"t().uiNextPage\"\n [tooltip]=\"t().uiForward\"\n [disabled]=\"isLast() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber + 1)\" />\n </nav>\n\n <nav rtElem=\"nav\" qa-dataid=\"pagination-nav\" [attr.aria-label]=\"t().uiPagination\">\n <rt-icon-button\n qa-dataid=\"pagination-prev\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"prevIcon()\"\n [ariaLabel]=\"t().uiPrevPage\"\n [tooltip]=\"t().uiBack\"\n [disabled]=\"isFirst() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber - 1)\" />\n\n @for (item of pageItems(); track $index) {\n @if (item === 'gap') {\n <span rtElem=\"gap\" aria-hidden=\"true\">…</span>\n } @else {\n <button\n rtElem=\"page\"\n qa-dataid=\"pagination-page\"\n type=\"button\"\n [attr.data-page]=\"item\"\n [rtMod]=\"{ current: item === pageModel().pageNumber }\"\n [attr.aria-current]=\"item === pageModel().pageNumber ? 'page' : null\"\n [disabled]=\"loading()\"\n (click)=\"goTo(item)\">\n {{ item }}\n </button>\n }\n }\n\n <rt-icon-button\n qa-dataid=\"pagination-next\"\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"nextIcon()\"\n [ariaLabel]=\"t().uiNextPage\"\n [tooltip]=\"t().uiForward\"\n [disabled]=\"isLast() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber + 1)\" />\n </nav>\n }\n\n <div rtElem=\"per-page\">\n <span rtElem=\"per-page-label\">{{ t().uiPerPage }}</span>\n <rt-select\n qa-dataid=\"pagination-per-page\"\n [ariaLabel]=\"t().uiItemsPerPage\"\n [options]=\"perPageSelectOptions()\"\n [clearable]=\"false\"\n [ngModel]=\"pageModel().pageSize\"\n [ngModelOptions]=\"{ standalone: true }\"\n (selectionChange)=\"onPerPage($event)\" />\n </div>\n</ng-container>\n"],"mappings":"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"}
@@ -1,170 +1,213 @@
1
- import { CdkTrapFocus } from '@angular/cdk/a11y';
2
- import { NgTemplateOutlet } from '@angular/common';
3
- import * as i0 from '@angular/core';
4
- import { inject, ElementRef, viewChild, signal, computed, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
5
- import { BlockDirective, ElemDirective } from '@rt-tools/core';
6
- import { RT_KIT_LABELS } from '@rt-tools/ui-kit-v2/core';
7
- import { RT_DIALOG_DATA, RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';
8
- import { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';
9
-
10
- /**
11
- * Чистая логика просмотрщика: переходы между кадрами и то, какие из них стоит
12
- * держать загруженными.
13
- */
14
- /** Листание по кругу: с последнего кадра стрелка вправо ведёт на первый */
1
+ import { CdkTrapFocus } from "@angular/cdk/a11y";
2
+ import { NgTemplateOutlet } from "@angular/common";
3
+ import * as i0 from "@angular/core";
4
+ import { ChangeDetectionStrategy, Component, ElementRef, ViewEncapsulation, afterNextRender, computed, inject, signal, viewChild } from "@angular/core";
5
+ import { BlockDirective, ElemDirective } from "@rt-tools/core";
6
+ import { RT_KIT_LABELS } from "@rt-tools/ui-kit-v2/core";
7
+ import { RT_DIALOG_DATA, RtDialogRef } from "@rt-tools/ui-kit-v2/dialog";
8
+ import { RtIconButtonComponent } from "@rt-tools/ui-kit-v2/icon-button";
15
9
  function nextPhotoIndex(current, total, step) {
16
- if (total <= 0) {
17
- return 0;
18
- }
19
- return (((current + step) % total) + total) % total;
10
+ if (total <= 0) return 0;
11
+ return ((current + step) % total + total) % total;
20
12
  }
21
- /** Индекс из адреса: в URL кадры нумеруются с единицы, внутри — с нуля */
22
13
  function indexFromParam(param, total) {
23
- const parsed = Number(param);
24
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > total) {
25
- return -1;
26
- }
27
- return parsed - 1;
14
+ const parsed = Number(param);
15
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > total) return -1;
16
+ return parsed - 1;
28
17
  }
29
18
  function paramFromIndex(index) {
30
- return String(index + 1);
19
+ return String(index + 1);
31
20
  }
32
- /**
33
- * Индекс кадра по позиции прокрутки. Нужен, потому что листать можно свайпом:
34
- * без этого счётчик показывал бы один кадр, а на экране был бы другой.
35
- */
36
21
  function indexFromScroll(scrollLeft, slideWidth, total) {
37
- if (slideWidth <= 0 || total <= 0) {
38
- return 0;
39
- }
40
- const index = Math.round(scrollLeft / slideWidth);
41
- return Math.min(Math.max(index, 0), total - 1);
22
+ if (slideWidth <= 0 || total <= 0) return 0;
23
+ const index = Math.round(scrollLeft / slideWidth);
24
+ return Math.min(Math.max(index, 0), total - 1);
42
25
  }
43
- /**
44
- * Кадры, которые держим в разметке: текущий и соседние. Полноразмерная
45
- * фотография весит сотни килобайт, и загружать все пятьдесят разом — значит
46
- * выесть трафик пользователя ради кадров, до которых он не долистает.
47
- */
48
26
  function shouldRenderPhoto(index, current, total) {
49
- if (total <= 0) {
50
- return false;
51
- }
52
- const distance = Math.min(Math.abs(index - current), total - Math.abs(index - current));
53
- return distance <= 1;
54
- }
55
-
56
- const BEM_BLOCK = 'rt-photo-viewer';
57
- /**
58
- * Полноэкранный просмотрщик фотографий поверх `RtDialogService`.
59
- *
60
- * Листание — нативный `scroll-snap`: свайп, инерцию и прилипание даёт сам
61
- * браузер, и на телефоне это ощущается правильнее любой реализации на
62
- * обработчиках. Готовую библиотеку не берём — весь кит здесь собственный, а
63
- * токены, BEM и `qa-dataid` ложатся естественно.
64
- *
65
- * Руками сделаны: стрелки, клавиши `←` `→`, счётчик и предзагрузка соседних
66
- * кадров. `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог.
67
- */
68
- class RtPhotoViewerComponent {
69
- #data;
70
- #dialogRef;
71
- #host;
72
- constructor() {
73
- this.#data = inject(RT_DIALOG_DATA);
74
- this.#dialogRef = inject((RtDialogRef));
75
- this.#host = inject((ElementRef));
76
- this.t = inject(RT_KIT_LABELS);
77
- this.track = viewChild('track', /* @ts-ignore */
78
- ...(ngDevMode ? [{ debugName: "track" }] : /* istanbul ignore next */ []));
79
- this.photos = this.#data.photos;
80
- /** Панель правки под дорожкой; там, где кадр только смотрят, её нет */
81
- this.panel = this.#data.panel ?? null;
82
- this.currentIndex = signal(this.#data.startIndex, /* @ts-ignore */
83
- ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
84
- this.counter = computed(() => `${this.currentIndex() + 1} / ${this.photos.length}`, /* @ts-ignore */
85
- ...(ngDevMode ? [{ debugName: "counter" }] : /* istanbul ignore next */ []));
86
- /** Соседние кадры готовятся заранее, дальние в разметку не попадают */
87
- this.rendered = computed(() => this.photos.map((_photo, index) => shouldRenderPhoto(index, this.currentIndex(), this.photos.length)), /* @ts-ignore */
88
- ...(ngDevMode ? [{ debugName: "rendered" }] : /* istanbul ignore next */ []));
89
- // Открытие может быть не с первого кадра: без явной прокрутки счётчик
90
- // показывал бы второй кадр, а на экране остался бы первый
91
- afterNextRender(() => {
92
- this.#host.nativeElement.focus();
93
- this.#scrollToCurrent();
94
- });
95
- }
96
- /** Наружу отдаётся кадр, на котором закрыли: по нему вызывающая сторона чистит адрес */
97
- close() {
98
- this.#dialogRef.close(this.currentIndex());
99
- }
100
- move(step) {
101
- this.select(nextPhotoIndex(this.currentIndex(), this.photos.length, step));
102
- }
103
- select(index) {
104
- this.currentIndex.set(index);
105
- this.#scrollToCurrent();
106
- }
107
- /** Свайп листает мимо стрелок — счётчик догоняет позицию дорожки */
108
- onScroll() {
109
- const track = this.track()?.nativeElement;
110
- if (track) {
111
- this.currentIndex.set(indexFromScroll(track.scrollLeft, track.clientWidth, this.photos.length));
112
- }
113
- }
114
- /**
115
- * Прокрутка ленты вслед за выбором. Позиция считается от ширины дорожки, а
116
- * не от `scrollWidth`: кадры за пределами окна ещё не отрисованы, и общая
117
- * ширина до их появления меньше реальной.
118
- *
119
- * Переход мгновенный: `behavior: 'smooth'` внутри оверлея молча не двигает
120
- * дорожку — счётчик менялся, а кадр оставался прежним. Свайп при этом
121
- * по-прежнему анимирует сам браузер через scroll-snap.
122
- */
123
- #scrollToCurrent() {
124
- const track = this.track()?.nativeElement;
125
- if (!track) {
126
- return;
127
- }
128
- track.scrollTo({ left: track.clientWidth * this.currentIndex(), behavior: 'instant' });
129
- }
130
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtPhotoViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
131
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtPhotoViewerComponent, isStandalone: true, selector: "rt-photo-viewer", host: { attributes: { "role": "dialog", "aria-modal": "true", "tabindex": "-1" }, listeners: { "keydown.escape": "close()", "keydown.arrowLeft": "move(-1)", "keydown.arrowRight": "move(1)" }, classAttribute: "rt-photo-viewer" }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"], dependencies: [{ kind: "directive", type:
132
- // angular
133
- CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
134
- // rt-tools
135
- BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "component", type:
136
- // components
137
- RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
27
+ if (total <= 0) return false;
28
+ return Math.min(Math.abs(index - current), total - Math.abs(index - current)) <= 1;
138
29
  }
139
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtPhotoViewerComponent, decorators: [{
140
- type: Component,
141
- args: [{ selector: 'rt-photo-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
142
- // angular
143
- CdkTrapFocus,
144
- NgTemplateOutlet,
145
- // rt-tools
146
- BlockDirective,
147
- ElemDirective,
148
- // components
149
- RtIconButtonComponent,
150
- // transloco
151
- ], host: {
152
- class: BEM_BLOCK,
153
- role: 'dialog',
154
- 'aria-modal': 'true',
155
- // Клавиши слушает сам просмотрщик, поэтому фокус переводится на него
156
- // сразу после открытия — иначе стрелки и Esc не работали бы, пока
157
- // пользователь куда-нибудь не кликнет
158
- tabindex: '-1',
159
- '(keydown.escape)': 'close()',
160
- '(keydown.arrowLeft)': 'move(-1)',
161
- '(keydown.arrowRight)': 'move(1)',
162
- }, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"] }]
163
- }], ctorParameters: () => [], propDecorators: { track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
164
-
165
- /**
166
- * Generated bundle index. Do not edit.
167
- */
168
-
30
+ const BEM_BLOCK = "rt-photo-viewer";
31
+ var RtPhotoViewerComponent = class RtPhotoViewerComponent {
32
+ #data;
33
+ #dialogRef;
34
+ #host;
35
+ constructor() {
36
+ this.#data = inject(RT_DIALOG_DATA);
37
+ this.#dialogRef = inject(RtDialogRef);
38
+ this.#host = inject(ElementRef);
39
+ this.t = inject(RT_KIT_LABELS);
40
+ this.track = viewChild("track", ...ngDevMode ? [{ debugName: "track" }] : /* istanbul ignore next */ []);
41
+ this.photos = this.#data.photos;
42
+ this.panel = this.#data.panel ?? null;
43
+ this.currentIndex = signal(this.#data.startIndex, ...ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []);
44
+ this.counter = computed(() => `${this.currentIndex() + 1} / ${this.photos.length}`, ...ngDevMode ? [{ debugName: "counter" }] : /* istanbul ignore next */ []);
45
+ this.rendered = computed(() => this.photos.map((_photo, index) => shouldRenderPhoto(index, this.currentIndex(), this.photos.length)), ...ngDevMode ? [{ debugName: "rendered" }] : /* istanbul ignore next */ []);
46
+ afterNextRender(() => {
47
+ this.#host.nativeElement.focus();
48
+ this.#scrollToCurrent();
49
+ });
50
+ }
51
+ close() {
52
+ this.#dialogRef.close(this.currentIndex());
53
+ }
54
+ move(step) {
55
+ this.select(nextPhotoIndex(this.currentIndex(), this.photos.length, step));
56
+ }
57
+ select(index) {
58
+ this.currentIndex.set(index);
59
+ this.#scrollToCurrent();
60
+ }
61
+ onScroll() {
62
+ const track = this.track()?.nativeElement;
63
+ if (track) this.currentIndex.set(indexFromScroll(track.scrollLeft, track.clientWidth, this.photos.length));
64
+ }
65
+ #scrollToCurrent() {
66
+ const track = this.track()?.nativeElement;
67
+ if (!track) return;
68
+ track.scrollTo({
69
+ left: track.clientWidth * this.currentIndex(),
70
+ behavior: "instant"
71
+ });
72
+ }
73
+ static {
74
+ this.ɵfac = i0.ɵɵngDeclareFactory({
75
+ minVersion: "12.0.0",
76
+ version: "22.2.2",
77
+ ngImport: i0,
78
+ type: RtPhotoViewerComponent,
79
+ deps: [],
80
+ target: i0.ɵɵFactoryTarget.Component
81
+ });
82
+ }
83
+ static {
84
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
85
+ minVersion: "17.0.0",
86
+ version: "22.2.2",
87
+ type: RtPhotoViewerComponent,
88
+ isStandalone: true,
89
+ selector: "rt-photo-viewer",
90
+ host: {
91
+ attributes: {
92
+ "role": "dialog",
93
+ "aria-modal": "true",
94
+ "tabindex": "-1"
95
+ },
96
+ listeners: {
97
+ "keydown.escape": "close()",
98
+ "keydown.arrowLeft": "move(-1)",
99
+ "keydown.arrowRight": "move(1)"
100
+ },
101
+ classAttribute: "rt-photo-viewer"
102
+ },
103
+ viewQueries: [{
104
+ propertyName: "track",
105
+ first: true,
106
+ predicate: ["track"],
107
+ descendants: true,
108
+ isSignal: true
109
+ }],
110
+ ngImport: i0,
111
+ template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому\n авто-захвата здесь нет: он перебивал фокус на просмотрщике, и клавиши\n переставали листать -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- Панель приходит снаружи: где-то кадр только смотрят, где-то им ещё и\n управляют, и решение принимается там же, где кадр виден крупно -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n",
112
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"],
113
+ dependencies: [
114
+ {
115
+ kind: "directive",
116
+ type: CdkTrapFocus,
117
+ selector: "[cdkTrapFocus]",
118
+ inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"],
119
+ exportAs: ["cdkTrapFocus"]
120
+ },
121
+ {
122
+ kind: "directive",
123
+ type: NgTemplateOutlet,
124
+ selector: "[ngTemplateOutlet]",
125
+ inputs: [
126
+ "ngTemplateOutletContext",
127
+ "ngTemplateOutlet",
128
+ "ngTemplateOutletInjector"
129
+ ]
130
+ },
131
+ {
132
+ kind: "directive",
133
+ type: BlockDirective,
134
+ selector: "[rtBlock]",
135
+ inputs: ["rtMod"]
136
+ },
137
+ {
138
+ kind: "directive",
139
+ type: ElemDirective,
140
+ selector: "[rtElem]",
141
+ inputs: ["rtMod"]
142
+ },
143
+ {
144
+ kind: "component",
145
+ type: RtIconButtonComponent,
146
+ selector: "rt-icon-button",
147
+ inputs: [
148
+ "icon",
149
+ "glyph",
150
+ "ariaLabel",
151
+ "variant",
152
+ "iconColor",
153
+ "iconFill",
154
+ "size",
155
+ "iconSize",
156
+ "type",
157
+ "tooltip",
158
+ "tooltipHideOnClick",
159
+ "tabIndex",
160
+ "loading",
161
+ "disabled",
162
+ "active",
163
+ "expanded",
164
+ "indicator"
165
+ ],
166
+ outputs: ["clicked"]
167
+ }
168
+ ],
169
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
170
+ encapsulation: i0.ViewEncapsulation.None
171
+ });
172
+ }
173
+ };
174
+ i0.ɵɵngDeclareClassMetadata({
175
+ minVersion: "12.0.0",
176
+ version: "22.2.2",
177
+ ngImport: i0,
178
+ type: RtPhotoViewerComponent,
179
+ decorators: [{
180
+ type: Component,
181
+ args: [{
182
+ selector: "rt-photo-viewer",
183
+ changeDetection: ChangeDetectionStrategy.OnPush,
184
+ encapsulation: ViewEncapsulation.None,
185
+ imports: [
186
+ CdkTrapFocus,
187
+ NgTemplateOutlet,
188
+ BlockDirective,
189
+ ElemDirective,
190
+ RtIconButtonComponent
191
+ ],
192
+ host: {
193
+ class: BEM_BLOCK,
194
+ role: "dialog",
195
+ "aria-modal": "true",
196
+ tabindex: "-1",
197
+ "(keydown.escape)": "close()",
198
+ "(keydown.arrowLeft)": "move(-1)",
199
+ "(keydown.arrowRight)": "move(1)"
200
+ },
201
+ template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому\n авто-захвата здесь нет: он перебивал фокус на просмотрщике, и клавиши\n переставали листать -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- Панель приходит снаружи: где-то кадр только смотрят, где-то им ещё и\n управляют, и решение принимается там же, где кадр виден крупно -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n",
202
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"]
203
+ }]
204
+ }],
205
+ ctorParameters: () => [],
206
+ propDecorators: { track: [{
207
+ type: i0.ViewChild,
208
+ args: ["track", { isSignal: true }]
209
+ }] }
210
+ });
169
211
  export { RtPhotoViewerComponent, indexFromParam, indexFromScroll, nextPhotoIndex, paramFromIndex, shouldRenderPhoto };
170
- //# sourceMappingURL=rt-tools-ui-kit-v2-photo-viewer.mjs.map
212
+
213
+ //# sourceMappingURL=rt-tools-ui-kit-v2-photo-viewer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-photo-viewer.mjs","sources":["../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.logic.ts","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.component.ts","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.component.html","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-tools-ui-kit-v2-photo-viewer.ts"],"sourcesContent":["/**\n * Чистая логика просмотрщика: переходы между кадрами и то, какие из них стоит\n * держать загруженными.\n */\n\n/** Листание по кругу: с последнего кадра стрелка вправо ведёт на первый */\nexport function nextPhotoIndex(current: number, total: number, step: number): number {\n if (total <= 0) {\n return 0;\n }\n return (((current + step) % total) + total) % total;\n}\n\n/** Индекс из адреса: в URL кадры нумеруются с единицы, внутри — с нуля */\nexport function indexFromParam(param: string | null, total: number): number {\n const parsed: number = Number(param);\n if (!Number.isInteger(parsed) || parsed < 1 || parsed > total) {\n return -1;\n }\n return parsed - 1;\n}\n\nexport function paramFromIndex(index: number): string {\n return String(index + 1);\n}\n\n/**\n * Индекс кадра по позиции прокрутки. Нужен, потому что листать можно свайпом:\n * без этого счётчик показывал бы один кадр, а на экране был бы другой.\n */\nexport function indexFromScroll(scrollLeft: number, slideWidth: number, total: number): number {\n if (slideWidth <= 0 || total <= 0) {\n return 0;\n }\n const index: number = Math.round(scrollLeft / slideWidth);\n return Math.min(Math.max(index, 0), total - 1);\n}\n\n/**\n * Кадры, которые держим в разметке: текущий и соседние. Полноразмерная\n * фотография весит сотни килобайт, и загружать все пятьдесят разом — значит\n * выесть трафик пользователя ради кадров, до которых он не долистает.\n */\nexport function shouldRenderPhoto(index: number, current: number, total: number): boolean {\n if (total <= 0) {\n return false;\n }\n const distance: number = Math.min(Math.abs(index - current), total - Math.abs(index - current));\n return distance <= 1;\n}\n","import { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n inject,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n Signal,\n TemplateRef,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';\nimport { RT_DIALOG_DATA } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { indexFromScroll, nextPhotoIndex, shouldRenderPhoto } from './rt-photo-viewer.logic';\nimport { IRtPhotoViewer } from './rt-photo-viewer.model';\n\nconst BEM_BLOCK: string = 'rt-photo-viewer';\n\n/**\n * Полноэкранный просмотрщик фотографий поверх `RtDialogService`.\n *\n * Листание — нативный `scroll-snap`: свайп, инерцию и прилипание даёт сам\n * браузер, и на телефоне это ощущается правильнее любой реализации на\n * обработчиках. Готовую библиотеку не берём — весь кит здесь собственный, а\n * токены, BEM и `qa-dataid` ложатся естественно.\n *\n * Руками сделаны: стрелки, клавиши `←` `→`, счётчик и предзагрузка соседних\n * кадров. `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог.\n */\n@Component({\n selector: 'rt-photo-viewer',\n templateUrl: './rt-photo-viewer.component.html',\n styleUrl: './rt-photo-viewer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n CdkTrapFocus,\n NgTemplateOutlet,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtIconButtonComponent,\n\n // transloco\n ],\n host: {\n class: BEM_BLOCK,\n role: 'dialog',\n 'aria-modal': 'true',\n // Клавиши слушает сам просмотрщик, поэтому фокус переводится на него\n // сразу после открытия — иначе стрелки и Esc не работали бы, пока\n // пользователь куда-нибудь не кликнет\n tabindex: '-1',\n '(keydown.escape)': 'close()',\n '(keydown.arrowLeft)': 'move(-1)',\n '(keydown.arrowRight)': 'move(1)',\n },\n})\nexport class RtPhotoViewerComponent {\n readonly #data: IRtPhotoViewer.Data = inject(RT_DIALOG_DATA) as IRtPhotoViewer.Data;\n readonly #dialogRef: RtDialogRef<number> = inject(RtDialogRef<number>);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef<HTMLElement>);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly track: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('track');\n\n protected readonly photos: readonly IRtPhotoViewer.Photo[] = this.#data.photos;\n\n /** Панель правки под дорожкой; там, где кадр только смотрят, её нет */\n protected readonly panel: TemplateRef<IRtPhotoViewer.PanelContext> | null = this.#data.panel ?? null;\n\n protected readonly currentIndex: WritableSignal<number> = signal<number>(this.#data.startIndex);\n\n protected readonly counter: Signal<string> = computed((): string => `${this.currentIndex() + 1} / ${this.photos.length}`);\n\n /** Соседние кадры готовятся заранее, дальние в разметку не попадают */\n protected readonly rendered: Signal<readonly boolean[]> = computed((): readonly boolean[] =>\n this.photos.map((_photo: IRtPhotoViewer.Photo, index: number): boolean =>\n shouldRenderPhoto(index, this.currentIndex(), this.photos.length)\n )\n );\n\n constructor() {\n // Открытие может быть не с первого кадра: без явной прокрутки счётчик\n // показывал бы второй кадр, а на экране остался бы первый\n afterNextRender((): void => {\n this.#host.nativeElement.focus();\n this.#scrollToCurrent();\n });\n }\n\n /** Наружу отдаётся кадр, на котором закрыли: по нему вызывающая сторона чистит адрес */\n public close(): void {\n this.#dialogRef.close(this.currentIndex());\n }\n\n protected move(step: number): void {\n this.select(nextPhotoIndex(this.currentIndex(), this.photos.length, step));\n }\n\n protected select(index: number): void {\n this.currentIndex.set(index);\n this.#scrollToCurrent();\n }\n\n /** Свайп листает мимо стрелок — счётчик догоняет позицию дорожки */\n protected onScroll(): void {\n const track: HTMLElement | undefined = this.track()?.nativeElement;\n if (track) {\n this.currentIndex.set(indexFromScroll(track.scrollLeft, track.clientWidth, this.photos.length));\n }\n }\n\n /**\n * Прокрутка ленты вслед за выбором. Позиция считается от ширины дорожки, а\n * не от `scrollWidth`: кадры за пределами окна ещё не отрисованы, и общая\n * ширина до их появления меньше реальной.\n *\n * Переход мгновенный: `behavior: 'smooth'` внутри оверлея молча не двигает\n * дорожку — счётчик менялся, а кадр оставался прежним. Свайп при этом\n * по-прежнему анимирует сам браузер через scroll-snap.\n */\n #scrollToCurrent(): void {\n const track: HTMLElement | undefined = this.track()?.nativeElement;\n if (!track) {\n return;\n }\n track.scrollTo({ left: track.clientWidth * this.currentIndex(), behavior: 'instant' });\n }\n}\n","<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому\n авто-захвата здесь нет: он перебивал фокус на просмотрщике, и клавиши\n переставали листать -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- Панель приходит снаружи: где-то кадр только смотрят, где-то им ещё и\n управляют, и решение принимается там же, где кадр виден крупно -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-photo-viewer.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.logic.ts","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.component.ts","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.component.html"],"sourcesContent":["/**\n * Чистая логика просмотрщика: переходы между кадрами и то, какие из них стоит\n * держать загруженными.\n */\n\n/** Листание по кругу: с последнего кадра стрелка вправо ведёт на первый */\nexport function nextPhotoIndex(current: number, total: number, step: number): number {\n if (total <= 0) {\n return 0;\n }\n return (((current + step) % total) + total) % total;\n}\n\n/** Индекс из адреса: в URL кадры нумеруются с единицы, внутри — с нуля */\nexport function indexFromParam(param: string | null, total: number): number {\n const parsed: number = Number(param);\n if (!Number.isInteger(parsed) || parsed < 1 || parsed > total) {\n return -1;\n }\n return parsed - 1;\n}\n\nexport function paramFromIndex(index: number): string {\n return String(index + 1);\n}\n\n/**\n * Индекс кадра по позиции прокрутки. Нужен, потому что листать можно свайпом:\n * без этого счётчик показывал бы один кадр, а на экране был бы другой.\n */\nexport function indexFromScroll(scrollLeft: number, slideWidth: number, total: number): number {\n if (slideWidth <= 0 || total <= 0) {\n return 0;\n }\n const index: number = Math.round(scrollLeft / slideWidth);\n return Math.min(Math.max(index, 0), total - 1);\n}\n\n/**\n * Кадры, которые держим в разметке: текущий и соседние. Полноразмерная\n * фотография весит сотни килобайт, и загружать все пятьдесят разом — значит\n * выесть трафик пользователя ради кадров, до которых он не долистает.\n */\nexport function shouldRenderPhoto(index: number, current: number, total: number): boolean {\n if (total <= 0) {\n return false;\n }\n const distance: number = Math.min(Math.abs(index - current), total - Math.abs(index - current));\n return distance <= 1;\n}\n","import { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n inject,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n Signal,\n TemplateRef,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';\nimport { RT_DIALOG_DATA } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { indexFromScroll, nextPhotoIndex, shouldRenderPhoto } from './rt-photo-viewer.logic';\nimport { IRtPhotoViewer } from './rt-photo-viewer.model';\n\nconst BEM_BLOCK: string = 'rt-photo-viewer';\n\n/**\n * Полноэкранный просмотрщик фотографий поверх `RtDialogService`.\n *\n * Листание — нативный `scroll-snap`: свайп, инерцию и прилипание даёт сам\n * браузер, и на телефоне это ощущается правильнее любой реализации на\n * обработчиках. Готовую библиотеку не берём — весь кит здесь собственный, а\n * токены, BEM и `qa-dataid` ложатся естественно.\n *\n * Руками сделаны: стрелки, клавиши `←` `→`, счётчик и предзагрузка соседних\n * кадров. `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог.\n */\n@Component({\n selector: 'rt-photo-viewer',\n templateUrl: './rt-photo-viewer.component.html',\n styleUrl: './rt-photo-viewer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n CdkTrapFocus,\n NgTemplateOutlet,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtIconButtonComponent,\n\n // transloco\n ],\n host: {\n class: BEM_BLOCK,\n role: 'dialog',\n 'aria-modal': 'true',\n // Клавиши слушает сам просмотрщик, поэтому фокус переводится на него\n // сразу после открытия — иначе стрелки и Esc не работали бы, пока\n // пользователь куда-нибудь не кликнет\n tabindex: '-1',\n '(keydown.escape)': 'close()',\n '(keydown.arrowLeft)': 'move(-1)',\n '(keydown.arrowRight)': 'move(1)',\n },\n})\nexport class RtPhotoViewerComponent {\n readonly #data: IRtPhotoViewer.Data = inject(RT_DIALOG_DATA) as IRtPhotoViewer.Data;\n readonly #dialogRef: RtDialogRef<number> = inject(RtDialogRef<number>);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef<HTMLElement>);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly track: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('track');\n\n protected readonly photos: readonly IRtPhotoViewer.Photo[] = this.#data.photos;\n\n /** Панель правки под дорожкой; там, где кадр только смотрят, её нет */\n protected readonly panel: TemplateRef<IRtPhotoViewer.PanelContext> | null = this.#data.panel ?? null;\n\n protected readonly currentIndex: WritableSignal<number> = signal<number>(this.#data.startIndex);\n\n protected readonly counter: Signal<string> = computed((): string => `${this.currentIndex() + 1} / ${this.photos.length}`);\n\n /** Соседние кадры готовятся заранее, дальние в разметку не попадают */\n protected readonly rendered: Signal<readonly boolean[]> = computed((): readonly boolean[] =>\n this.photos.map((_photo: IRtPhotoViewer.Photo, index: number): boolean =>\n shouldRenderPhoto(index, this.currentIndex(), this.photos.length)\n )\n );\n\n constructor() {\n // Открытие может быть не с первого кадра: без явной прокрутки счётчик\n // показывал бы второй кадр, а на экране остался бы первый\n afterNextRender((): void => {\n this.#host.nativeElement.focus();\n this.#scrollToCurrent();\n });\n }\n\n /** Наружу отдаётся кадр, на котором закрыли: по нему вызывающая сторона чистит адрес */\n public close(): void {\n this.#dialogRef.close(this.currentIndex());\n }\n\n protected move(step: number): void {\n this.select(nextPhotoIndex(this.currentIndex(), this.photos.length, step));\n }\n\n protected select(index: number): void {\n this.currentIndex.set(index);\n this.#scrollToCurrent();\n }\n\n /** Свайп листает мимо стрелок — счётчик догоняет позицию дорожки */\n protected onScroll(): void {\n const track: HTMLElement | undefined = this.track()?.nativeElement;\n if (track) {\n this.currentIndex.set(indexFromScroll(track.scrollLeft, track.clientWidth, this.photos.length));\n }\n }\n\n /**\n * Прокрутка ленты вслед за выбором. Позиция считается от ширины дорожки, а\n * не от `scrollWidth`: кадры за пределами окна ещё не отрисованы, и общая\n * ширина до их появления меньше реальной.\n *\n * Переход мгновенный: `behavior: 'smooth'` внутри оверлея молча не двигает\n * дорожку — счётчик менялся, а кадр оставался прежним. Свайп при этом\n * по-прежнему анимирует сам браузер через scroll-snap.\n */\n #scrollToCurrent(): void {\n const track: HTMLElement | undefined = this.track()?.nativeElement;\n if (!track) {\n return;\n }\n track.scrollTo({ left: track.clientWidth * this.currentIndex(), behavior: 'instant' });\n }\n}\n","<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому\n авто-захвата здесь нет: он перебивал фокус на просмотрщике, и клавиши\n переставали листать -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- Панель приходит снаружи: где-то кадр только смотрят, где-то им ещё и\n управляют, и решение принимается там же, где кадр виден крупно -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n"],"mappings":"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"}