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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +609 -33
  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 +57 -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 +291 -6
  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
@@ -325,6 +325,7 @@ var RtScrollAreaComponent = class RtScrollAreaComponent {
325
325
  inputs: [
326
326
  "rtTooltip",
327
327
  "rtTooltipPlacement",
328
+ "rtTooltipShowDelay",
328
329
  "rtTooltipWhenTruncated",
329
330
  "rtTooltipHideOnClick"
330
331
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-scroll-area.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/scroll-area/rt-scroll-area.directives.ts","../../../projects/ui-kit-v2/src/lib/components/scroll-area/rt-scroll-area.component.ts","../../../projects/ui-kit-v2/src/lib/components/scroll-area/rt-scroll-area.component.html"],"sourcesContent":["import { Directive, inject, TemplateRef } from '@angular/core';\n\n/**\n * Маркеры частей области прокрутки — своим файлом, как у соседних семей.\n *\n * Каждый маркер держит свой шаблон и не знает об области ничего: область читает маркеры среди\n * своего содержимого и берёт у них шаблон. Часть, которую потребитель не объявил, для области\n * просто отсутствует — рисовать нечего.\n */\n\n/**\n * Шапка области: стоит сверху и не прокручивается.\n *\n * @example\n * ```html\n * <rt-scroll-area>\n * <ng-container *rtScrollAreaHeader>\n * <h3>Заголовок</h3>\n * </ng-container>\n * </rt-scroll-area>\n * ```\n */\n@Directive({\n selector: '[rtScrollAreaHeader]',\n})\nexport class RtScrollAreaHeaderDirective {\n public readonly templateRef: TemplateRef<void> = inject<TemplateRef<void>>(TemplateRef);\n}\n\n/** Тело области: единственная часть, которая прокручивается. */\n@Directive({\n selector: '[rtScrollAreaContent]',\n})\nexport class RtScrollAreaContentDirective {\n public readonly templateRef: TemplateRef<void> = inject<TemplateRef<void>>(TemplateRef);\n}\n\n/** Подвал области: стоит снизу и не прокручивается. */\n@Directive({\n selector: '[rtScrollAreaFooter]',\n})\nexport class RtScrollAreaFooterDirective {\n public readonly templateRef: TemplateRef<void> = inject<TemplateRef<void>>(TemplateRef);\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterRenderEffect,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n contentChild,\n DestroyRef,\n ElementRef,\n inject,\n input,\n InputSignalWithTransform,\n signal,\n Signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\nimport { TNullable } from '@rt-tools/utils';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtScrollAreaContentDirective, RtScrollAreaFooterDirective, RtScrollAreaHeaderDirective } from './rt-scroll-area.directives';\n\nconst BEM_BLOCK: string = 'rt-scroll-area';\n\n/**\n * Целый пункт запаса при сравнении прокрутки с высотой.\n *\n * Дробная высота строки оставляет остаток, при котором прокрутка уже в самом низу, а разность\n * всё ещё больше нуля: без запаса признак непоказанного висел бы навсегда.\n */\nconst BOTTOM_SLACK_PX: number = 1;\n\n/**\n * Область прокрутки: шапка и подвал стоят, тело прокручивается.\n *\n * Части объявляет потребитель — по директиве на каждую. Необъявленная часть не рисуется вовсе:\n * пустая шапка это полоса отступа над списком, а высоту она забирает у тела, ради которого\n * область и открыли.\n *\n * Договорённость — `docs/specs/ui-kit-v2/scroll-area/`.\n */\n@Component({\n selector: 'rt-scroll-area',\n templateUrl: './rt-scroll-area.component.html',\n styleUrls: ['./rt-scroll-area.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n NgTemplateOutlet,\n\n // standalone components / directives\n RtIconComponent,\n RtTooltipDirective,\n BlockDirective,\n ElemDirective,\n ],\n host: { class: BEM_BLOCK },\n})\nexport class RtScrollAreaComponent {\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly bodyRef: Signal<TNullable<ElementRef<HTMLElement>>> = viewChild<ElementRef<HTMLElement>>('body');\n protected readonly footerRef: Signal<TNullable<ElementRef<HTMLElement>>> = viewChild<ElementRef<HTMLElement>>('foot');\n\n /**\n * Ниже окна области ещё есть содержимое. Считается по трём числам самого узла, а не по числу\n * пунктов: область не знает, что ей подали, и высота строки у каждого потребителя своя.\n */\n protected readonly hasMoreBelow: WritableSignal<boolean> = signal(false);\n\n /**\n * На сколько полоса растушёвки поднята над низом области. Она обязана кончиться там, где\n * начинается содержимое подвала: разделитель потребитель рисует первой его строкой, и полоса,\n * оборвавшаяся выше, оставляет до него чистый фон. Считается замером — верхний отступ подвала\n * потребитель пишет как хочет, и вычесть его в стилях не из чего. Без подвала полоса стоит у\n * самого низа.\n */\n protected readonly hintBottom: WritableSignal<number> = signal(0);\n\n public readonly headerTpl: Signal<TNullable<RtScrollAreaHeaderDirective>> = contentChild(RtScrollAreaHeaderDirective);\n public readonly contentTpl: Signal<TNullable<RtScrollAreaContentDirective>> = contentChild(RtScrollAreaContentDirective);\n public readonly footerTpl: Signal<TNullable<RtScrollAreaFooterDirective>> = contentChild(RtScrollAreaFooterDirective);\n\n /**\n * Признак того, что снизу осталось непоказанное. Выключен по умолчанию: область рисуют\n * десятки экранов, и признак, поставленный безусловно, сдвинул бы вид каждому из них.\n */\n public readonly isScrollHintShown: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n readonly #destroyRef: DestroyRef = inject(DestroyRef);\n\n /**\n * Наблюдатель за размером тела, его содержимого и подвала. Событие прокрутки приходит только\n * после движения руки, а список не влезает уже в первую минуту показа: без наблюдателя признак\n * появлялся бы у того, кто и так догадался прокрутить, и молчал у того, кому он нужен.\n */\n #sizeWatch: TNullable<ResizeObserver> = null;\n\n /**\n * Наблюдатель за составом тела. Тело — окно постоянной высоты, и когда список дорастает под ним,\n * размер тела не меняется: наблюдатель размера молчит. Пришедшие узлы ставятся под наблюдение\n * размера здесь же — их высота меняется и после того, как они легли.\n */\n #contentWatch: TNullable<MutationObserver> = null;\n\n /** Узлы, за которыми наблюдают сейчас: пересозданное тело или подвал ставятся под наблюдение заново. */\n #watchedBody: HTMLElement | null = null;\n #watchedFooter: HTMLElement | null = null;\n\n /** Наблюдение поставлено: тело и подвал могут быть и пустыми, флаг отличает «снято» от «нечего смотреть». */\n #isWatching: boolean = false;\n\n /** Перемер по концу перехода или анимации внутри тела; одна ссылка — чтобы снять тот же слушатель. */\n readonly #onSettled: () => void = (): void => this.onBodyScroll();\n\n constructor() {\n // Наблюдатели живут, только пока признак включён: выключенный признак ничего не рисует, а\n // наблюдатель состава с поддеревом заставлял бы браузер пересчитывать раскладку на каждую\n // правку внутри тела у каждой области экрана. Включение ставит их, выключение снимает.\n afterRenderEffect((): void => {\n const body: HTMLElement | null = this.bodyRef()?.nativeElement ?? null;\n const footer: HTMLElement | null = this.footerRef()?.nativeElement ?? null;\n\n if (!this.isScrollHintShown()) {\n this.#unwatch();\n this.hasMoreBelow.set(false);\n\n return;\n }\n\n this.onBodyScroll();\n\n if (!this.#isWatching || body !== this.#watchedBody || footer !== this.#watchedFooter) {\n this.#watch(body, footer);\n }\n });\n\n this.#destroyRef.onDestroy((): void => this.#unwatch());\n }\n\n /**\n * Нажатие на значок уводит тело к самому низу. Нажатие останавливается здесь и в содержимое\n * под значком не уходит: под ним живой пункт, и человек, целившийся в подсказку, уехал бы на\n * чужой экран.\n */\n public onScrollHintClick(event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n\n const body: TNullable<ElementRef<HTMLElement>> = this.bodyRef();\n\n if (!body) {\n return;\n }\n\n body.nativeElement.scrollTo({ top: body.nativeElement.scrollHeight, behavior: 'smooth' });\n }\n\n public onBodyScroll(): void {\n // Выключенный признак не рисуется, и замер для него — чтение раскладки на каждое движение.\n if (!this.isScrollHintShown()) {\n return;\n }\n\n this.#measureHintBottom();\n\n const body: TNullable<ElementRef<HTMLElement>> = this.bodyRef();\n\n if (!body) {\n this.hasMoreBelow.set(false);\n\n return;\n }\n\n const node: HTMLElement = body.nativeElement;\n\n this.hasMoreBelow.set(node.scrollHeight - node.scrollTop - node.clientHeight > BOTTOM_SLACK_PX);\n }\n\n /**\n * Подъём полосы над низом области: высота подвала без его верхнего отступа. Ровно на этом\n * месте начинается содержимое подвала, и до него полоса и должна доходить.\n */\n #measureHintBottom(): void {\n const footer: TNullable<ElementRef<HTMLElement>> = this.footerRef();\n\n if (!footer) {\n this.hintBottom.set(0);\n\n return;\n }\n\n const node: HTMLElement = footer.nativeElement;\n const paddingTop: number = parseFloat(getComputedStyle(node).paddingTop) || 0;\n\n this.hintBottom.set(Math.max(node.offsetHeight - paddingTop, 0));\n }\n\n /** Ставит под наблюдение нынешние тело и подвал; прежние наблюдатели снимаются. */\n #watch(body: HTMLElement | null, footer: HTMLElement | null): void {\n this.#unwatch();\n this.#watchedBody = body;\n this.#watchedFooter = footer;\n this.#isWatching = true;\n\n if (!body) {\n return;\n }\n\n if (typeof ResizeObserver !== 'undefined') {\n this.#sizeWatch = new ResizeObserver((): void => this.onBodyScroll());\n this.#observeSizes();\n }\n\n if (typeof MutationObserver !== 'undefined') {\n this.#contentWatch = new MutationObserver((): void => {\n this.#observeSizes();\n this.onBodyScroll();\n });\n this.#contentWatch.observe(body, { childList: true, subtree: true, characterData: true });\n }\n\n // Раскрытая папка дорастает переходом высоты от нуля: состав меняется в миг вставки, когда\n // высота ещё ноль, а наблюдатель размера пропускал конец роста. Конец перехода и анимации\n // всплывает к телу и перемеряет его по вставшей высоте.\n body.addEventListener('transitionend', this.#onSettled);\n body.addEventListener('animationend', this.#onSettled);\n }\n\n /**\n * Размер наблюдается у тела, у каждого его прямого потомка и у подвала. Подвал — наравне с\n * телом: от его высоты считается подъём полосы, и не наблюдавшаяся, она осталась бы стоять по\n * прежней высоте подвала и наехала бы на его первую строку.\n */\n #observeSizes(): void {\n const watch: TNullable<ResizeObserver> = this.#sizeWatch;\n const body: HTMLElement | null = this.#watchedBody;\n\n if (!watch || !body) {\n return;\n }\n\n // Состав тела сменился — ушедшие узлы снимаются вместе со всеми, пришедшие ставятся заново.\n watch.disconnect();\n watch.observe(body);\n Array.from(body.children).forEach((child: Element): void => watch.observe(child));\n\n if (this.#watchedFooter) {\n watch.observe(this.#watchedFooter);\n }\n }\n\n #unwatch(): void {\n this.#watchedBody?.removeEventListener('transitionend', this.#onSettled);\n this.#watchedBody?.removeEventListener('animationend', this.#onSettled);\n this.#sizeWatch?.disconnect();\n this.#sizeWatch = null;\n this.#contentWatch?.disconnect();\n this.#contentWatch = null;\n this.#watchedBody = null;\n this.#watchedFooter = null;\n this.#isWatching = false;\n }\n}\n","<ng-container rtBlock=\"rt-scroll-area\">\n @if (headerTpl()) {\n <div rtElem=\"header\" qa-dataid=\"scroll-area-header\">\n <ng-container [ngTemplateOutlet]=\"headerTpl()?.templateRef ?? null\" />\n </div>\n }\n\n @if (contentTpl()) {\n <div #body rtElem=\"body\" qa-dataid=\"scroll-area-body\" (scroll)=\"onBodyScroll()\">\n <ng-container [ngTemplateOutlet]=\"contentTpl()?.templateRef ?? null\" />\n </div>\n }\n\n @if (footerTpl()) {\n <div #foot rtElem=\"footer\" qa-dataid=\"scroll-area-footer\">\n <ng-container [ngTemplateOutlet]=\"footerTpl()?.templateRef ?? null\" />\n </div>\n }\n\n <!--\n Признак того, что снизу осталось непоказанное: край списка, ровно обрезанный подвалом,\n читается как конец списка, и человек считает, что пункт пропал. Полоса живёт на хосте, а\n не внутри тела: тело обрезает всё по своему краю, и полоса обрывалась бы на верхнем\n отступе подвала — между ней и разделителем, который потребитель рисует первой строкой\n подвала, оставалась бы полоса чистого фона.\n -->\n @if (isScrollHintShown() && hasMoreBelow()) {\n <div rtElem=\"scroll-hint\" qa-dataid=\"scroll-area-scroll-hint\" [style.bottom.px]=\"hintBottom()\">\n <rt-icon\n rtElem=\"scroll-hint-icon\"\n qa-dataid=\"scroll-area-scroll-hint-icon\"\n name=\"chevron-down\"\n size=\"md\"\n role=\"button\"\n tabindex=\"0\"\n rtTooltipPlacement=\"top\"\n [rtTooltip]=\"t().uiScrollDown\"\n [attr.aria-label]=\"t().uiScrollDown\"\n (click)=\"onScrollHintClick($event)\"\n (keydown.enter)=\"onScrollHintClick($event)\" />\n </div>\n }\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-scroll-area.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/scroll-area/rt-scroll-area.directives.ts","../../../projects/ui-kit-v2/src/lib/components/scroll-area/rt-scroll-area.component.ts","../../../projects/ui-kit-v2/src/lib/components/scroll-area/rt-scroll-area.component.html"],"sourcesContent":["import { Directive, inject, TemplateRef } from '@angular/core';\n\n/**\n * Маркеры частей области прокрутки — своим файлом, как у соседних семей.\n *\n * Каждый маркер держит свой шаблон и не знает об области ничего: область читает маркеры среди\n * своего содержимого и берёт у них шаблон. Часть, которую потребитель не объявил, для области\n * просто отсутствует — рисовать нечего.\n */\n\n/**\n * Шапка области: стоит сверху и не прокручивается.\n *\n * @example\n * ```html\n * <rt-scroll-area>\n * <ng-container *rtScrollAreaHeader>\n * <h3>Заголовок</h3>\n * </ng-container>\n * </rt-scroll-area>\n * ```\n */\n@Directive({\n selector: '[rtScrollAreaHeader]',\n})\nexport class RtScrollAreaHeaderDirective {\n public readonly templateRef: TemplateRef<void> = inject<TemplateRef<void>>(TemplateRef);\n}\n\n/** Тело области: единственная часть, которая прокручивается. */\n@Directive({\n selector: '[rtScrollAreaContent]',\n})\nexport class RtScrollAreaContentDirective {\n public readonly templateRef: TemplateRef<void> = inject<TemplateRef<void>>(TemplateRef);\n}\n\n/** Подвал области: стоит снизу и не прокручивается. */\n@Directive({\n selector: '[rtScrollAreaFooter]',\n})\nexport class RtScrollAreaFooterDirective {\n public readonly templateRef: TemplateRef<void> = inject<TemplateRef<void>>(TemplateRef);\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterRenderEffect,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n contentChild,\n DestroyRef,\n ElementRef,\n inject,\n input,\n InputSignalWithTransform,\n signal,\n Signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\nimport { TNullable } from '@rt-tools/utils';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtScrollAreaContentDirective, RtScrollAreaFooterDirective, RtScrollAreaHeaderDirective } from './rt-scroll-area.directives';\n\nconst BEM_BLOCK: string = 'rt-scroll-area';\n\n/**\n * Целый пункт запаса при сравнении прокрутки с высотой.\n *\n * Дробная высота строки оставляет остаток, при котором прокрутка уже в самом низу, а разность\n * всё ещё больше нуля: без запаса признак непоказанного висел бы навсегда.\n */\nconst BOTTOM_SLACK_PX: number = 1;\n\n/**\n * Область прокрутки: шапка и подвал стоят, тело прокручивается.\n *\n * Части объявляет потребитель — по директиве на каждую. Необъявленная часть не рисуется вовсе:\n * пустая шапка это полоса отступа над списком, а высоту она забирает у тела, ради которого\n * область и открыли.\n *\n * Договорённость — `docs/specs/ui-kit-v2/scroll-area/`.\n */\n@Component({\n selector: 'rt-scroll-area',\n templateUrl: './rt-scroll-area.component.html',\n styleUrls: ['./rt-scroll-area.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n NgTemplateOutlet,\n\n // standalone components / directives\n RtIconComponent,\n RtTooltipDirective,\n BlockDirective,\n ElemDirective,\n ],\n host: { class: BEM_BLOCK },\n})\nexport class RtScrollAreaComponent {\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly bodyRef: Signal<TNullable<ElementRef<HTMLElement>>> = viewChild<ElementRef<HTMLElement>>('body');\n protected readonly footerRef: Signal<TNullable<ElementRef<HTMLElement>>> = viewChild<ElementRef<HTMLElement>>('foot');\n\n /**\n * Ниже окна области ещё есть содержимое. Считается по трём числам самого узла, а не по числу\n * пунктов: область не знает, что ей подали, и высота строки у каждого потребителя своя.\n */\n protected readonly hasMoreBelow: WritableSignal<boolean> = signal(false);\n\n /**\n * На сколько полоса растушёвки поднята над низом области. Она обязана кончиться там, где\n * начинается содержимое подвала: разделитель потребитель рисует первой его строкой, и полоса,\n * оборвавшаяся выше, оставляет до него чистый фон. Считается замером — верхний отступ подвала\n * потребитель пишет как хочет, и вычесть его в стилях не из чего. Без подвала полоса стоит у\n * самого низа.\n */\n protected readonly hintBottom: WritableSignal<number> = signal(0);\n\n public readonly headerTpl: Signal<TNullable<RtScrollAreaHeaderDirective>> = contentChild(RtScrollAreaHeaderDirective);\n public readonly contentTpl: Signal<TNullable<RtScrollAreaContentDirective>> = contentChild(RtScrollAreaContentDirective);\n public readonly footerTpl: Signal<TNullable<RtScrollAreaFooterDirective>> = contentChild(RtScrollAreaFooterDirective);\n\n /**\n * Признак того, что снизу осталось непоказанное. Выключен по умолчанию: область рисуют\n * десятки экранов, и признак, поставленный безусловно, сдвинул бы вид каждому из них.\n */\n public readonly isScrollHintShown: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n readonly #destroyRef: DestroyRef = inject(DestroyRef);\n\n /**\n * Наблюдатель за размером тела, его содержимого и подвала. Событие прокрутки приходит только\n * после движения руки, а список не влезает уже в первую минуту показа: без наблюдателя признак\n * появлялся бы у того, кто и так догадался прокрутить, и молчал у того, кому он нужен.\n */\n #sizeWatch: TNullable<ResizeObserver> = null;\n\n /**\n * Наблюдатель за составом тела. Тело — окно постоянной высоты, и когда список дорастает под ним,\n * размер тела не меняется: наблюдатель размера молчит. Пришедшие узлы ставятся под наблюдение\n * размера здесь же — их высота меняется и после того, как они легли.\n */\n #contentWatch: TNullable<MutationObserver> = null;\n\n /** Узлы, за которыми наблюдают сейчас: пересозданное тело или подвал ставятся под наблюдение заново. */\n #watchedBody: HTMLElement | null = null;\n #watchedFooter: HTMLElement | null = null;\n\n /** Наблюдение поставлено: тело и подвал могут быть и пустыми, флаг отличает «снято» от «нечего смотреть». */\n #isWatching: boolean = false;\n\n /** Перемер по концу перехода или анимации внутри тела; одна ссылка — чтобы снять тот же слушатель. */\n readonly #onSettled: () => void = (): void => this.onBodyScroll();\n\n constructor() {\n // Наблюдатели живут, только пока признак включён: выключенный признак ничего не рисует, а\n // наблюдатель состава с поддеревом заставлял бы браузер пересчитывать раскладку на каждую\n // правку внутри тела у каждой области экрана. Включение ставит их, выключение снимает.\n afterRenderEffect((): void => {\n const body: HTMLElement | null = this.bodyRef()?.nativeElement ?? null;\n const footer: HTMLElement | null = this.footerRef()?.nativeElement ?? null;\n\n if (!this.isScrollHintShown()) {\n this.#unwatch();\n this.hasMoreBelow.set(false);\n\n return;\n }\n\n this.onBodyScroll();\n\n if (!this.#isWatching || body !== this.#watchedBody || footer !== this.#watchedFooter) {\n this.#watch(body, footer);\n }\n });\n\n this.#destroyRef.onDestroy((): void => this.#unwatch());\n }\n\n /**\n * Нажатие на значок уводит тело к самому низу. Нажатие останавливается здесь и в содержимое\n * под значком не уходит: под ним живой пункт, и человек, целившийся в подсказку, уехал бы на\n * чужой экран.\n */\n public onScrollHintClick(event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n\n const body: TNullable<ElementRef<HTMLElement>> = this.bodyRef();\n\n if (!body) {\n return;\n }\n\n body.nativeElement.scrollTo({ top: body.nativeElement.scrollHeight, behavior: 'smooth' });\n }\n\n public onBodyScroll(): void {\n // Выключенный признак не рисуется, и замер для него — чтение раскладки на каждое движение.\n if (!this.isScrollHintShown()) {\n return;\n }\n\n this.#measureHintBottom();\n\n const body: TNullable<ElementRef<HTMLElement>> = this.bodyRef();\n\n if (!body) {\n this.hasMoreBelow.set(false);\n\n return;\n }\n\n const node: HTMLElement = body.nativeElement;\n\n this.hasMoreBelow.set(node.scrollHeight - node.scrollTop - node.clientHeight > BOTTOM_SLACK_PX);\n }\n\n /**\n * Подъём полосы над низом области: высота подвала без его верхнего отступа. Ровно на этом\n * месте начинается содержимое подвала, и до него полоса и должна доходить.\n */\n #measureHintBottom(): void {\n const footer: TNullable<ElementRef<HTMLElement>> = this.footerRef();\n\n if (!footer) {\n this.hintBottom.set(0);\n\n return;\n }\n\n const node: HTMLElement = footer.nativeElement;\n const paddingTop: number = parseFloat(getComputedStyle(node).paddingTop) || 0;\n\n this.hintBottom.set(Math.max(node.offsetHeight - paddingTop, 0));\n }\n\n /** Ставит под наблюдение нынешние тело и подвал; прежние наблюдатели снимаются. */\n #watch(body: HTMLElement | null, footer: HTMLElement | null): void {\n this.#unwatch();\n this.#watchedBody = body;\n this.#watchedFooter = footer;\n this.#isWatching = true;\n\n if (!body) {\n return;\n }\n\n if (typeof ResizeObserver !== 'undefined') {\n this.#sizeWatch = new ResizeObserver((): void => this.onBodyScroll());\n this.#observeSizes();\n }\n\n if (typeof MutationObserver !== 'undefined') {\n this.#contentWatch = new MutationObserver((): void => {\n this.#observeSizes();\n this.onBodyScroll();\n });\n this.#contentWatch.observe(body, { childList: true, subtree: true, characterData: true });\n }\n\n // Раскрытая папка дорастает переходом высоты от нуля: состав меняется в миг вставки, когда\n // высота ещё ноль, а наблюдатель размера пропускал конец роста. Конец перехода и анимации\n // всплывает к телу и перемеряет его по вставшей высоте.\n body.addEventListener('transitionend', this.#onSettled);\n body.addEventListener('animationend', this.#onSettled);\n }\n\n /**\n * Размер наблюдается у тела, у каждого его прямого потомка и у подвала. Подвал — наравне с\n * телом: от его высоты считается подъём полосы, и не наблюдавшаяся, она осталась бы стоять по\n * прежней высоте подвала и наехала бы на его первую строку.\n */\n #observeSizes(): void {\n const watch: TNullable<ResizeObserver> = this.#sizeWatch;\n const body: HTMLElement | null = this.#watchedBody;\n\n if (!watch || !body) {\n return;\n }\n\n // Состав тела сменился — ушедшие узлы снимаются вместе со всеми, пришедшие ставятся заново.\n watch.disconnect();\n watch.observe(body);\n Array.from(body.children).forEach((child: Element): void => watch.observe(child));\n\n if (this.#watchedFooter) {\n watch.observe(this.#watchedFooter);\n }\n }\n\n #unwatch(): void {\n this.#watchedBody?.removeEventListener('transitionend', this.#onSettled);\n this.#watchedBody?.removeEventListener('animationend', this.#onSettled);\n this.#sizeWatch?.disconnect();\n this.#sizeWatch = null;\n this.#contentWatch?.disconnect();\n this.#contentWatch = null;\n this.#watchedBody = null;\n this.#watchedFooter = null;\n this.#isWatching = false;\n }\n}\n","<ng-container rtBlock=\"rt-scroll-area\">\n @if (headerTpl()) {\n <div rtElem=\"header\" qa-dataid=\"scroll-area-header\">\n <ng-container [ngTemplateOutlet]=\"headerTpl()?.templateRef ?? null\" />\n </div>\n }\n\n @if (contentTpl()) {\n <div #body rtElem=\"body\" qa-dataid=\"scroll-area-body\" (scroll)=\"onBodyScroll()\">\n <ng-container [ngTemplateOutlet]=\"contentTpl()?.templateRef ?? null\" />\n </div>\n }\n\n @if (footerTpl()) {\n <div #foot rtElem=\"footer\" qa-dataid=\"scroll-area-footer\">\n <ng-container [ngTemplateOutlet]=\"footerTpl()?.templateRef ?? null\" />\n </div>\n }\n\n <!--\n Признак того, что снизу осталось непоказанное: край списка, ровно обрезанный подвалом,\n читается как конец списка, и человек считает, что пункт пропал. Полоса живёт на хосте, а\n не внутри тела: тело обрезает всё по своему краю, и полоса обрывалась бы на верхнем\n отступе подвала — между ней и разделителем, который потребитель рисует первой строкой\n подвала, оставалась бы полоса чистого фона.\n -->\n @if (isScrollHintShown() && hasMoreBelow()) {\n <div rtElem=\"scroll-hint\" qa-dataid=\"scroll-area-scroll-hint\" [style.bottom.px]=\"hintBottom()\">\n <rt-icon\n rtElem=\"scroll-hint-icon\"\n qa-dataid=\"scroll-area-scroll-hint-icon\"\n name=\"chevron-down\"\n size=\"md\"\n role=\"button\"\n tabindex=\"0\"\n rtTooltipPlacement=\"top\"\n [rtTooltip]=\"t().uiScrollDown\"\n [attr.aria-label]=\"t().uiScrollDown\"\n (click)=\"onScrollHintClick($event)\"\n (keydown.enter)=\"onScrollHintClick($event)\" />\n </div>\n }\n</ng-container>\n"],"mappings":"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"}
@@ -5,13 +5,48 @@ import * as i2 from "@angular/forms";
5
5
  import { FormsModule } from "@angular/forms";
6
6
  import { BlockDirective, ElemDirective, ModDirective } from "@rt-tools/core";
7
7
  import { RT_KIT_LABELS, RtOptionList, rtKitLabel, rtTreeFind, rtTreeIsTree, rtTreeOpenFor, rtTreeRows, rtTreeSideKey, rtTreeToggle } from "@rt-tools/ui-kit-v2/core";
8
- import { RtFormControlBase } from "@rt-tools/ui-kit-v2/form-control";
8
+ import { RtFormControlBase, rtNotchWidth } from "@rt-tools/ui-kit-v2/form-control";
9
9
  import { RtIconComponent } from "@rt-tools/ui-kit-v2/icon";
10
10
  import { RtIconButtonComponent } from "@rt-tools/ui-kit-v2/icon-button";
11
11
  import { RtInputComponent } from "@rt-tools/ui-kit-v2/input";
12
12
  import { RtPopoverDirective } from "@rt-tools/ui-kit-v2/popover";
13
13
  import * as i1 from "@rt-tools/ui-kit-v2/radius";
14
14
  import { RtRadiusDirective } from "@rt-tools/ui-kit-v2/radius";
15
+ var RtSelectOptionDirective = class RtSelectOptionDirective {
16
+ constructor() {
17
+ this.templateRef = inject(TemplateRef);
18
+ }
19
+ static {
20
+ this.ɵfac = i0.ɵɵngDeclareFactory({
21
+ minVersion: "12.0.0",
22
+ version: "22.2.2",
23
+ ngImport: i0,
24
+ type: RtSelectOptionDirective,
25
+ deps: [],
26
+ target: i0.ɵɵFactoryTarget.Directive
27
+ });
28
+ }
29
+ static {
30
+ this.ɵdir = i0.ɵɵngDeclareDirective({
31
+ minVersion: "14.0.0",
32
+ version: "22.2.2",
33
+ type: RtSelectOptionDirective,
34
+ isStandalone: true,
35
+ selector: "[rtSelectOption]",
36
+ ngImport: i0
37
+ });
38
+ }
39
+ };
40
+ i0.ɵɵngDeclareClassMetadata({
41
+ minVersion: "12.0.0",
42
+ version: "22.2.2",
43
+ ngImport: i0,
44
+ type: RtSelectOptionDirective,
45
+ decorators: [{
46
+ type: Directive,
47
+ args: [{ selector: "[rtSelectOption]" }]
48
+ }]
49
+ });
15
50
  var RtSelectTriggerDirective = class RtSelectTriggerDirective {
16
51
  constructor() {
17
52
  this.templateRef = inject(TemplateRef);
@@ -62,6 +97,8 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
62
97
  this.filterPlaceholderText = computed(() => this.filterPlaceholder() || this.#t_uiSearch(), ...ngDevMode ? [{ debugName: "filterPlaceholderText" }] : /* istanbul ignore next */ []);
63
98
  this.popover = viewChild.required(RtPopoverDirective, ...ngDevMode ? [{ debugName: "popover" }] : /* istanbul ignore next */ []);
64
99
  this.triggerEl = viewChild("triggerEl", ...ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []);
100
+ this.floatingLabelEl = viewChild("floatingLabelEl", ...ngDevMode ? [{ debugName: "floatingLabelEl" }] : /* istanbul ignore next */ []);
101
+ this.notchWidth = rtNotchWidth(this.floatingLabelEl);
65
102
  this.filterTerm = signal("", ...ngDevMode ? [{ debugName: "filterTerm" }] : /* istanbul ignore next */ []);
66
103
  this.openBranches = signal(/* @__PURE__ */ new Set(), ...ngDevMode ? [{ debugName: "openBranches" }] : /* istanbul ignore next */ []);
67
104
  this.isOpen = computed(() => this.popover().isOpen(), ...ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []);
@@ -77,12 +114,27 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
77
114
  this.activeIndex = this.optionList.activeIndex;
78
115
  this.activeOptionId = this.optionList.activeOptionId;
79
116
  this.triggerTpl = contentChild(RtSelectTriggerDirective, ...ngDevMode ? [{ debugName: "triggerTpl" }] : /* istanbul ignore next */ []);
117
+ this.optionTpl = contentChild(RtSelectOptionDirective, ...ngDevMode ? [{ debugName: "optionTpl" }] : /* istanbul ignore next */ []);
80
118
  this.hasValue = computed(() => this.value() !== null, ...ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []);
81
- this.selectedLabel = computed(() => {
119
+ this.floatingLabelId = `${this.panelId}-label`;
120
+ this.valueTextId = `${this.panelId}-value`;
121
+ this.labelledBy = computed(() => this.floatingLabel() && !this.ariaLabel() ? `${this.floatingLabelId} ${this.valueTextId}` : null, ...ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []);
122
+ this.selectedOption = computed(() => {
82
123
  const v = this.value();
83
- if (v === null) return "";
84
- return rtTreeFind(this.options(), v)?.label ?? "";
85
- }, ...ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []);
124
+ return v === null ? null : rtTreeFind(this.options(), v) ?? null;
125
+ }, ...ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []);
126
+ this.chosenOwn = computed(() => {
127
+ const tpl = this.optionTpl();
128
+ const option = this.selectedOption();
129
+ return tpl && option ? {
130
+ templateRef: tpl.templateRef,
131
+ context: {
132
+ $implicit: option,
133
+ selected: true
134
+ }
135
+ } : null;
136
+ }, ...ngDevMode ? [{ debugName: "chosenOwn" }] : /* istanbul ignore next */ []);
137
+ this.selectedLabel = computed(() => this.selectedOption()?.label ?? "", ...ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []);
86
138
  this.isTree = computed(() => rtTreeIsTree(this.options()), ...ngDevMode ? [{ debugName: "isTree" }] : /* istanbul ignore next */ []);
87
139
  this.rows = computed(() => rtTreeRows(this.options(), this.openBranches(), this.filterTerm()), ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
88
140
  this.triggerState = computed(() => ({
@@ -91,11 +143,13 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
91
143
  label: this.selectedLabel(),
92
144
  isDisabled: this.isDisabled()
93
145
  }), ...ngDevMode ? [{ debugName: "triggerState" }] : /* istanbul ignore next */ []);
146
+ this.fieldLabelInside = true;
94
147
  this.appearance = input("outline", ...ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []);
95
148
  this.displayText = computed(() => this.selectedLabel(), ...ngDevMode ? [{ debugName: "displayText" }] : /* istanbul ignore next */ []);
96
149
  this.options = input([], ...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
97
150
  this.placeholder = input("", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
98
151
  this.iconLeft = input(null, ...ngDevMode ? [{ debugName: "iconLeft" }] : /* istanbul ignore next */ []);
152
+ this.floatingLabel = input(null, ...ngDevMode ? [{ debugName: "floatingLabel" }] : /* istanbul ignore next */ []);
99
153
  this.filter = input(false, {
100
154
  ...ngDevMode ? { debugName: "filter" } : /* istanbul ignore next */ {},
101
155
  transform: booleanAttribute
@@ -182,6 +236,7 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
182
236
  this.#selectActive();
183
237
  break;
184
238
  case "close":
239
+ event.stopPropagation();
185
240
  this.popover().close();
186
241
  break;
187
242
  case "side": this.#sideKey(event.key);
@@ -244,6 +299,13 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
244
299
  isRequired: false,
245
300
  transformFunction: null
246
301
  },
302
+ floatingLabel: {
303
+ classPropertyName: "floatingLabel",
304
+ publicName: "floatingLabel",
305
+ isSignal: true,
306
+ isRequired: false,
307
+ transformFunction: null
308
+ },
247
309
  filter: {
248
310
  classPropertyName: "filter",
249
311
  publicName: "filter",
@@ -284,7 +346,10 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
284
346
  "class.rt-select--appearance--fill": "appearance() === 'fill'",
285
347
  "class.rt-select--size--sm": "size() === 'sm'",
286
348
  "class.rt-select--size--lg": "size() === 'lg'",
287
- "class.rt-select--with-icon-left": "!!iconLeft()"
349
+ "class.rt-select--with-icon-left": "!!iconLeft()",
350
+ "class.rt-select--floating": "!!floatingLabel()",
351
+ "style.--rt-select-notch-width": "notchWidth() + 'px'",
352
+ "class.rt-select--filled": "hasValue()"
288
353
  },
289
354
  classAttribute: "rt-select"
290
355
  },
@@ -298,28 +363,49 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
298
363
  predicate: RtSelectTriggerDirective,
299
364
  descendants: true,
300
365
  isSignal: true
301
- }],
302
- viewQueries: [{
303
- propertyName: "popover",
304
- first: true,
305
- predicate: RtPopoverDirective,
306
- descendants: true,
307
- isSignal: true
308
366
  }, {
309
- propertyName: "triggerEl",
367
+ propertyName: "optionTpl",
310
368
  first: true,
311
- predicate: ["triggerEl"],
369
+ predicate: RtSelectOptionDirective,
312
370
  descendants: true,
313
371
  isSignal: true
314
372
  }],
373
+ viewQueries: [
374
+ {
375
+ propertyName: "popover",
376
+ first: true,
377
+ predicate: RtPopoverDirective,
378
+ descendants: true,
379
+ isSignal: true
380
+ },
381
+ {
382
+ propertyName: "triggerEl",
383
+ first: true,
384
+ predicate: ["triggerEl"],
385
+ descendants: true,
386
+ isSignal: true
387
+ },
388
+ {
389
+ propertyName: "floatingLabelEl",
390
+ first: true,
391
+ predicate: ["floatingLabelEl"],
392
+ descendants: true,
393
+ isSignal: true
394
+ }
395
+ ],
315
396
  usesInheritance: true,
316
397
  hostDirectives: [{
317
398
  directive: i1.RtRadiusDirective,
318
399
  inputs: ["radius", "radius"]
319
400
  }],
320
401
  ngImport: i0,
321
- template: "<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n {{ selectedLabel() || placeholder() }}\n </span>\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [style.--rt-select-panel-max-height]=\"panelMaxHeight()\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input\n iconLeft=\"ico-search\"\n [placeholder]=\"filterPlaceholderText()\"\n [ngModel]=\"filterTerm()\"\n (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n Опции listbox'а выбираются мышью через (click); keyboard-nav —\n на trigger'е (activedescendant: ArrowUp/Down/Enter), опции не\n focusable индивидуально, поэтому a11y-правила здесь не применимы.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-select-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: row.option.value === value(),\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"row.option.value === value()\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"select(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"select-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n <span rtElem=\"option-label\">{{ row.option.label }}</span>\n @if (row.option.value === value()) {\n <rt-icon rtElem=\"check\" name=\"check\" size=\"sm\" aria-hidden=\"true\" />\n }\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n",
322
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-radius: var(--rt-input-radius);--rt-select-fill-radius: var(--rt-radius-sm);--rt-select-caret-transition-duration: var(--rt-duration-fast);--rt-select-own-outline-width: var(--rt-border-width-medium);position:relative;display:block;width:100%}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select--appearance--fill .rt-select__trigger{border-color:transparent;border-radius:var(--rt-select-fill-radius) var(--rt-select-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-select-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select__trigger--own{width:auto;min-height:0;padding:0;border-color:transparent;background-color:transparent;line-height:var(--rt-leading-none)}.rt-select__trigger--own:focus{border-color:transparent;box-shadow:none}.rt-select__trigger--own:focus-visible{outline:var(--rt-select-own-outline-width) solid var(--rt-input-color-border-focus);outline-offset:var(--rt-input-focus-outline-offset)}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--open .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--invalid .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger--own:focus{box-shadow:none}.rt-select--disabled{opacity:var(--rt-input-disabled-opacity)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}:is(.rt-preset-material,[data-preset=material]) .rt-select__chevron{max-width:10px;max-height:5px;background-color:currentcolor;clip-path:polygon(0 0,100% 0,50% 100%);transform:none}:is(.rt-preset-material,[data-preset=material]) .rt-select__chevron svg{display:none}.rt-select__panel{--rt-select-panel-shadow: var(--rt-overlay-select-shadow);--rt-select-panel-radius: var(--rt-overlay-select-radius);--rt-select-option-font-size: var(--rt-overlay-select-option-font-size);--rt-select-selected-bg: var(--rt-overlay-select-selected-bg);--rt-select-selected-weight: var(--rt-overlay-select-selected-weight);--rt-select-check-display: var(--rt-overlay-select-check-display);--rt-select-option-min-height: var(--rt-overlay-select-option-min-height);--rt-select-panel-border-width: var(--rt-overlay-select-panel-border-width);--rt-select-tree-indent: var(--rt-space-lg);--rt-select-panel-max-height: var(--rt-input-panel-max-height);width:100%;max-height:var(--rt-select-panel-max-height);box-sizing:border-box;border:var(--rt-select-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-select-panel-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{--rt-select-tree-level: 0;display:flex;min-height:var(--rt-select-option-min-height);box-sizing:border-box;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-select-option-font-size);gap:var(--rt-space-sm);padding-inline-start:calc(var(--rt-space-md) + var(--rt-select-tree-indent) * var(--rt-select-tree-level))}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-select-selected-bg);font-weight:var(--rt-select-selected-weight)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__option-label{min-width:0;flex:1 1 auto}.rt-select__toggle,.rt-select__toggle-space{display:inline-flex;flex-shrink:0;block-size:var(--rt-input-icon-size);inline-size:var(--rt-input-icon-size)}.rt-select__toggle{align-items:center;justify-content:center;padding:0;border:0;background:transparent;cursor:pointer}.rt-select__check{display:var(--rt-select-check-display);flex-shrink:0}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}.rt-select[data-rt-radius=none]{--rt-select-radius: var(--rt-radius-none)}.rt-select[data-rt-radius=xs]{--rt-select-radius: var(--rt-radius-xs)}.rt-select[data-rt-radius=sm]{--rt-select-radius: var(--rt-radius-sm)}.rt-select[data-rt-radius=ms]{--rt-select-radius: var(--rt-radius-ms)}.rt-select[data-rt-radius=md]{--rt-select-radius: var(--rt-radius-md)}.rt-select[data-rt-radius=lg]{--rt-select-radius: var(--rt-radius-lg)}.rt-select[data-rt-radius=xl]{--rt-select-radius: var(--rt-radius-xl)}.rt-select[data-rt-radius=\"2xl\"]{--rt-select-radius: var(--rt-radius-2xl)}.rt-select[data-rt-radius=full]{--rt-select-radius: var(--rt-radius-full)}}\n"],
402
+ template: "<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [id]=\"valueTextId\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n <!-- Шаблон строки рисует и выбранное значение: цветная метка из списка не пропадает в поле. -->\n @if (chosenOwn(); as own) {\n <span rtElem=\"option-content\" qa-dataid=\"select-value-content\">\n <ng-container [ngTemplateOutlet]=\"own.templateRef\" [ngTemplateOutletContext]=\"own.context\" />\n </span>\n } @else {\n {{ selectedLabel() || placeholder() }}\n }\n </span>\n @if (showClearButton()) {\n @if (clearTabbable()) {\n <!-- Место крестика в строке: сам крестик в порядке Tab стоит рядом с кнопкой, ниже. -->\n <span rtElem=\"clear-slot\" aria-hidden=\"true\"></span>\n } @else {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n @if (showClearButton() && clearTabbable() && !triggerTpl()) {\n <!--\n Крестик в порядке обхода — соседняя кнопка поверх своего места в строке, а не кнопка в\n кнопке: вложенную кнопку скринридер не объявляет, а её Enter открывал бы список.\n -->\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"select-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [rtMod]=\"{ outside: true }\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"0\"\n (clicked)=\"clear($event)\" />\n }\n\n @if (floatingLabel(); as label) {\n <!-- Подпись — картинка поверх указателя: клик проходит сквозь неё в кнопку, имя кнопке она даёт по aria-labelledby. -->\n <span #floatingLabelEl rtElem=\"floating-label\" qa-dataid=\"select-floating-label\" aria-hidden=\"true\" [id]=\"floatingLabelId\">\n {{ label }}\n </span>\n }\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [style.--rt-select-panel-max-height]=\"panelMaxHeight()\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input\n iconLeft=\"ico-search\"\n [placeholder]=\"filterPlaceholderText()\"\n [ngModel]=\"filterTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n Опции listbox'а выбираются мышью через (click); keyboard-nav —\n на trigger'е (activedescendant: ArrowUp/Down/Enter), опции не\n focusable индивидуально, поэтому a11y-правила здесь не применимы.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-select-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: row.option.value === value(),\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"row.option.value === value()\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"select(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"select-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n @if (optionTpl(); as tpl) {\n <span rtElem=\"option-content\" qa-dataid=\"select-option-content\">\n <ng-container\n [ngTemplateOutlet]=\"tpl.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: row.option, selected: row.option.value === value() }\" />\n </span>\n } @else {\n <span rtElem=\"option-label\">{{ row.option.label }}</span>\n }\n @if (row.option.value === value()) {\n <rt-icon rtElem=\"check\" name=\"check\" size=\"sm\" aria-hidden=\"true\" />\n }\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n",
403
+ styles: [
404
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-radius: var(--rt-input-radius);--rt-select-fill-radius: var(--rt-radius-sm);--rt-select-caret-transition-duration: var(--rt-duration-fast);--rt-select-own-outline-width: var(--rt-border-width-medium);--rt-select-floating-label-color: var(--rt-input-color-text-placeholder);--rt-select-floating-label-color-focus: var(--rt-input-color-border-focus);--rt-select-floating-label-color-error: var(--rt-input-color-border-error);--rt-select-floating-label-bg: var(--rt-input-color-bg);--rt-select-floating-label-font-size: var(--rt-text-xs);--rt-select-floating-label-start: var(--rt-input-padding-x);--rt-select-floating-label-rest-start: var(--rt-select-floating-label-start);--rt-select-floating-label-raised-start: var(--rt-select-floating-label-start);--rt-select-notch-start: calc(var(--rt-select-floating-label-raised-start) - var(--rt-space-1));--rt-select-notch-depth: calc(2 * var(--rt-input-border-width));--rt-select-notch-width: 0px;--rt-select-fill-floating-height: var(--rt-input-height-md);--rt-select-color-border-hover: var(--rt-color-text-primary);--rt-select-floating-label-duration: var(--rt-duration-fast);position:relative;display:block;width:100%;min-width:var(--rt-input-min-width)}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select--appearance--fill .rt-select__trigger{border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-select-fill-radius) var(--rt-select-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-select-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__trigger:where(:hover:not(:disabled)){border-color:var(--rt-select-color-border-hover)}.rt-select__trigger--own{width:auto;min-height:0;padding:0;border-color:transparent;background-color:transparent;line-height:var(--rt-leading-none)}.rt-select__trigger--own:focus{border-color:transparent;box-shadow:none}.rt-select__trigger--own:focus-visible{outline:var(--rt-select-own-outline-width) solid var(--rt-input-color-border-focus);outline-offset:var(--rt-input-focus-outline-offset)}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--open .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--invalid .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger--own:focus{box-shadow:none}.rt-select--disabled{opacity:var(--rt-input-disabled-field-opacity)}.rt-select--disabled.rt-select--appearance--fill .rt-select__trigger{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;letter-spacing:var(--rt-input-letter-spacing, inherit);text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}.rt-select__panel{--rt-select-panel-shadow: var(--rt-overlay-select-shadow);--rt-select-panel-radius: var(--rt-overlay-select-radius);--rt-select-option-font-size: var(--rt-overlay-select-option-font-size);--rt-select-selected-bg: var(--rt-overlay-select-selected-bg);--rt-select-selected-weight: var(--rt-overlay-select-selected-weight);--rt-select-check-display: var(--rt-overlay-select-check-display);--rt-select-option-min-height: var(--rt-overlay-select-option-min-height);--rt-select-panel-border-width: var(--rt-overlay-select-panel-border-width);--rt-select-tree-indent: var(--rt-space-lg);--rt-select-panel-max-height: var(--rt-input-panel-max-height);width:100%;max-height:var(--rt-select-panel-max-height);box-sizing:border-box;border:var(--rt-select-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-select-panel-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{--rt-select-tree-level: 0;display:flex;min-height:var(--rt-select-option-min-height);box-sizing:border-box;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-select-option-font-size);gap:var(--rt-space-sm);padding-inline-start:calc(var(--rt-space-md) + var(--rt-select-tree-indent) * var(--rt-select-tree-level))}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-select-selected-bg);font-weight:var(--rt-select-selected-weight)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__option-label{min-width:0;flex:1 1 auto}.rt-select__option-content{display:inline-flex;min-width:0;flex:1 1 auto;align-items:center;gap:var(--rt-space-sm)}.rt-select__toggle,.rt-select__toggle-space{display:inline-flex;flex-shrink:0;block-size:var(--rt-input-icon-size);inline-size:var(--rt-input-icon-size)}.rt-select__toggle{align-items:center;justify-content:center;padding:0;border:0;background:transparent;cursor:pointer}.rt-select__check{display:var(--rt-select-check-display);flex-shrink:0}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}.rt-select[data-rt-radius=none]{--rt-select-radius: var(--rt-radius-none)}.rt-select[data-rt-radius=xs]{--rt-select-radius: var(--rt-radius-xs)}.rt-select[data-rt-radius=sm]{--rt-select-radius: var(--rt-radius-sm)}.rt-select[data-rt-radius=ms]{--rt-select-radius: var(--rt-radius-ms)}.rt-select[data-rt-radius=md]{--rt-select-radius: var(--rt-radius-md)}.rt-select[data-rt-radius=lg]{--rt-select-radius: var(--rt-radius-lg)}.rt-select[data-rt-radius=xl]{--rt-select-radius: var(--rt-radius-xl)}.rt-select[data-rt-radius=\"2xl\"]{--rt-select-radius: var(--rt-radius-2xl)}.rt-select[data-rt-radius=full]{--rt-select-radius: var(--rt-radius-full)}}\n",
405
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select--with-icon-left{--rt-select-floating-label-rest-start: calc( var(--rt-select-floating-label-start) + var(--rt-input-icon-size) + var(--rt-space-sm) )}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){--rt-select-floating-label-bg: transparent;--rt-select-floating-label-raised-start: max( var(--rt-select-floating-label-start), calc(min(var(--rt-select-radius), var(--rt-input-height) / 2) + var(--rt-space-2)) )}.rt-select--appearance--fill{--rt-select-floating-label-bg: var(--rt-color-bg-surface-subtle-2);--rt-select-floating-label-raised-start: var(--rt-select-floating-label-start)}.rt-select__floating-label{position:absolute;top:50%;left:var(--rt-select-floating-label-rest-start);display:none;overflow:hidden;max-width:calc(100% - var(--rt-select-floating-label-rest-start) - var(--rt-input-padding-x));color:var(--rt-select-floating-label-color);font-size:var(--rt-input-font-size);line-height:var(--rt-input-line-height);pointer-events:none;text-overflow:ellipsis;transform:translateY(-50%);transition:top var(--rt-select-floating-label-duration) ease,left var(--rt-select-floating-label-duration) ease,font-size var(--rt-select-floating-label-duration) ease,color var(--rt-select-floating-label-duration) ease;white-space:nowrap}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__floating-label{display:block}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):not(.rt-select--filled,.rt-select--open) .rt-select__label--placeholder{color:transparent}.rt-select--filled .rt-select__floating-label,.rt-select--open .rt-select__floating-label{top:0;left:var(--rt-select-floating-label-raised-start);max-width:calc(100% - var(--rt-select-floating-label-raised-start) - var(--rt-select-floating-label-start));padding:0 var(--rt-space-1);margin-left:calc(-1 * var(--rt-space-1));background-color:var(--rt-select-floating-label-bg);font-size:var(--rt-select-floating-label-font-size);line-height:1}.rt-select--open .rt-select__floating-label{color:var(--rt-select-floating-label-color-focus)}.rt-select--invalid .rt-select__floating-label{color:var(--rt-select-floating-label-color-error)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill{--rt-input-height: var(--rt-select-fill-floating-height)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill .rt-select__floating-label{line-height:var(--rt-leading-tight)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill .rt-select__label{align-self:stretch;padding-top:calc(var(--rt-input-height) / 2 - var(--rt-space-1) - var(--rt-input-fill-edge));padding-bottom:calc(var(--rt-input-height) / 2 - var(--rt-space-5) - var(--rt-input-border-width));line-height:var(--rt-leading-normal)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill.rt-select--filled .rt-select__floating-label,.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill.rt-select--open .rt-select__floating-label{top:calc(var(--rt-input-height) / 2 - var(--rt-space-5));left:var(--rt-select-floating-label-rest-start);padding:0;margin-left:0;background-color:transparent;transform:none}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):not(.rt-select--appearance--fill).rt-select--filled .rt-select__trigger,.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):not(.rt-select--appearance--fill).rt-select--open .rt-select__trigger{clip-path:polygon(0 0,var(--rt-select-notch-start) 0,var(--rt-select-notch-start) var(--rt-select-notch-depth),calc(var(--rt-select-notch-start) + var(--rt-select-notch-width)) var(--rt-select-notch-depth),calc(var(--rt-select-notch-start) + var(--rt-select-notch-width)) 0,100% 0,100% 100%,0 100%)}.rt-select--appearance--fill .rt-select__trigger:not(.rt-select__trigger--own):focus{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-select--appearance--fill.rt-select--invalid .rt-select__trigger:not(.rt-select__trigger--own):focus{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-select--readonly .rt-select__floating-label{display:none}}\n",
406
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-chevron-width: var(--rt-size-4)}.rt-select--size--sm{--rt-input-height: var(--rt-input-height-sm)}.rt-select--size--lg{--rt-input-height: var(--rt-input-height-lg)}.rt-select__clear{--rt-icon-color-muted: var(--rt-input-clear-color);--rt-icon-step-sm: var(--rt-input-clear-icon-size)}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__clear .rt-icon-button{width:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width));height:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width))}.rt-select .rt-select__clear .rt-icon-button{--rt-icon-button-radius: var(--rt-input-clear-radius);--rt-icon-button-bg-variant: var(--rt-input-clear-bg);--rt-icon-button-color-bg-hover: var(--rt-input-clear-bg-hover)}.rt-select__clear-slot{width:var(--rt-input-clear-size);height:var(--rt-input-clear-size);flex-shrink:0}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__clear-slot{width:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width));height:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width))}.rt-select__clear--outside{position:absolute;top:50%;right:calc(var(--rt-select-text-inset) + var(--rt-select-chevron-width) + var(--rt-space-sm));transform:translateY(-50%)}.rt-select--readonly .rt-select__clear--outside{display:none}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__chevron{--rt-icon-color-muted: var(--rt-input-icon-color);max-width:10px;max-height:5px;background-color:currentcolor;clip-path:polygon(0 0,100% 0,50% 100%)}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__chevron svg{display:none}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){--rt-select-chevron-width: 10px}}\n",
407
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-edge: var(--rt-input-border-width);--rt-select-text-inset: calc(var(--rt-input-padding-x) + var(--rt-select-edge))}.rt-select--appearance--fill{--rt-select-edge: var(--rt-input-fill-edge)}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){--rt-select-text-inset: var(--rt-input-padding-x)}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__trigger{padding-inline:calc(var(--rt-select-text-inset) - var(--rt-select-edge))}.rt-select--disabled .rt-select__trigger{color:var(--rt-input-color-text-disabled)}.rt-select--disabled .rt-select__label--placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-select--disabled:not(.rt-select--invalid,.rt-select--appearance--fill,.rt-select--borderless) .rt-select__trigger:where(:not(.rt-select__trigger--own)){border-color:var(--rt-input-color-border-disabled)}.rt-select--disabled:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__chevron{--rt-icon-color-muted: var(--rt-input-color-placeholder-disabled)}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__label>.rt-select__option-content{vertical-align:top}}\n"
408
+ ],
323
409
  dependencies: [
324
410
  {
325
411
  kind: "ngmodule",
@@ -404,8 +490,17 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
404
490
  "iconLeft",
405
491
  "iconRight",
406
492
  "passwordToggle",
407
- "autocomplete"
408
- ]
493
+ "autocomplete",
494
+ "maxlength",
495
+ "floatingLabel",
496
+ "iconLeftAction",
497
+ "iconRightAction",
498
+ "iconLeftDisabled",
499
+ "iconRightDisabled",
500
+ "iconLeftTooltip",
501
+ "iconRightTooltip"
502
+ ],
503
+ outputs: ["iconLeftClick", "iconRightClick"]
409
504
  },
410
505
  {
411
506
  kind: "directive",
@@ -490,10 +585,18 @@ i0.ɵɵngDeclareClassMetadata({
490
585
  "[class.rt-select--appearance--fill]": "appearance() === 'fill'",
491
586
  "[class.rt-select--size--sm]": "size() === 'sm'",
492
587
  "[class.rt-select--size--lg]": "size() === 'lg'",
493
- "[class.rt-select--with-icon-left]": "!!iconLeft()"
588
+ "[class.rt-select--with-icon-left]": "!!iconLeft()",
589
+ "[class.rt-select--floating]": "!!floatingLabel()",
590
+ "[style.--rt-select-notch-width]": "notchWidth() + 'px'",
591
+ "[class.rt-select--filled]": "hasValue()"
494
592
  },
495
- template: "<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n {{ selectedLabel() || placeholder() }}\n </span>\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [style.--rt-select-panel-max-height]=\"panelMaxHeight()\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input\n iconLeft=\"ico-search\"\n [placeholder]=\"filterPlaceholderText()\"\n [ngModel]=\"filterTerm()\"\n (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n Опции listbox'а выбираются мышью через (click); keyboard-nav —\n на trigger'е (activedescendant: ArrowUp/Down/Enter), опции не\n focusable индивидуально, поэтому a11y-правила здесь не применимы.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-select-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: row.option.value === value(),\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"row.option.value === value()\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"select(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"select-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n <span rtElem=\"option-label\">{{ row.option.label }}</span>\n @if (row.option.value === value()) {\n <rt-icon rtElem=\"check\" name=\"check\" size=\"sm\" aria-hidden=\"true\" />\n }\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n",
496
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-radius: var(--rt-input-radius);--rt-select-fill-radius: var(--rt-radius-sm);--rt-select-caret-transition-duration: var(--rt-duration-fast);--rt-select-own-outline-width: var(--rt-border-width-medium);position:relative;display:block;width:100%}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select--appearance--fill .rt-select__trigger{border-color:transparent;border-radius:var(--rt-select-fill-radius) var(--rt-select-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-select-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select__trigger--own{width:auto;min-height:0;padding:0;border-color:transparent;background-color:transparent;line-height:var(--rt-leading-none)}.rt-select__trigger--own:focus{border-color:transparent;box-shadow:none}.rt-select__trigger--own:focus-visible{outline:var(--rt-select-own-outline-width) solid var(--rt-input-color-border-focus);outline-offset:var(--rt-input-focus-outline-offset)}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--open .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--invalid .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger--own:focus{box-shadow:none}.rt-select--disabled{opacity:var(--rt-input-disabled-opacity)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}:is(.rt-preset-material,[data-preset=material]) .rt-select__chevron{max-width:10px;max-height:5px;background-color:currentcolor;clip-path:polygon(0 0,100% 0,50% 100%);transform:none}:is(.rt-preset-material,[data-preset=material]) .rt-select__chevron svg{display:none}.rt-select__panel{--rt-select-panel-shadow: var(--rt-overlay-select-shadow);--rt-select-panel-radius: var(--rt-overlay-select-radius);--rt-select-option-font-size: var(--rt-overlay-select-option-font-size);--rt-select-selected-bg: var(--rt-overlay-select-selected-bg);--rt-select-selected-weight: var(--rt-overlay-select-selected-weight);--rt-select-check-display: var(--rt-overlay-select-check-display);--rt-select-option-min-height: var(--rt-overlay-select-option-min-height);--rt-select-panel-border-width: var(--rt-overlay-select-panel-border-width);--rt-select-tree-indent: var(--rt-space-lg);--rt-select-panel-max-height: var(--rt-input-panel-max-height);width:100%;max-height:var(--rt-select-panel-max-height);box-sizing:border-box;border:var(--rt-select-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-select-panel-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{--rt-select-tree-level: 0;display:flex;min-height:var(--rt-select-option-min-height);box-sizing:border-box;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-select-option-font-size);gap:var(--rt-space-sm);padding-inline-start:calc(var(--rt-space-md) + var(--rt-select-tree-indent) * var(--rt-select-tree-level))}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-select-selected-bg);font-weight:var(--rt-select-selected-weight)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__option-label{min-width:0;flex:1 1 auto}.rt-select__toggle,.rt-select__toggle-space{display:inline-flex;flex-shrink:0;block-size:var(--rt-input-icon-size);inline-size:var(--rt-input-icon-size)}.rt-select__toggle{align-items:center;justify-content:center;padding:0;border:0;background:transparent;cursor:pointer}.rt-select__check{display:var(--rt-select-check-display);flex-shrink:0}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}.rt-select[data-rt-radius=none]{--rt-select-radius: var(--rt-radius-none)}.rt-select[data-rt-radius=xs]{--rt-select-radius: var(--rt-radius-xs)}.rt-select[data-rt-radius=sm]{--rt-select-radius: var(--rt-radius-sm)}.rt-select[data-rt-radius=ms]{--rt-select-radius: var(--rt-radius-ms)}.rt-select[data-rt-radius=md]{--rt-select-radius: var(--rt-radius-md)}.rt-select[data-rt-radius=lg]{--rt-select-radius: var(--rt-radius-lg)}.rt-select[data-rt-radius=xl]{--rt-select-radius: var(--rt-radius-xl)}.rt-select[data-rt-radius=\"2xl\"]{--rt-select-radius: var(--rt-radius-2xl)}.rt-select[data-rt-radius=full]{--rt-select-radius: var(--rt-radius-full)}}\n"]
593
+ template: "<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [id]=\"valueTextId\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n <!-- Шаблон строки рисует и выбранное значение: цветная метка из списка не пропадает в поле. -->\n @if (chosenOwn(); as own) {\n <span rtElem=\"option-content\" qa-dataid=\"select-value-content\">\n <ng-container [ngTemplateOutlet]=\"own.templateRef\" [ngTemplateOutletContext]=\"own.context\" />\n </span>\n } @else {\n {{ selectedLabel() || placeholder() }}\n }\n </span>\n @if (showClearButton()) {\n @if (clearTabbable()) {\n <!-- Место крестика в строке: сам крестик в порядке Tab стоит рядом с кнопкой, ниже. -->\n <span rtElem=\"clear-slot\" aria-hidden=\"true\"></span>\n } @else {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n @if (showClearButton() && clearTabbable() && !triggerTpl()) {\n <!--\n Крестик в порядке обхода — соседняя кнопка поверх своего места в строке, а не кнопка в\n кнопке: вложенную кнопку скринридер не объявляет, а её Enter открывал бы список.\n -->\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"select-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [rtMod]=\"{ outside: true }\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"0\"\n (clicked)=\"clear($event)\" />\n }\n\n @if (floatingLabel(); as label) {\n <!-- Подпись — картинка поверх указателя: клик проходит сквозь неё в кнопку, имя кнопке она даёт по aria-labelledby. -->\n <span #floatingLabelEl rtElem=\"floating-label\" qa-dataid=\"select-floating-label\" aria-hidden=\"true\" [id]=\"floatingLabelId\">\n {{ label }}\n </span>\n }\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [style.--rt-select-panel-max-height]=\"panelMaxHeight()\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input\n iconLeft=\"ico-search\"\n [placeholder]=\"filterPlaceholderText()\"\n [ngModel]=\"filterTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n Опции listbox'а выбираются мышью через (click); keyboard-nav —\n на trigger'е (activedescendant: ArrowUp/Down/Enter), опции не\n focusable индивидуально, поэтому a11y-правила здесь не применимы.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-select-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: row.option.value === value(),\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"row.option.value === value()\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"select(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"select-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n @if (optionTpl(); as tpl) {\n <span rtElem=\"option-content\" qa-dataid=\"select-option-content\">\n <ng-container\n [ngTemplateOutlet]=\"tpl.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: row.option, selected: row.option.value === value() }\" />\n </span>\n } @else {\n <span rtElem=\"option-label\">{{ row.option.label }}</span>\n }\n @if (row.option.value === value()) {\n <rt-icon rtElem=\"check\" name=\"check\" size=\"sm\" aria-hidden=\"true\" />\n }\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n",
594
+ styles: [
595
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-radius: var(--rt-input-radius);--rt-select-fill-radius: var(--rt-radius-sm);--rt-select-caret-transition-duration: var(--rt-duration-fast);--rt-select-own-outline-width: var(--rt-border-width-medium);--rt-select-floating-label-color: var(--rt-input-color-text-placeholder);--rt-select-floating-label-color-focus: var(--rt-input-color-border-focus);--rt-select-floating-label-color-error: var(--rt-input-color-border-error);--rt-select-floating-label-bg: var(--rt-input-color-bg);--rt-select-floating-label-font-size: var(--rt-text-xs);--rt-select-floating-label-start: var(--rt-input-padding-x);--rt-select-floating-label-rest-start: var(--rt-select-floating-label-start);--rt-select-floating-label-raised-start: var(--rt-select-floating-label-start);--rt-select-notch-start: calc(var(--rt-select-floating-label-raised-start) - var(--rt-space-1));--rt-select-notch-depth: calc(2 * var(--rt-input-border-width));--rt-select-notch-width: 0px;--rt-select-fill-floating-height: var(--rt-input-height-md);--rt-select-color-border-hover: var(--rt-color-text-primary);--rt-select-floating-label-duration: var(--rt-duration-fast);position:relative;display:block;width:100%;min-width:var(--rt-input-min-width)}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select--appearance--fill .rt-select__trigger{border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-select-fill-radius) var(--rt-select-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-select-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__trigger:where(:hover:not(:disabled)){border-color:var(--rt-select-color-border-hover)}.rt-select__trigger--own{width:auto;min-height:0;padding:0;border-color:transparent;background-color:transparent;line-height:var(--rt-leading-none)}.rt-select__trigger--own:focus{border-color:transparent;box-shadow:none}.rt-select__trigger--own:focus-visible{outline:var(--rt-select-own-outline-width) solid var(--rt-input-color-border-focus);outline-offset:var(--rt-input-focus-outline-offset)}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--open .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--invalid .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger--own:focus{box-shadow:none}.rt-select--disabled{opacity:var(--rt-input-disabled-field-opacity)}.rt-select--disabled.rt-select--appearance--fill .rt-select__trigger{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;letter-spacing:var(--rt-input-letter-spacing, inherit);text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}.rt-select__panel{--rt-select-panel-shadow: var(--rt-overlay-select-shadow);--rt-select-panel-radius: var(--rt-overlay-select-radius);--rt-select-option-font-size: var(--rt-overlay-select-option-font-size);--rt-select-selected-bg: var(--rt-overlay-select-selected-bg);--rt-select-selected-weight: var(--rt-overlay-select-selected-weight);--rt-select-check-display: var(--rt-overlay-select-check-display);--rt-select-option-min-height: var(--rt-overlay-select-option-min-height);--rt-select-panel-border-width: var(--rt-overlay-select-panel-border-width);--rt-select-tree-indent: var(--rt-space-lg);--rt-select-panel-max-height: var(--rt-input-panel-max-height);width:100%;max-height:var(--rt-select-panel-max-height);box-sizing:border-box;border:var(--rt-select-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-select-panel-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{--rt-select-tree-level: 0;display:flex;min-height:var(--rt-select-option-min-height);box-sizing:border-box;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-select-option-font-size);gap:var(--rt-space-sm);padding-inline-start:calc(var(--rt-space-md) + var(--rt-select-tree-indent) * var(--rt-select-tree-level))}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-select-selected-bg);font-weight:var(--rt-select-selected-weight)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__option-label{min-width:0;flex:1 1 auto}.rt-select__option-content{display:inline-flex;min-width:0;flex:1 1 auto;align-items:center;gap:var(--rt-space-sm)}.rt-select__toggle,.rt-select__toggle-space{display:inline-flex;flex-shrink:0;block-size:var(--rt-input-icon-size);inline-size:var(--rt-input-icon-size)}.rt-select__toggle{align-items:center;justify-content:center;padding:0;border:0;background:transparent;cursor:pointer}.rt-select__check{display:var(--rt-select-check-display);flex-shrink:0}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}.rt-select[data-rt-radius=none]{--rt-select-radius: var(--rt-radius-none)}.rt-select[data-rt-radius=xs]{--rt-select-radius: var(--rt-radius-xs)}.rt-select[data-rt-radius=sm]{--rt-select-radius: var(--rt-radius-sm)}.rt-select[data-rt-radius=ms]{--rt-select-radius: var(--rt-radius-ms)}.rt-select[data-rt-radius=md]{--rt-select-radius: var(--rt-radius-md)}.rt-select[data-rt-radius=lg]{--rt-select-radius: var(--rt-radius-lg)}.rt-select[data-rt-radius=xl]{--rt-select-radius: var(--rt-radius-xl)}.rt-select[data-rt-radius=\"2xl\"]{--rt-select-radius: var(--rt-radius-2xl)}.rt-select[data-rt-radius=full]{--rt-select-radius: var(--rt-radius-full)}}\n",
596
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select--with-icon-left{--rt-select-floating-label-rest-start: calc( var(--rt-select-floating-label-start) + var(--rt-input-icon-size) + var(--rt-space-sm) )}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){--rt-select-floating-label-bg: transparent;--rt-select-floating-label-raised-start: max( var(--rt-select-floating-label-start), calc(min(var(--rt-select-radius), var(--rt-input-height) / 2) + var(--rt-space-2)) )}.rt-select--appearance--fill{--rt-select-floating-label-bg: var(--rt-color-bg-surface-subtle-2);--rt-select-floating-label-raised-start: var(--rt-select-floating-label-start)}.rt-select__floating-label{position:absolute;top:50%;left:var(--rt-select-floating-label-rest-start);display:none;overflow:hidden;max-width:calc(100% - var(--rt-select-floating-label-rest-start) - var(--rt-input-padding-x));color:var(--rt-select-floating-label-color);font-size:var(--rt-input-font-size);line-height:var(--rt-input-line-height);pointer-events:none;text-overflow:ellipsis;transform:translateY(-50%);transition:top var(--rt-select-floating-label-duration) ease,left var(--rt-select-floating-label-duration) ease,font-size var(--rt-select-floating-label-duration) ease,color var(--rt-select-floating-label-duration) ease;white-space:nowrap}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__floating-label{display:block}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):not(.rt-select--filled,.rt-select--open) .rt-select__label--placeholder{color:transparent}.rt-select--filled .rt-select__floating-label,.rt-select--open .rt-select__floating-label{top:0;left:var(--rt-select-floating-label-raised-start);max-width:calc(100% - var(--rt-select-floating-label-raised-start) - var(--rt-select-floating-label-start));padding:0 var(--rt-space-1);margin-left:calc(-1 * var(--rt-space-1));background-color:var(--rt-select-floating-label-bg);font-size:var(--rt-select-floating-label-font-size);line-height:1}.rt-select--open .rt-select__floating-label{color:var(--rt-select-floating-label-color-focus)}.rt-select--invalid .rt-select__floating-label{color:var(--rt-select-floating-label-color-error)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill{--rt-input-height: var(--rt-select-fill-floating-height)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill .rt-select__floating-label{line-height:var(--rt-leading-tight)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill .rt-select__label{align-self:stretch;padding-top:calc(var(--rt-input-height) / 2 - var(--rt-space-1) - var(--rt-input-fill-edge));padding-bottom:calc(var(--rt-input-height) / 2 - var(--rt-space-5) - var(--rt-input-border-width));line-height:var(--rt-leading-normal)}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill.rt-select--filled .rt-select__floating-label,.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]).rt-select--appearance--fill.rt-select--open .rt-select__floating-label{top:calc(var(--rt-input-height) / 2 - var(--rt-space-5));left:var(--rt-select-floating-label-rest-start);padding:0;margin-left:0;background-color:transparent;transform:none}.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):not(.rt-select--appearance--fill).rt-select--filled .rt-select__trigger,.rt-select--floating:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):not(.rt-select--appearance--fill).rt-select--open .rt-select__trigger{clip-path:polygon(0 0,var(--rt-select-notch-start) 0,var(--rt-select-notch-start) var(--rt-select-notch-depth),calc(var(--rt-select-notch-start) + var(--rt-select-notch-width)) var(--rt-select-notch-depth),calc(var(--rt-select-notch-start) + var(--rt-select-notch-width)) 0,100% 0,100% 100%,0 100%)}.rt-select--appearance--fill .rt-select__trigger:not(.rt-select__trigger--own):focus{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-select--appearance--fill.rt-select--invalid .rt-select__trigger:not(.rt-select__trigger--own):focus{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-select--readonly .rt-select__floating-label{display:none}}\n",
597
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-chevron-width: var(--rt-size-4)}.rt-select--size--sm{--rt-input-height: var(--rt-input-height-sm)}.rt-select--size--lg{--rt-input-height: var(--rt-input-height-lg)}.rt-select__clear{--rt-icon-color-muted: var(--rt-input-clear-color);--rt-icon-step-sm: var(--rt-input-clear-icon-size)}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__clear .rt-icon-button{width:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width));height:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width))}.rt-select .rt-select__clear .rt-icon-button{--rt-icon-button-radius: var(--rt-input-clear-radius);--rt-icon-button-bg-variant: var(--rt-input-clear-bg);--rt-icon-button-color-bg-hover: var(--rt-input-clear-bg-hover)}.rt-select__clear-slot{width:var(--rt-input-clear-size);height:var(--rt-input-clear-size);flex-shrink:0}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__clear-slot{width:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width));height:min(var(--rt-input-clear-size),var(--rt-input-height) - 2 * var(--rt-input-border-width))}.rt-select__clear--outside{position:absolute;top:50%;right:calc(var(--rt-select-text-inset) + var(--rt-select-chevron-width) + var(--rt-space-sm));transform:translateY(-50%)}.rt-select--readonly .rt-select__clear--outside{display:none}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__chevron{--rt-icon-color-muted: var(--rt-input-icon-color);max-width:10px;max-height:5px;background-color:currentcolor;clip-path:polygon(0 0,100% 0,50% 100%)}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__chevron svg{display:none}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){--rt-select-chevron-width: 10px}}\n",
598
+ "@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-edge: var(--rt-input-border-width);--rt-select-text-inset: calc(var(--rt-input-padding-x) + var(--rt-select-edge))}.rt-select--appearance--fill{--rt-select-edge: var(--rt-input-fill-edge)}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){--rt-select-text-inset: var(--rt-input-padding-x)}.rt-select:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__trigger{padding-inline:calc(var(--rt-select-text-inset) - var(--rt-select-edge))}.rt-select--disabled .rt-select__trigger{color:var(--rt-input-color-text-disabled)}.rt-select--disabled .rt-select__label--placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-select--disabled:not(.rt-select--invalid,.rt-select--appearance--fill,.rt-select--borderless) .rt-select__trigger:where(:not(.rt-select__trigger--own)){border-color:var(--rt-input-color-border-disabled)}.rt-select--disabled:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__chevron{--rt-icon-color-muted: var(--rt-input-color-placeholder-disabled)}.rt-select:is(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]) .rt-select__label>.rt-select__option-content{vertical-align:top}}\n"
599
+ ]
497
600
  }]
498
601
  }],
499
602
  propDecorators: {
@@ -505,10 +608,18 @@ i0.ɵɵngDeclareClassMetadata({
505
608
  type: i0.ViewChild,
506
609
  args: ["triggerEl", { isSignal: true }]
507
610
  }],
611
+ floatingLabelEl: [{
612
+ type: i0.ViewChild,
613
+ args: ["floatingLabelEl", { isSignal: true }]
614
+ }],
508
615
  triggerTpl: [{
509
616
  type: i0.ContentChild,
510
617
  args: [i0.forwardRef(() => RtSelectTriggerDirective), { isSignal: true }]
511
618
  }],
619
+ optionTpl: [{
620
+ type: i0.ContentChild,
621
+ args: [i0.forwardRef(() => RtSelectOptionDirective), { isSignal: true }]
622
+ }],
512
623
  appearance: [{
513
624
  type: i0.Input,
514
625
  args: [{
@@ -541,6 +652,14 @@ i0.ɵɵngDeclareClassMetadata({
541
652
  required: false
542
653
  }]
543
654
  }],
655
+ floatingLabel: [{
656
+ type: i0.Input,
657
+ args: [{
658
+ isSignal: true,
659
+ alias: "floatingLabel",
660
+ required: false
661
+ }]
662
+ }],
544
663
  filter: [{
545
664
  type: i0.Input,
546
665
  args: [{
@@ -579,6 +698,6 @@ i0.ɵɵngDeclareClassMetadata({
579
698
  }]
580
699
  }
581
700
  });
582
- export { RtSelectComponent, RtSelectTriggerDirective };
701
+ export { RtSelectComponent, RtSelectOptionDirective, RtSelectTriggerDirective };
583
702
 
584
703
  //# sourceMappingURL=rt-tools-ui-kit-v2-select.mjs.map