@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
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-inline-edit.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/inline-edit/rt-inline-edit.logic.ts","../../../projects/ui-kit-v2/src/lib/components/inline-edit/rt-inline-edit.component.ts","../../../projects/ui-kit-v2/src/lib/components/inline-edit/rt-inline-edit.component.html"],"sourcesContent":["import { IRtInlineEdit } from './rt-inline-edit.model';\n\n/** Сколько знаков конца текста видно всегда при обрезке посередине. */\nconst MIDDLE_TAIL: number = 10;\n\n/**\n * Чем кончается сохранение черновика. Черновик обрезается по краям; пустой не уходит, пока его не\n * разрешили; равный значению закрывает поле и ничего не шлёт.\n */\nexport function inlineEditOutcome(value: string, draft: string, allowEmpty: boolean): IRtInlineEdit.Outcome {\n const next: string = draft.trim();\n\n if (next === '' && !allowEmpty) {\n return { kind: 'refuse' };\n }\n\n return next === value ? { kind: 'close' } : { kind: 'save', value: next };\n}\n\n/**\n * Текст для обрезки посередине: последние десять знаков видны всегда, многоточие встаёт в начале.\n * Текст не длиннее двух концов не режется — весь он идёт началом. Знаки считаются по кодовым точкам,\n * поэтому пара суррогатов не рвётся.\n */\nexport function inlineEditMiddle(text: string): IRtInlineEdit.MiddleParts {\n const chars: string[] = Array.from(text);\n\n if (chars.length <= MIDDLE_TAIL * 2) {\n return { head: text, tail: '' };\n }\n\n return { head: chars.slice(0, -MIDDLE_TAIL).join(''), tail: chars.slice(-MIDDLE_TAIL).join('') };\n}\n\n/**\n * Ушёл ли фокус из компонента. Переход на свои кнопки уходом не считается: нажатие «Сохранить» или\n * «Отмена» начинается с ухода фокуса из поля. Неизвестная цель — окно или пустое место — уход.\n */\nexport function inlineEditLeaves(host: Node, target: EventTarget | null): boolean {\n return !(target instanceof Node && host.contains(target));\n}\n","import {\n afterNextRender,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n Injector,\n input,\n InputSignal,\n InputSignalWithTransform,\n model,\n ModelSignal,\n output,\n OutputEmitterRef,\n signal,\n Signal,\n untracked,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { IRtIcon, IRtInput, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { inlineEditLeaves, inlineEditMiddle, inlineEditOutcome } from './rt-inline-edit.logic';\nimport { IRtInlineEdit } from './rt-inline-edit.model';\n\nconst BEM_BLOCK: string = 'rt-inline-edit';\n\n/**\n * Правка короткого значения на месте. В покое значение — кнопка с карандашом; нажатие открывает поле\n * кита с черновиком, кнопкой сохранения и кнопкой отмены. Enter сохраняет, Escape отменяет.\n *\n * Значение принадлежит приложению: компонент отдаёт новое выходом и сам его не хранит. Приложение,\n * которое отвечает позже, держит поле открытым входом `closeOnSave` и закрывает его само.\n */\n@Component({\n selector: 'rt-inline-edit',\n templateUrl: './rt-inline-edit.component.html',\n styleUrl: './rt-inline-edit.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n ReactiveFormsModule,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtIconComponent,\n RtIconButtonComponent,\n RtInputComponent,\n RtTooltipDirective,\n ],\n host: {\n class: BEM_BLOCK,\n '[class.rt-inline-edit--chip]': \"look() === 'chip'\",\n '[class.rt-inline-edit--chip-field]': 'isChipField()',\n '[class.rt-inline-edit--size-md]': \"size() === 'md'\",\n '[class.rt-inline-edit--size-lg]': \"size() === 'lg'\",\n '(focusin)': 'onFocusIn()',\n '(focusout)': 'onFocusOut($event)',\n },\n})\nexport class RtInlineEditComponent {\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef);\n readonly #injector: Injector = inject(Injector);\n\n readonly #t_uiEdit: Signal<string> = rtKitLabel('uiEdit');\n\n /**\n * Фокус был внутри и не ушёл на другой элемент страницы. Недоступное на время сохранения поле\n * роняет фокус в никуда, и без этой отметки его некуда вернуть, когда поле закроет приложение.\n */\n #focusInside: boolean = false;\n #wasEditing: boolean = false;\n #wasSaving: boolean = false;\n /** Фокус в поле плашки: пока человек правит, новое значение приложения не переписывает черновик. */\n #chipFocused: boolean = false;\n /** Enter или Escape уже сохранили черновик и снимают фокус сами: уход не сохраняет второй раз. */\n #chipLeaving: boolean = false;\n\n protected readonly saveLabel: Signal<string> = rtKitLabel('uiSave');\n protected readonly cancelLabel: Signal<string> = rtKitLabel('uiCancel');\n\n /** Что видно в покое: значение, а пустое — подсказка. */\n protected readonly shown: Signal<string> = computed((): string => this.value() || this.placeholder());\n protected readonly isEmpty: Signal<boolean> = computed((): boolean => this.value() === '');\n /** Имя кнопки покоя: действие и то, что правится. */\n protected readonly startLabel: Signal<string> = computed((): string => `${this.#t_uiEdit()}: ${this.shown()}`);\n\n /** Что набрано в открытом поле; зеркало поля для расчёта доступности сохранения. */\n readonly #draft: WritableSignal<string> = signal('');\n\n /**\n * Поле черновика. Форма, а не двусторонняя привязка: форма пишет значение в поле в том же\n * проходе отрисовки, и выделение текста при открытии застаёт его на месте.\n */\n protected readonly draftControl: FormControl<string> = new FormControl<string>('', { nonNullable: true });\n protected readonly canSave: Signal<boolean> = computed(\n (): boolean => inlineEditOutcome(this.value(), this.#draft(), this.allowEmpty()).kind !== 'refuse'\n );\n\n /** Плашка сама остаётся полем: нет покоя и открытия, нет кнопок. */\n protected readonly isChipField: Signal<boolean> = computed((): boolean => this.look() === 'chip-field');\n /** Текст, по которому плашка-поле берёт ширину: черновик, а пустой — подсказка. */\n protected readonly chipText: Signal<string> = computed((): string => this.#draft() || this.placeholder());\n /** Видимый вне фокуса текст плашки-поля, разрезанный посередине; пустой черновик не режется. */\n protected readonly middleParts: Signal<IRtInlineEdit.MiddleParts | null> = computed((): IRtInlineEdit.MiddleParts | null =>\n this.truncate() === 'middle' && this.#draft() !== '' ? inlineEditMiddle(this.#draft()) : null\n );\n\n /** Значение приложения: видно в покое и встаёт в поле при открытии. */\n public readonly value: InputSignal<string> = input.required<string>();\n /** Подсказка пустого значения в покое и пустого поля. */\n public readonly placeholder: InputSignal<string> = input<string>('');\n /** Вид в покое: строка текста или плашка. */\n public readonly look: InputSignal<IRtInlineEdit.Look> = input<IRtInlineEdit.Look>('text');\n /** Где плашка-поле режет длинный текст вне фокуса. Читается только видом `chip-field`. */\n public readonly truncate: InputSignal<IRtInlineEdit.Truncate> = input<IRtInlineEdit.Truncate>('end');\n /** Ступень поля и кнопок. */\n public readonly size: InputSignal<IRtInput.Size> = input<IRtInput.Size>('sm');\n /** Имя поля для чтения экрана; пусто — подсказка поля. */\n public readonly fieldLabel: InputSignal<string> = input<string>('');\n /** Компонент недоступен: поле не открывается. */\n public readonly disabled: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, { transform: booleanAttribute });\n /** Приложение сохраняет: кнопка сохранения крутится, поле и отмена недоступны. */\n public readonly saving: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, { transform: booleanAttribute });\n /** Пустой черновик сохраняется; выключено — кнопка сохранения недоступна. */\n public readonly allowEmpty: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Поле закрывается при сохранении; выключено — его закрывает приложение через `editing`. */\n public readonly closeOnSave: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /** Что делает уход фокуса из компонента. */\n public readonly blurAction: InputSignal<IRtInlineEdit.BlurAction> = input<IRtInlineEdit.BlurAction>('keep');\n /** Значок перед текстом плашки-поля: имя значка кита. */\n public readonly fieldIcon: InputSignal<IRtIcon.Name> = input<IRtIcon.Name>('pencil');\n\n /** Поле открыто. Приложение может открыть и закрыть его само. */\n public readonly editing: ModelSignal<boolean> = model<boolean>(false);\n\n /** Новое значение, уже обрезанное по краям. Равное прежнему не уходит. */\n public readonly saved: OutputEmitterRef<string> = output<string>();\n /** Правка отменена: поле закрыто, значение прежнее. */\n public readonly cancelled: OutputEmitterRef<void> = output<void>();\n\n constructor() {\n this.draftControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((draft: string): void => this.#draft.set(draft));\n\n // Поле открылось — нажатием или входом приложения: черновик берёт значение, фокус встаёт в поле.\n // Поле закрылось — своей кнопкой или входом приложения: фокус возвращается на значение.\n effect((): void => {\n const editing: boolean = this.editing();\n\n if (editing) {\n this.draftControl.setValue(untracked(this.value));\n afterNextRender((): void => this.focusField(), { injector: this.#injector });\n } else if (this.#wasEditing) {\n this.#restoreFocus('.rt-inline-edit__value');\n } else {\n // Покой без перехода: фокус никто не трогает.\n }\n this.#wasEditing = editing;\n });\n\n // Плашка-поле показывает значение приложения, пока человек её не правит.\n effect((): void => {\n const value: string = this.value();\n\n if (this.isChipField() && !this.#chipFocused) {\n this.draftControl.setValue(value);\n }\n });\n\n // Пока приложение сохраняет, поле недоступно; отказ оставил поле открытым — фокус снова в нём.\n // Плашка-поле всегда на месте, и недоступный компонент запирает её саму.\n effect((): void => {\n const saving: boolean = this.saving();\n\n if (saving || (this.isChipField() && this.disabled())) {\n this.draftControl.disable({ emitEvent: false });\n } else {\n this.draftControl.enable({ emitEvent: false });\n if (this.#wasSaving && untracked(this.editing)) {\n this.#restoreFocus('.rt-inline-edit__field input');\n }\n }\n this.#wasSaving = saving;\n });\n }\n\n /** Нажатие на значение в покое. */\n public open(): void {\n if (this.disabled() || this.editing()) {\n return;\n }\n this.editing.set(true);\n }\n\n public save(): void {\n if (this.saving()) {\n return;\n }\n const outcome: IRtInlineEdit.Outcome = inlineEditOutcome(this.value(), this.draftControl.value, this.allowEmpty());\n\n if (outcome.kind === 'refuse') {\n return;\n }\n if (outcome.kind === 'save') {\n this.saved.emit(outcome.value);\n if (!this.closeOnSave()) {\n return;\n }\n }\n this.#close();\n }\n\n public cancel(): void {\n if (this.saving()) {\n return;\n }\n this.#close();\n this.cancelled.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n this.save();\n } else if (event.key === 'Escape') {\n // Escape принадлежит полю: окно или панель вокруг не закрываются вместе с правкой.\n event.preventDefault();\n event.stopPropagation();\n this.cancel();\n } else {\n // Остальные клавиши правят черновик в самом поле.\n }\n }\n\n protected onFocusIn(): void {\n this.#focusInside = true;\n }\n\n protected onFocusOut(event: FocusEvent): void {\n if (event.relatedTarget instanceof Node && !this.#host.nativeElement.contains(event.relatedTarget)) {\n this.#focusInside = false;\n }\n if (!this.editing() || this.saving() || !inlineEditLeaves(this.#host.nativeElement, event.relatedTarget)) {\n return;\n }\n if (this.blurAction() === 'save') {\n this.save();\n } else if (this.blurAction() === 'cancel') {\n this.cancel();\n } else {\n // Поле держится открытым: случайное нажатие мимо не теряет набранное.\n }\n }\n\n /** Фокус пришёл в плашку-поле: весь текст выделен, и первое нажатие заменяет значение целиком. */\n protected onChipFocus(event: FocusEvent): void {\n this.#chipFocused = true;\n if (event.target instanceof HTMLInputElement) {\n event.target.select();\n }\n }\n\n /** Уход из плашки-поля сохраняет черновик. */\n protected onChipBlur(): void {\n this.#chipFocused = false;\n if (!this.#chipLeaving) {\n this.#commitChip();\n }\n }\n\n protected onChipKeydown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n this.#leaveChip(event);\n } else if (event.key === 'Escape') {\n // Escape принадлежит полю: окно или панель вокруг не закрываются вместе с правкой.\n event.preventDefault();\n event.stopPropagation();\n this.draftControl.setValue(this.value());\n this.#leaveChip(event);\n this.cancelled.emit();\n } else {\n // Остальные клавиши правят черновик в самом поле.\n }\n }\n\n /** Фокус в поле и весь текст выделен: первое нажатие клавиши заменяет значение целиком. */\n protected focusField(): void {\n const field: HTMLInputElement | null = this.#host.nativeElement.querySelector('.rt-inline-edit__field input');\n\n field?.focus();\n field?.select();\n }\n\n #close(): void {\n this.editing.set(false);\n }\n\n /**\n * Черновик плашки-поля уходит приложению. Пустой, который не разрешён, и равный значению молча\n * возвращают значение: у плашки нет недоступной кнопки, которой можно было бы отказать.\n */\n #commitChip(): void {\n if (this.saving()) {\n return;\n }\n const outcome: IRtInlineEdit.Outcome = inlineEditOutcome(this.value(), this.draftControl.value, this.allowEmpty());\n\n if (outcome.kind === 'save') {\n this.saved.emit(outcome.value);\n this.draftControl.setValue(outcome.value);\n } else {\n this.draftControl.setValue(this.value());\n }\n }\n\n /** Enter и Escape сохраняют сами и снимают фокус: уход, который за этим следует, не сохраняет второй раз. */\n #leaveChip(event: KeyboardEvent): void {\n const target: EventTarget | null = event.target;\n\n this.#commitChip();\n if (target instanceof HTMLElement && target.ownerDocument.activeElement === target) {\n this.#chipLeaving = true;\n target.blur();\n this.#chipLeaving = false;\n }\n }\n\n /**\n * Фокус, бывший внутри, возвращается на элемент компонента после отрисовки. Ушедший на другой\n * элемент страницы не трогается: человек уже там.\n */\n #restoreFocus(selector: string): void {\n const host: HTMLElement = this.#host.nativeElement;\n const active: Element | null = host.ownerDocument.activeElement;\n\n if (!this.#focusInside || (active !== null && active !== host.ownerDocument.body && !host.contains(active))) {\n return;\n }\n afterNextRender((): void => host.querySelector<HTMLElement>(selector)?.focus(), { injector: this.#injector });\n }\n}\n","<ng-container rtBlock=\"rt-inline-edit\">\n @if (isChipField()) {\n <!-- Плашка-поле: значок и поле внутри одной плашки. Ширину задаёт невидимая копия текста под\n полем — плашка растёт с текстом до своего предела, дальше поле прокручивается. -->\n <label rtElem=\"chip-field\" [rtMod]=\"{ disabled: disabled() }\">\n <rt-icon rtElem=\"chip-icon\" size=\"sm\" [name]=\"fieldIcon()\" />\n <span rtElem=\"sizer\" rtTooltipWhenTruncated [rtTooltip]=\"chipText()\">\n <span rtElem=\"mirror\" aria-hidden=\"true\">{{ chipText() }}</span>\n <!-- Обрезка посередине: копия выше по-прежнему задаёт ширину и будит подсказку, а этот\n слой в той же клетке показывает начало с многоточием и конец, пока поле не в фокусе. -->\n @if (middleParts(); as parts) {\n <span rtElem=\"middle\" aria-hidden=\"true\">\n <span rtElem=\"head\">{{ parts.head }}</span>\n <span rtElem=\"tail\">{{ parts.tail }}</span>\n </span>\n }\n <input\n type=\"text\"\n rtElem=\"chip-input\"\n qa-dataid=\"inline-edit-chip-input\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"fieldLabel() || placeholder() || null\"\n [formControl]=\"draftControl\"\n (focus)=\"onChipFocus($event)\"\n (blur)=\"onChipBlur()\"\n (keydown)=\"onChipKeydown($event)\" />\n </span>\n </label>\n } @else if (editing()) {\n <rt-input\n rtElem=\"field\"\n qa-dataid=\"inline-edit-field\"\n [size]=\"size()\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"fieldLabel() || placeholder() || null\"\n [clearable]=\"false\"\n [formControl]=\"draftControl\"\n (keydown)=\"onKeydown($event)\" />\n\n <rt-icon-button\n rtElem=\"save\"\n qa-dataid=\"inline-edit-save\"\n icon=\"check\"\n [size]=\"size()\"\n [ariaLabel]=\"saveLabel()\"\n [tooltip]=\"saveLabel()\"\n [loading]=\"saving()\"\n [disabled]=\"!canSave()\"\n (clicked)=\"save()\" />\n\n <rt-icon-button\n rtElem=\"cancel\"\n qa-dataid=\"inline-edit-cancel\"\n icon=\"close\"\n [size]=\"size()\"\n [ariaLabel]=\"cancelLabel()\"\n [tooltip]=\"cancelLabel()\"\n [disabled]=\"saving()\"\n (clicked)=\"cancel()\" />\n } @else {\n <button\n type=\"button\"\n rtElem=\"value\"\n qa-dataid=\"inline-edit-value\"\n [rtMod]=\"{ empty: isEmpty() }\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"startLabel()\"\n (click)=\"open()\">\n <span rtElem=\"text\" rtTooltipWhenTruncated [rtTooltip]=\"shown()\">{{ shown() }}</span>\n\n @if (!disabled()) {\n <rt-icon rtElem=\"pencil\" name=\"pencil\" size=\"sm\" />\n }\n </button>\n }\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-inline-edit.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/inline-edit/rt-inline-edit.logic.ts","../../../projects/ui-kit-v2/src/lib/components/inline-edit/rt-inline-edit.component.ts","../../../projects/ui-kit-v2/src/lib/components/inline-edit/rt-inline-edit.component.html"],"sourcesContent":["import { IRtInlineEdit } from './rt-inline-edit.model';\n\n/** Сколько знаков конца текста видно всегда при обрезке посередине. */\nconst MIDDLE_TAIL: number = 10;\n\n/**\n * Чем кончается сохранение черновика. Черновик обрезается по краям; пустой не уходит, пока его не\n * разрешили; равный значению закрывает поле и ничего не шлёт.\n */\nexport function inlineEditOutcome(value: string, draft: string, allowEmpty: boolean): IRtInlineEdit.Outcome {\n const next: string = draft.trim();\n\n if (next === '' && !allowEmpty) {\n return { kind: 'refuse' };\n }\n\n return next === value ? { kind: 'close' } : { kind: 'save', value: next };\n}\n\n/**\n * Текст для обрезки посередине: последние десять знаков видны всегда, многоточие встаёт в начале.\n * Текст не длиннее двух концов не режется — весь он идёт началом. Знаки считаются по кодовым точкам,\n * поэтому пара суррогатов не рвётся.\n */\nexport function inlineEditMiddle(text: string): IRtInlineEdit.MiddleParts {\n const chars: string[] = Array.from(text);\n\n if (chars.length <= MIDDLE_TAIL * 2) {\n return { head: text, tail: '' };\n }\n\n return { head: chars.slice(0, -MIDDLE_TAIL).join(''), tail: chars.slice(-MIDDLE_TAIL).join('') };\n}\n\n/**\n * Ушёл ли фокус из компонента. Переход на свои кнопки уходом не считается: нажатие «Сохранить» или\n * «Отмена» начинается с ухода фокуса из поля. Неизвестная цель — окно или пустое место — уход.\n */\nexport function inlineEditLeaves(host: Node, target: EventTarget | null): boolean {\n return !(target instanceof Node && host.contains(target));\n}\n","import {\n afterNextRender,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n Injector,\n input,\n InputSignal,\n InputSignalWithTransform,\n model,\n ModelSignal,\n output,\n OutputEmitterRef,\n signal,\n Signal,\n untracked,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { IRtIcon, IRtInput, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { inlineEditLeaves, inlineEditMiddle, inlineEditOutcome } from './rt-inline-edit.logic';\nimport { IRtInlineEdit } from './rt-inline-edit.model';\n\nconst BEM_BLOCK: string = 'rt-inline-edit';\n\n/**\n * Правка короткого значения на месте. В покое значение — кнопка с карандашом; нажатие открывает поле\n * кита с черновиком, кнопкой сохранения и кнопкой отмены. Enter сохраняет, Escape отменяет.\n *\n * Значение принадлежит приложению: компонент отдаёт новое выходом и сам его не хранит. Приложение,\n * которое отвечает позже, держит поле открытым входом `closeOnSave` и закрывает его само.\n */\n@Component({\n selector: 'rt-inline-edit',\n templateUrl: './rt-inline-edit.component.html',\n styleUrl: './rt-inline-edit.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n ReactiveFormsModule,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtIconComponent,\n RtIconButtonComponent,\n RtInputComponent,\n RtTooltipDirective,\n ],\n host: {\n class: BEM_BLOCK,\n '[class.rt-inline-edit--chip]': \"look() === 'chip'\",\n '[class.rt-inline-edit--chip-field]': 'isChipField()',\n '[class.rt-inline-edit--size-md]': \"size() === 'md'\",\n '[class.rt-inline-edit--size-lg]': \"size() === 'lg'\",\n '(focusin)': 'onFocusIn()',\n '(focusout)': 'onFocusOut($event)',\n },\n})\nexport class RtInlineEditComponent {\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef);\n readonly #injector: Injector = inject(Injector);\n\n readonly #t_uiEdit: Signal<string> = rtKitLabel('uiEdit');\n\n /**\n * Фокус был внутри и не ушёл на другой элемент страницы. Недоступное на время сохранения поле\n * роняет фокус в никуда, и без этой отметки его некуда вернуть, когда поле закроет приложение.\n */\n #focusInside: boolean = false;\n #wasEditing: boolean = false;\n #wasSaving: boolean = false;\n /** Фокус в поле плашки: пока человек правит, новое значение приложения не переписывает черновик. */\n #chipFocused: boolean = false;\n /** Enter или Escape уже сохранили черновик и снимают фокус сами: уход не сохраняет второй раз. */\n #chipLeaving: boolean = false;\n\n protected readonly saveLabel: Signal<string> = rtKitLabel('uiSave');\n protected readonly cancelLabel: Signal<string> = rtKitLabel('uiCancel');\n\n /** Что видно в покое: значение, а пустое — подсказка. */\n protected readonly shown: Signal<string> = computed((): string => this.value() || this.placeholder());\n protected readonly isEmpty: Signal<boolean> = computed((): boolean => this.value() === '');\n /** Имя кнопки покоя: действие и то, что правится. */\n protected readonly startLabel: Signal<string> = computed((): string => `${this.#t_uiEdit()}: ${this.shown()}`);\n\n /** Что набрано в открытом поле; зеркало поля для расчёта доступности сохранения. */\n readonly #draft: WritableSignal<string> = signal('');\n\n /**\n * Поле черновика. Форма, а не двусторонняя привязка: форма пишет значение в поле в том же\n * проходе отрисовки, и выделение текста при открытии застаёт его на месте.\n */\n protected readonly draftControl: FormControl<string> = new FormControl<string>('', { nonNullable: true });\n protected readonly canSave: Signal<boolean> = computed(\n (): boolean => inlineEditOutcome(this.value(), this.#draft(), this.allowEmpty()).kind !== 'refuse'\n );\n\n /** Плашка сама остаётся полем: нет покоя и открытия, нет кнопок. */\n protected readonly isChipField: Signal<boolean> = computed((): boolean => this.look() === 'chip-field');\n /** Текст, по которому плашка-поле берёт ширину: черновик, а пустой — подсказка. */\n protected readonly chipText: Signal<string> = computed((): string => this.#draft() || this.placeholder());\n /** Видимый вне фокуса текст плашки-поля, разрезанный посередине; пустой черновик не режется. */\n protected readonly middleParts: Signal<IRtInlineEdit.MiddleParts | null> = computed((): IRtInlineEdit.MiddleParts | null =>\n this.truncate() === 'middle' && this.#draft() !== '' ? inlineEditMiddle(this.#draft()) : null\n );\n\n /** Значение приложения: видно в покое и встаёт в поле при открытии. */\n public readonly value: InputSignal<string> = input.required<string>();\n /** Подсказка пустого значения в покое и пустого поля. */\n public readonly placeholder: InputSignal<string> = input<string>('');\n /** Вид в покое: строка текста или плашка. */\n public readonly look: InputSignal<IRtInlineEdit.Look> = input<IRtInlineEdit.Look>('text');\n /** Где плашка-поле режет длинный текст вне фокуса. Читается только видом `chip-field`. */\n public readonly truncate: InputSignal<IRtInlineEdit.Truncate> = input<IRtInlineEdit.Truncate>('end');\n /** Ступень поля и кнопок. */\n public readonly size: InputSignal<IRtInput.Size> = input<IRtInput.Size>('sm');\n /** Имя поля для чтения экрана; пусто — подсказка поля. */\n public readonly fieldLabel: InputSignal<string> = input<string>('');\n /** Компонент недоступен: поле не открывается. */\n public readonly disabled: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, { transform: booleanAttribute });\n /** Приложение сохраняет: кнопка сохранения крутится, поле и отмена недоступны. */\n public readonly saving: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, { transform: booleanAttribute });\n /** Пустой черновик сохраняется; выключено — кнопка сохранения недоступна. */\n public readonly allowEmpty: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Поле закрывается при сохранении; выключено — его закрывает приложение через `editing`. */\n public readonly closeOnSave: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /** Что делает уход фокуса из компонента. */\n public readonly blurAction: InputSignal<IRtInlineEdit.BlurAction> = input<IRtInlineEdit.BlurAction>('keep');\n /** Значок перед текстом плашки-поля: имя значка кита. */\n public readonly fieldIcon: InputSignal<IRtIcon.Name> = input<IRtIcon.Name>('pencil');\n\n /** Поле открыто. Приложение может открыть и закрыть его само. */\n public readonly editing: ModelSignal<boolean> = model<boolean>(false);\n\n /** Новое значение, уже обрезанное по краям. Равное прежнему не уходит. */\n public readonly saved: OutputEmitterRef<string> = output<string>();\n /** Правка отменена: поле закрыто, значение прежнее. */\n public readonly cancelled: OutputEmitterRef<void> = output<void>();\n\n constructor() {\n this.draftControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((draft: string): void => this.#draft.set(draft));\n\n // Поле открылось — нажатием или входом приложения: черновик берёт значение, фокус встаёт в поле.\n // Поле закрылось — своей кнопкой или входом приложения: фокус возвращается на значение.\n effect((): void => {\n const editing: boolean = this.editing();\n\n if (editing) {\n this.draftControl.setValue(untracked(this.value));\n afterNextRender((): void => this.focusField(), { injector: this.#injector });\n } else if (this.#wasEditing) {\n this.#restoreFocus('.rt-inline-edit__value');\n } else {\n // Покой без перехода: фокус никто не трогает.\n }\n this.#wasEditing = editing;\n });\n\n // Плашка-поле показывает значение приложения, пока человек её не правит.\n effect((): void => {\n const value: string = this.value();\n\n if (this.isChipField() && !this.#chipFocused) {\n this.draftControl.setValue(value);\n }\n });\n\n // Пока приложение сохраняет, поле недоступно; отказ оставил поле открытым — фокус снова в нём.\n // Плашка-поле всегда на месте, и недоступный компонент запирает её саму.\n effect((): void => {\n const saving: boolean = this.saving();\n\n if (saving || (this.isChipField() && this.disabled())) {\n this.draftControl.disable({ emitEvent: false });\n } else {\n this.draftControl.enable({ emitEvent: false });\n if (this.#wasSaving && untracked(this.editing)) {\n this.#restoreFocus('.rt-inline-edit__field input');\n }\n }\n this.#wasSaving = saving;\n });\n }\n\n /** Нажатие на значение в покое. */\n public open(): void {\n if (this.disabled() || this.editing()) {\n return;\n }\n this.editing.set(true);\n }\n\n public save(): void {\n if (this.saving()) {\n return;\n }\n const outcome: IRtInlineEdit.Outcome = inlineEditOutcome(this.value(), this.draftControl.value, this.allowEmpty());\n\n if (outcome.kind === 'refuse') {\n return;\n }\n if (outcome.kind === 'save') {\n this.saved.emit(outcome.value);\n if (!this.closeOnSave()) {\n return;\n }\n }\n this.#close();\n }\n\n public cancel(): void {\n if (this.saving()) {\n return;\n }\n this.#close();\n this.cancelled.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n this.save();\n } else if (event.key === 'Escape') {\n // Escape принадлежит полю: окно или панель вокруг не закрываются вместе с правкой.\n event.preventDefault();\n event.stopPropagation();\n this.cancel();\n } else {\n // Остальные клавиши правят черновик в самом поле.\n }\n }\n\n protected onFocusIn(): void {\n this.#focusInside = true;\n }\n\n protected onFocusOut(event: FocusEvent): void {\n if (event.relatedTarget instanceof Node && !this.#host.nativeElement.contains(event.relatedTarget)) {\n this.#focusInside = false;\n }\n if (!this.editing() || this.saving() || !inlineEditLeaves(this.#host.nativeElement, event.relatedTarget)) {\n return;\n }\n if (this.blurAction() === 'save') {\n this.save();\n } else if (this.blurAction() === 'cancel') {\n this.cancel();\n } else {\n // Поле держится открытым: случайное нажатие мимо не теряет набранное.\n }\n }\n\n /** Фокус пришёл в плашку-поле: весь текст выделен, и первое нажатие заменяет значение целиком. */\n protected onChipFocus(event: FocusEvent): void {\n this.#chipFocused = true;\n if (event.target instanceof HTMLInputElement) {\n event.target.select();\n }\n }\n\n /** Уход из плашки-поля сохраняет черновик. */\n protected onChipBlur(): void {\n this.#chipFocused = false;\n if (!this.#chipLeaving) {\n this.#commitChip();\n }\n }\n\n protected onChipKeydown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n this.#leaveChip(event);\n } else if (event.key === 'Escape') {\n // Escape принадлежит полю: окно или панель вокруг не закрываются вместе с правкой.\n event.preventDefault();\n event.stopPropagation();\n this.draftControl.setValue(this.value());\n this.#leaveChip(event);\n this.cancelled.emit();\n } else {\n // Остальные клавиши правят черновик в самом поле.\n }\n }\n\n /** Фокус в поле и весь текст выделен: первое нажатие клавиши заменяет значение целиком. */\n protected focusField(): void {\n const field: HTMLInputElement | null = this.#host.nativeElement.querySelector('.rt-inline-edit__field input');\n\n field?.focus();\n field?.select();\n }\n\n #close(): void {\n this.editing.set(false);\n }\n\n /**\n * Черновик плашки-поля уходит приложению. Пустой, который не разрешён, и равный значению молча\n * возвращают значение: у плашки нет недоступной кнопки, которой можно было бы отказать.\n */\n #commitChip(): void {\n if (this.saving()) {\n return;\n }\n const outcome: IRtInlineEdit.Outcome = inlineEditOutcome(this.value(), this.draftControl.value, this.allowEmpty());\n\n if (outcome.kind === 'save') {\n this.saved.emit(outcome.value);\n this.draftControl.setValue(outcome.value);\n } else {\n this.draftControl.setValue(this.value());\n }\n }\n\n /** Enter и Escape сохраняют сами и снимают фокус: уход, который за этим следует, не сохраняет второй раз. */\n #leaveChip(event: KeyboardEvent): void {\n const target: EventTarget | null = event.target;\n\n this.#commitChip();\n if (target instanceof HTMLElement && target.ownerDocument.activeElement === target) {\n this.#chipLeaving = true;\n target.blur();\n this.#chipLeaving = false;\n }\n }\n\n /**\n * Фокус, бывший внутри, возвращается на элемент компонента после отрисовки. Ушедший на другой\n * элемент страницы не трогается: человек уже там.\n */\n #restoreFocus(selector: string): void {\n const host: HTMLElement = this.#host.nativeElement;\n const active: Element | null = host.ownerDocument.activeElement;\n\n if (!this.#focusInside || (active !== null && active !== host.ownerDocument.body && !host.contains(active))) {\n return;\n }\n afterNextRender((): void => host.querySelector<HTMLElement>(selector)?.focus(), { injector: this.#injector });\n }\n}\n","<ng-container rtBlock=\"rt-inline-edit\">\n @if (isChipField()) {\n <!-- Плашка-поле: значок и поле внутри одной плашки. Ширину задаёт невидимая копия текста под\n полем — плашка растёт с текстом до своего предела, дальше поле прокручивается. -->\n <label rtElem=\"chip-field\" [rtMod]=\"{ disabled: disabled() }\">\n <rt-icon rtElem=\"chip-icon\" size=\"sm\" [name]=\"fieldIcon()\" />\n <span rtElem=\"sizer\" rtTooltipWhenTruncated [rtTooltip]=\"chipText()\">\n <span rtElem=\"mirror\" aria-hidden=\"true\">{{ chipText() }}</span>\n <!-- Обрезка посередине: копия выше по-прежнему задаёт ширину и будит подсказку, а этот\n слой в той же клетке показывает начало с многоточием и конец, пока поле не в фокусе. -->\n @if (middleParts(); as parts) {\n <span rtElem=\"middle\" aria-hidden=\"true\">\n <span rtElem=\"head\">{{ parts.head }}</span>\n <span rtElem=\"tail\">{{ parts.tail }}</span>\n </span>\n }\n <input\n type=\"text\"\n rtElem=\"chip-input\"\n qa-dataid=\"inline-edit-chip-input\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"fieldLabel() || placeholder() || null\"\n [formControl]=\"draftControl\"\n (focus)=\"onChipFocus($event)\"\n (blur)=\"onChipBlur()\"\n (keydown)=\"onChipKeydown($event)\" />\n </span>\n </label>\n } @else if (editing()) {\n <rt-input\n rtElem=\"field\"\n qa-dataid=\"inline-edit-field\"\n [size]=\"size()\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"fieldLabel() || placeholder() || null\"\n [clearable]=\"false\"\n [formControl]=\"draftControl\"\n (keydown)=\"onKeydown($event)\" />\n\n <rt-icon-button\n rtElem=\"save\"\n qa-dataid=\"inline-edit-save\"\n icon=\"check\"\n [size]=\"size()\"\n [ariaLabel]=\"saveLabel()\"\n [tooltip]=\"saveLabel()\"\n [loading]=\"saving()\"\n [disabled]=\"!canSave()\"\n (clicked)=\"save()\" />\n\n <rt-icon-button\n rtElem=\"cancel\"\n qa-dataid=\"inline-edit-cancel\"\n icon=\"close\"\n [size]=\"size()\"\n [ariaLabel]=\"cancelLabel()\"\n [tooltip]=\"cancelLabel()\"\n [disabled]=\"saving()\"\n (clicked)=\"cancel()\" />\n } @else {\n <button\n type=\"button\"\n rtElem=\"value\"\n qa-dataid=\"inline-edit-value\"\n [rtMod]=\"{ empty: isEmpty() }\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"startLabel()\"\n (click)=\"open()\">\n <span rtElem=\"text\" rtTooltipWhenTruncated [rtTooltip]=\"shown()\">{{ shown() }}</span>\n\n @if (!disabled()) {\n <rt-icon rtElem=\"pencil\" name=\"pencil\" size=\"sm\" />\n }\n </button>\n }\n</ng-container>\n"],"mappings":";;;;;;;;;;;AASA,SAAgB,kBAAkB,OAAe,OAAe,YAAmB;CAC/E,MAAM,OAAe,MAAM,KAAI;CAE/B,IAAI,SAAS,MAAM,CAAC,YAChB,OAAO,EAAE,MAAM,SAAQ;CAG3B,OAAO,SAAS,QAAQ,EAAE,MAAM,QAAO,IAAK;EAAE,MAAM;EAAQ,OAAO;CAAI;AAC3E;AAOA,SAAgB,iBAAiB,MAAY;CACzC,MAAM,QAAkB,MAAM,KAAK,IAAI;CAEvC,IAAI,MAAM,UAAU,IAChB,OAAO;EAAE,MAAM;EAAM,MAAM;CAAE;CAGjC,OAAO;EAAE,MAAM,MAAM,MAAM,GAAG,GAAY,CAAC,CAAC,KAAK,EAAE;EAAG,MAAM,MAAM,MAAM,GAAY,CAAC,CAAC,KAAK,EAAE;CAAC;AAClG;AAMA,SAAgB,iBAAiB,MAAY,QAA0B;CACnE,OAAO,EAAE,kBAAkB,QAAQ,KAAK,SAAS,MAAM;AAC3D;ACJA,MAAM,YAAoB;AAmC1B,IAAa,wBAAb,MAAa,sBAAqB;CACrB;CACA;CAEA;CAMT;CACA;CACA;CAEA;CAEA;CAYS;CAyDT,cAAA;EApFS,KAAA,QAAiC,OAAO,UAAU;EAClD,KAAA,YAAsB,OAAO,QAAQ;EAErC,KAAA,YAA4B,WAAW,QAAQ;EAMxD,KAAA,eAAwB;EACxB,KAAA,cAAuB;EACvB,KAAA,aAAsB;EAEtB,KAAA,eAAwB;EAExB,KAAA,eAAwB;EAEL,KAAA,YAA4B,WAAW,QAAQ;EAC/C,KAAA,cAA8B,WAAW,UAAU;EAGnD,KAAA,QAAwB,eAAuB,KAAK,MAAK,KAAM,KAAK,YAAW,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAClF,KAAA,UAA2B,eAAwB,KAAK,MAAK,MAAO,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;EAEvE,KAAA,aAA6B,eAAuB,GAAG,KAAK,UAAS,EAAE,IAAK,KAAK,MAAK,KAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;EAGrG,KAAA,SAAiC,OAAO,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;EAMjC,KAAA,eAAoC,IAAI,YAAoB,IAAI,EAAE,aAAa,KAAI,CAAE;EACrF,KAAA,UAA2B,eAC3B,kBAAkB,KAAK,MAAK,GAAI,KAAK,OAAM,GAAI,KAAK,WAAU,CAAE,CAAC,CAAC,SAAS,UAC7F,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;EAGkB,KAAA,cAA+B,eAAwB,KAAK,KAAI,MAAO,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAEpF,KAAA,WAA2B,eAAuB,KAAK,OAAM,KAAM,KAAK,YAAW,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;EAEtF,KAAA,cAAwD,eACvE,KAAK,SAAQ,MAAO,YAAY,KAAK,OAAM,MAAO,KAAK,iBAAiB,KAAK,OAAM,CAAE,IAAI,MAC5F,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAGe,KAAA,QAA6B,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EAErD,KAAA,cAAmC,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAEpD,KAAA,OAAwC,MAA0B,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAEzE,KAAA,WAAgD,MAA8B,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;EAEpF,KAAA,OAAmC,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;EAE7D,KAAA,aAAkC,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;EAEnD,KAAA,WAAuD,MAAwB,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,WAAA,+BAAA,CAAA;GAAI,WAAW;EAAgB,CAAA;EAEnH,KAAA,SAAqD,MAAwB,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,SAAA,+BAAA,CAAA;GAAI,WAAW;EAAgB,CAAA;EAEjH,KAAA,aAAyD,MAAwB,OAAK;GAAA,GAAA,YAAA,EAAA,WAAA,aAAA,+BAAA,CAAA;GAClG,WAAW;EAAgB,CAAA;EAGf,KAAA,cAA0D,MAAwB,MAAI;GAAA,GAAA,YAAA,EAAA,WAAA,cAAA,+BAAA,CAAA;GAClG,WAAW;EAAgB,CAAA;EAGf,KAAA,aAAoD,MAAgC,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;EAE3F,KAAA,YAAuC,MAAoB,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAGpE,KAAA,UAAgC,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;EAGrD,KAAA,QAAkC,OAAM;EAExC,KAAA,YAAoC,OAAM;EAGtD,KAAK,aAAa,aAAa,KAAK,mBAAkB,CAAE,CAAC,CAAC,WAAW,UAAwB,KAAK,OAAO,IAAI,KAAK,CAAC;EAInH,aAAkB;GACd,MAAM,UAAmB,KAAK,QAAO;GAErC,IAAI,SAAS;IACT,KAAK,aAAa,SAAS,UAAU,KAAK,KAAK,CAAC;IAChD,sBAA4B,KAAK,WAAU,GAAI,EAAE,UAAU,KAAK,UAAS,CAAE;GAC/E,OAAO,IAAI,KAAK,aACZ,KAAK,cAAc,wBAAwB;GAI/C,KAAK,cAAc;EACvB,CAAC;EAGD,aAAkB;GACd,MAAM,QAAgB,KAAK,MAAK;GAEhC,IAAI,KAAK,YAAW,KAAM,CAAC,KAAK,cAC5B,KAAK,aAAa,SAAS,KAAK;EAExC,CAAC;EAID,aAAkB;GACd,MAAM,SAAkB,KAAK,OAAM;GAEnC,IAAI,UAAW,KAAK,YAAW,KAAM,KAAK,SAAQ,GAC9C,KAAK,aAAa,QAAQ,EAAE,WAAW,MAAK,CAAE;QAC3C;IACH,KAAK,aAAa,OAAO,EAAE,WAAW,MAAK,CAAE;IAC7C,IAAI,KAAK,cAAc,UAAU,KAAK,OAAO,GACzC,KAAK,cAAc,8BAA8B;GAEzD;GACA,KAAK,aAAa;EACtB,CAAC;CACL;CAGO,OAAI;EACP,IAAI,KAAK,SAAQ,KAAM,KAAK,QAAO,GAC/B;EAEJ,KAAK,QAAQ,IAAI,IAAI;CACzB;CAEO,OAAI;EACP,IAAI,KAAK,OAAM,GACX;EAEJ,MAAM,UAAiC,kBAAkB,KAAK,MAAK,GAAI,KAAK,aAAa,OAAO,KAAK,WAAU,CAAE;EAEjH,IAAI,QAAQ,SAAS,UACjB;EAEJ,IAAI,QAAQ,SAAS,QAAQ;GACzB,KAAK,MAAM,KAAK,QAAQ,KAAK;GAC7B,IAAI,CAAC,KAAK,YAAW,GACjB;EAER;EACA,KAAK,OAAM;CACf;CAEO,SAAM;EACT,IAAI,KAAK,OAAM,GACX;EAEJ,KAAK,OAAM;EACX,KAAK,UAAU,KAAI;CACvB;CAEU,UAAU,OAAoB;EACpC,IAAI,MAAM,QAAQ,SAAS;GACvB,MAAM,eAAc;GACpB,KAAK,KAAI;EACb,OAAO,IAAI,MAAM,QAAQ,UAAU;GAE/B,MAAM,eAAc;GACpB,MAAM,gBAAe;GACrB,KAAK,OAAM;EACf;CAGJ;CAEU,YAAS;EACf,KAAK,eAAe;CACxB;CAEU,WAAW,OAAiB;EAClC,IAAI,MAAM,yBAAyB,QAAQ,CAAC,KAAK,MAAM,cAAc,SAAS,MAAM,aAAa,GAC7F,KAAK,eAAe;EAExB,IAAI,CAAC,KAAK,QAAO,KAAM,KAAK,OAAM,KAAM,CAAC,iBAAiB,KAAK,MAAM,eAAe,MAAM,aAAa,GACnG;EAEJ,IAAI,KAAK,WAAU,MAAO,QACtB,KAAK,KAAI;OACN,IAAI,KAAK,WAAU,MAAO,UAC7B,KAAK,OAAM;CAInB;CAGU,YAAY,OAAiB;EACnC,KAAK,eAAe;EACpB,IAAI,MAAM,kBAAkB,kBACxB,MAAM,OAAO,OAAM;CAE3B;CAGU,aAAU;EAChB,KAAK,eAAe;EACpB,IAAI,CAAC,KAAK,cACN,KAAK,YAAW;CAExB;CAEU,cAAc,OAAoB;EACxC,IAAI,MAAM,QAAQ,SAAS;GACvB,MAAM,eAAc;GACpB,KAAK,WAAW,KAAK;EACzB,OAAO,IAAI,MAAM,QAAQ,UAAU;GAE/B,MAAM,eAAc;GACpB,MAAM,gBAAe;GACrB,KAAK,aAAa,SAAS,KAAK,MAAK,CAAE;GACvC,KAAK,WAAW,KAAK;GACrB,KAAK,UAAU,KAAI;EACvB;CAGJ;CAGU,aAAU;EAChB,MAAM,QAAiC,KAAK,MAAM,cAAc,cAAc,8BAA8B;EAE5G,OAAO,MAAK;EACZ,OAAO,OAAM;CACjB;CAEA,SAAM;EACF,KAAK,QAAQ,IAAI,KAAK;CAC1B;CAMA,cAAW;EACP,IAAI,KAAK,OAAM,GACX;EAEJ,MAAM,UAAiC,kBAAkB,KAAK,MAAK,GAAI,KAAK,aAAa,OAAO,KAAK,WAAU,CAAE;EAEjH,IAAI,QAAQ,SAAS,QAAQ;GACzB,KAAK,MAAM,KAAK,QAAQ,KAAK;GAC7B,KAAK,aAAa,SAAS,QAAQ,KAAK;EAC5C,OACI,KAAK,aAAa,SAAS,KAAK,MAAK,CAAE;CAE/C;CAGA,WAAW,OAAoB;EAC3B,MAAM,SAA6B,MAAM;EAEzC,KAAK,YAAW;EAChB,IAAI,kBAAkB,eAAe,OAAO,cAAc,kBAAkB,QAAQ;GAChF,KAAK,eAAe;GACpB,OAAO,KAAI;GACX,KAAK,eAAe;EACxB;CACJ;CAMA,cAAc,UAAgB;EAC1B,MAAM,OAAoB,KAAK,MAAM;EACrC,MAAM,SAAyB,KAAK,cAAc;EAElD,IAAI,CAAC,KAAK,gBAAiB,WAAW,QAAQ,WAAW,KAAK,cAAc,QAAQ,CAAC,KAAK,SAAS,MAAM,GACrG;EAEJ,sBAA4B,KAAK,cAA2B,QAAQ,CAAC,EAAE,MAAK,GAAI,EAAE,UAAU,KAAK,UAAS,CAAE;CAChH;CA5RS;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAqB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAArB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAqB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,aAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,MAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,UAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,MAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,YAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,UAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,QAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,YAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,aAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,YAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,WAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,SAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,SAAA;IAAA,SAAA;IAAA,OAAA;IAAA,WAAA;GAAA;GAAA,MAAA;IAAA,WAAA;KAAA,WAAA;KAAA,YAAA;IAAA;IAAA,YAAA;KAAA,8BAAA;KAAA,oCAAA;KAAA,iCAAA;KAAA,iCAAA;IAAA;IAAA,gBAAA;GAAA;GAAA,UAAA;GAAA,UCvElC;GA4EA,QAAA,CAAA,4zNAAA;GAAA,cAAA;IAAA;KAAA,MAAA;KAAA,MDxBQ;IAAmB;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAAA,GAAA;KAAA,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,eAAA;KAAA,UAAA,CAAA,QAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACnB;KAAc,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACd;KAAa,UAAA;KAAA,QAAA,CAAA,OAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACb;KAAY,UAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACZ;KAAe,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACf;KAAqB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,SAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MACrB;KAAgB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,SAAA,CAAA,iBAAA,gBAAA;IAAA;IAAA;KAAA,MAAA;KAAA,MAChB;KAAkB,UAAA;KAAA,QAAA;MAAA;MAAA;MAAA;MAAA;MAAA;KAAA;KAAA,UAAA,CAAA,WAAA;IAAA;GAAA;GAAA,iBAAA,GAAA,wBAAA;GAAA,eAAA,GAAA,kBAAA;EAAA,CAAA;CAAA;;AAYb,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAqB,YAAA,CAAA;EA1BjC,MAAA;EACa,MAAA,CAAA;GAAA,UAAA;GAAgB,iBAGT,wBAAwB;GAAM,eAChC,kBAAkB;GAAI,SAC5B;IACL;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACH;GAAA,MACK;IACF,OAAO;IACP,gCAAgC;IAChC,sCAAsC;IACtC,mCAAmC;IACnC,mCAAmC;IACnC,aAAa;IACb,cAAc;GACjB;GAAA,UAAA;GAAA,QAAA,CAAA,4zNAAA;EAAA,CAAA"}
@@ -40,6 +40,7 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
40
40
  this.fieldEl = viewChild("fieldEl", ...ngDevMode ? [{ debugName: "fieldEl" }] : /* istanbul ignore next */ []);
41
41
  this.displayValue = signal("", ...ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []);
42
42
  this.hasValue = computed(() => this.displayValue() !== "" && this.value() !== null && this.value() !== 0, ...ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []);
43
+ this.fieldLabelInside = true;
43
44
  this.appearance = input("outline", ...ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []);
44
45
  this.displayText = computed(() => this.displayValue(), ...ngDevMode ? [{ debugName: "displayText" }] : /* istanbul ignore next */ []);
45
46
  this.iconLeft = input(null, ...ngDevMode ? [{ debugName: "iconLeft" }] : /* istanbul ignore next */ []);
@@ -277,7 +278,7 @@ var RtInputNumberComponent = class RtInputNumberComponent extends RtFormControlB
277
278
  }],
278
279
  ngImport: i0,
279
280
  template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-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</ng-container>\n",
280
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);position:relative;display:inline-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-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{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-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"],
281
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);--rt-input-number-color-border-hover: var(--rt-color-text-primary);--rt-input-number-edge: var(--rt-input-border-width);position:relative;display:inline-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-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{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-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):where(:hover:not(:has(:disabled))){border-color:var(--rt-input-number-color-border-hover)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{--rt-input-number-edge: var(--rt-input-fill-edge);border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--appearance--fill:focus-within{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-input-number--appearance--fill.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-input-number--disabled{color:var(--rt-input-color-text-disabled);cursor:not-allowed;opacity:var(--rt-input-disabled-field-opacity)}.rt-input-number--appearance--fill.rt-input-number--disabled{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-input-number--disabled:not(.rt-input-number--invalid,.rt-input-number--appearance--fill,.rt-input-number--appearance--pill,.rt-input-number--borderless,.rt-input-number--readonly){border-color:var(--rt-input-color-border-disabled)}.rt-input-number--disabled .rt-input-number__field::placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){padding-inline:calc(var(--rt-input-padding-x) - var(--rt-input-number-edge))}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;letter-spacing:var(--rt-input-letter-spacing, normal);outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"],
281
282
  dependencies: [
282
283
  {
283
284
  kind: "component",
@@ -381,7 +382,7 @@ i0.ɵɵngDeclareClassMetadata({
381
382
  "(mousedown)": "onHostMousedown($event)"
382
383
  },
383
384
  template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-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</ng-container>\n",
384
- styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);position:relative;display:inline-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-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{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-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"]
385
+ styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{--rt-input-number-radius: var(--rt-input-radius);--rt-input-number-fill-radius: var(--rt-radius-sm);--rt-input-number-color-border-hover: var(--rt-color-text-primary);--rt-input-number-edge: var(--rt-input-border-width);position:relative;display:inline-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-input-number-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{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-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]):where(:hover:not(:has(:disabled))){border-color:var(--rt-input-number-color-border-hover)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--appearance--fill{--rt-input-number-edge: var(--rt-input-fill-edge);border-width:var(--rt-input-fill-edge) var(--rt-input-fill-edge) var(--rt-input-border-width);border-color:transparent;border-radius:var(--rt-input-number-fill-radius) var(--rt-input-number-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--appearance--fill:where([data-rt-radius]){border-radius:var(--rt-input-number-radius);border-bottom-color:transparent}.rt-input-number--appearance--pill{--rt-input-number-radius: var(--rt-radius-full);border-color:transparent;background-color:var(--rt-color-bg-surface-subtle-2)}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--appearance--fill:focus-within{box-shadow:var(--rt-input-fill-focus-shadow)}.rt-input-number--appearance--fill.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-fill-focus-shadow-error)}.rt-input-number--disabled{color:var(--rt-input-color-text-disabled);cursor:not-allowed;opacity:var(--rt-input-disabled-field-opacity)}.rt-input-number--appearance--fill.rt-input-number--disabled{background-color:var(--rt-input-fill-color-bg-disabled)}.rt-input-number--disabled:not(.rt-input-number--invalid,.rt-input-number--appearance--fill,.rt-input-number--appearance--pill,.rt-input-number--borderless,.rt-input-number--readonly){border-color:var(--rt-input-color-border-disabled)}.rt-input-number--disabled .rt-input-number__field::placeholder{color:var(--rt-input-color-placeholder-disabled)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number:where(.rt-preset-material *,[data-preset=material] *,.rt-preset-material,[data-preset=material]){padding-inline:calc(var(--rt-input-padding-x) - var(--rt-input-number-edge))}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;letter-spacing:var(--rt-input-letter-spacing, normal);outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}.rt-input-number[data-rt-radius=none]{--rt-input-number-radius: var(--rt-radius-none)}.rt-input-number[data-rt-radius=xs]{--rt-input-number-radius: var(--rt-radius-xs)}.rt-input-number[data-rt-radius=sm]{--rt-input-number-radius: var(--rt-radius-sm)}.rt-input-number[data-rt-radius=ms]{--rt-input-number-radius: var(--rt-radius-ms)}.rt-input-number[data-rt-radius=md]{--rt-input-number-radius: var(--rt-radius-md)}.rt-input-number[data-rt-radius=lg]{--rt-input-number-radius: var(--rt-radius-lg)}.rt-input-number[data-rt-radius=xl]{--rt-input-number-radius: var(--rt-radius-xl)}.rt-input-number[data-rt-radius=\"2xl\"]{--rt-input-number-radius: var(--rt-radius-2xl)}.rt-input-number[data-rt-radius=full]{--rt-input-number-radius: var(--rt-radius-full)}}\n"]
385
386
  }]
386
387
  }],
387
388
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-input-number.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.ts","../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n forwardRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n LOCALE_ID,\n signal,\n Signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\n\nconst BEM_BLOCK: string = 'rt-input-number';\n\n/**\n * Парсит строку в `number | null` по разделителям локали: `groupSep` и любые\n * пробелы удаляются, оставшийся разделитель дробной части приводится к точке.\n *\n * Разбор обязан знать локаль, иначе он расходится с показом: в `en-US` разряды\n * группируются запятой, и набранное `1000` показывалось как `1,000`, а в форму\n * уходила `1` — безусловная замена первой запятой на точку читала разделитель\n * разрядов как десятичный.\n *\n * Пустая строка / NaN → null.\n */\nfunction parseNumber(text: string, groupSep: string): number | null {\n const withoutSpaces: string = text.replace(/\\s/g, '');\n // Пробельный разделитель разрядов (ru) уже снят предыдущей строкой.\n const withoutGroups: string = groupSep.trim() === '' ? withoutSpaces : withoutSpaces.split(groupSep).join('');\n const cleaned: string = withoutGroups.replace(/,/g, '.');\n if (cleaned === '') {\n return null;\n }\n const parsed: number = parseFloat(cleaned);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Клампит `value` в диапазон `[min, max]`. `null` границы игнорируются —\n * соответствующая сторона остаётся неограниченной.\n */\nfunction clamp(value: number, min: number | null, max: number | null): number {\n let result: number = value;\n if (min !== null && result < min) {\n result = min;\n }\n if (max !== null && result > max) {\n result = max;\n }\n return result;\n}\n\n/**\n * Форматирует `number | null` в строку с группировкой разрядов (неразрывный\n * пробел) и заданным fractionDigits diapason. `value === null` → \"\".\n * `maxFractionDigits` ограничивается сверху 20 (лимит Intl). Итоговый\n * precision = `max(min, min(max, 20))` и фиксирует одинаковую нижнюю/верхнюю\n * границу дробной части (как прежний `toFixed`), меняется только группировка\n * и десятичный разделитель — их даёт `locale`.\n *\n * `grouped === false` снимает разделители разрядов: у года, номера дома и кода\n * разряды значения не несут, и `2026` показывалось как `2 026`.\n */\nfunction formatNumber(\n value: number | null,\n minFractionDigits: number,\n maxFractionDigits: number,\n locale: string,\n grouped: boolean\n): string {\n if (value === null) {\n return '';\n }\n const cappedMax: number = Math.min(maxFractionDigits, 20);\n const precision: number = Math.min(20, Math.max(minFractionDigits, cappedMax));\n return value.toLocaleString(locale, {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n useGrouping: grouped,\n });\n}\n\n/**\n * Numeric input с parse/format/clamp logic. Использует native\n * `<input type=\"text\" inputmode=\"decimal\">` (НЕ `type=\"number\"` — избегаем\n * browser spinner quirks: scroll-wheel value changes, locale-specific decimal\n * separators, inconsistent step rounding).\n *\n * CVA через общий `RtFormControlBase` — авто-подсветка invalid, `clearable`\n * (крестик при непустом display). Parser принимает `,`/`.`, невалидный ввод\n * (NaN) не эмитится во время набора; на blur невалидное → null, валидное —\n * clamp + format.\n *\n * Визуально и по токенам идентичен rt-input (`--rt-input-*`): host — box,\n * `<input>` — прозрачный `__field`.\n */\n@Component({\n selector: 'rt-input-number',\n templateUrl: './rt-input-number.component.html',\n styleUrl: './rt-input-number.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtInputNumberComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-input-number--disabled]': 'isDisabled()',\n '[class.rt-input-number--invalid]': 'isInvalid()',\n '[class.rt-input-number--readonly]': 'isReadonly()',\n '[class.rt-input-number--borderless]': '!bordered()',\n '[class.rt-input-number--appearance--fill]': \"appearance() === 'fill'\",\n '[class.rt-input-number--appearance--pill]': \"appearance() === 'pill'\",\n '[class.rt-input-number--size--sm]': \"size() === 'sm'\",\n '[class.rt-input-number--size--lg]': \"size() === 'lg'\",\n '(mousedown)': 'onHostMousedown($event)',\n },\n})\nexport class RtInputNumberComponent extends RtFormControlBase<number | null> {\n readonly #locale: string = inject(LOCALE_ID);\n\n // Символ группировки разрядов из локали (для ru — неразрывный пробел). Берём\n // тот же, что отдаёт toLocaleString на blur — тогда live и blur выглядят\n // одинаково, а parseNumber (стрипает \\s) парсит обратно обе формы.\n readonly #groupSep: string = (1000).toLocaleString(this.#locale).replace(/\\d/g, '') || ' ';\n\n /**\n * Что в этой локали можно набрать как разделитель дробной части: всё из\n * «.,», кроме того, чем локаль разделяет разряды. В `ru` разряды разделяет\n * неразрывный пробел, поэтому годятся оба знака; в `en-US` запятая — это\n * разряды, и десятичной остаётся только точка. Иначе набранное `1,5`\n * значило бы в показе одно, а в разборе другое.\n */\n readonly #decimalSepPattern: RegExp = new RegExp(`[${['.', ','].filter((char: string): boolean => char !== this.#groupSep).join('')}]`);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly fieldEl: Signal<ElementRef<HTMLInputElement> | undefined> = viewChild<ElementRef<HTMLInputElement>>('fieldEl');\n\n protected readonly displayValue: WritableSignal<string> = signal<string>('');\n\n // Крестик показываем только при непустом и ненулевом значении: 0 — это и\n // есть результат очистки, поэтому при нём кнопка сброса не нужна.\n protected readonly hasValue: Signal<boolean> = computed(\n (): boolean => this.displayValue() !== '' && this.value() !== null && this.value() !== 0\n );\n\n /** Вид рамки: `outline` — рамка со всех сторон, `fill` — залитое поле с чертой снизу. */\n public readonly appearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n\n public readonly displayText: Signal<string> = computed((): string => this.displayValue());\n\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n /** Текстовый префикс слева от значения (напр. «₽» для рублёвых сумм). */\n public readonly prefix: InputSignal<string | null> = input<string | null>(null);\n public readonly placeholder: InputSignal<string> = input<string>('');\n public readonly min: InputSignal<number | null> = input<number | null>(null);\n public readonly max: InputSignal<number | null> = input<number | null>(null);\n public readonly minFractionDigits: InputSignal<number> = input<number>(0);\n public readonly maxFractionDigits: InputSignal<number> = input<number>(2);\n /**\n * Группировать ли разряды. Снимается у чисел, которые разрядами не читаются:\n * год, номер дома, код. У года разделитель превращает `2026` в `2 026`.\n */\n public readonly grouped: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(true, {\n transform: booleanAttribute,\n });\n\n public override writeValue(value: number | null): void {\n super.writeValue(value);\n this.displayValue.set(formatNumber(this.value(), this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected getEmptyValue(): number | null {\n return null;\n }\n\n protected focusAfterClear(): void {\n this.fieldEl()?.nativeElement.focus();\n }\n\n /**\n * Падинг/гэп рамки принадлежат host-контейнеру, а не `<input>`, поэтому клик\n * по этой зоне сам по себе не фокусирует поле. Перехватываем mousedown и\n * переводим фокус в input — но только если клик не пришёлся на само поле\n * (там нужно нативное позиционирование курсора) и не на интерактивный\n * постфикс (крестик-очистка со своим обработчиком).\n */\n protected onHostMousedown(event: MouseEvent): void {\n if (this.isDisabled()) {\n return;\n }\n const field: HTMLInputElement | undefined = this.fieldEl()?.nativeElement;\n const target: HTMLElement = event.target as HTMLElement;\n if (!field || target === field || target.closest('button, rt-icon-button')) {\n return;\n }\n event.preventDefault();\n field.focus();\n }\n\n /**\n * Крестик сбрасывает поле не в пустое/null, а в 0 — отформатированный по\n * текущему fractionDigits (например `0,00`). Числовые поля всегда имеют\n * осмысленный ноль, поэтому «очистка» = обнуление, а не пустое значение.\n */\n protected override clearValue(): void {\n this.value.set(0);\n this.displayValue.set(formatNumber(0, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected onInput(event: Event): void {\n const target: HTMLInputElement = event.target as HTMLInputElement;\n const raw: string = target.value;\n const caret: number = target.selectionStart ?? raw.length;\n\n // Live-группировка разрядов прямо во время набора (1000000 → 1 000 000).\n const formatted: string = this.#groupDisplay(raw);\n const nextCaret: number = this.#restoreCaret(raw, caret, formatted);\n\n // Пишем value императивно ДО Angular CD: тогда повторная запись биндинга\n // `[value]` будет той же строкой и не сбросит каретку в конец. Затем\n // восстанавливаем позицию по числу значимых символов слева от курсора.\n target.value = formatted;\n target.setSelectionRange(nextCaret, nextCaret);\n this.displayValue.set(formatted);\n\n const parsed: number | null = parseNumber(formatted, this.#groupSep);\n if (parsed === null) {\n // Только реально пустой ввод → emit null (пользователь очистил поле).\n // Нечисловой ввод стрипается из отображения, но prev value держится до\n // blur — поэтому проверяем исходный `raw`, а не очищенный `formatted`.\n if (raw.trim() === '') {\n this.value.set(null);\n this.emitChange(null);\n }\n return;\n }\n this.value.set(parsed);\n this.emitChange(parsed);\n }\n\n protected onBlur(): void {\n const parsed: number | null = parseNumber(this.displayValue(), this.#groupSep);\n if (parsed === null) {\n this.value.set(null);\n this.displayValue.set('');\n this.emitChange(null);\n this.markTouched();\n return;\n }\n const clamped: number = clamp(parsed, this.min(), this.max());\n const formatted: string = formatNumber(clamped, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped());\n // `formatted` содержит разделители локали — обратный парс через\n // parseNumber (а не parseFloat) даёт округлённое числовое значение.\n const rounded: number = parseNumber(formatted, this.#groupSep) ?? clamped;\n this.value.set(rounded);\n this.displayValue.set(formatted);\n this.emitChange(rounded);\n this.markTouched();\n }\n\n /**\n * Live-форматирование строки во время набора: целая часть группируется по\n * разрядам (`#groupSep`), дробная остаётся как набрана (хвостовая запятая и\n * нули сохраняются, чтобы их можно было дописать). Разделителем дробной\n * части считается только то, что им может быть в этой локали\n * (`#decimalSepPattern`), — иначе запятая-разряды в `en-US` читалась бы как\n * десятичная. Разделитель допускается всегда — округление по\n * `maxFractionDigits` происходит на blur\n * (поэтому в целочисленном поле «1000,5» парсится и округлится, а не склеится\n * в «10005»). Группировка — линейным проходом, а не `Number()`, чтобы не\n * терять точность на суммах длиннее 2^53.\n */\n #groupDisplay(raw: string): string {\n const negative: boolean = raw.trimStart().startsWith('-');\n\n let sep: string = '';\n let fracDigits: string = '';\n let intDigits: string;\n\n const sepIndex: number = raw.search(this.#decimalSepPattern);\n if (sepIndex >= 0) {\n sep = raw.charAt(sepIndex);\n intDigits = raw.slice(0, sepIndex).replace(/\\D/g, '');\n fracDigits = raw.slice(sepIndex + 1).replace(/\\D/g, '');\n } else {\n intDigits = raw.replace(/\\D/g, '');\n }\n\n // Ведущие нули убираем, но сохраняем один «0» перед десятичным разделителем.\n intDigits = intDigits.replace(/^0+(?=\\d)/, '');\n\n let intPart: string;\n if (intDigits === '') {\n intPart = sep === '' ? '' : '0';\n } else {\n // Набор идёт тем же видом, что и показ на blur: иначе разделители\n // появлялись бы при наборе и пропадали после ухода из поля.\n intPart = this.grouped() ? this.#groupInteger(intDigits) : intDigits;\n }\n\n let out: string = negative ? `-${intPart}` : intPart;\n if (sep !== '') {\n out += sep + fracDigits;\n }\n return out;\n }\n\n /**\n * Группирует строку цифр по три справа налево, вставляя `#groupSep`. Без\n * регэкспа намеренно — `(\\d{3})+`-lookahead флагуется как ReDoS-уязвимый, а\n * линейный проход и быстрее, и безопасен.\n */\n #groupInteger(digits: string): string {\n const length: number = digits.length;\n let out: string = '';\n for (let i: number = 0; i < length; i++) {\n if (i > 0 && (length - i) % 3 === 0) {\n out += this.#groupSep;\n }\n out += digits.charAt(i);\n }\n return out;\n }\n\n /**\n * Пересчитывает позицию каретки после вставки разделителей разрядов: считает\n * значимые символы (цифры и десятичный разделитель — не group-separator)\n * слева от старой каретки и находит ту же точку в новой строке. Иначе курсор\n * прыгал бы в конец при каждом вставленном пробеле.\n */\n #restoreCaret(raw: string, caret: number, formatted: string): number {\n const isAnchor: (ch: string) => boolean = (ch: string): boolean => /[\\d.,]/.test(ch);\n\n let need: number = 0;\n for (let i: number = 0; i < caret && i < raw.length; i++) {\n if (isAnchor(raw.charAt(i))) {\n need++;\n }\n }\n if (need === 0) {\n return formatted.startsWith('-') ? 1 : 0;\n }\n\n let seen: number = 0;\n let pos: number = 0;\n while (pos < formatted.length) {\n if (isAnchor(formatted.charAt(pos))) {\n seen++;\n if (seen === need) {\n pos++;\n break;\n }\n }\n pos++;\n }\n return pos;\n }\n}\n","<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-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</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-input-number.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.ts","../../../projects/ui-kit-v2/src/lib/components/input-number/rt-input-number.component.html"],"sourcesContent":["import { BooleanInput } from '@angular/cdk/coercion';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ElementRef,\n forwardRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n LOCALE_ID,\n signal,\n Signal,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\n\nconst BEM_BLOCK: string = 'rt-input-number';\n\n/**\n * Парсит строку в `number | null` по разделителям локали: `groupSep` и любые\n * пробелы удаляются, оставшийся разделитель дробной части приводится к точке.\n *\n * Разбор обязан знать локаль, иначе он расходится с показом: в `en-US` разряды\n * группируются запятой, и набранное `1000` показывалось как `1,000`, а в форму\n * уходила `1` — безусловная замена первой запятой на точку читала разделитель\n * разрядов как десятичный.\n *\n * Пустая строка / NaN → null.\n */\nfunction parseNumber(text: string, groupSep: string): number | null {\n const withoutSpaces: string = text.replace(/\\s/g, '');\n // Пробельный разделитель разрядов (ru) уже снят предыдущей строкой.\n const withoutGroups: string = groupSep.trim() === '' ? withoutSpaces : withoutSpaces.split(groupSep).join('');\n const cleaned: string = withoutGroups.replace(/,/g, '.');\n if (cleaned === '') {\n return null;\n }\n const parsed: number = parseFloat(cleaned);\n return Number.isNaN(parsed) ? null : parsed;\n}\n\n/**\n * Клампит `value` в диапазон `[min, max]`. `null` границы игнорируются —\n * соответствующая сторона остаётся неограниченной.\n */\nfunction clamp(value: number, min: number | null, max: number | null): number {\n let result: number = value;\n if (min !== null && result < min) {\n result = min;\n }\n if (max !== null && result > max) {\n result = max;\n }\n return result;\n}\n\n/**\n * Форматирует `number | null` в строку с группировкой разрядов (неразрывный\n * пробел) и заданным fractionDigits diapason. `value === null` → \"\".\n * `maxFractionDigits` ограничивается сверху 20 (лимит Intl). Итоговый\n * precision = `max(min, min(max, 20))` и фиксирует одинаковую нижнюю/верхнюю\n * границу дробной части (как прежний `toFixed`), меняется только группировка\n * и десятичный разделитель — их даёт `locale`.\n *\n * `grouped === false` снимает разделители разрядов: у года, номера дома и кода\n * разряды значения не несут, и `2026` показывалось как `2 026`.\n */\nfunction formatNumber(\n value: number | null,\n minFractionDigits: number,\n maxFractionDigits: number,\n locale: string,\n grouped: boolean\n): string {\n if (value === null) {\n return '';\n }\n const cappedMax: number = Math.min(maxFractionDigits, 20);\n const precision: number = Math.min(20, Math.max(minFractionDigits, cappedMax));\n return value.toLocaleString(locale, {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n useGrouping: grouped,\n });\n}\n\n/**\n * Numeric input с parse/format/clamp logic. Использует native\n * `<input type=\"text\" inputmode=\"decimal\">` (НЕ `type=\"number\"` — избегаем\n * browser spinner quirks: scroll-wheel value changes, locale-specific decimal\n * separators, inconsistent step rounding).\n *\n * CVA через общий `RtFormControlBase` — авто-подсветка invalid, `clearable`\n * (крестик при непустом display). Parser принимает `,`/`.`, невалидный ввод\n * (NaN) не эмитится во время набора; на blur невалидное → null, валидное —\n * clamp + format.\n *\n * Визуально и по токенам идентичен rt-input (`--rt-input-*`): host — box,\n * `<input>` — прозрачный `__field`.\n */\n@Component({\n selector: 'rt-input-number',\n templateUrl: './rt-input-number.component.html',\n styleUrl: './rt-input-number.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtInputNumberComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-input-number--disabled]': 'isDisabled()',\n '[class.rt-input-number--invalid]': 'isInvalid()',\n '[class.rt-input-number--readonly]': 'isReadonly()',\n '[class.rt-input-number--borderless]': '!bordered()',\n '[class.rt-input-number--appearance--fill]': \"appearance() === 'fill'\",\n '[class.rt-input-number--appearance--pill]': \"appearance() === 'pill'\",\n '[class.rt-input-number--size--sm]': \"size() === 'sm'\",\n '[class.rt-input-number--size--lg]': \"size() === 'lg'\",\n '(mousedown)': 'onHostMousedown($event)',\n },\n})\nexport class RtInputNumberComponent extends RtFormControlBase<number | null> {\n readonly #locale: string = inject(LOCALE_ID);\n\n // Символ группировки разрядов из локали (для ru — неразрывный пробел). Берём\n // тот же, что отдаёт toLocaleString на blur — тогда live и blur выглядят\n // одинаково, а parseNumber (стрипает \\s) парсит обратно обе формы.\n readonly #groupSep: string = (1000).toLocaleString(this.#locale).replace(/\\d/g, '') || ' ';\n\n /**\n * Что в этой локали можно набрать как разделитель дробной части: всё из\n * «.,», кроме того, чем локаль разделяет разряды. В `ru` разряды разделяет\n * неразрывный пробел, поэтому годятся оба знака; в `en-US` запятая — это\n * разряды, и десятичной остаётся только точка. Иначе набранное `1,5`\n * значило бы в показе одно, а в разборе другое.\n */\n readonly #decimalSepPattern: RegExp = new RegExp(`[${['.', ','].filter((char: string): boolean => char !== this.#groupSep).join('')}]`);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly fieldEl: Signal<ElementRef<HTMLInputElement> | undefined> = viewChild<ElementRef<HTMLInputElement>>('fieldEl');\n\n protected readonly displayValue: WritableSignal<string> = signal<string>('');\n\n // Крестик показываем только при непустом и ненулевом значении: 0 — это и\n // есть результат очистки, поэтому при нём кнопка сброса не нужна.\n protected readonly hasValue: Signal<boolean> = computed(\n (): boolean => this.displayValue() !== '' && this.value() !== null && this.value() !== 0\n );\n\n /** Вид рамки: `outline` — рамка со всех сторон, `fill` — залитое поле с чертой снизу. */\n public override readonly fieldLabelInside: boolean = true;\n public readonly appearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n\n public readonly displayText: Signal<string> = computed((): string => this.displayValue());\n\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n /** Текстовый префикс слева от значения (напр. «₽» для рублёвых сумм). */\n public readonly prefix: InputSignal<string | null> = input<string | null>(null);\n public readonly placeholder: InputSignal<string> = input<string>('');\n public readonly min: InputSignal<number | null> = input<number | null>(null);\n public readonly max: InputSignal<number | null> = input<number | null>(null);\n public readonly minFractionDigits: InputSignal<number> = input<number>(0);\n public readonly maxFractionDigits: InputSignal<number> = input<number>(2);\n /**\n * Группировать ли разряды. Снимается у чисел, которые разрядами не читаются:\n * год, номер дома, код. У года разделитель превращает `2026` в `2 026`.\n */\n public readonly grouped: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(true, {\n transform: booleanAttribute,\n });\n\n public override writeValue(value: number | null): void {\n super.writeValue(value);\n this.displayValue.set(formatNumber(this.value(), this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected getEmptyValue(): number | null {\n return null;\n }\n\n protected focusAfterClear(): void {\n this.fieldEl()?.nativeElement.focus();\n }\n\n /**\n * Падинг/гэп рамки принадлежат host-контейнеру, а не `<input>`, поэтому клик\n * по этой зоне сам по себе не фокусирует поле. Перехватываем mousedown и\n * переводим фокус в input — но только если клик не пришёлся на само поле\n * (там нужно нативное позиционирование курсора) и не на интерактивный\n * постфикс (крестик-очистка со своим обработчиком).\n */\n protected onHostMousedown(event: MouseEvent): void {\n if (this.isDisabled()) {\n return;\n }\n const field: HTMLInputElement | undefined = this.fieldEl()?.nativeElement;\n const target: HTMLElement = event.target as HTMLElement;\n if (!field || target === field || target.closest('button, rt-icon-button')) {\n return;\n }\n event.preventDefault();\n field.focus();\n }\n\n /**\n * Крестик сбрасывает поле не в пустое/null, а в 0 — отформатированный по\n * текущему fractionDigits (например `0,00`). Числовые поля всегда имеют\n * осмысленный ноль, поэтому «очистка» = обнуление, а не пустое значение.\n */\n protected override clearValue(): void {\n this.value.set(0);\n this.displayValue.set(formatNumber(0, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped()));\n }\n\n protected onInput(event: Event): void {\n const target: HTMLInputElement = event.target as HTMLInputElement;\n const raw: string = target.value;\n const caret: number = target.selectionStart ?? raw.length;\n\n // Live-группировка разрядов прямо во время набора (1000000 → 1 000 000).\n const formatted: string = this.#groupDisplay(raw);\n const nextCaret: number = this.#restoreCaret(raw, caret, formatted);\n\n // Пишем value императивно ДО Angular CD: тогда повторная запись биндинга\n // `[value]` будет той же строкой и не сбросит каретку в конец. Затем\n // восстанавливаем позицию по числу значимых символов слева от курсора.\n target.value = formatted;\n target.setSelectionRange(nextCaret, nextCaret);\n this.displayValue.set(formatted);\n\n const parsed: number | null = parseNumber(formatted, this.#groupSep);\n if (parsed === null) {\n // Только реально пустой ввод → emit null (пользователь очистил поле).\n // Нечисловой ввод стрипается из отображения, но prev value держится до\n // blur — поэтому проверяем исходный `raw`, а не очищенный `formatted`.\n if (raw.trim() === '') {\n this.value.set(null);\n this.emitChange(null);\n }\n return;\n }\n this.value.set(parsed);\n this.emitChange(parsed);\n }\n\n protected onBlur(): void {\n const parsed: number | null = parseNumber(this.displayValue(), this.#groupSep);\n if (parsed === null) {\n this.value.set(null);\n this.displayValue.set('');\n this.emitChange(null);\n this.markTouched();\n return;\n }\n const clamped: number = clamp(parsed, this.min(), this.max());\n const formatted: string = formatNumber(clamped, this.minFractionDigits(), this.maxFractionDigits(), this.#locale, this.grouped());\n // `formatted` содержит разделители локали — обратный парс через\n // parseNumber (а не parseFloat) даёт округлённое числовое значение.\n const rounded: number = parseNumber(formatted, this.#groupSep) ?? clamped;\n this.value.set(rounded);\n this.displayValue.set(formatted);\n this.emitChange(rounded);\n this.markTouched();\n }\n\n /**\n * Live-форматирование строки во время набора: целая часть группируется по\n * разрядам (`#groupSep`), дробная остаётся как набрана (хвостовая запятая и\n * нули сохраняются, чтобы их можно было дописать). Разделителем дробной\n * части считается только то, что им может быть в этой локали\n * (`#decimalSepPattern`), — иначе запятая-разряды в `en-US` читалась бы как\n * десятичная. Разделитель допускается всегда — округление по\n * `maxFractionDigits` происходит на blur\n * (поэтому в целочисленном поле «1000,5» парсится и округлится, а не склеится\n * в «10005»). Группировка — линейным проходом, а не `Number()`, чтобы не\n * терять точность на суммах длиннее 2^53.\n */\n #groupDisplay(raw: string): string {\n const negative: boolean = raw.trimStart().startsWith('-');\n\n let sep: string = '';\n let fracDigits: string = '';\n let intDigits: string;\n\n const sepIndex: number = raw.search(this.#decimalSepPattern);\n if (sepIndex >= 0) {\n sep = raw.charAt(sepIndex);\n intDigits = raw.slice(0, sepIndex).replace(/\\D/g, '');\n fracDigits = raw.slice(sepIndex + 1).replace(/\\D/g, '');\n } else {\n intDigits = raw.replace(/\\D/g, '');\n }\n\n // Ведущие нули убираем, но сохраняем один «0» перед десятичным разделителем.\n intDigits = intDigits.replace(/^0+(?=\\d)/, '');\n\n let intPart: string;\n if (intDigits === '') {\n intPart = sep === '' ? '' : '0';\n } else {\n // Набор идёт тем же видом, что и показ на blur: иначе разделители\n // появлялись бы при наборе и пропадали после ухода из поля.\n intPart = this.grouped() ? this.#groupInteger(intDigits) : intDigits;\n }\n\n let out: string = negative ? `-${intPart}` : intPart;\n if (sep !== '') {\n out += sep + fracDigits;\n }\n return out;\n }\n\n /**\n * Группирует строку цифр по три справа налево, вставляя `#groupSep`. Без\n * регэкспа намеренно — `(\\d{3})+`-lookahead флагуется как ReDoS-уязвимый, а\n * линейный проход и быстрее, и безопасен.\n */\n #groupInteger(digits: string): string {\n const length: number = digits.length;\n let out: string = '';\n for (let i: number = 0; i < length; i++) {\n if (i > 0 && (length - i) % 3 === 0) {\n out += this.#groupSep;\n }\n out += digits.charAt(i);\n }\n return out;\n }\n\n /**\n * Пересчитывает позицию каретки после вставки разделителей разрядов: считает\n * значимые символы (цифры и десятичный разделитель — не group-separator)\n * слева от старой каретки и находит ту же точку в новой строке. Иначе курсор\n * прыгал бы в конец при каждом вставленном пробеле.\n */\n #restoreCaret(raw: string, caret: number, formatted: string): number {\n const isAnchor: (ch: string) => boolean = (ch: string): boolean => /[\\d.,]/.test(ch);\n\n let need: number = 0;\n for (let i: number = 0; i < caret && i < raw.length; i++) {\n if (isAnchor(raw.charAt(i))) {\n need++;\n }\n }\n if (need === 0) {\n return formatted.startsWith('-') ? 1 : 0;\n }\n\n let seen: number = 0;\n let pos: number = 0;\n while (pos < formatted.length) {\n if (isAnchor(formatted.charAt(pos))) {\n seen++;\n if (seen === need) {\n pos++;\n break;\n }\n }\n pos++;\n }\n return pos;\n }\n}\n","<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '—' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-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</ng-container>\n"],"mappings":"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"}