@rt-tools/ui-kit-v2 0.33.0 → 0.34.1

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 (217) hide show
  1. package/README.md +4 -3
  2. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs +1 -0
  3. package/fesm2022/rt-tools-ui-kit-v2-ai-run-status.mjs.map +1 -1
  4. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +92 -5
  5. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  6. package/fesm2022/rt-tools-ui-kit-v2-badge.mjs +191 -0
  7. package/fesm2022/rt-tools-ui-kit-v2-badge.mjs.map +1 -0
  8. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs +3 -2
  9. package/fesm2022/rt-tools-ui-kit-v2-breadcrumbs.mjs.map +1 -1
  10. package/fesm2022/rt-tools-ui-kit-v2-button.mjs +6 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-button.mjs.map +1 -1
  12. package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs +35 -4
  13. package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs.map +1 -1
  14. package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs +13 -4
  15. package/fesm2022/rt-tools-ui-kit-v2-color-palette.mjs.map +1 -1
  16. package/fesm2022/rt-tools-ui-kit-v2-container.mjs +245 -60
  17. package/fesm2022/rt-tools-ui-kit-v2-container.mjs.map +1 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-copy-value.mjs +30 -5
  19. package/fesm2022/rt-tools-ui-kit-v2-copy-value.mjs.map +1 -1
  20. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +113 -2
  21. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  22. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +14 -3
  23. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
  24. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +17 -3
  25. package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +6 -3
  27. package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
  28. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +2 -2
  29. package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
  30. package/fesm2022/rt-tools-ui-kit-v2-divider.mjs +112 -0
  31. package/fesm2022/rt-tools-ui-kit-v2-divider.mjs.map +1 -0
  32. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +1 -0
  33. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-drawer.mjs +854 -0
  35. package/fesm2022/rt-tools-ui-kit-v2-drawer.mjs.map +1 -0
  36. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +16 -5
  37. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -1
  38. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +580 -65
  39. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  40. package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs +203 -15
  41. package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs.map +1 -1
  42. package/fesm2022/rt-tools-ui-kit-v2-field.mjs +40 -8
  43. package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -1
  44. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs +1 -0
  45. package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs.map +1 -1
  46. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +7 -3
  47. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -1
  48. package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs +53 -2
  49. package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs.map +1 -1
  50. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs +14 -4
  51. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree-selector.mjs.map +1 -1
  52. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +6 -3
  53. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -1
  54. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs +16 -5
  55. package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs.map +1 -1
  56. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs +1 -0
  57. package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs.map +1 -1
  58. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs +14 -4
  59. package/fesm2022/rt-tools-ui-kit-v2-inline-edit.mjs.map +1 -1
  60. package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +3 -2
  61. package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -1
  62. package/fesm2022/rt-tools-ui-kit-v2-input.mjs +207 -7
  63. package/fesm2022/rt-tools-ui-kit-v2-input.mjs.map +1 -1
  64. package/fesm2022/rt-tools-ui-kit-v2-menu.mjs +1 -0
  65. package/fesm2022/rt-tools-ui-kit-v2-menu.mjs.map +1 -1
  66. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +3 -2
  67. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  68. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +2 -0
  69. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
  70. package/fesm2022/rt-tools-ui-kit-v2-pagination.mjs +3 -2
  71. package/fesm2022/rt-tools-ui-kit-v2-pagination.mjs.map +1 -1
  72. package/fesm2022/rt-tools-ui-kit-v2-profile-menu.mjs +3 -2
  73. package/fesm2022/rt-tools-ui-kit-v2-profile-menu.mjs.map +1 -1
  74. package/fesm2022/rt-tools-ui-kit-v2-progress-bar.mjs +171 -0
  75. package/fesm2022/rt-tools-ui-kit-v2-progress-bar.mjs.map +1 -0
  76. package/fesm2022/rt-tools-ui-kit-v2-radio-button.mjs +176 -6
  77. package/fesm2022/rt-tools-ui-kit-v2-radio-button.mjs.map +1 -1
  78. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +2 -1
  79. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  80. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs +1 -0
  81. package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs.map +1 -1
  82. package/fesm2022/rt-tools-ui-kit-v2-select.mjs +142 -23
  83. package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
  84. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +22 -9
  85. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  86. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs +3 -2
  87. package/fesm2022/rt-tools-ui-kit-v2-speed-menu.mjs.map +1 -1
  88. package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs +105 -8
  89. package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs.map +1 -1
  90. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +13 -2
  91. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  92. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +654 -43
  93. package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -1
  94. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +1 -0
  95. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  96. package/fesm2022/rt-tools-ui-kit-v2-textarea.mjs +45 -7
  97. package/fesm2022/rt-tools-ui-kit-v2-textarea.mjs.map +1 -1
  98. package/fesm2022/rt-tools-ui-kit-v2-theme-toggle.mjs +2 -2
  99. package/fesm2022/rt-tools-ui-kit-v2-theme-toggle.mjs.map +1 -1
  100. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +11 -2
  101. package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -1
  102. package/fesm2022/rt-tools-ui-kit-v2-toast.mjs +152 -11
  103. package/fesm2022/rt-tools-ui-kit-v2-toast.mjs.map +1 -1
  104. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +199 -17
  105. package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -1
  106. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs +35 -5
  107. package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs.map +1 -1
  108. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +14 -4
  109. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -1
  110. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +6 -3
  111. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -1
  112. package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +21 -6
  113. package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -1
  114. package/fesm2022/rt-tools-ui-kit-v2.mjs +4 -0
  115. package/package.json +17 -1
  116. package/src/assets/icons/volume-off.svg +2 -1
  117. package/src/lib/components/autocomplete/CONTEXT.md +25 -14
  118. package/src/lib/components/autocomplete/rt-autocomplete.component.scss +19 -0
  119. package/src/lib/components/badge/CONTEXT.md +33 -0
  120. package/src/lib/components/badge/rt-badge.directive.scss +104 -0
  121. package/src/lib/components/breadcrumbs/rt-breadcrumbs.component.scss +2 -2
  122. package/src/lib/components/button/CONTEXT.md +5 -0
  123. package/src/lib/components/button/rt-button.directive.scss +3 -1
  124. package/src/lib/components/checkbox/CONTEXT.md +15 -8
  125. package/src/lib/components/color-palette/rt-color-palette.component.scss +2 -2
  126. package/src/lib/components/container/CONTEXT.md +23 -6
  127. package/src/lib/components/container/rt-container.component.scss +90 -1
  128. package/src/lib/components/copy-value/CONTEXT.md +6 -5
  129. package/src/lib/components/copy-value/rt-copy-value.component.scss +16 -0
  130. package/src/lib/components/date-picker/rt-date-picker.component.scss +7 -0
  131. package/src/lib/components/date-range/rt-date-range.component.scss +7 -0
  132. package/src/lib/components/divider/CONTEXT.md +21 -0
  133. package/src/lib/components/divider/rt-divider.component.scss +34 -0
  134. package/src/lib/components/drawer/CONTEXT.md +73 -0
  135. package/src/lib/components/drawer/rt-drawer-container.component.scss +19 -0
  136. package/src/lib/components/drawer/rt-drawer.component.scss +168 -0
  137. package/src/lib/components/drawer/rt-resize-handle.component.scss +42 -0
  138. package/src/lib/components/dynamic-selector/CONTEXT.md +5 -0
  139. package/src/lib/components/expansion-panel/CONTEXT.md +32 -8
  140. package/src/lib/components/expansion-panel/rt-expansion-panel.component.scss +67 -2
  141. package/src/lib/components/field/CONTEXT.md +43 -0
  142. package/src/lib/components/field/rt-field-label-inside.scss +179 -0
  143. package/src/lib/components/field/rt-field.component.scss +51 -2
  144. package/src/lib/components/icon-button/CONTEXT.md +17 -15
  145. package/src/lib/components/icon-button/rt-icon-button.component.scss +3 -1
  146. package/src/lib/components/inline-edit/rt-inline-edit.component.scss +4 -4
  147. package/src/lib/components/input/CONTEXT.md +45 -11
  148. package/src/lib/components/input/rt-input-floating-label.scss +192 -0
  149. package/src/lib/components/input/rt-input-icons.scss +105 -0
  150. package/src/lib/components/input/rt-input.component.scss +91 -13
  151. package/src/lib/components/input-number/CONTEXT.md +7 -0
  152. package/src/lib/components/input-number/rt-input-number.component.scss +58 -1
  153. package/src/lib/components/menu/CONTEXT.md +1 -0
  154. package/src/lib/components/multiselect/CONTEXT.md +2 -1
  155. package/src/lib/components/multiselect/rt-multiselect.component.scss +7 -0
  156. package/src/lib/components/pagination/CONTEXT.md +3 -0
  157. package/src/lib/components/pagination/rt-pagination.component.scss +7 -0
  158. package/src/lib/components/progress-bar/CONTEXT.md +22 -0
  159. package/src/lib/components/progress-bar/rt-progress-bar.component.scss +77 -0
  160. package/src/lib/components/radio-button/CONTEXT.md +9 -0
  161. package/src/lib/components/radio-button/rt-radio-group.component.scss +13 -0
  162. package/src/lib/components/select/CONTEXT.md +28 -0
  163. package/src/lib/components/select/rt-select-clear.scss +88 -0
  164. package/src/lib/components/select/rt-select-floating-label.scss +155 -0
  165. package/src/lib/components/select/rt-select-material.scss +54 -0
  166. package/src/lib/components/select/rt-select.component.scss +54 -16
  167. package/src/lib/components/side-menu/CONTEXT.md +5 -0
  168. package/src/lib/components/speed-menu/rt-speed-menu.component.scss +5 -5
  169. package/src/lib/components/stepper/CONTEXT.md +13 -5
  170. package/src/lib/components/stepper/rt-stepper.component.scss +93 -0
  171. package/src/lib/components/tabs/CONTEXT.md +61 -9
  172. package/src/lib/components/tabs/rt-tabs.component.scss +201 -5
  173. package/src/lib/components/textarea/CONTEXT.md +12 -1
  174. package/src/lib/components/textarea/rt-textarea.component.scss +116 -1
  175. package/src/lib/components/toast/CONTEXT.md +23 -0
  176. package/src/lib/components/toast/rt-toast.component.scss +37 -0
  177. package/src/lib/components/toggle-button-group/CONTEXT.md +24 -4
  178. package/src/lib/components/toggle-button-group/rt-toggle-button-group-chips.scss +151 -0
  179. package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +35 -0
  180. package/src/lib/components/tooltip/CONTEXT.md +7 -1
  181. package/src/lib/components/tree/rt-tree.component.scss +2 -2
  182. package/src/styles/_color-scheme.scss +1 -1
  183. package/src/styles/_density.scss +48 -0
  184. package/src/styles/_index.scss +8 -0
  185. package/src/styles/_preset-material.scss +160 -70
  186. package/src/styles/_primitives.scss +1 -0
  187. package/src/styles/_semantic-navigation.scss +117 -0
  188. package/src/styles/_semantic.scss +29 -98
  189. package/src/styles/_theme-dark.scss +8 -0
  190. package/styles/tokens.css +2066 -1134
  191. package/types/rt-tools-ui-kit-v2-autocomplete.d.ts +34 -0
  192. package/types/rt-tools-ui-kit-v2-badge.d.ts +71 -0
  193. package/types/rt-tools-ui-kit-v2-checkbox.d.ts +17 -3
  194. package/types/rt-tools-ui-kit-v2-container.d.ts +65 -9
  195. package/types/rt-tools-ui-kit-v2-copy-value.d.ts +11 -1
  196. package/types/rt-tools-ui-kit-v2-core.d.ts +109 -4
  197. package/types/rt-tools-ui-kit-v2-divider.d.ts +29 -0
  198. package/types/rt-tools-ui-kit-v2-drawer.d.ts +257 -0
  199. package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +240 -25
  200. package/types/rt-tools-ui-kit-v2-expansion-panel.d.ts +87 -3
  201. package/types/rt-tools-ui-kit-v2-field.d.ts +18 -1
  202. package/types/rt-tools-ui-kit-v2-form-control.d.ts +42 -4
  203. package/types/rt-tools-ui-kit-v2-icon-button.d.ts +2 -1
  204. package/types/rt-tools-ui-kit-v2-input-number.d.ts +1 -0
  205. package/types/rt-tools-ui-kit-v2-input.d.ts +98 -4
  206. package/types/rt-tools-ui-kit-v2-progress-bar.d.ts +53 -0
  207. package/types/rt-tools-ui-kit-v2-radio-button.d.ts +80 -1
  208. package/types/rt-tools-ui-kit-v2-select.d.ts +65 -1
  209. package/types/rt-tools-ui-kit-v2-side-menu.d.ts +3 -0
  210. package/types/rt-tools-ui-kit-v2-stepper.d.ts +57 -4
  211. package/types/rt-tools-ui-kit-v2-tabs.d.ts +337 -8
  212. package/types/rt-tools-ui-kit-v2-textarea.d.ts +16 -0
  213. package/types/rt-tools-ui-kit-v2-toast.d.ts +38 -2
  214. package/types/rt-tools-ui-kit-v2-toggle-button-group.d.ts +83 -3
  215. package/types/rt-tools-ui-kit-v2-tooltip.d.ts +17 -2
  216. package/types/rt-tools-ui-kit-v2.d.ts +4 -0
  217. package/src/lib/components/dynamic-selector/stories/component/test-dynamic-selector-popup-frame.component.scss +0 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-badge.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/badge/rt-badge.directive.ts"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport {\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n Renderer2,\n Signal,\n} from '@angular/core';\n\nimport { IRtBadge } from './rt-badge.model';\n\nconst BEM_BLOCK: string = 'rt-badge';\n\n/** Метка узла пузырька: по ней клиент находит пузырёк из серверной разметки и не ставит второй. */\nconst BUBBLE_MARK: string = 'data-rt-badge';\n\n/**\n * Пузырёк-счётчик на углу любого элемента: «Отели 12» у подписи вкладки, непрочитанные у пункта\n * меню, отметка на дне календаря. Своей обёртки нет: директива ставится на сам элемент, и пузырёк\n * встаёт в него последним узлом.\n *\n * С `rtBadgeOverlap` (умолчание) пузырёк накрывает угол хоста, как у значка; без него стоит рядом,\n * за краем хоста, — так подпись не перекрывается. Пустое значение и `rtBadgeHidden` убирают его.\n *\n * Стили директивы подключены общим набором кита: своего представления у неё нет.\n */\n@Directive({\n selector: '[rtBadge]',\n host: {\n class: 'rt-badge-host',\n },\n})\nexport class RtBadgeDirective {\n readonly #host: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #renderer: Renderer2 = inject(Renderer2);\n readonly #doc: Document = inject(DOCUMENT);\n #bubble: HTMLElement | null = null;\n\n /** Текст пузырька: число как есть, пустая строка и `null` — пузырька нет. */\n protected readonly text: Signal<string> = computed((): string => {\n const value: IRtBadge.Value = this.rtBadge();\n return value === null ? '' : String(value);\n });\n\n protected readonly classes: Signal<readonly string[]> = computed((): readonly string[] => [\n BEM_BLOCK,\n `${BEM_BLOCK}--position--${this.rtBadgePosition()}`,\n `${BEM_BLOCK}--size--${this.rtBadgeSize()}`,\n `${BEM_BLOCK}--color--${this.rtBadgeColor()}`,\n ...(this.rtBadgeOverlap() ? [`${BEM_BLOCK}--overlap`] : []),\n ...(this.rtBadgeHidden() || this.text() === '' ? [`${BEM_BLOCK}--hidden`] : []),\n ]);\n\n public readonly rtBadge: InputSignalWithTransform<IRtBadge.Value, IRtBadge.ValueInput> = input<IRtBadge.Value, IRtBadge.ValueInput>(\n null,\n {\n transform: (value: IRtBadge.ValueInput): IRtBadge.Value => value ?? null,\n }\n );\n\n /** Пузырёк накрывает угол хоста; без этого стоит рядом, за его краем. */\n public readonly rtBadgeOverlap: InputSignalWithTransform<boolean, unknown> = input(true, { transform: booleanAttribute });\n\n public readonly rtBadgeHidden: InputSignalWithTransform<boolean, unknown> = input(false, { transform: booleanAttribute });\n\n public readonly rtBadgePosition: InputSignal<IRtBadge.Position> = input<IRtBadge.Position>('above-after');\n\n public readonly rtBadgeSize: InputSignal<IRtBadge.Size> = input<IRtBadge.Size>('md');\n\n public readonly rtBadgeColor: InputSignal<IRtBadge.Color> = input<IRtBadge.Color>('primary');\n\n constructor() {\n inject(DestroyRef).onDestroy((): void => this.#bubble?.remove());\n\n effect((): void => {\n // Пузырёк встаёт в хост при первом проходе, а не в конструкторе: в конструкторе детей\n // хоста ещё нет, и пузырёк оказался бы первым узлом — число читалось бы до подписи.\n const bubble: HTMLElement = (this.#bubble ??= this.#takeBubble());\n if (bubble.parentNode !== this.#host.nativeElement) {\n this.#renderer.appendChild(this.#host.nativeElement, bubble);\n }\n bubble.textContent = this.text();\n bubble.className = this.classes().join(' ');\n });\n }\n\n /**\n * Пузырёк хоста. После серверной отрисовки он уже стоит в хосте: гидратация оставляет узлы, а\n * директива на клиенте создаётся заново, и новый узел встал бы вторым пузырьком рядом.\n */\n #takeBubble(): HTMLElement {\n const rendered: Element | undefined = Array.from(this.#host.nativeElement.children).find((child: Element): boolean =>\n child.hasAttribute(BUBBLE_MARK)\n );\n if (rendered instanceof HTMLElement) {\n return rendered;\n }\n\n const bubble: HTMLElement = this.#doc.createElement('span');\n bubble.setAttribute('qa-dataid', 'badge');\n bubble.setAttribute(BUBBLE_MARK, '');\n return bubble;\n }\n}\n"],"mappings":";;;AAkBA,MAAM,YAAoB;AAG1B,MAAM,cAAsB;AAkB5B,IAAa,mBAAb,MAAa,iBAAgB;CAChB;CACA;CACA;CACT;CAmCA,cAAA;EAtCS,KAAA,QAAiC,OAAgC,UAAU;EAC3E,KAAA,YAAuB,OAAO,SAAS;EACvC,KAAA,OAAiB,OAAO,QAAQ;EACzC,KAAA,UAA8B;EAGX,KAAA,OAAuB,eAAsB;GAC5D,MAAM,QAAwB,KAAK,QAAO;GAC1C,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK;EAC7C,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAEiB,KAAA,UAAqC,eAAkC;GACtF;GACA,GAAG,UAAS,cAAe,KAAK,gBAAe;GAC/C,GAAG,UAAS,UAAW,KAAK,YAAW;GACvC,GAAG,UAAS,WAAY,KAAK,aAAY;GACzC,GAAI,KAAK,eAAc,IAAK,CAAC,GAAG,UAAS,UAAW,IAAI,CAAA;GACxD,GAAI,KAAK,cAAa,KAAM,KAAK,KAAI,MAAO,KAAK,CAAC,GAAG,UAAS,SAAU,IAAI,CAAA;EAC/E,GAAC,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;EAEc,KAAA,UAAyE,MACrF,MAAI;GAAA,GAAA,YAAA,EAAA,WAAA,UAAA,+BAAA,CAAA;GAEA,YAAY,UAA+C,SAAS;EAAI,CAAA;EAKhE,KAAA,iBAA6D,MAAM,MAAI;GAAA,GAAA,YAAA,EAAA,WAAA,iBAAA,+BAAA,CAAA;GAAI,WAAW;EAAgB,CAAA;EAEtG,KAAA,gBAA4D,MAAM,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,gBAAA,+BAAA,CAAA;GAAI,WAAW;EAAgB,CAAA;EAEtG,KAAA,kBAAkD,MAAyB,eAAc,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;EAEzF,KAAA,cAA0C,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAEpE,KAAA,eAA4C,MAAsB,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;EAGxF,OAAO,UAAU,CAAC,CAAC,gBAAsB,KAAK,SAAS,OAAM,CAAE;EAE/D,aAAkB;GAGd,MAAM,SAAuB,KAAK,YAAY,KAAK,YAAW;GAC9D,IAAI,OAAO,eAAe,KAAK,MAAM,eACjC,KAAK,UAAU,YAAY,KAAK,MAAM,eAAe,MAAM;GAE/D,OAAO,cAAc,KAAK,KAAI;GAC9B,OAAO,YAAY,KAAK,QAAO,CAAE,CAAC,KAAK,GAAG;EAC9C,CAAC;CACL;CAMA,cAAW;EACP,MAAM,WAAgC,MAAM,KAAK,KAAK,MAAM,cAAc,QAAQ,CAAC,CAAC,MAAM,UACtF,MAAM,aAAa,WAAW,CAAC;EAEnC,IAAI,oBAAoB,aACpB,OAAO;EAGX,MAAM,SAAsB,KAAK,KAAK,cAAc,MAAM;EAC1D,OAAO,aAAa,aAAa,OAAO;EACxC,OAAO,aAAa,aAAa,EAAE;EACnC,OAAO;CACX;CAtES;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAgB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAAhB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAgB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,gBAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,eAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,iBAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,aAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,cAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,MAAA,EAAA,gBAAA,gBAAA;GAAA,UAAA;EAAA,CAAA;CAAA;;AAAhB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAN5B,MAAA;EAAU,MAAA,CAAA;GACP,UAAU;GACV,MAAM,EACF,OAAO,gBACV;EACJ,CAAA"}
@@ -151,7 +151,7 @@ var RtBreadcrumbsComponent = class RtBreadcrumbsComponent {
151
151
  }],
152
152
  ngImport: i0,
153
153
  template: "<ng-container rtBlock=\"rt-breadcrumbs\">\n @if (rowShown()) {\n <div rtElem=\"row\">\n @if (backCrumb(); as backTo) {\n <a rtElem=\"back\" qa-dataid=\"breadcrumbs-back\" [routerLink]=\"backTo.link\" [attr.aria-label]=\"backLabel()\">\n <rt-icon name=\"arrow-left\" size=\"md\" />\n </a>\n }\n\n <ol\n #list\n rtElem=\"list\"\n qa-dataid=\"breadcrumbs-list\"\n [rtMod]=\"{ clipped: clipped(), fitted: fitted() }\"\n (scroll)=\"onListScroll()\">\n @for (crumb of fold().head; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (fold().hidden.length) {\n <li rtElem=\"item\" qa-dataid=\"breadcrumbs-fold\">\n <span rtElem=\"fold\">\n <rt-menu size=\"sm\" align=\"start\" [ariaLabel]=\"foldLabel()\">\n @for (crumb of fold().hidden; track $index) {\n <rt-menu-item qa-dataid=\"breadcrumbs-fold-item\" [label]=\"crumb.label\" [link]=\"crumb.link ?? null\" />\n }\n </rt-menu>\n </span>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @for (crumb of fold().tail; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (look() === 'inline' && current(); as page) {\n <li rtElem=\"item\">\n <span\n rtElem=\"current\"\n qa-dataid=\"breadcrumbs-current\"\n aria-current=\"page\"\n rtTooltipWhenTruncated\n [rtTooltip]=\"page.label\">\n {{ page.label }}\n </span>\n </li>\n }\n </ol>\n </div>\n }\n\n @if (look() === 'heading' && current(); as page) {\n <div rtElem=\"heading\">\n <h1 rtElem=\"title\" qa-dataid=\"breadcrumbs-current\" aria-current=\"page\" rtTooltipWhenTruncated [rtTooltip]=\"page.label\">\n {{ page.label }}\n </h1>\n <ng-content />\n </div>\n }\n</ng-container>\n",
154
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-breadcrumbs{--rt-breadcrumbs-back-radius: var(--rt-radius-full);--rt-breadcrumbs-duration: var(--rt-duration-fast);--rt-breadcrumbs-gap: var(--rt-space-xs);--rt-breadcrumbs-font-size: var(--rt-text-sm);--rt-breadcrumbs-font-weight: var(--rt-font-weight-medium);--rt-breadcrumbs-line-height: var(--rt-leading-snug);--rt-breadcrumbs-link-color: var(--rt-color-text-muted);--rt-breadcrumbs-link-color-hover: var(--rt-color-text-primary);--rt-breadcrumbs-link-radius: var(--rt-radius-xs);--rt-breadcrumbs-separator-color: var(--rt-color-text-disabled);--rt-breadcrumbs-separator-gap: var(--rt-space-xs);--rt-breadcrumbs-current-color: var(--rt-color-text-primary);--rt-breadcrumbs-current-min: var(--rt-size-30);--rt-breadcrumbs-back-size: var(--rt-control-height-sm);--rt-breadcrumbs-back-icon-size: var(--rt-size-5);--rt-breadcrumbs-back-color: var(--rt-color-text-primary);--rt-breadcrumbs-back-bg-hover: var(--rt-color-bg-hover);--rt-breadcrumbs-heading-gap: var(--rt-space-sm);--rt-breadcrumbs-heading-offset: var(--rt-space-xs);--rt-breadcrumbs-title-color: var(--rt-color-text-primary);--rt-breadcrumbs-title-font-size: var(--rt-text-2xl);--rt-breadcrumbs-title-font-weight: var(--rt-font-weight-bold);--rt-breadcrumbs-title-line-height: var(--rt-leading-tight);display:block;min-inline-size:0}@media(pointer:coarse){.rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-gap: var(--rt-size-2);--rt-breadcrumbs-separator-gap: var(--rt-size-2);--rt-breadcrumbs-link-color: var(--mat-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-separator-color: var(--mat-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-back-size: var(--rt-size-10);--rt-breadcrumbs-back-icon-size: var(--rt-size-6)}@media(pointer:coarse){:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}.rt-breadcrumbs__row{display:flex;align-items:center;gap:var(--rt-breadcrumbs-gap);min-inline-size:0}.rt-breadcrumbs__list{display:flex;align-items:center;padding:var(--rt-focus-ring-width);margin:calc(-1 * var(--rt-focus-ring-width));list-style:none;min-inline-size:0;overflow-x:auto;scrollbar-width:none}.rt-breadcrumbs__list--clipped{mask-image:linear-gradient(to right,transparent,black var(--rt-space-lg))}.rt-breadcrumbs__list--fitted>.rt-breadcrumbs__item:last-child{flex:0 1 auto;min-inline-size:0}.rt-breadcrumbs__item{display:flex;flex:none;align-items:center;white-space:nowrap}.rt-breadcrumbs__item:last-child{max-inline-size:100%}.rt-breadcrumbs__link,.rt-breadcrumbs__separator,.rt-breadcrumbs__current{font-size:var(--rt-breadcrumbs-font-size);font-weight:var(--rt-breadcrumbs-font-weight);line-height:var(--rt-breadcrumbs-line-height)}.rt-breadcrumbs__link{border-radius:var(--rt-breadcrumbs-link-radius);color:var(--rt-breadcrumbs-link-color);text-decoration:none;transition:color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__link:hover{color:var(--rt-breadcrumbs-link-color-hover)}.rt-breadcrumbs__link:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs__fold{display:inline-flex;margin-inline:calc(-1 * var(--rt-space-sm))}.rt-breadcrumbs__separator{color:var(--rt-breadcrumbs-separator-color);margin-inline:var(--rt-breadcrumbs-separator-gap)}.rt-breadcrumbs__current{overflow:hidden;color:var(--rt-breadcrumbs-current-color);min-inline-size:0;text-overflow:ellipsis}.rt-breadcrumbs__back{display:inline-flex;flex:none;align-items:center;justify-content:center;--rt-icon-step-md: var(--rt-breadcrumbs-back-icon-size);border-radius:var(--rt-breadcrumbs-back-radius);block-size:var(--rt-breadcrumbs-back-size);color:var(--rt-breadcrumbs-back-color);inline-size:var(--rt-breadcrumbs-back-size);transition:background-color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__back:hover{background-color:var(--rt-breadcrumbs-back-bg-hover)}.rt-breadcrumbs__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs--heading .rt-breadcrumbs__item:last-child .rt-breadcrumbs__separator{display:none}.rt-breadcrumbs__heading{display:flex;flex-flow:row wrap;align-items:center;gap:var(--rt-breadcrumbs-heading-gap);min-inline-size:0}.rt-breadcrumbs__row+.rt-breadcrumbs__heading{margin-block-start:var(--rt-breadcrumbs-heading-offset)}.rt-breadcrumbs__title{overflow:hidden;margin:0;color:var(--rt-breadcrumbs-title-color);font-size:var(--rt-breadcrumbs-title-font-size);font-weight:var(--rt-breadcrumbs-title-font-weight);line-height:var(--rt-breadcrumbs-title-line-height);max-inline-size:100%;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}}\n"],
154
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-breadcrumbs{--rt-breadcrumbs-back-radius: var(--rt-radius-full);--rt-breadcrumbs-duration: var(--rt-duration-fast);--rt-breadcrumbs-gap: var(--rt-space-xs);--rt-breadcrumbs-font-size: var(--rt-text-sm);--rt-breadcrumbs-font-weight: var(--rt-font-weight-medium);--rt-breadcrumbs-line-height: var(--rt-leading-snug);--rt-breadcrumbs-link-color: var(--rt-color-text-muted);--rt-breadcrumbs-link-color-hover: var(--rt-color-text-primary);--rt-breadcrumbs-link-radius: var(--rt-radius-xs);--rt-breadcrumbs-separator-color: var(--rt-color-text-disabled);--rt-breadcrumbs-separator-gap: var(--rt-space-xs);--rt-breadcrumbs-current-color: var(--rt-color-text-primary);--rt-breadcrumbs-current-min: var(--rt-size-30);--rt-breadcrumbs-back-size: var(--rt-control-height-sm);--rt-breadcrumbs-back-icon-size: var(--rt-size-5);--rt-breadcrumbs-back-color: var(--rt-color-text-primary);--rt-breadcrumbs-back-bg-hover: var(--rt-color-bg-hover);--rt-breadcrumbs-heading-gap: var(--rt-space-sm);--rt-breadcrumbs-heading-offset: var(--rt-space-xs);--rt-breadcrumbs-title-color: var(--rt-color-text-primary);--rt-breadcrumbs-title-font-size: var(--rt-text-2xl);--rt-breadcrumbs-title-font-weight: var(--rt-font-weight-bold);--rt-breadcrumbs-title-line-height: var(--rt-leading-tight);display:block;min-inline-size:0}@media(pointer:coarse){.rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-gap: var(--rt-size-2);--rt-breadcrumbs-separator-gap: var(--rt-size-2);--rt-breadcrumbs-link-color: var(--rt-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-separator-color: var(--rt-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-back-size: var(--rt-size-10);--rt-breadcrumbs-back-icon-size: var(--rt-size-6)}@media(pointer:coarse){:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}.rt-breadcrumbs__row{display:flex;align-items:center;gap:var(--rt-breadcrumbs-gap);min-inline-size:0}.rt-breadcrumbs__list{display:flex;align-items:center;padding:var(--rt-focus-ring-width);margin:calc(-1 * var(--rt-focus-ring-width));list-style:none;min-inline-size:0;overflow-x:auto;scrollbar-width:none}.rt-breadcrumbs__list--clipped{mask-image:linear-gradient(to right,transparent,black var(--rt-space-lg))}.rt-breadcrumbs__list--fitted>.rt-breadcrumbs__item:last-child{flex:0 1 auto;min-inline-size:0}.rt-breadcrumbs__item{display:flex;flex:none;align-items:center;white-space:nowrap}.rt-breadcrumbs__item:last-child{max-inline-size:100%}.rt-breadcrumbs__link,.rt-breadcrumbs__separator,.rt-breadcrumbs__current{font-size:var(--rt-breadcrumbs-font-size);font-weight:var(--rt-breadcrumbs-font-weight);line-height:var(--rt-breadcrumbs-line-height)}.rt-breadcrumbs__link{border-radius:var(--rt-breadcrumbs-link-radius);color:var(--rt-breadcrumbs-link-color);text-decoration:none;transition:color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__link:hover{color:var(--rt-breadcrumbs-link-color-hover)}.rt-breadcrumbs__link:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs__fold{display:inline-flex;margin-inline:calc(-1 * var(--rt-space-sm))}.rt-breadcrumbs__separator{color:var(--rt-breadcrumbs-separator-color);margin-inline:var(--rt-breadcrumbs-separator-gap)}.rt-breadcrumbs__current{overflow:hidden;color:var(--rt-breadcrumbs-current-color);min-inline-size:0;text-overflow:ellipsis}.rt-breadcrumbs__back{display:inline-flex;flex:none;align-items:center;justify-content:center;--rt-icon-step-md: var(--rt-breadcrumbs-back-icon-size);border-radius:var(--rt-breadcrumbs-back-radius);block-size:var(--rt-breadcrumbs-back-size);color:var(--rt-breadcrumbs-back-color);inline-size:var(--rt-breadcrumbs-back-size);transition:background-color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__back:hover{background-color:var(--rt-breadcrumbs-back-bg-hover)}.rt-breadcrumbs__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs--heading .rt-breadcrumbs__item:last-child .rt-breadcrumbs__separator{display:none}.rt-breadcrumbs__heading{display:flex;flex-flow:row wrap;align-items:center;gap:var(--rt-breadcrumbs-heading-gap);min-inline-size:0}.rt-breadcrumbs__row+.rt-breadcrumbs__heading{margin-block-start:var(--rt-breadcrumbs-heading-offset)}.rt-breadcrumbs__title{overflow:hidden;margin:0;color:var(--rt-breadcrumbs-title-color);font-size:var(--rt-breadcrumbs-title-font-size);font-weight:var(--rt-breadcrumbs-title-font-weight);line-height:var(--rt-breadcrumbs-title-line-height);max-inline-size:100%;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}}\n"],
155
155
  dependencies: [
156
156
  {
157
157
  kind: "directive",
@@ -248,6 +248,7 @@ var RtBreadcrumbsComponent = class RtBreadcrumbsComponent {
248
248
  inputs: [
249
249
  "rtTooltip",
250
250
  "rtTooltipPlacement",
251
+ "rtTooltipShowDelay",
251
252
  "rtTooltipWhenTruncated",
252
253
  "rtTooltipHideOnClick"
253
254
  ],
@@ -287,7 +288,7 @@ i0.ɵɵngDeclareClassMetadata({
287
288
  "[class.rt-breadcrumbs--heading]": "look() === 'heading'"
288
289
  },
289
290
  template: "<ng-container rtBlock=\"rt-breadcrumbs\">\n @if (rowShown()) {\n <div rtElem=\"row\">\n @if (backCrumb(); as backTo) {\n <a rtElem=\"back\" qa-dataid=\"breadcrumbs-back\" [routerLink]=\"backTo.link\" [attr.aria-label]=\"backLabel()\">\n <rt-icon name=\"arrow-left\" size=\"md\" />\n </a>\n }\n\n <ol\n #list\n rtElem=\"list\"\n qa-dataid=\"breadcrumbs-list\"\n [rtMod]=\"{ clipped: clipped(), fitted: fitted() }\"\n (scroll)=\"onListScroll()\">\n @for (crumb of fold().head; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (fold().hidden.length) {\n <li rtElem=\"item\" qa-dataid=\"breadcrumbs-fold\">\n <span rtElem=\"fold\">\n <rt-menu size=\"sm\" align=\"start\" [ariaLabel]=\"foldLabel()\">\n @for (crumb of fold().hidden; track $index) {\n <rt-menu-item qa-dataid=\"breadcrumbs-fold-item\" [label]=\"crumb.label\" [link]=\"crumb.link ?? null\" />\n }\n </rt-menu>\n </span>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @for (crumb of fold().tail; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (look() === 'inline' && current(); as page) {\n <li rtElem=\"item\">\n <span\n rtElem=\"current\"\n qa-dataid=\"breadcrumbs-current\"\n aria-current=\"page\"\n rtTooltipWhenTruncated\n [rtTooltip]=\"page.label\">\n {{ page.label }}\n </span>\n </li>\n }\n </ol>\n </div>\n }\n\n @if (look() === 'heading' && current(); as page) {\n <div rtElem=\"heading\">\n <h1 rtElem=\"title\" qa-dataid=\"breadcrumbs-current\" aria-current=\"page\" rtTooltipWhenTruncated [rtTooltip]=\"page.label\">\n {{ page.label }}\n </h1>\n <ng-content />\n </div>\n }\n</ng-container>\n",
290
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-breadcrumbs{--rt-breadcrumbs-back-radius: var(--rt-radius-full);--rt-breadcrumbs-duration: var(--rt-duration-fast);--rt-breadcrumbs-gap: var(--rt-space-xs);--rt-breadcrumbs-font-size: var(--rt-text-sm);--rt-breadcrumbs-font-weight: var(--rt-font-weight-medium);--rt-breadcrumbs-line-height: var(--rt-leading-snug);--rt-breadcrumbs-link-color: var(--rt-color-text-muted);--rt-breadcrumbs-link-color-hover: var(--rt-color-text-primary);--rt-breadcrumbs-link-radius: var(--rt-radius-xs);--rt-breadcrumbs-separator-color: var(--rt-color-text-disabled);--rt-breadcrumbs-separator-gap: var(--rt-space-xs);--rt-breadcrumbs-current-color: var(--rt-color-text-primary);--rt-breadcrumbs-current-min: var(--rt-size-30);--rt-breadcrumbs-back-size: var(--rt-control-height-sm);--rt-breadcrumbs-back-icon-size: var(--rt-size-5);--rt-breadcrumbs-back-color: var(--rt-color-text-primary);--rt-breadcrumbs-back-bg-hover: var(--rt-color-bg-hover);--rt-breadcrumbs-heading-gap: var(--rt-space-sm);--rt-breadcrumbs-heading-offset: var(--rt-space-xs);--rt-breadcrumbs-title-color: var(--rt-color-text-primary);--rt-breadcrumbs-title-font-size: var(--rt-text-2xl);--rt-breadcrumbs-title-font-weight: var(--rt-font-weight-bold);--rt-breadcrumbs-title-line-height: var(--rt-leading-tight);display:block;min-inline-size:0}@media(pointer:coarse){.rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-gap: var(--rt-size-2);--rt-breadcrumbs-separator-gap: var(--rt-size-2);--rt-breadcrumbs-link-color: var(--mat-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-separator-color: var(--mat-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-back-size: var(--rt-size-10);--rt-breadcrumbs-back-icon-size: var(--rt-size-6)}@media(pointer:coarse){:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}.rt-breadcrumbs__row{display:flex;align-items:center;gap:var(--rt-breadcrumbs-gap);min-inline-size:0}.rt-breadcrumbs__list{display:flex;align-items:center;padding:var(--rt-focus-ring-width);margin:calc(-1 * var(--rt-focus-ring-width));list-style:none;min-inline-size:0;overflow-x:auto;scrollbar-width:none}.rt-breadcrumbs__list--clipped{mask-image:linear-gradient(to right,transparent,black var(--rt-space-lg))}.rt-breadcrumbs__list--fitted>.rt-breadcrumbs__item:last-child{flex:0 1 auto;min-inline-size:0}.rt-breadcrumbs__item{display:flex;flex:none;align-items:center;white-space:nowrap}.rt-breadcrumbs__item:last-child{max-inline-size:100%}.rt-breadcrumbs__link,.rt-breadcrumbs__separator,.rt-breadcrumbs__current{font-size:var(--rt-breadcrumbs-font-size);font-weight:var(--rt-breadcrumbs-font-weight);line-height:var(--rt-breadcrumbs-line-height)}.rt-breadcrumbs__link{border-radius:var(--rt-breadcrumbs-link-radius);color:var(--rt-breadcrumbs-link-color);text-decoration:none;transition:color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__link:hover{color:var(--rt-breadcrumbs-link-color-hover)}.rt-breadcrumbs__link:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs__fold{display:inline-flex;margin-inline:calc(-1 * var(--rt-space-sm))}.rt-breadcrumbs__separator{color:var(--rt-breadcrumbs-separator-color);margin-inline:var(--rt-breadcrumbs-separator-gap)}.rt-breadcrumbs__current{overflow:hidden;color:var(--rt-breadcrumbs-current-color);min-inline-size:0;text-overflow:ellipsis}.rt-breadcrumbs__back{display:inline-flex;flex:none;align-items:center;justify-content:center;--rt-icon-step-md: var(--rt-breadcrumbs-back-icon-size);border-radius:var(--rt-breadcrumbs-back-radius);block-size:var(--rt-breadcrumbs-back-size);color:var(--rt-breadcrumbs-back-color);inline-size:var(--rt-breadcrumbs-back-size);transition:background-color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__back:hover{background-color:var(--rt-breadcrumbs-back-bg-hover)}.rt-breadcrumbs__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs--heading .rt-breadcrumbs__item:last-child .rt-breadcrumbs__separator{display:none}.rt-breadcrumbs__heading{display:flex;flex-flow:row wrap;align-items:center;gap:var(--rt-breadcrumbs-heading-gap);min-inline-size:0}.rt-breadcrumbs__row+.rt-breadcrumbs__heading{margin-block-start:var(--rt-breadcrumbs-heading-offset)}.rt-breadcrumbs__title{overflow:hidden;margin:0;color:var(--rt-breadcrumbs-title-color);font-size:var(--rt-breadcrumbs-title-font-size);font-weight:var(--rt-breadcrumbs-title-font-weight);line-height:var(--rt-breadcrumbs-title-line-height);max-inline-size:100%;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}}\n"]
291
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-breadcrumbs{--rt-breadcrumbs-back-radius: var(--rt-radius-full);--rt-breadcrumbs-duration: var(--rt-duration-fast);--rt-breadcrumbs-gap: var(--rt-space-xs);--rt-breadcrumbs-font-size: var(--rt-text-sm);--rt-breadcrumbs-font-weight: var(--rt-font-weight-medium);--rt-breadcrumbs-line-height: var(--rt-leading-snug);--rt-breadcrumbs-link-color: var(--rt-color-text-muted);--rt-breadcrumbs-link-color-hover: var(--rt-color-text-primary);--rt-breadcrumbs-link-radius: var(--rt-radius-xs);--rt-breadcrumbs-separator-color: var(--rt-color-text-disabled);--rt-breadcrumbs-separator-gap: var(--rt-space-xs);--rt-breadcrumbs-current-color: var(--rt-color-text-primary);--rt-breadcrumbs-current-min: var(--rt-size-30);--rt-breadcrumbs-back-size: var(--rt-control-height-sm);--rt-breadcrumbs-back-icon-size: var(--rt-size-5);--rt-breadcrumbs-back-color: var(--rt-color-text-primary);--rt-breadcrumbs-back-bg-hover: var(--rt-color-bg-hover);--rt-breadcrumbs-heading-gap: var(--rt-space-sm);--rt-breadcrumbs-heading-offset: var(--rt-space-xs);--rt-breadcrumbs-title-color: var(--rt-color-text-primary);--rt-breadcrumbs-title-font-size: var(--rt-text-2xl);--rt-breadcrumbs-title-font-weight: var(--rt-font-weight-bold);--rt-breadcrumbs-title-line-height: var(--rt-leading-tight);display:block;min-inline-size:0}@media(pointer:coarse){.rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-gap: var(--rt-size-2);--rt-breadcrumbs-separator-gap: var(--rt-size-2);--rt-breadcrumbs-link-color: var(--rt-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-separator-color: var(--rt-sys-on-surface-variant, var(--rt-color-text-muted));--rt-breadcrumbs-back-size: var(--rt-size-10);--rt-breadcrumbs-back-icon-size: var(--rt-size-6)}@media(pointer:coarse){:is(.rt-preset-material,[data-preset=material]) .rt-breadcrumbs{--rt-breadcrumbs-back-size: var(--rt-control-height-lg)}}.rt-breadcrumbs__row{display:flex;align-items:center;gap:var(--rt-breadcrumbs-gap);min-inline-size:0}.rt-breadcrumbs__list{display:flex;align-items:center;padding:var(--rt-focus-ring-width);margin:calc(-1 * var(--rt-focus-ring-width));list-style:none;min-inline-size:0;overflow-x:auto;scrollbar-width:none}.rt-breadcrumbs__list--clipped{mask-image:linear-gradient(to right,transparent,black var(--rt-space-lg))}.rt-breadcrumbs__list--fitted>.rt-breadcrumbs__item:last-child{flex:0 1 auto;min-inline-size:0}.rt-breadcrumbs__item{display:flex;flex:none;align-items:center;white-space:nowrap}.rt-breadcrumbs__item:last-child{max-inline-size:100%}.rt-breadcrumbs__link,.rt-breadcrumbs__separator,.rt-breadcrumbs__current{font-size:var(--rt-breadcrumbs-font-size);font-weight:var(--rt-breadcrumbs-font-weight);line-height:var(--rt-breadcrumbs-line-height)}.rt-breadcrumbs__link{border-radius:var(--rt-breadcrumbs-link-radius);color:var(--rt-breadcrumbs-link-color);text-decoration:none;transition:color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__link:hover{color:var(--rt-breadcrumbs-link-color-hover)}.rt-breadcrumbs__link:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs__fold{display:inline-flex;margin-inline:calc(-1 * var(--rt-space-sm))}.rt-breadcrumbs__separator{color:var(--rt-breadcrumbs-separator-color);margin-inline:var(--rt-breadcrumbs-separator-gap)}.rt-breadcrumbs__current{overflow:hidden;color:var(--rt-breadcrumbs-current-color);min-inline-size:0;text-overflow:ellipsis}.rt-breadcrumbs__back{display:inline-flex;flex:none;align-items:center;justify-content:center;--rt-icon-step-md: var(--rt-breadcrumbs-back-icon-size);border-radius:var(--rt-breadcrumbs-back-radius);block-size:var(--rt-breadcrumbs-back-size);color:var(--rt-breadcrumbs-back-color);inline-size:var(--rt-breadcrumbs-back-size);transition:background-color var(--rt-breadcrumbs-duration) linear}.rt-breadcrumbs__back:hover{background-color:var(--rt-breadcrumbs-back-bg-hover)}.rt-breadcrumbs__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:var(--rt-focus-ring-offset)}.rt-breadcrumbs--heading .rt-breadcrumbs__item:last-child .rt-breadcrumbs__separator{display:none}.rt-breadcrumbs__heading{display:flex;flex-flow:row wrap;align-items:center;gap:var(--rt-breadcrumbs-heading-gap);min-inline-size:0}.rt-breadcrumbs__row+.rt-breadcrumbs__heading{margin-block-start:var(--rt-breadcrumbs-heading-offset)}.rt-breadcrumbs__title{overflow:hidden;margin:0;color:var(--rt-breadcrumbs-title-color);font-size:var(--rt-breadcrumbs-title-font-size);font-weight:var(--rt-breadcrumbs-title-font-weight);line-height:var(--rt-breadcrumbs-title-line-height);max-inline-size:100%;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}}\n"]
291
292
  }]
292
293
  }],
293
294
  ctorParameters: () => [],
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-breadcrumbs.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/breadcrumbs/rt-breadcrumbs.logic.ts","../../../projects/ui-kit-v2/src/lib/components/breadcrumbs/rt-breadcrumbs.component.ts","../../../projects/ui-kit-v2/src/lib/components/breadcrumbs/rt-breadcrumbs.component.html"],"sourcesContent":["import { IRtBreadcrumbs } from './rt-breadcrumbs.model';\n\n/** С какого числа предков середина пути сворачивается на узком экране. */\nexport const RT_BREADCRUMBS_FOLD_FROM: number = 3;\n\n/**\n * Помещаются ли предки рядом с текущим звеном, урезанным до порога или до своей полной подписи,\n * если она короче. Тогда звено сжимается с многоточием и строка не едет; иначе строка встаёт в\n * конец, и предки уезжают влево.\n */\nexport function rtBreadcrumbsFits(room: number, ancestors: number, current: number, floor: number): boolean {\n return room - ancestors >= Math.min(current, floor);\n}\n\n/**\n * Раскладка предков. На узком экране длинный путь оставляет первого и последнего предка ссылками,\n * а середину отдаёт в меню; на широком и при коротком пути свёртки нет.\n */\nexport function rtBreadcrumbsFold(ancestors: readonly IRtBreadcrumbs.Crumb[], narrow: boolean): IRtBreadcrumbs.Fold {\n if (!narrow || ancestors.length < RT_BREADCRUMBS_FOLD_FROM) {\n return { head: ancestors, hidden: [], tail: [] };\n }\n\n return {\n head: ancestors.slice(0, 1),\n hidden: ancestors.slice(1, -1),\n tail: ancestors.slice(-1),\n };\n}\n","import {\n afterRenderEffect,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n Signal,\n signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { BreakpointsService, rtKitLabel, TRtKitLabelParams } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtMenuComponent, RtMenuItemComponent } from '@rt-tools/ui-kit-v2/menu';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\n\nimport { rtBreadcrumbsFits, rtBreadcrumbsFold } from './rt-breadcrumbs.logic';\nimport { IRtBreadcrumbs } from './rt-breadcrumbs.model';\n\nconst BEM_BLOCK: string = 'rt-breadcrumbs';\n\n/**\n * Путь до открытой страницы: предки ссылками, текущее звено текстом.\n *\n * Цепочку отдаёт приложение: имя открытой записи приходит с ответом сервера, и знает его только\n * страница. Текущее звено рисуется в строке пути или заголовком строкой ниже — тогда рядом с ним\n * встаёт то, что страница спроецировала. На узком экране середина длинного пути уходит в меню.\n */\n@Component({\n selector: 'rt-breadcrumbs',\n templateUrl: './rt-breadcrumbs.component.html',\n styleUrl: './rt-breadcrumbs.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n RouterLink,\n\n // standalone components / directives\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtIconComponent,\n RtMenuComponent,\n RtMenuItemComponent,\n RtTooltipDirective,\n ],\n host: {\n class: BEM_BLOCK,\n role: 'navigation',\n '[attr.aria-label]': 'landmarkLabel()',\n '[class.rt-breadcrumbs--heading]': \"look() === 'heading'\",\n },\n})\nexport class RtBreadcrumbsComponent {\n #itemsWatch: ResizeObserver | null = null;\n\n readonly #narrow: Signal<boolean> = inject(BreakpointsService).narrow;\n\n readonly #ancestors: Signal<readonly IRtBreadcrumbs.Crumb[]> = computed((): readonly IRtBreadcrumbs.Crumb[] =>\n this.crumbs().slice(0, -1)\n );\n\n readonly #backParams: Signal<TRtKitLabelParams> = computed((): TRtKitLabelParams => ({ name: this.backCrumb()?.label ?? '' }));\n\n /** Строка звеньев: её прокрутку ставит в конец эффект после отрисовки. `#`-поле `viewChild` не принимает. */\n protected readonly list: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('list');\n\n /** Начало пути уехало влево за край строки. */\n protected readonly clipped: WritableSignal<boolean> = signal<boolean>(false);\n\n /** Предки помещаются рядом с урезанным текущим звеном: звено сжимается, строка не едет. */\n protected readonly fitted: WritableSignal<boolean> = signal<boolean>(false);\n\n protected readonly landmarkLabel: Signal<string> = rtKitLabel('breadcrumbsLabel');\n protected readonly foldLabel: Signal<string> = rtKitLabel('breadcrumbsHidden');\n protected readonly backLabel: Signal<string> = rtKitLabel('breadcrumbsBack', this.#backParams);\n\n protected readonly fold: Signal<IRtBreadcrumbs.Fold> = computed((): IRtBreadcrumbs.Fold =>\n rtBreadcrumbsFold(this.#ancestors(), this.#narrow())\n );\n\n protected readonly current: Signal<IRtBreadcrumbs.Crumb | null> = computed(\n (): IRtBreadcrumbs.Crumb | null => this.crumbs().at(-1) ?? null\n );\n\n /** Строка пути нужна, пока в ней есть что рисовать: у одного заголовка её нет. */\n protected readonly rowShown: Signal<boolean> = computed((): boolean => this.look() === 'inline' || this.#ancestors().length > 0);\n\n /** Куда ведёт кнопка «назад»: к последнему предку, а без предков кнопки нет. */\n protected readonly backCrumb: Signal<IRtBreadcrumbs.Crumb | null> = computed((): IRtBreadcrumbs.Crumb | null =>\n this.back() ? (this.#ancestors().at(-1) ?? null) : null\n );\n\n /** Путь от верха до открытой страницы; последнее звено — сама страница. */\n public readonly crumbs: InputSignal<readonly IRtBreadcrumbs.Crumb[]> = input.required<readonly IRtBreadcrumbs.Crumb[]>();\n\n /** Как рисуется текущее звено: в строке пути или заголовком строкой ниже. */\n public readonly look: InputSignal<IRtBreadcrumbs.Look> = input<IRtBreadcrumbs.Look>('inline');\n\n /** Кнопка «назад» перед путём; ведёт к последнему предку. */\n public readonly back: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n\n /** Знак между звеньями. */\n public readonly separator: InputSignal<string> = input<string>('/');\n\n constructor() {\n // Длинный путь едет внутри своей строки, и видна должна быть открытая страница, а не верх\n // пути. Строка встаёт в конец при смене цепочки и при смене ширины звеньев: шрифт страницы\n // приходит позже первой отрисовки и раздвигает звенья уже после неё.\n afterRenderEffect((): void => {\n this.crumbs();\n this.fold();\n this.#watchItems();\n this.#scrollToEnd();\n });\n\n inject(DestroyRef).onDestroy((): void => this.#itemsWatch?.disconnect());\n }\n\n protected onListScroll(): void {\n const list: HTMLElement | undefined = this.list()?.nativeElement;\n this.clipped.set(list !== undefined && list.scrollLeft > 0);\n }\n\n #watchItems(): void {\n const list: HTMLElement | undefined = this.list()?.nativeElement;\n if (list === undefined || typeof ResizeObserver === 'undefined') {\n return;\n }\n this.#itemsWatch?.disconnect();\n this.#itemsWatch = new ResizeObserver((): void => this.#scrollToEnd());\n Array.from(list.children).forEach((item: Element): void => this.#itemsWatch?.observe(item));\n }\n\n #scrollToEnd(): void {\n const list: HTMLElement | undefined = this.list()?.nativeElement;\n if (list !== undefined) {\n this.fitted.set(this.#measureFit(list));\n list.scrollLeft = list.scrollWidth;\n this.clipped.set(list.scrollLeft > 0);\n }\n }\n\n /**\n * Ширина строки без внутренних полей, предки целиком и полная подпись текущего звена против\n * порога из свойства блока. Без текущего звена в строке сжимать нечего.\n */\n #measureFit(list: HTMLElement): boolean {\n const items: readonly Element[] = Array.from(list.children);\n const current: Element | null = items.at(-1)?.querySelector('.rt-breadcrumbs__current') ?? null;\n if (current === null) {\n return false;\n }\n const listStyle: CSSStyleDeclaration = getComputedStyle(list);\n const room: number = list.clientWidth - parseFloat(listStyle.paddingInlineStart) - parseFloat(listStyle.paddingInlineEnd);\n const ancestors: number = items\n .slice(0, -1)\n .reduce((sum: number, item: Element): number => sum + item.getBoundingClientRect().width, 0);\n const floor: number = parseFloat(listStyle.getPropertyValue('--rt-breadcrumbs-current-min')) || 0;\n\n return rtBreadcrumbsFits(room, ancestors, current.scrollWidth, floor);\n }\n}\n","<ng-container rtBlock=\"rt-breadcrumbs\">\n @if (rowShown()) {\n <div rtElem=\"row\">\n @if (backCrumb(); as backTo) {\n <a rtElem=\"back\" qa-dataid=\"breadcrumbs-back\" [routerLink]=\"backTo.link\" [attr.aria-label]=\"backLabel()\">\n <rt-icon name=\"arrow-left\" size=\"md\" />\n </a>\n }\n\n <ol\n #list\n rtElem=\"list\"\n qa-dataid=\"breadcrumbs-list\"\n [rtMod]=\"{ clipped: clipped(), fitted: fitted() }\"\n (scroll)=\"onListScroll()\">\n @for (crumb of fold().head; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (fold().hidden.length) {\n <li rtElem=\"item\" qa-dataid=\"breadcrumbs-fold\">\n <span rtElem=\"fold\">\n <rt-menu size=\"sm\" align=\"start\" [ariaLabel]=\"foldLabel()\">\n @for (crumb of fold().hidden; track $index) {\n <rt-menu-item qa-dataid=\"breadcrumbs-fold-item\" [label]=\"crumb.label\" [link]=\"crumb.link ?? null\" />\n }\n </rt-menu>\n </span>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @for (crumb of fold().tail; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (look() === 'inline' && current(); as page) {\n <li rtElem=\"item\">\n <span\n rtElem=\"current\"\n qa-dataid=\"breadcrumbs-current\"\n aria-current=\"page\"\n rtTooltipWhenTruncated\n [rtTooltip]=\"page.label\">\n {{ page.label }}\n </span>\n </li>\n }\n </ol>\n </div>\n }\n\n @if (look() === 'heading' && current(); as page) {\n <div rtElem=\"heading\">\n <h1 rtElem=\"title\" qa-dataid=\"breadcrumbs-current\" aria-current=\"page\" rtTooltipWhenTruncated [rtTooltip]=\"page.label\">\n {{ page.label }}\n </h1>\n <ng-content />\n </div>\n }\n</ng-container>\n"],"mappings":";;;;;;;;AAGA,MAAa,2BAAmC;AAOhD,SAAgB,kBAAkB,MAAc,WAAmB,SAAiB,OAAa;CAC7F,OAAO,OAAO,aAAa,KAAK,IAAI,SAAS,KAAK;AACtD;AAMA,SAAgB,kBAAkB,WAA4C,QAAe;CACzF,IAAI,CAAC,UAAU,UAAU,SAAA,GACrB,OAAO;EAAE,MAAM;EAAW,QAAQ,CAAA;EAAI,MAAM,CAAA;CAAE;CAGlD,OAAO;EACH,MAAM,UAAU,MAAM,GAAG,CAAC;EAC1B,QAAQ,UAAU,MAAM,GAAG,EAAE;EAC7B,MAAM,UAAU,MAAM,EAAE;CAC3B;AACL;ACEA,MAAM,YAAoB;AAmC1B,IAAa,yBAAb,MAAa,uBAAsB;CAC/B;CAES;CAEA;CAIA;CA6CT,cAAA;EArDA,KAAA,cAAqC;EAE5B,KAAA,UAA2B,OAAO,kBAAkB,CAAC,CAAC;EAEtD,KAAA,aAAsD,eAC3D,KAAK,OAAM,CAAE,CAAC,MAAM,GAAG,EAAE,GAC5B,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;EAEQ,KAAA,cAAyC,gBAAmC,EAAE,MAAM,KAAK,UAAS,CAAE,EAAE,SAAS,GAAE,IAAI,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAG3G,KAAA,OAAoD,UAAmC,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAG9F,KAAA,UAAmC,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;EAGzD,KAAA,SAAkC,OAAgB,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;EAExD,KAAA,gBAAgC,WAAW,kBAAkB;EAC7D,KAAA,YAA4B,WAAW,mBAAmB;EAC1D,KAAA,YAA4B,WAAW,mBAAmB,KAAK,WAAW;EAE1E,KAAA,OAAoC,eACnD,kBAAkB,KAAK,WAAU,GAAI,KAAK,QAAO,CAAE,GACtD,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAEkB,KAAA,UAA+C,eAC3B,KAAK,OAAM,CAAE,CAAC,GAAG,EAAE,KAAK,MAC9D,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;EAGkB,KAAA,WAA4B,eAAwB,KAAK,KAAI,MAAO,YAAY,KAAK,WAAU,CAAE,CAAC,SAAS,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;EAG7G,KAAA,YAAiD,eAChE,KAAK,KAAI,IAAM,KAAK,WAAU,CAAE,CAAC,GAAG,EAAE,KAAK,OAAQ,MACtD,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAGe,KAAA,SAAuD,MAAM,SAA2C,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;EAGxG,KAAA,OAAyC,MAA2B,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAG7E,KAAA,OAAmD,MAAwB,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,OAAA,+BAAA,CAAA;GAC5F,WAAW;EAAgB,CAAA;EAIf,KAAA,YAAiC,MAAc,KAAI,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAM/D,wBAA6B;GACzB,KAAK,OAAM;GACX,KAAK,KAAI;GACT,KAAK,YAAW;GAChB,KAAK,aAAY;EACrB,CAAC;EAED,OAAO,UAAU,CAAC,CAAC,gBAAsB,KAAK,aAAa,WAAU,CAAE;CAC3E;CAEU,eAAY;EAClB,MAAM,OAAgC,KAAK,KAAI,CAAE,EAAE;EACnD,KAAK,QAAQ,IAAI,SAAS,UAAa,KAAK,aAAa,CAAC;CAC9D;CAEA,cAAW;EACP,MAAM,OAAgC,KAAK,KAAI,CAAE,EAAE;EACnD,IAAI,SAAS,UAAa,OAAO,mBAAmB,aAChD;EAEJ,KAAK,aAAa,WAAU;EAC5B,KAAK,cAAc,IAAI,qBAA2B,KAAK,aAAY,CAAE;EACrE,MAAM,KAAK,KAAK,QAAQ,CAAC,CAAC,SAAS,SAAwB,KAAK,aAAa,QAAQ,IAAI,CAAC;CAC9F;CAEA,eAAY;EACR,MAAM,OAAgC,KAAK,KAAI,CAAE,EAAE;EACnD,IAAI,SAAS,QAAW;GACpB,KAAK,OAAO,IAAI,KAAK,YAAY,IAAI,CAAC;GACtC,KAAK,aAAa,KAAK;GACvB,KAAK,QAAQ,IAAI,KAAK,aAAa,CAAC;EACxC;CACJ;CAMA,YAAY,MAAiB;EACzB,MAAM,QAA4B,MAAM,KAAK,KAAK,QAAQ;EAC1D,MAAM,UAA0B,MAAM,GAAG,EAAE,CAAC,EAAE,cAAc,0BAA0B,KAAK;EAC3F,IAAI,YAAY,MACZ,OAAO;EAEX,MAAM,YAAiC,iBAAiB,IAAI;EAC5D,MAAM,OAAe,KAAK,cAAc,WAAW,UAAU,kBAAkB,IAAI,WAAW,UAAU,gBAAgB;EACxH,MAAM,YAAoB,MACrB,MAAM,GAAG,EAAE,CAAC,CACZ,QAAQ,KAAa,SAA0B,MAAM,KAAK,sBAAqB,CAAE,CAAC,OAAO,CAAC;EAC/F,MAAM,QAAgB,WAAW,UAAU,iBAAiB,8BAA8B,CAAC,KAAK;EAEhG,OAAO,kBAAkB,MAAM,WAAW,QAAQ,aAAa,KAAK;CACxE;CA9GS;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAsB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAAtB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAsB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,QAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,MAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,MAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,WAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,MAAA;IAAA,YAAA,EAAA,QAAA,aAAA;IAAA,YAAA;KAAA,mBAAA;KAAA,iCAAA;IAAA;IAAA,gBAAA;GAAA;GAAA,aAAA,CAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,MAAA;IAAA,aAAA;IAAA,UAAA;GAAA,CAAA;GAAA,UAAA;GAAA,UCjEnC;GAmEA,QAAA,CAAA,mxJAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MDpBQ;KAAU,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAGV;KAAc,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACd;KAAa,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACb;KAAY,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACZ;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,cAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAmB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACnB;KAAkB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,UAAA,CAAA,WAAA;IAAA;GAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AASb,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EA1BlC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAgB,iBAGT,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IAEL;IAGA;IACA;IACA;IACA;IACA;IACA;IACA;GACH;GAAA,MACK;IACF,OAAO;IACP,MAAM;IACN,qBAAqB;IACrB,mCAAmC;GACtC;GAAA,UAAA;GAAA,QAAA,CAAA,mxJAAA;EAAA,CAAA;CAcyG,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,QAAM,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-breadcrumbs.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/breadcrumbs/rt-breadcrumbs.logic.ts","../../../projects/ui-kit-v2/src/lib/components/breadcrumbs/rt-breadcrumbs.component.ts","../../../projects/ui-kit-v2/src/lib/components/breadcrumbs/rt-breadcrumbs.component.html"],"sourcesContent":["import { IRtBreadcrumbs } from './rt-breadcrumbs.model';\n\n/** С какого числа предков середина пути сворачивается на узком экране. */\nexport const RT_BREADCRUMBS_FOLD_FROM: number = 3;\n\n/**\n * Помещаются ли предки рядом с текущим звеном, урезанным до порога или до своей полной подписи,\n * если она короче. Тогда звено сжимается с многоточием и строка не едет; иначе строка встаёт в\n * конец, и предки уезжают влево.\n */\nexport function rtBreadcrumbsFits(room: number, ancestors: number, current: number, floor: number): boolean {\n return room - ancestors >= Math.min(current, floor);\n}\n\n/**\n * Раскладка предков. На узком экране длинный путь оставляет первого и последнего предка ссылками,\n * а середину отдаёт в меню; на широком и при коротком пути свёртки нет.\n */\nexport function rtBreadcrumbsFold(ancestors: readonly IRtBreadcrumbs.Crumb[], narrow: boolean): IRtBreadcrumbs.Fold {\n if (!narrow || ancestors.length < RT_BREADCRUMBS_FOLD_FROM) {\n return { head: ancestors, hidden: [], tail: [] };\n }\n\n return {\n head: ancestors.slice(0, 1),\n hidden: ancestors.slice(1, -1),\n tail: ancestors.slice(-1),\n };\n}\n","import {\n afterRenderEffect,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n Signal,\n signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { BreakpointsService, rtKitLabel, TRtKitLabelParams } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtMenuComponent, RtMenuItemComponent } from '@rt-tools/ui-kit-v2/menu';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\n\nimport { rtBreadcrumbsFits, rtBreadcrumbsFold } from './rt-breadcrumbs.logic';\nimport { IRtBreadcrumbs } from './rt-breadcrumbs.model';\n\nconst BEM_BLOCK: string = 'rt-breadcrumbs';\n\n/**\n * Путь до открытой страницы: предки ссылками, текущее звено текстом.\n *\n * Цепочку отдаёт приложение: имя открытой записи приходит с ответом сервера, и знает его только\n * страница. Текущее звено рисуется в строке пути или заголовком строкой ниже — тогда рядом с ним\n * встаёт то, что страница спроецировала. На узком экране середина длинного пути уходит в меню.\n */\n@Component({\n selector: 'rt-breadcrumbs',\n templateUrl: './rt-breadcrumbs.component.html',\n styleUrl: './rt-breadcrumbs.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n RouterLink,\n\n // standalone components / directives\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtIconComponent,\n RtMenuComponent,\n RtMenuItemComponent,\n RtTooltipDirective,\n ],\n host: {\n class: BEM_BLOCK,\n role: 'navigation',\n '[attr.aria-label]': 'landmarkLabel()',\n '[class.rt-breadcrumbs--heading]': \"look() === 'heading'\",\n },\n})\nexport class RtBreadcrumbsComponent {\n #itemsWatch: ResizeObserver | null = null;\n\n readonly #narrow: Signal<boolean> = inject(BreakpointsService).narrow;\n\n readonly #ancestors: Signal<readonly IRtBreadcrumbs.Crumb[]> = computed((): readonly IRtBreadcrumbs.Crumb[] =>\n this.crumbs().slice(0, -1)\n );\n\n readonly #backParams: Signal<TRtKitLabelParams> = computed((): TRtKitLabelParams => ({ name: this.backCrumb()?.label ?? '' }));\n\n /** Строка звеньев: её прокрутку ставит в конец эффект после отрисовки. `#`-поле `viewChild` не принимает. */\n protected readonly list: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('list');\n\n /** Начало пути уехало влево за край строки. */\n protected readonly clipped: WritableSignal<boolean> = signal<boolean>(false);\n\n /** Предки помещаются рядом с урезанным текущим звеном: звено сжимается, строка не едет. */\n protected readonly fitted: WritableSignal<boolean> = signal<boolean>(false);\n\n protected readonly landmarkLabel: Signal<string> = rtKitLabel('breadcrumbsLabel');\n protected readonly foldLabel: Signal<string> = rtKitLabel('breadcrumbsHidden');\n protected readonly backLabel: Signal<string> = rtKitLabel('breadcrumbsBack', this.#backParams);\n\n protected readonly fold: Signal<IRtBreadcrumbs.Fold> = computed((): IRtBreadcrumbs.Fold =>\n rtBreadcrumbsFold(this.#ancestors(), this.#narrow())\n );\n\n protected readonly current: Signal<IRtBreadcrumbs.Crumb | null> = computed(\n (): IRtBreadcrumbs.Crumb | null => this.crumbs().at(-1) ?? null\n );\n\n /** Строка пути нужна, пока в ней есть что рисовать: у одного заголовка её нет. */\n protected readonly rowShown: Signal<boolean> = computed((): boolean => this.look() === 'inline' || this.#ancestors().length > 0);\n\n /** Куда ведёт кнопка «назад»: к последнему предку, а без предков кнопки нет. */\n protected readonly backCrumb: Signal<IRtBreadcrumbs.Crumb | null> = computed((): IRtBreadcrumbs.Crumb | null =>\n this.back() ? (this.#ancestors().at(-1) ?? null) : null\n );\n\n /** Путь от верха до открытой страницы; последнее звено — сама страница. */\n public readonly crumbs: InputSignal<readonly IRtBreadcrumbs.Crumb[]> = input.required<readonly IRtBreadcrumbs.Crumb[]>();\n\n /** Как рисуется текущее звено: в строке пути или заголовком строкой ниже. */\n public readonly look: InputSignal<IRtBreadcrumbs.Look> = input<IRtBreadcrumbs.Look>('inline');\n\n /** Кнопка «назад» перед путём; ведёт к последнему предку. */\n public readonly back: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n\n /** Знак между звеньями. */\n public readonly separator: InputSignal<string> = input<string>('/');\n\n constructor() {\n // Длинный путь едет внутри своей строки, и видна должна быть открытая страница, а не верх\n // пути. Строка встаёт в конец при смене цепочки и при смене ширины звеньев: шрифт страницы\n // приходит позже первой отрисовки и раздвигает звенья уже после неё.\n afterRenderEffect((): void => {\n this.crumbs();\n this.fold();\n this.#watchItems();\n this.#scrollToEnd();\n });\n\n inject(DestroyRef).onDestroy((): void => this.#itemsWatch?.disconnect());\n }\n\n protected onListScroll(): void {\n const list: HTMLElement | undefined = this.list()?.nativeElement;\n this.clipped.set(list !== undefined && list.scrollLeft > 0);\n }\n\n #watchItems(): void {\n const list: HTMLElement | undefined = this.list()?.nativeElement;\n if (list === undefined || typeof ResizeObserver === 'undefined') {\n return;\n }\n this.#itemsWatch?.disconnect();\n this.#itemsWatch = new ResizeObserver((): void => this.#scrollToEnd());\n Array.from(list.children).forEach((item: Element): void => this.#itemsWatch?.observe(item));\n }\n\n #scrollToEnd(): void {\n const list: HTMLElement | undefined = this.list()?.nativeElement;\n if (list !== undefined) {\n this.fitted.set(this.#measureFit(list));\n list.scrollLeft = list.scrollWidth;\n this.clipped.set(list.scrollLeft > 0);\n }\n }\n\n /**\n * Ширина строки без внутренних полей, предки целиком и полная подпись текущего звена против\n * порога из свойства блока. Без текущего звена в строке сжимать нечего.\n */\n #measureFit(list: HTMLElement): boolean {\n const items: readonly Element[] = Array.from(list.children);\n const current: Element | null = items.at(-1)?.querySelector('.rt-breadcrumbs__current') ?? null;\n if (current === null) {\n return false;\n }\n const listStyle: CSSStyleDeclaration = getComputedStyle(list);\n const room: number = list.clientWidth - parseFloat(listStyle.paddingInlineStart) - parseFloat(listStyle.paddingInlineEnd);\n const ancestors: number = items\n .slice(0, -1)\n .reduce((sum: number, item: Element): number => sum + item.getBoundingClientRect().width, 0);\n const floor: number = parseFloat(listStyle.getPropertyValue('--rt-breadcrumbs-current-min')) || 0;\n\n return rtBreadcrumbsFits(room, ancestors, current.scrollWidth, floor);\n }\n}\n","<ng-container rtBlock=\"rt-breadcrumbs\">\n @if (rowShown()) {\n <div rtElem=\"row\">\n @if (backCrumb(); as backTo) {\n <a rtElem=\"back\" qa-dataid=\"breadcrumbs-back\" [routerLink]=\"backTo.link\" [attr.aria-label]=\"backLabel()\">\n <rt-icon name=\"arrow-left\" size=\"md\" />\n </a>\n }\n\n <ol\n #list\n rtElem=\"list\"\n qa-dataid=\"breadcrumbs-list\"\n [rtMod]=\"{ clipped: clipped(), fitted: fitted() }\"\n (scroll)=\"onListScroll()\">\n @for (crumb of fold().head; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (fold().hidden.length) {\n <li rtElem=\"item\" qa-dataid=\"breadcrumbs-fold\">\n <span rtElem=\"fold\">\n <rt-menu size=\"sm\" align=\"start\" [ariaLabel]=\"foldLabel()\">\n @for (crumb of fold().hidden; track $index) {\n <rt-menu-item qa-dataid=\"breadcrumbs-fold-item\" [label]=\"crumb.label\" [link]=\"crumb.link ?? null\" />\n }\n </rt-menu>\n </span>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @for (crumb of fold().tail; track $index) {\n <li rtElem=\"item\">\n <a rtElem=\"link\" qa-dataid=\"breadcrumbs-link\" [routerLink]=\"crumb.link\">{{ crumb.label }}</a>\n <span rtElem=\"separator\" aria-hidden=\"true\">{{ separator() }}</span>\n </li>\n }\n\n @if (look() === 'inline' && current(); as page) {\n <li rtElem=\"item\">\n <span\n rtElem=\"current\"\n qa-dataid=\"breadcrumbs-current\"\n aria-current=\"page\"\n rtTooltipWhenTruncated\n [rtTooltip]=\"page.label\">\n {{ page.label }}\n </span>\n </li>\n }\n </ol>\n </div>\n }\n\n @if (look() === 'heading' && current(); as page) {\n <div rtElem=\"heading\">\n <h1 rtElem=\"title\" qa-dataid=\"breadcrumbs-current\" aria-current=\"page\" rtTooltipWhenTruncated [rtTooltip]=\"page.label\">\n {{ page.label }}\n </h1>\n <ng-content />\n </div>\n }\n</ng-container>\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
+ import { FocusMonitor } from "@angular/cdk/a11y";
1
2
  import { DOCUMENT } from "@angular/common";
2
3
  import * as i0 from "@angular/core";
3
- import { Directive, ElementRef, HostBinding, Renderer2, afterNextRender, booleanAttribute, computed, effect, inject, input, untracked } from "@angular/core";
4
+ import { DestroyRef, Directive, ElementRef, HostBinding, Renderer2, afterNextRender, booleanAttribute, computed, effect, inject, input, untracked } from "@angular/core";
4
5
  import { RT_RADIUS_DEFAULT, rtKitDefault } from "@rt-tools/ui-kit-v2/core";
5
6
  import * as i2 from "@rt-tools/ui-kit-v2/radius";
6
7
  import { RtRadiusDirective } from "@rt-tools/ui-kit-v2/radius";
@@ -12,6 +13,7 @@ const LOADING_FADE_OUT_MS = 300;
12
13
  var RtButtonDirective = class RtButtonDirective {
13
14
  #el;
14
15
  #renderer;
16
+ #focusMonitor;
15
17
  #doc;
16
18
  #iconRegistry;
17
19
  #glyphStrategy;
@@ -25,6 +27,7 @@ var RtButtonDirective = class RtButtonDirective {
25
27
  constructor() {
26
28
  this.#el = inject(ElementRef);
27
29
  this.#renderer = inject(Renderer2);
30
+ this.#focusMonitor = inject(FocusMonitor);
28
31
  this.#doc = inject(DOCUMENT);
29
32
  this.#iconRegistry = inject(RtIconRegistry);
30
33
  this.#glyphStrategy = inject(RT_ICON_GLYPH_STRATEGY);
@@ -50,6 +53,8 @@ var RtButtonDirective = class RtButtonDirective {
50
53
  this.loadingLabel = input("keep", ...ngDevMode ? [{ debugName: "loadingLabel" }] : /* istanbul ignore next */ []);
51
54
  this.pressed = input(null, ...ngDevMode ? [{ debugName: "pressed" }] : /* istanbul ignore next */ []);
52
55
  afterNextRender(() => untracked(() => this.#updateContent()));
56
+ this.#focusMonitor.monitor(this.#el);
57
+ inject(DestroyRef).onDestroy(() => this.#focusMonitor.stopMonitoring(this.#el));
53
58
  effect(() => {
54
59
  this.label();
55
60
  this.icon();
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-button.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/button/rt-button.directive.ts"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport { DOCUMENT } from '@angular/common';\nimport {\n afterNextRender,\n booleanAttribute,\n computed,\n Directive,\n effect,\n ElementRef,\n HostBinding,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n Renderer2,\n Signal,\n untracked,\n} from '@angular/core';\n\nimport { IRtKitConfig } from '@rt-tools/ui-kit-v2/core';\nimport { rtKitDefault } from '@rt-tools/ui-kit-v2/core';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RT_RADIUS_DEFAULT, TRtRadius } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport {\n RT_ICON_GLYPH_STRATEGY,\n RT_ICON_MATERIAL_PRESET_SELECTOR,\n RtIconFontService,\n RtIconRegistry,\n resolveIconGlyph,\n} from '@rt-tools/ui-kit-v2/icon';\nimport { iconMaterialDrawn } from '@rt-tools/ui-kit-v2/icon';\nimport { RtRippleDirective } from '@rt-tools/ui-kit-v2/ripple';\nimport { IButton } from '@rt-tools/ui-kit-v2/core';\n\nconst BEM_BLOCK: string = 'rt-button';\n\n/** Длительность затухания спиннера при выходе из загрузки. */\nconst LOADING_FADE_OUT_MS: number = 300;\n\n/** Запас поверх затухания, после которого подпись возвращается без анимации. */\nconst LOADING_FADE_OUT_BUFFER_MS: number = 50;\n\n/**\n * Директива стилизованной кнопки. Применяется к `<button>` или `<a>`\n * (для ссылок-кнопок с `routerLink`/`href`).\n *\n * Иконка и лейбл рендерятся внутрь элемента через `Renderer2`, чтобы можно\n * было применять директиву к существующему элементу без обёртки.\n * Поддерживает анимированный переход в/из loading.\n *\n * @example\n * ```html\n * <button rtButton label=\"Войти\" theme=\"primary\"></button>\n * <button rtButton label=\"Скачать\" icon=\"ico-download\" theme=\"success\"></button>\n * <button rtButton icon=\"pencil\" theme=\"info\" appearance=\"text\"></button>\n * <button rtButton label=\"Сохранение...\" [loading]=\"saving()\" [disabled]=\"saving()\"></button>\n * <a rtButton label=\"Главная\" [routerLink]=\"'/'\"></a>\n * ```\n */\n@Directive({\n selector: 'button[rtButton], a[rtButton]',\n hostDirectives: [\n { directive: RtRippleDirective, inputs: ['rippleDisabled'] },\n { directive: RtRadiusDirective, inputs: ['radius'] },\n ],\n /* Шаг скругления без входа берётся из настроек кита: приложение, скруглившее все кнопки разом,\n называет шаг один раз, а вход на кнопке по-прежнему сильнее. */\n providers: [\n {\n provide: RT_RADIUS_DEFAULT,\n useFactory: (): TRtRadius | null =>\n rtKitDefault('button', (it: IRtKitConfig.Button): TRtRadius | null | undefined => it.radius, null),\n },\n ],\n})\nexport class RtButtonDirective {\n readonly #el: ElementRef<HTMLButtonElement | HTMLAnchorElement> = inject<ElementRef<HTMLButtonElement | HTMLAnchorElement>>(ElementRef);\n readonly #renderer: Renderer2 = inject(Renderer2);\n readonly #doc: Document = inject(DOCUMENT);\n readonly #iconRegistry: RtIconRegistry = inject(RtIconRegistry);\n readonly #glyphStrategy: IRtIcon.GlyphStrategy = inject(RT_ICON_GLYPH_STRATEGY);\n readonly #fontReady: Signal<boolean> = inject(RtIconFontService).ready;\n\n /* С чего стартуют три входа, которые приложение вправе задать киту разом. Значение считается\n из настроек при объявлении входа: так публичный вид входа не меняется — ни тип, ни имя, — а\n вход, написанный в разметке, по-прежнему перебивает всё. Цена — настройки читаются один раз,\n когда узел создан. */\n readonly #appearance: IButton.Appearance = rtKitDefault(\n 'button',\n (it: IRtKitConfig.Button): IButton.Appearance | undefined => it.appearance,\n 'filled'\n );\n readonly #size: IButton.Size = rtKitDefault('button', (it: IRtKitConfig.Button): IButton.Size | undefined => it.size, 'md');\n\n #iconEl: HTMLElement | null = null;\n #labelEl: HTMLElement | null = null;\n #spinnerEl: HTMLElement | null = null;\n #wasLoading: boolean = false;\n\n protected readonly isIconOnly: Signal<boolean> = computed(() => !!this.icon() && !this.label());\n\n /** Текстовый лейбл кнопки. Если null — кнопка только с иконкой. */\n public readonly label: InputSignal<string | null> = input<string | null>(null);\n /**\n * Значок кнопки: имя кита (`check`) или имя Material (`arrow_back`). Имя Material рисуется\n * так же, как вход `glyph` у `rt-icon`: парой из перечня кита или лигатурой шрифта — по\n * настройке `glyphStrategy` у `provideRtIcons()`. Если null — без иконки.\n */\n public readonly icon: InputSignal<string | null> = input<string | null>(null);\n /** Сторона размещения иконки относительно лейбла. */\n public readonly iconPos: InputSignal<IButton.IconPos> = input<IButton.IconPos>('left');\n /** Семантическая палитра. */\n public readonly theme: InputSignal<IButton.Theme> = input<IButton.Theme>('primary');\n /** Внешний вид (filled / outlined / text). Умолчание — из настроек кита. */\n public readonly appearance: InputSignal<IButton.Appearance> = input<IButton.Appearance>(this.#appearance);\n /** Размер. Умолчание — из настроек кита. */\n public readonly size: InputSignal<IButton.Size> = input<IButton.Size>(this.#size);\n /** Состояние загрузки: показывает спиннер вместо иконки, блокирует клики. */\n public readonly loading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** Кастомный CSS-класс иконки в loading. Если null — встроенный CSS-спиннер. */\n public readonly loadingIcon: InputSignal<string | null> = input<string | null>(null);\n /**\n * Подпись во время загрузки. `hide` оставляет подпись и иконку в кнопке невидимыми — ширина\n * не скачет, — а спиннер ставит по центру; имя кнопки для скринридера остаётся.\n */\n public readonly loadingLabel: InputSignal<IButton.LoadingLabel> = input<IButton.LoadingLabel>('keep');\n\n /**\n * Положение двухпозиционной кнопки: `true` — нажата, `false` — отжата, `null` — положения нет\n * вовсе.\n *\n * Три значения, а не два, потому что обычная кнопка переключателем не является и объявлять\n * себя двухпозиционной не должна: сказанное вспомогательным средствам «эта кнопка отжата» на\n * кнопке, которая просто запускает действие, — ложь, а не умолчание. Отжатое положение при\n * этом объявляется наравне с нажатым: молчащая в отжатом виде кнопка неотличима от обычной, и\n * о втором положении узнают только нажав.\n *\n * Само по нажатию положение не меняется: кнопка говорит о нажатии наружу, а положение\n * возвращает вызывающий — иначе вид кнопки и состояние приложения расходятся при первом же\n * отказе сохранения.\n */\n public readonly pressed: InputSignal<boolean | null> = input<boolean | null>(null);\n\n constructor() {\n afterNextRender(() => untracked(() => this.#updateContent()));\n\n effect(() => {\n this.label();\n this.icon();\n this.iconPos();\n this.loading();\n this.loadingIcon();\n this.loadingLabel();\n // Лигатура до готовности шрифтов скрыта: готовность перерисовывает содержимое.\n this.#fontReady();\n\n untracked(() => this.#updateContent());\n });\n }\n\n /** Положение наружу: атрибута нет вовсе там, где положения у кнопки не бывает. */\n @HostBinding('attr.aria-pressed')\n protected get ariaPressed(): string | null {\n const pressed: boolean | null = this.pressed();\n\n return pressed === null ? null : String(pressed);\n }\n\n @HostBinding('class')\n protected get hostClasses(): Record<string, boolean> {\n const theme: IButton.Theme = this.theme();\n const appearance: IButton.Appearance = this.appearance();\n const size: IButton.Size = this.size();\n\n return {\n [BEM_BLOCK]: true,\n [`${BEM_BLOCK}--${theme}`]: theme !== 'primary',\n [`${BEM_BLOCK}--outlined`]: appearance === 'outlined',\n [`${BEM_BLOCK}--text`]: appearance === 'text',\n [`${BEM_BLOCK}--loading`]: this.loading(),\n [`${BEM_BLOCK}--loading-label-hidden`]: this.loading() && this.loadingLabel() === 'hide',\n [`${BEM_BLOCK}--icon-only`]: this.isIconOnly(),\n [`${BEM_BLOCK}--${size}`]: size !== 'md',\n [`${BEM_BLOCK}--pressed`]: this.pressed() === true,\n };\n }\n\n #updateContent(): void {\n const button: HTMLButtonElement | HTMLAnchorElement = this.#el.nativeElement;\n const isLoading: boolean = this.loading();\n const wasLoading: boolean = this.#wasLoading;\n this.#wasLoading = isLoading;\n\n if (wasLoading && !isLoading) {\n this.#transitionFromLoading(button);\n return;\n }\n\n this.#clearContent(button);\n\n if (isLoading) {\n this.#renderLoading(button);\n return;\n }\n\n this.#renderIconAndLabel(button);\n }\n\n #transitionFromLoading(button: HTMLElement): void {\n const spinnerEl: HTMLElement | null = this.#spinnerEl;\n\n if (spinnerEl === null) {\n this.#restoreContent(button);\n return;\n }\n\n spinnerEl.style.animation = `rt-button-fade-out ${LOADING_FADE_OUT_MS}ms ease-out forwards`;\n\n // settled-гард делает animationend и страховочный таймаут взаимно\n // идемпотентными — сработает и снимет listener только то, что раньше.\n // Без таймаута подпись возвращалась бы только по анимации, а она не\n // идёт, пока кнопка спрятана (свёрнутая панель, неактивная вкладка):\n // выход из загрузки не наступал никогда, и на кнопке навсегда\n // оставался голый спиннер.\n let settled: boolean = false;\n\n const finish: () => void = (): void => {\n if (settled) {\n return;\n }\n settled = true;\n spinnerEl.removeEventListener('animationend', finish);\n this.#restoreContent(button);\n };\n\n spinnerEl.addEventListener('animationend', finish);\n setTimeout(finish, LOADING_FADE_OUT_MS + LOADING_FADE_OUT_BUFFER_MS);\n }\n\n #restoreContent(button: HTMLElement): void {\n this.#clearContent(button);\n this.#renderIconAndLabel(button);\n }\n\n /**\n * Чистим по классу, а не по своим ссылкам. Под серверным рендером разметку\n * кнопки рисует сервер: после гидратации лейбл и иконка уже лежат в DOM, но создавал их\n * другой экземпляр директивы, и `#labelEl` у клиентского — пустой. Ссылки\n * тогда не находят ничего, а `#renderIconAndLabel` дорисовывает второй\n * лейбл — подпись кнопки удваивается («СохранитьСохранить»).\n */\n #clearContent(button: HTMLElement): void {\n const ownClasses: ReadonlyArray<string> = [`${BEM_BLOCK}__icon`, `${BEM_BLOCK}__label`, `${BEM_BLOCK}__spinner`];\n\n // Обход по детям, а не селектором `:scope >`: тот же код исполняется на\n // сервере, где DOM эмулированный и селекторный движок урезан\n for (const child of Array.from(button.children)) {\n if (ownClasses.some((ownClass: string): boolean => child.classList.contains(ownClass))) {\n this.#renderer.removeChild(button, child);\n }\n }\n\n this.#iconEl = null;\n this.#labelEl = null;\n this.#spinnerEl = null;\n }\n\n #renderLoading(button: HTMLElement): void {\n const loadingIcon: string | null = this.loadingIcon();\n const loader: HTMLElement = loadingIcon ? this.#createIcon(loadingIcon) : this.#createSpinner();\n\n if (this.loadingLabel() === 'hide') {\n // Обычное содержимое остаётся и держит ширину; стили гасят его и ставят\n // загрузчик поверх по центру\n this.#renderIconAndLabel(button);\n this.#renderer.addClass(loader, `${BEM_BLOCK}__loader`);\n this.#renderer.appendChild(button, loader);\n if (!loadingIcon) {\n this.#spinnerEl = loader;\n }\n return;\n }\n\n if (loadingIcon) {\n this.#iconEl = loader;\n this.#renderer.appendChild(button, this.#iconEl);\n } else {\n this.#spinnerEl = loader;\n this.#renderer.appendChild(button, this.#spinnerEl);\n }\n\n const label: string | null = this.label();\n if (label) {\n this.#labelEl = this.#createLabel(label);\n this.#renderer.appendChild(button, this.#labelEl);\n }\n }\n\n #renderIconAndLabel(button: HTMLElement): void {\n const iconClass: string | null = this.icon();\n const label: string | null = this.label();\n const pos: IButton.IconPos = this.iconPos();\n\n const iconEl: HTMLElement | null = iconClass ? this.#createIcon(iconClass) : null;\n const labelEl: HTMLElement | null = label ? this.#createLabel(label) : null;\n\n if (pos === 'left') {\n if (iconEl) {\n this.#renderer.appendChild(button, iconEl);\n }\n if (labelEl) {\n this.#renderer.appendChild(button, labelEl);\n }\n } else {\n if (labelEl) {\n this.#renderer.appendChild(button, labelEl);\n }\n if (iconEl) {\n this.#renderer.appendChild(button, iconEl);\n }\n }\n\n this.#iconEl = iconEl;\n this.#labelEl = labelEl;\n }\n\n #createIcon(iconName: string): HTMLElement {\n const resolved: IRtIcon.Resolved | null = resolveIconGlyph(null, iconName, this.#glyphStrategy);\n if (resolved?.kind === 'kit') {\n return this.#createKitIcon(resolved.name);\n }\n return this.#createGlyph(iconName);\n }\n\n #createKitIcon(name: IRtIcon.Name): HTMLElement {\n const svg: SVGSVGElement = this.#doc.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('focusable', 'false');\n svg.setAttribute('aria-hidden', 'true');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.classList.add(`${BEM_BLOCK}__icon`);\n\n // Тот же путь, что и у компонента значка: имя просится у реестра, а он ходит за файлом\n // один раз на страницу — значок, спрошенный обеими разметками сразу, едет одним запросом.\n const drawing: IRtIcon.Drawing = this.#drawingOf(name);\n this.#iconRegistry.request(name, drawing);\n\n const use: SVGUseElement = this.#doc.createElementNS('http://www.w3.org/2000/svg', 'use');\n use.setAttribute('href', this.#iconRegistry.symbolHref(name, drawing));\n svg.appendChild(use);\n\n return svg as unknown as HTMLElement;\n }\n\n /**\n * Набор рисунка — как у компонента значка: материальный рисунок под разметкой с признаком\n * материального набора, если у имени он есть. Признак читается при каждой отрисовке\n * содержимого, а после первой отрисовки она повторяется — контейнер к тому времени на месте.\n */\n #drawingOf(name: IRtIcon.Name): IRtIcon.Drawing {\n const underMaterial: boolean = this.#el.nativeElement.closest(RT_ICON_MATERIAL_PRESET_SELECTOR) !== null;\n return underMaterial && iconMaterialDrawn.has(name) ? 'material' : 'base';\n }\n\n /** Лигатура шрифта Material Symbols для имени без пары. Шрифт подключает приложение. */\n #createGlyph(glyph: string): HTMLElement {\n const el: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(el, `${BEM_BLOCK}__icon`);\n this.#renderer.addClass(el, `${BEM_BLOCK}__glyph`);\n if (!this.#fontReady()) {\n this.#renderer.addClass(el, `${BEM_BLOCK}__glyph--pending`);\n }\n this.#renderer.setAttribute(el, 'aria-hidden', 'true');\n this.#renderer.appendChild(el, this.#renderer.createText(glyph));\n return el;\n }\n\n #createLabel(text: string): HTMLElement {\n const el: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(el, `${BEM_BLOCK}__label`);\n this.#renderer.appendChild(el, this.#renderer.createText(text));\n return el;\n }\n\n #createSpinner(): HTMLElement {\n const wrapper: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(wrapper, `${BEM_BLOCK}__spinner`);\n\n const circle: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(circle, `${BEM_BLOCK}__spinner-circle`);\n this.#renderer.appendChild(wrapper, circle);\n\n return wrapper;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-button.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/button/rt-button.directive.ts"],"sourcesContent":["import { FocusMonitor } from '@angular/cdk/a11y';\nimport { BooleanInput } from '@angular/cdk/coercion';\nimport { DOCUMENT } from '@angular/common';\nimport {\n afterNextRender,\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n HostBinding,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n Renderer2,\n Signal,\n untracked,\n} from '@angular/core';\n\nimport { IRtKitConfig } from '@rt-tools/ui-kit-v2/core';\nimport { rtKitDefault } from '@rt-tools/ui-kit-v2/core';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RT_RADIUS_DEFAULT, TRtRadius } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport {\n RT_ICON_GLYPH_STRATEGY,\n RT_ICON_MATERIAL_PRESET_SELECTOR,\n RtIconFontService,\n RtIconRegistry,\n resolveIconGlyph,\n} from '@rt-tools/ui-kit-v2/icon';\nimport { iconMaterialDrawn } from '@rt-tools/ui-kit-v2/icon';\nimport { RtRippleDirective } from '@rt-tools/ui-kit-v2/ripple';\nimport { IButton } from '@rt-tools/ui-kit-v2/core';\n\nconst BEM_BLOCK: string = 'rt-button';\n\n/** Длительность затухания спиннера при выходе из загрузки. */\nconst LOADING_FADE_OUT_MS: number = 300;\n\n/** Запас поверх затухания, после которого подпись возвращается без анимации. */\nconst LOADING_FADE_OUT_BUFFER_MS: number = 50;\n\n/**\n * Директива стилизованной кнопки. Применяется к `<button>` или `<a>`\n * (для ссылок-кнопок с `routerLink`/`href`).\n *\n * Иконка и лейбл рендерятся внутрь элемента через `Renderer2`, чтобы можно\n * было применять директиву к существующему элементу без обёртки.\n * Поддерживает анимированный переход в/из loading.\n *\n * @example\n * ```html\n * <button rtButton label=\"Войти\" theme=\"primary\"></button>\n * <button rtButton label=\"Скачать\" icon=\"ico-download\" theme=\"success\"></button>\n * <button rtButton icon=\"pencil\" theme=\"info\" appearance=\"text\"></button>\n * <button rtButton label=\"Сохранение...\" [loading]=\"saving()\" [disabled]=\"saving()\"></button>\n * <a rtButton label=\"Главная\" [routerLink]=\"'/'\"></a>\n * ```\n */\n@Directive({\n selector: 'button[rtButton], a[rtButton]',\n hostDirectives: [\n { directive: RtRippleDirective, inputs: ['rippleDisabled'] },\n { directive: RtRadiusDirective, inputs: ['radius'] },\n ],\n /* Шаг скругления без входа берётся из настроек кита: приложение, скруглившее все кнопки разом,\n называет шаг один раз, а вход на кнопке по-прежнему сильнее. */\n providers: [\n {\n provide: RT_RADIUS_DEFAULT,\n useFactory: (): TRtRadius | null =>\n rtKitDefault('button', (it: IRtKitConfig.Button): TRtRadius | null | undefined => it.radius, null),\n },\n ],\n})\nexport class RtButtonDirective {\n readonly #el: ElementRef<HTMLButtonElement | HTMLAnchorElement> = inject<ElementRef<HTMLButtonElement | HTMLAnchorElement>>(ElementRef);\n readonly #renderer: Renderer2 = inject(Renderer2);\n readonly #focusMonitor: FocusMonitor = inject(FocusMonitor);\n readonly #doc: Document = inject(DOCUMENT);\n readonly #iconRegistry: RtIconRegistry = inject(RtIconRegistry);\n readonly #glyphStrategy: IRtIcon.GlyphStrategy = inject(RT_ICON_GLYPH_STRATEGY);\n readonly #fontReady: Signal<boolean> = inject(RtIconFontService).ready;\n\n /* С чего стартуют три входа, которые приложение вправе задать киту разом. Значение считается\n из настроек при объявлении входа: так публичный вид входа не меняется — ни тип, ни имя, — а\n вход, написанный в разметке, по-прежнему перебивает всё. Цена — настройки читаются один раз,\n когда узел создан. */\n readonly #appearance: IButton.Appearance = rtKitDefault(\n 'button',\n (it: IRtKitConfig.Button): IButton.Appearance | undefined => it.appearance,\n 'filled'\n );\n readonly #size: IButton.Size = rtKitDefault('button', (it: IRtKitConfig.Button): IButton.Size | undefined => it.size, 'md');\n\n #iconEl: HTMLElement | null = null;\n #labelEl: HTMLElement | null = null;\n #spinnerEl: HTMLElement | null = null;\n #wasLoading: boolean = false;\n\n protected readonly isIconOnly: Signal<boolean> = computed(() => !!this.icon() && !this.label());\n\n /** Текстовый лейбл кнопки. Если null — кнопка только с иконкой. */\n public readonly label: InputSignal<string | null> = input<string | null>(null);\n /**\n * Значок кнопки: имя кита (`check`) или имя Material (`arrow_back`). Имя Material рисуется\n * так же, как вход `glyph` у `rt-icon`: парой из перечня кита или лигатурой шрифта — по\n * настройке `glyphStrategy` у `provideRtIcons()`. Если null — без иконки.\n */\n public readonly icon: InputSignal<string | null> = input<string | null>(null);\n /** Сторона размещения иконки относительно лейбла. */\n public readonly iconPos: InputSignal<IButton.IconPos> = input<IButton.IconPos>('left');\n /** Семантическая палитра. */\n public readonly theme: InputSignal<IButton.Theme> = input<IButton.Theme>('primary');\n /** Внешний вид (filled / outlined / text). Умолчание — из настроек кита. */\n public readonly appearance: InputSignal<IButton.Appearance> = input<IButton.Appearance>(this.#appearance);\n /** Размер. Умолчание — из настроек кита. */\n public readonly size: InputSignal<IButton.Size> = input<IButton.Size>(this.#size);\n /** Состояние загрузки: показывает спиннер вместо иконки, блокирует клики. */\n public readonly loading: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n /** Кастомный CSS-класс иконки в loading. Если null — встроенный CSS-спиннер. */\n public readonly loadingIcon: InputSignal<string | null> = input<string | null>(null);\n /**\n * Подпись во время загрузки. `hide` оставляет подпись и иконку в кнопке невидимыми — ширина\n * не скачет, — а спиннер ставит по центру; имя кнопки для скринридера остаётся.\n */\n public readonly loadingLabel: InputSignal<IButton.LoadingLabel> = input<IButton.LoadingLabel>('keep');\n\n /**\n * Положение двухпозиционной кнопки: `true` — нажата, `false` — отжата, `null` — положения нет\n * вовсе.\n *\n * Три значения, а не два, потому что обычная кнопка переключателем не является и объявлять\n * себя двухпозиционной не должна: сказанное вспомогательным средствам «эта кнопка отжата» на\n * кнопке, которая просто запускает действие, — ложь, а не умолчание. Отжатое положение при\n * этом объявляется наравне с нажатым: молчащая в отжатом виде кнопка неотличима от обычной, и\n * о втором положении узнают только нажав.\n *\n * Само по нажатию положение не меняется: кнопка говорит о нажатии наружу, а положение\n * возвращает вызывающий — иначе вид кнопки и состояние приложения расходятся при первом же\n * отказе сохранения.\n */\n public readonly pressed: InputSignal<boolean | null> = input<boolean | null>(null);\n\n constructor() {\n afterNextRender(() => untracked(() => this.#updateContent()));\n\n /* Источник фокуса пишет CDK классами `cdk-keyboard-focused`, `cdk-mouse-focused` и\n соседними, как у Material: кольцо фокуса рисуется только не от мыши и не от касания.\n Сам монитор, а не `CdkMonitorFocus` хозяином: приложение вправе поставить на ту же\n кнопку `cdkMonitorElementFocus`, и директива дважды на узле — ошибка NG0309. */\n this.#focusMonitor.monitor(this.#el);\n inject(DestroyRef).onDestroy((): void => this.#focusMonitor.stopMonitoring(this.#el));\n\n effect(() => {\n this.label();\n this.icon();\n this.iconPos();\n this.loading();\n this.loadingIcon();\n this.loadingLabel();\n // Лигатура до готовности шрифтов скрыта: готовность перерисовывает содержимое.\n this.#fontReady();\n\n untracked(() => this.#updateContent());\n });\n }\n\n /** Положение наружу: атрибута нет вовсе там, где положения у кнопки не бывает. */\n @HostBinding('attr.aria-pressed')\n protected get ariaPressed(): string | null {\n const pressed: boolean | null = this.pressed();\n\n return pressed === null ? null : String(pressed);\n }\n\n @HostBinding('class')\n protected get hostClasses(): Record<string, boolean> {\n const theme: IButton.Theme = this.theme();\n const appearance: IButton.Appearance = this.appearance();\n const size: IButton.Size = this.size();\n\n return {\n [BEM_BLOCK]: true,\n [`${BEM_BLOCK}--${theme}`]: theme !== 'primary',\n [`${BEM_BLOCK}--outlined`]: appearance === 'outlined',\n [`${BEM_BLOCK}--text`]: appearance === 'text',\n [`${BEM_BLOCK}--loading`]: this.loading(),\n [`${BEM_BLOCK}--loading-label-hidden`]: this.loading() && this.loadingLabel() === 'hide',\n [`${BEM_BLOCK}--icon-only`]: this.isIconOnly(),\n [`${BEM_BLOCK}--${size}`]: size !== 'md',\n [`${BEM_BLOCK}--pressed`]: this.pressed() === true,\n };\n }\n\n #updateContent(): void {\n const button: HTMLButtonElement | HTMLAnchorElement = this.#el.nativeElement;\n const isLoading: boolean = this.loading();\n const wasLoading: boolean = this.#wasLoading;\n this.#wasLoading = isLoading;\n\n if (wasLoading && !isLoading) {\n this.#transitionFromLoading(button);\n return;\n }\n\n this.#clearContent(button);\n\n if (isLoading) {\n this.#renderLoading(button);\n return;\n }\n\n this.#renderIconAndLabel(button);\n }\n\n #transitionFromLoading(button: HTMLElement): void {\n const spinnerEl: HTMLElement | null = this.#spinnerEl;\n\n if (spinnerEl === null) {\n this.#restoreContent(button);\n return;\n }\n\n spinnerEl.style.animation = `rt-button-fade-out ${LOADING_FADE_OUT_MS}ms ease-out forwards`;\n\n // settled-гард делает animationend и страховочный таймаут взаимно\n // идемпотентными — сработает и снимет listener только то, что раньше.\n // Без таймаута подпись возвращалась бы только по анимации, а она не\n // идёт, пока кнопка спрятана (свёрнутая панель, неактивная вкладка):\n // выход из загрузки не наступал никогда, и на кнопке навсегда\n // оставался голый спиннер.\n let settled: boolean = false;\n\n const finish: () => void = (): void => {\n if (settled) {\n return;\n }\n settled = true;\n spinnerEl.removeEventListener('animationend', finish);\n this.#restoreContent(button);\n };\n\n spinnerEl.addEventListener('animationend', finish);\n setTimeout(finish, LOADING_FADE_OUT_MS + LOADING_FADE_OUT_BUFFER_MS);\n }\n\n #restoreContent(button: HTMLElement): void {\n this.#clearContent(button);\n this.#renderIconAndLabel(button);\n }\n\n /**\n * Чистим по классу, а не по своим ссылкам. Под серверным рендером разметку\n * кнопки рисует сервер: после гидратации лейбл и иконка уже лежат в DOM, но создавал их\n * другой экземпляр директивы, и `#labelEl` у клиентского — пустой. Ссылки\n * тогда не находят ничего, а `#renderIconAndLabel` дорисовывает второй\n * лейбл — подпись кнопки удваивается («СохранитьСохранить»).\n */\n #clearContent(button: HTMLElement): void {\n const ownClasses: ReadonlyArray<string> = [`${BEM_BLOCK}__icon`, `${BEM_BLOCK}__label`, `${BEM_BLOCK}__spinner`];\n\n // Обход по детям, а не селектором `:scope >`: тот же код исполняется на\n // сервере, где DOM эмулированный и селекторный движок урезан\n for (const child of Array.from(button.children)) {\n if (ownClasses.some((ownClass: string): boolean => child.classList.contains(ownClass))) {\n this.#renderer.removeChild(button, child);\n }\n }\n\n this.#iconEl = null;\n this.#labelEl = null;\n this.#spinnerEl = null;\n }\n\n #renderLoading(button: HTMLElement): void {\n const loadingIcon: string | null = this.loadingIcon();\n const loader: HTMLElement = loadingIcon ? this.#createIcon(loadingIcon) : this.#createSpinner();\n\n if (this.loadingLabel() === 'hide') {\n // Обычное содержимое остаётся и держит ширину; стили гасят его и ставят\n // загрузчик поверх по центру\n this.#renderIconAndLabel(button);\n this.#renderer.addClass(loader, `${BEM_BLOCK}__loader`);\n this.#renderer.appendChild(button, loader);\n if (!loadingIcon) {\n this.#spinnerEl = loader;\n }\n return;\n }\n\n if (loadingIcon) {\n this.#iconEl = loader;\n this.#renderer.appendChild(button, this.#iconEl);\n } else {\n this.#spinnerEl = loader;\n this.#renderer.appendChild(button, this.#spinnerEl);\n }\n\n const label: string | null = this.label();\n if (label) {\n this.#labelEl = this.#createLabel(label);\n this.#renderer.appendChild(button, this.#labelEl);\n }\n }\n\n #renderIconAndLabel(button: HTMLElement): void {\n const iconClass: string | null = this.icon();\n const label: string | null = this.label();\n const pos: IButton.IconPos = this.iconPos();\n\n const iconEl: HTMLElement | null = iconClass ? this.#createIcon(iconClass) : null;\n const labelEl: HTMLElement | null = label ? this.#createLabel(label) : null;\n\n if (pos === 'left') {\n if (iconEl) {\n this.#renderer.appendChild(button, iconEl);\n }\n if (labelEl) {\n this.#renderer.appendChild(button, labelEl);\n }\n } else {\n if (labelEl) {\n this.#renderer.appendChild(button, labelEl);\n }\n if (iconEl) {\n this.#renderer.appendChild(button, iconEl);\n }\n }\n\n this.#iconEl = iconEl;\n this.#labelEl = labelEl;\n }\n\n #createIcon(iconName: string): HTMLElement {\n const resolved: IRtIcon.Resolved | null = resolveIconGlyph(null, iconName, this.#glyphStrategy);\n if (resolved?.kind === 'kit') {\n return this.#createKitIcon(resolved.name);\n }\n return this.#createGlyph(iconName);\n }\n\n #createKitIcon(name: IRtIcon.Name): HTMLElement {\n const svg: SVGSVGElement = this.#doc.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('focusable', 'false');\n svg.setAttribute('aria-hidden', 'true');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.classList.add(`${BEM_BLOCK}__icon`);\n\n // Тот же путь, что и у компонента значка: имя просится у реестра, а он ходит за файлом\n // один раз на страницу — значок, спрошенный обеими разметками сразу, едет одним запросом.\n const drawing: IRtIcon.Drawing = this.#drawingOf(name);\n this.#iconRegistry.request(name, drawing);\n\n const use: SVGUseElement = this.#doc.createElementNS('http://www.w3.org/2000/svg', 'use');\n use.setAttribute('href', this.#iconRegistry.symbolHref(name, drawing));\n svg.appendChild(use);\n\n return svg as unknown as HTMLElement;\n }\n\n /**\n * Набор рисунка — как у компонента значка: материальный рисунок под разметкой с признаком\n * материального набора, если у имени он есть. Признак читается при каждой отрисовке\n * содержимого, а после первой отрисовки она повторяется — контейнер к тому времени на месте.\n */\n #drawingOf(name: IRtIcon.Name): IRtIcon.Drawing {\n const underMaterial: boolean = this.#el.nativeElement.closest(RT_ICON_MATERIAL_PRESET_SELECTOR) !== null;\n return underMaterial && iconMaterialDrawn.has(name) ? 'material' : 'base';\n }\n\n /** Лигатура шрифта Material Symbols для имени без пары. Шрифт подключает приложение. */\n #createGlyph(glyph: string): HTMLElement {\n const el: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(el, `${BEM_BLOCK}__icon`);\n this.#renderer.addClass(el, `${BEM_BLOCK}__glyph`);\n if (!this.#fontReady()) {\n this.#renderer.addClass(el, `${BEM_BLOCK}__glyph--pending`);\n }\n this.#renderer.setAttribute(el, 'aria-hidden', 'true');\n this.#renderer.appendChild(el, this.#renderer.createText(glyph));\n return el;\n }\n\n #createLabel(text: string): HTMLElement {\n const el: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(el, `${BEM_BLOCK}__label`);\n this.#renderer.appendChild(el, this.#renderer.createText(text));\n return el;\n }\n\n #createSpinner(): HTMLElement {\n const wrapper: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(wrapper, `${BEM_BLOCK}__spinner`);\n\n const circle: HTMLElement = this.#renderer.createElement('span') as HTMLElement;\n this.#renderer.addClass(circle, `${BEM_BLOCK}__spinner-circle`);\n this.#renderer.appendChild(wrapper, circle);\n\n return wrapper;\n }\n}\n"],"mappings":"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"}
@@ -1,17 +1,21 @@
1
1
  import * as i0 from "@angular/core";
2
- import { ChangeDetectionStrategy, Component, ViewEncapsulation, booleanAttribute, computed, forwardRef, input, signal } from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, ViewEncapsulation, booleanAttribute, computed, forwardRef, input, output, signal } from "@angular/core";
3
3
  import { NG_VALUE_ACCESSOR } from "@angular/forms";
4
4
  import { BlockDirective, ElemDirective, ModDirective } from "@rt-tools/core";
5
5
  import { RtIconComponent } from "@rt-tools/ui-kit-v2/icon";
6
6
  import * as i1 from "@rt-tools/ui-kit-v2/radius";
7
7
  import { RtRadiusDirective } from "@rt-tools/ui-kit-v2/radius";
8
8
  const BEM_BLOCK = "rt-checkbox";
9
+ function checkedAttribute(value) {
10
+ return value === null || value === void 0 ? null : booleanAttribute(value);
11
+ }
9
12
  var RtCheckboxComponent = class RtCheckboxComponent {
10
13
  constructor() {
11
14
  this.#onChange = () => void 0;
12
15
  this.#onTouched = () => void 0;
13
16
  this.#disabledByForm = signal(false, ...ngDevMode ? [{ debugName: "#disabledByForm" }] : /* istanbul ignore next */ []);
14
- this.isChecked = signal(false, ...ngDevMode ? [{ debugName: "isChecked" }] : /* istanbul ignore next */ []);
17
+ this.#model = signal(false, ...ngDevMode ? [{ debugName: "#model" }] : /* istanbul ignore next */ []);
18
+ this.isChecked = computed(() => this.checked() ?? this.#model(), ...ngDevMode ? [{ debugName: "isChecked" }] : /* istanbul ignore next */ []);
15
19
  this.isDisabled = computed(() => this.disabled() || this.#disabledByForm(), ...ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []);
16
20
  this.inputId = input(null, ...ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []);
17
21
  this.ariaLabel = input(null, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
@@ -19,6 +23,11 @@ var RtCheckboxComponent = class RtCheckboxComponent {
19
23
  ...ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {},
20
24
  transform: booleanAttribute
21
25
  });
26
+ this.checked = input(null, {
27
+ ...ngDevMode ? { debugName: "checked" } : /* istanbul ignore next */ {},
28
+ transform: checkedAttribute
29
+ });
30
+ this.checkedChange = output();
22
31
  this.indeterminate = input(false, {
23
32
  ...ngDevMode ? { debugName: "indeterminate" } : /* istanbul ignore next */ {},
24
33
  transform: booleanAttribute
@@ -27,8 +36,9 @@ var RtCheckboxComponent = class RtCheckboxComponent {
27
36
  #onChange;
28
37
  #onTouched;
29
38
  #disabledByForm;
39
+ #model;
30
40
  writeValue(value) {
31
- this.isChecked.set(value === true);
41
+ this.#model.set(value === true);
32
42
  }
33
43
  registerOnChange(fn) {
34
44
  this.#onChange = fn;
@@ -42,9 +52,10 @@ var RtCheckboxComponent = class RtCheckboxComponent {
42
52
  toggle() {
43
53
  if (this.isDisabled()) return;
44
54
  const next = this.indeterminate() ? true : !this.isChecked();
45
- this.isChecked.set(next);
55
+ this.#model.set(next);
46
56
  this.#onChange(next);
47
57
  this.#onTouched();
58
+ this.checkedChange.emit(next);
48
59
  }
49
60
  static {
50
61
  this.ɵfac = i0.ɵɵngDeclareFactory({
@@ -85,6 +96,13 @@ var RtCheckboxComponent = class RtCheckboxComponent {
85
96
  isRequired: false,
86
97
  transformFunction: null
87
98
  },
99
+ checked: {
100
+ classPropertyName: "checked",
101
+ publicName: "checked",
102
+ isSignal: true,
103
+ isRequired: false,
104
+ transformFunction: null
105
+ },
88
106
  indeterminate: {
89
107
  classPropertyName: "indeterminate",
90
108
  publicName: "indeterminate",
@@ -93,6 +111,7 @@ var RtCheckboxComponent = class RtCheckboxComponent {
93
111
  transformFunction: null
94
112
  }
95
113
  },
114
+ outputs: { checkedChange: "checkedChange" },
96
115
  host: { classAttribute: "rt-checkbox" },
97
116
  providers: [{
98
117
  provide: NG_VALUE_ACCESSOR,
@@ -201,6 +220,18 @@ i0.ɵɵngDeclareClassMetadata({
201
220
  required: false
202
221
  }]
203
222
  }],
223
+ checked: [{
224
+ type: i0.Input,
225
+ args: [{
226
+ isSignal: true,
227
+ alias: "checked",
228
+ required: false
229
+ }]
230
+ }],
231
+ checkedChange: [{
232
+ type: i0.Output,
233
+ args: ["checkedChange"]
234
+ }],
204
235
  indeterminate: [{
205
236
  type: i0.Input,
206
237
  args: [{
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-checkbox.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/checkbox/rt-checkbox.component.ts","../../../projects/ui-kit-v2/src/lib/components/checkbox/rt-checkbox.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n forwardRef,\n input,\n InputSignal,\n InputSignalWithTransform,\n Signal,\n signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\n\nconst BEM_BLOCK: string = 'rt-checkbox';\n\n/**\n * Чекбокс с `role=\"checkbox\"` + `aria-checked` (`true`/`false`/`mixed`) поверх\n * нативного `<button>` — клавиатурный toggle (Enter/Space) работает «из коробки»\n * без отдельных keydown-хендлеров. Галочка/прочерк рисуются через `rt-icon`\n * (`check`/`minus`), бокс и цвета — на токенах `--rt-*`.\n *\n * Реализует `ControlValueAccessor` для `boolean` value через\n * `useExisting: forwardRef(...)` — `useClass` на self-reference вызывает\n * NG0200 cycle при boot'е.\n *\n * Отключение складывается из двух независимых источников: входа `disabled` и\n * слова формы (CVA `setDisabledState`). Отключено, если так сказал любой из\n * них. Сводить их в один signal нельзя: форма зовёт `setDisabledState` при\n * связывании контрола, то есть раньше, чем чекбокс впервые обновит своё вью,\n * и мирроринг входа затирал бы её слово — контрол, созданный отключённым,\n * оказывался бы кликабельным.\n *\n * `indeterminate` — чисто визуальное mixed-состояние; клик по нему разрешается\n * в `true` (стандартное поведение tri-state чекбокса).\n *\n * Текстовая подпись проецируется через `<ng-content>`.\n */\n@Component({\n selector: 'rt-checkbox',\n templateUrl: './rt-checkbox.component.html',\n styleUrl: './rt-checkbox.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef((): typeof RtCheckboxComponent => RtCheckboxComponent),\n multi: true,\n },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtCheckboxComponent implements ControlValueAccessor {\n #onChange: (value: boolean) => void = (): void => undefined;\n #onTouched: () => void = (): void => undefined;\n\n /** Отключение, назначенное формой через CVA (`setDisabledState`). */\n readonly #disabledByForm: WritableSignal<boolean> = signal<boolean>(false);\n\n protected readonly isChecked: WritableSignal<boolean> = signal<boolean>(false);\n protected readonly isDisabled: Signal<boolean> = computed((): boolean => this.disabled() || this.#disabledByForm());\n\n public readonly inputId: InputSignal<string | null> = input<string | null>(null);\n\n public readonly ariaLabel: InputSignal<string | null> = input<string | null>(null);\n\n public readonly disabled: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n public readonly indeterminate: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n public writeValue(value: boolean | null | undefined): void {\n this.isChecked.set(value === true);\n }\n\n public registerOnChange(fn: (value: boolean) => void): void {\n this.#onChange = fn;\n }\n\n public registerOnTouched(fn: () => void): void {\n this.#onTouched = fn;\n }\n\n public setDisabledState(disabled: boolean): void {\n this.#disabledByForm.set(disabled);\n }\n\n protected toggle(): void {\n if (this.isDisabled()) {\n return;\n }\n // mixed-состояние при клике разрешается в checked (tri-state контракт).\n const next: boolean = this.indeterminate() ? true : !this.isChecked();\n this.isChecked.set(next);\n this.#onChange(next);\n this.#onTouched();\n }\n}\n","<button\n rtBlock=\"rt-checkbox\"\n qa-dataid=\"checkbox-control\"\n type=\"button\"\n role=\"checkbox\"\n [rtMod]=\"{\n checked: isChecked() && !indeterminate(),\n indeterminate: indeterminate(),\n disabled: isDisabled(),\n }\"\n [attr.id]=\"inputId()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : isChecked()\"\n [attr.aria-label]=\"ariaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\">\n <span rtElem=\"box\">\n @if (indeterminate()) {\n <rt-icon rtElem=\"icon\" name=\"minus\" size=\"xs\" color=\"inverse\" />\n } @else if (isChecked()) {\n <rt-icon rtElem=\"icon\" name=\"check\" size=\"xs\" color=\"inverse\" />\n }\n </span>\n\n <span rtElem=\"label\" qa-dataid=\"checkbox-label\">\n <ng-content />\n </span>\n</button>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-checkbox.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/checkbox/rt-checkbox.component.ts","../../../projects/ui-kit-v2/src/lib/components/checkbox/rt-checkbox.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n forwardRef,\n input,\n InputSignal,\n InputSignalWithTransform,\n output,\n OutputEmitterRef,\n Signal,\n signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\n\nconst BEM_BLOCK: string = 'rt-checkbox';\n\n/** Вход отметки: не заданный вход — `null`, и тогда отметку решает форма. */\nfunction checkedAttribute(value: BooleanInput | null | undefined): boolean | null {\n return value === null || value === undefined ? null : booleanAttribute(value);\n}\n\n/**\n * Чекбокс с `role=\"checkbox\"` + `aria-checked` (`true`/`false`/`mixed`) поверх\n * нативного `<button>` — клавиатурный toggle (Enter/Space) работает «из коробки»\n * без отдельных keydown-хендлеров. Галочка/прочерк рисуются через `rt-icon`\n * (`check`/`minus`), бокс и цвета — на токенах `--rt-*`.\n *\n * Реализует `ControlValueAccessor` для `boolean` value через\n * `useExisting: forwardRef(...)` — `useClass` на self-reference вызывает\n * NG0200 cycle при boot'е.\n *\n * Отключение складывается из двух независимых источников: входа `disabled` и\n * слова формы (CVA `setDisabledState`). Отключено, если так сказал любой из\n * них. Сводить их в один signal нельзя: форма зовёт `setDisabledState` при\n * связывании контрола, то есть раньше, чем чекбокс впервые обновит своё вью,\n * и мирроринг входа затирал бы её слово — контрол, созданный отключённым,\n * оказывался бы кликабельным.\n *\n * Без формы отметка ставится входом `checked`, а смена сообщается выходом `checkedChange` — так же,\n * как у радиокнопки. Заданный вход решает раньше модели формы; выход приходит и при работе с формой.\n *\n * `indeterminate` — чисто визуальное mixed-состояние; клик по нему разрешается\n * в `true` (стандартное поведение tri-state чекбокса).\n *\n * Текстовая подпись проецируется через `<ng-content>`.\n */\n@Component({\n selector: 'rt-checkbox',\n templateUrl: './rt-checkbox.component.html',\n styleUrl: './rt-checkbox.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef((): typeof RtCheckboxComponent => RtCheckboxComponent),\n multi: true,\n },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n },\n})\nexport class RtCheckboxComponent implements ControlValueAccessor {\n #onChange: (value: boolean) => void = (): void => undefined;\n #onTouched: () => void = (): void => undefined;\n\n /** Отключение, назначенное формой через CVA (`setDisabledState`). */\n readonly #disabledByForm: WritableSignal<boolean> = signal<boolean>(false);\n\n /** Отметка, записанная формой или нажатием. */\n readonly #model: WritableSignal<boolean> = signal<boolean>(false);\n\n protected readonly isChecked: Signal<boolean> = computed((): boolean => this.checked() ?? this.#model());\n protected readonly isDisabled: Signal<boolean> = computed((): boolean => this.disabled() || this.#disabledByForm());\n\n public readonly inputId: InputSignal<string | null> = input<string | null>(null);\n\n public readonly ariaLabel: InputSignal<string | null> = input<string | null>(null);\n\n public readonly disabled: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Отметка без формы; не заданный вход оставляет решение форме. */\n public readonly checked: InputSignalWithTransform<boolean | null, BooleanInput | null | undefined> = input<\n boolean | null,\n BooleanInput | null | undefined\n >(null, { transform: checkedAttribute });\n\n /** Отметку сменил человек: нажатием, пробелом или Enter. */\n public readonly checkedChange: OutputEmitterRef<boolean> = output<boolean>();\n\n public readonly indeterminate: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n public writeValue(value: boolean | null | undefined): void {\n this.#model.set(value === true);\n }\n\n public registerOnChange(fn: (value: boolean) => void): void {\n this.#onChange = fn;\n }\n\n public registerOnTouched(fn: () => void): void {\n this.#onTouched = fn;\n }\n\n public setDisabledState(disabled: boolean): void {\n this.#disabledByForm.set(disabled);\n }\n\n protected toggle(): void {\n if (this.isDisabled()) {\n return;\n }\n // mixed-состояние при клике разрешается в checked (tri-state контракт).\n const next: boolean = this.indeterminate() ? true : !this.isChecked();\n this.#model.set(next);\n this.#onChange(next);\n this.#onTouched();\n this.checkedChange.emit(next);\n }\n}\n","<button\n rtBlock=\"rt-checkbox\"\n qa-dataid=\"checkbox-control\"\n type=\"button\"\n role=\"checkbox\"\n [rtMod]=\"{\n checked: isChecked() && !indeterminate(),\n indeterminate: indeterminate(),\n disabled: isDisabled(),\n }\"\n [attr.id]=\"inputId()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : isChecked()\"\n [attr.aria-label]=\"ariaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\">\n <span rtElem=\"box\">\n @if (indeterminate()) {\n <rt-icon rtElem=\"icon\" name=\"minus\" size=\"xs\" color=\"inverse\" />\n } @else if (isChecked()) {\n <rt-icon rtElem=\"icon\" name=\"check\" size=\"xs\" color=\"inverse\" />\n }\n </span>\n\n <span rtElem=\"label\" qa-dataid=\"checkbox-label\">\n <ng-content />\n </span>\n</button>\n"],"mappings":"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"}